// Copyright (c) 2021. Huawei Technologies Co., Ltd. All rights reserved.

// Package common this file define WebCertUtil
package common

import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"github.com/gin-gonic/gin"
"huawei.com/npu-exporter/hwlog"
"huawei.com/npu-exporter/limiter"
"huawei.com/npu-exporter/utils"
"io/ioutil"
"net/http"
"strconv"
"strings"
"time"
)

const keyLength = 4096

var webCert *WebCertUtil

// WebCertUtil WebCertUtil
type WebCertUtil struct {
*CertificateUtils
}

// NewWebCertUtil NewWebCertUtil
func NewWebCertUtil(dnsName, dirName string, overdueTime int) *WebCertUtil {
ws := &WebCertUtil{
CertificateUtils: NewCertificateUtils(dnsName, dirName, overdueTime),
}
if err := utils.MakeSureDir(ws.KeyStore); err != nil {
hwlog.RunLog.Fatal(err)
}

return ws
}

// GetWebCertUtil return webCert
func GetWebCertUtil() *WebCertUtil {
return webCert
}

// CheckCertValidityPeriod CheckCertValidityPeriod
func (ws *WebCertUtil) CheckCertValidityPeriod(interval time.Duration) {
hwlog.RunLog.Info("start cert period check...")
ticker := time.NewTicker(interval)
defer ticker.Stop()
hasTask := false
for {
_, ok := <-ticker.C
if !ok {
return
}
if hasTask {
continue
}
ws.Lock.RLock()
if ws.Cert == nil {
ws.Lock.RUnlock()
continue
}
x509Cert, err := x509.ParseCertificate(ws.Cert.Certificate[0])
if err != nil {
ws.Lock.RUnlock()
continue
}
err = utils.CheckValidityPeriodWithError(x509Cert, ws.OverdueTime)
ws.Lock.RUnlock()
if err == nil {
continue
}
hwlog.RunLog.Warn(err)
go func() {
hasTask = true
defer func() {
hasTask = false
hwlog.RunLog.Info("apply for a new certificate success")
}()
hwlog.RunLog.Info("apply for a new certificate because the certificate has expired")
if err = ws.ApplyForCertificateByRequest(); err != nil {
hwlog.RunLog.Error(err)
}
}()
}
}

// GetCaByRequest get ca by request
func (ws *WebCertUtil) GetCaByRequest() ([]byte, error) {
caByte, err := DefaultClsMgrClient.GetRootCertificate()
if err != nil {
return nil, err
}
hwlog.RunLog.Info("succeeded in getting ca certificate by request")
if err = ioutil.WriteFile(ws.CaStore, caByte, utils.RWMode); err != nil {
return nil, errors.New("write caBytes to file failed")
}
hwlog.RunLog.Info("succeeded in saving ca certificate to file")
ws.CaBytes = caByte
return caByte, nil
}

// ApplyForCertificateByRequest apply for certificate by request
func (ws *WebCertUtil) ApplyForCertificateByRequest() error {
certPem, err := DefaultClsMgrClient.ApplyForCertificatePemByte(ws.DNSName, ws.CaBytes, ws.PrivateKey)
if err != nil {
return err
}
hwlog.RunLog.Info("succeeded in getting certificate by request")

derStream := x509.MarshalPKCS1PrivateKey(ws.PrivateKey)
block := &pem.Block{
Type: RSAPrivateKey,
Bytes: derStream,
}
keyPem := pem.EncodeToMemory(block)
tlsCert, err := utils.ValidateCertPair(certPem, keyPem, false, ws.OverdueTime)
if err != nil {
return err
}
if err = ioutil.WriteFile(ws.CertStore, certPem, utils.RWMode); err != nil {
return errors.New("write certBytes to file failed ")
}
hwlog.RunLog.Info("succeeded in saving certificate to file")

ws.Lock.RLock()
defer ws.Lock.RUnlock()

ws.refreshCertStatus(tlsCert)
ws.Cert = tlsCert
return nil
}

func (ws *WebCertUtil) refreshCertStatus(tlsCert *tls.Certificate) {
// clear cert status record
ws.ClearCertificateMap()

caCert, err := utils.LoadCertsFromPEM(ws.CaBytes)
if err == nil {
// refresh ca record
err = utils.AddToCertStatusTrace(caCert)
}
if err != nil {
hwlog.RunLog.Error("prepare refresh ca info failed: " + err.Error())
}

x509Cert, err := x509.ParseCertificate(tlsCert.Certificate[0])
if err == nil {
// refresh certificate record
err = utils.AddToCertStatusTrace(x509Cert)
}
if err != nil {
hwlog.RunLog.Error("prepare refresh certificate info failed: " + err.Error())
}
hwlog.RunLog.Info("succeeded in refresh ca/cert info")
}

