// 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. Javaweb__Jquery

    今日内容 1. JQuery 基础: 1. 概念 2. 快速入门 3. JQuery对象和JS对象区别与转换 4. 选择器 5. DOM操作 6. 案例 JQuery 基础: 1. 概念: 一个Jav ...

  2. KingbaseES图形化安装未弹出界面应该如何处理

      关键字: KingbaseES.X Windows.Gnome.DISPLAY 一.Linux下图形安装环境要求 1) 系统首先安装了X Windows的图形化支持软件包. 2) 系统安装了KDE ...

  3. Taurus.MVC-Java 版本打包上传到Maven中央仓库(详细过程):1、JIRA账号注册

    文章目录: Taurus.MVC-Java 版本打包上传到Maven中央仓库(详细过程):1.JIRA账号注册 Taurus.MVC-Java 版本打包上传到Maven中央仓库(详细过程):2.PGP ...

  4. 使用pktmon抓包

    在Windows上遇到网络问题,需要抓包的时候之前我们会使用netmon和Microsoft Message Analyzer.随着时间的推移,微软已经停止了对Microsoft Message An ...

  5. Django 之视图层

    JsonResponse 1 json格式的数据有什么用 前后端数据交互需要使用json作为过渡,实现跨语言传输数据 2 前后端方法对应 JSON.stringify() -  json.dumps( ...

  6. .NET Core Web APi类库如何内嵌运行?

    话题 我们知道在.NET Framework中可以嵌入运行Web APi,那么在.NET Core(.NET 6+称之为.NET)中如何内嵌运行Web Api呢,在实际项目中这种场景非常常见,那么我们 ...

  7. nginx配置文件中location的三个匹配规则定义

    #直接匹配网站根,通过域名访问网站首页比较频繁,使用这个会加速处理,官网如是说. #这里是直接转发给后端应用服务器了,也可以是一个静态首页 # 第一个必选规则 location = / { #prox ...

  8. PostgreSQL 选择数据库

    数据库的命令窗口 PostgreSQL 命令窗口中,我们可以命令提示符后面输入 SQL 语句: postgres=# 使用 \l 用于查看已经存在的数据库: postgres=# \l List of ...

  9. 19. Fluentd输入插件:in_http用法详解

    in_http插件允许使用HTTP协议来采集日志事件.这个插件会建立一个支持REST风格的HTTP端点,来接收日志事件请求. 配置示例 <source> @type http port 9 ...

  10. AVX图像算法优化系列一: 初步接触AVX。

    弄了SSE指令集,必然会在不同的场合不同的人群中了解到还有更为高级的AVX指令集的存在,早些年也确实有偶尔写点AVX的函数,但是一直没有深入的去了解,今年十一期间也没到那里去玩,一个人在家里抽空就折腾 ...