// SetHTTPSServer set http server config for one or two way auth
func (ws *WebCertUtil) SetHTTPSServer(s *http.Server, concurrency, maxConcurrency int,
cipherSuites []uint16, ginObj *gin.Engine) error {
tlsConfig, err := utils.NewTLSConfigV2(ws.CaBytes, *ws.Cert, cipherSuites)
if err != nil {
return err
}

tlsConfig.Certificates = nil
tlsConfig.GetCertificate = ws.GetCertificateFunc()
s.Handler = limiter.NewLimitHandler(concurrency, maxConcurrency, utils.Interceptor(ginObj, ws.CrlList), false)
s.TLSConfig = tlsConfig

return nil
}

// ReApplyCertificate reapply certificate
func (ws *WebCertUtil) ReApplyCertificate(algorithm int) error {
hwlog.RunLog.Info("prepare to reapply a new certificate")
if ws.PrivateKey == nil {
if _, err := ws.GenerateRSAPrivateKey(keyLength, algorithm); err != nil {
return err
}
}
if err := ws.ApplyForCertificateByRequest(); err != nil {
return err
}

return nil
}

// GetRequestClient get http client for request
func (ws *WebCertUtil) GetRequestClient(authMode string) (*http.Client, error) {
// https auth two way
if authMode == TwoWay {
requestClient, err := ws.GetTwoWayAuthRequestClient()
if err != nil {
return nil, err
}

return requestClient, nil
}

// https auth one way
pool, err := x509.SystemCertPool()
if err != nil {
return nil, fmt.Errorf("cannot get system trusted certificates pool: %w", err)
}
if !pool.AppendCertsFromPEM(ws.CaBytes) {
return nil, fmt.Errorf("failed to append to certificates pool")
}
c := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: pool,
},
},
}

return c, nil
}

// InitCert init cert and ca
func InitCert(sp *ServerParam) error {
ws := NewWebCertUtil(sp.DNSName, sp.DirName, sp.OverdueTime)
var reApply bool
var caBytes []byte
// load ca
_, err := ws.LoadCAOnStart(sp.AuthMode)
if err != nil && strings.Contains(err.Error(), ReApply) {
reApply = true
}
// reapply ca
if reApply {
caBytes, err = ws.GetCaByRequest()
if len(caBytes) == 0 || err != nil {
return err
}
reApply = false
}
if err != nil {
return err
}
// load cert and private key
_, err = ws.LoadCertAndKeyOnStart(sp.EncryptAlgorithm)
if err != nil && strings.Contains(err.Error(), ReApply) {
reApply = true
}
// reapply cert
if reApply {
if err = ws.ReApplyCertificate(sp.EncryptAlgorithm); err != nil {
return err
}
}
if err != nil {
return err
}

// init global WebCertUtil
webCert = ws
return nil
}

// StartServerListen StartServerListen
func StartServerListen(sp *ServerParam, engine *gin.Engine) {
s := &http.Server{
ReadTimeout: sp.ReadTimeOut * time.Second,
WriteTimeout: sp.WriteTimeOut * time.Second,
Addr: sp.IP + ":" + strconv.Itoa(sp.Port),
Handler: limiter.NewLimitHandler(sp.Concurrency, sp.MaxConcurrency, engine, false),
}
// http
if sp.EnableHTTP {
// http
hwlog.RunLog.Warn("Service started with an insecure http server enabled")
if err := s.ListenAndServe(); err != nil {
hwlog.RunLog.Error("Http server error and stopped")
}
return
}

// init web cert util
err := InitCert(sp)
if err != nil {
hwlog.RunLog.Error(err)
return
}
ParseTLSParams(sp)
wc := GetWebCertUtil()
if err = wc.SetHTTPSServer(s, sp.Concurrency, sp.MaxConcurrency, sp.CipherSuites,
engine); err != nil {
hwlog.RunLog.Error(err)
return
}
hwlog.RunLog.Info("start https server now...")
// start certificate period check
go wc.CheckCertValidityPeriod(sp.CheckCertPeriod)

if err = s.ListenAndServeTLS("", ""); err != nil {
hwlog.RunLog.Error("Https server error by: %s and stopped", err.Error())
}
}

mindxdl--common--web_cert_utils.go的更多相关文章

  1. Socket聊天程序——Common

    写在前面: 上一篇记录了Socket聊天程序的客户端设计,为了记录的完整性,这里还是将Socket聊天的最后一个模块--Common模块记录一下.Common的设计如下: 功能说明: Common模块 ...

  2. angularjs 1 开发简单案例(包含common.js,service.js,controller.js,page)

    common.js var app = angular.module('app', ['ngFileUpload']) .factory('SV_Common', function ($http) { ...

  3. Common Bugs in C Programming

    There are some Common Bugs in C Programming. Most of the contents are directly from or modified from ...

  4. ANSI Common Lisp Practice - My Answers - Chatper - 3

    Ok, Go ahead. 1 (a) (b) (c) (d) 2 注:union 在 Common Lisp 中的作用就是求两个集合的并集.但是这有一个前提,即给的两个列表已经满足集合的属性了.具体 ...

  5. [LeetCode] Lowest Common Ancestor of a Binary Tree 二叉树的最小共同父节点

    Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree. According ...

  6. [LeetCode] Lowest Common Ancestor of a Binary Search Tree 二叉搜索树的最小共同父节点

    Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BS ...

  7. [LeetCode] Longest Common Prefix 最长共同前缀

    Write a function to find the longest common prefix string amongst an array of strings. 这道题让我们求一系列字符串 ...

  8. 48. 二叉树两结点的最低共同父结点(3种变种情况)[Get lowest common ancestor of binary tree]

    [题目] 输入二叉树中的两个结点,输出这两个结点在数中最低的共同父结点. 二叉树的结点定义如下:  C++ Code  123456   struct BinaryTreeNode {     int ...

  9. 动态规划求最长公共子序列(Longest Common Subsequence, LCS)

    1. 问题描述 子串应该比较好理解,至于什么是子序列,这里给出一个例子:有两个母串 cnblogs belong 比如序列bo, bg, lg在母串cnblogs与belong中都出现过并且出现顺序与 ...

  10. 【leetcode】Longest Common Prefix

    题目简述: Write a function to find the longest common prefix string amongst an array of strings. 解题思路: c ...

随机推荐

  1. Linux虚拟机 RHEL8.0安装步骤

    一. 创建空白虚拟机 1.打开 VMware 虚拟机软件依次选择新建虚拟机并选择选择"自定义" 自定义功能更加全面,典型就是比较简单的配置 2.选择对应的 VMware 版本,此则 ...

  2. git cherry-pick 总结

    git cherry-pick cherry-pick : 精心挑选,挑选一个我们需要的 commit 进行操作.它可以用于将在其他分支上的 commit 移植到当前的分支. 用法: // 复制com ...

  3. Java开发学习(三十一)----Maven属性与版本管理

    一.属性 1.1 问题分析 如下图所示 你会发现,如果现在想更新Spring的版本,你会发现依然需要更新多个jar包的版本,这样的话还是有可能出现漏改导致程序出问题,而且改起来也是比较麻烦. 问题清楚 ...

  4. 【SQLServer】max worker threads参数配置

    查看和设置max worker threads USE master; //选中你想设置max worker threads的数据库.master表示在实例级别进行设置 GO EXEC sp_conf ...

  5. RabbitMQ之消息模式简单易懂,超详细分享~~~

    前言 上一篇对RabbitMQ的流程和相关的理论进行初步的概述,如果小伙伴之前对消息队列不是很了解,那么在看理论时会有些困惑,这里以消息模式为切入点,结合理论细节和代码实践的方式一起来学习. 正文 常 ...

  6. 02-MyBatisPlus入门

    快速开始参考:https://baomidou.com/pages/226c21/ 测试项目: mybatis_plus 数据库:mybatis_plus 一.创建并初始化数据库 1.创建数据库: m ...

  7. 如何使用Arthas定位问题

    在我们日常的工作中,经常会遇到一些线上才会遇到的问题.Arthas无疑是我们在工作中,定位线上问题的神奇.下面,我将介绍一下我们在工作中经常用到的一些功能. dashboard 首先我们可以通过das ...

  8. HTTPS安全加固配置最佳实践指南

    转载自:https://www.bilibili.com/read/cv16067729?spm_id_from=333.999.0.0 0x02 HTTPS安全加固指南 描述: 当你的网站上了 HT ...

  9. 在 Kubernetes 上运行高可用的 Kafka 集群

    转载自:https://www.qikqiak.com/post/deploy-kafka-ha-on-k8s/ Apache Kafka 是目前最流行的分布式消息发布订阅系统,虽然 Kafka 非常 ...

  10. Rook Toolbox

    官方文档:https://rook.io/docs/rook/v1.8/ceph-toolbox.html Rook工具箱是一个包含用于Rook调试和测试的常用工具的容器.工具箱基于CentOS,因此 ...