Golang ssl 证书 验证

代码平平无奇,需要注意的是http client初始化部分。

因为是短连接,如果是大量域名去验证的话,短时间内将导致大量TIME_WAIT

go 复制代码
package util

import (
	"context"
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/panjf2000/ants/v2"
	log "github.com/sirupsen/logrus"
)

var client = &http.Client{
	Transport: &http.Transport{
		// 注意如果证书已过期,那么只有在关闭证书校验的情况下链接才能建立成功
		TLSClientConfig:   &tls.Config{InsecureSkipVerify: true},
		Proxy:             http.ProxyFromEnvironment,
		DisableKeepAlives: true,
		DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
			d := net.Dialer{Timeout: 10 * time.Second}
			conn, err := d.DialContext(ctx, network, addr)
			if err != nil {
				return nil, err
			}
			tcpConn, ok := conn.(*net.TCPConn)
			if ok {
				tcpConn.SetLinger(0) // RST fix TIME_WAIT问题
				return tcpConn, nil
			}
			return conn, nil
		},
	},
}

func BatchCheckSsl(domains []string, batch int) (map[string]CheckSslResp, error) {
	domains = Uniq(domains)
	var domainSyncMap sync.Map
	var wg sync.WaitGroup
	pool, err := ants.NewPool(batch)
	if err != nil {
		log.Errorln(err)
		return nil, err
	}
	defer pool.Release()
	wg.Add(len(domains))
	for _, item := range domains {
		err = pool.Submit(wrapperCheckSSL(item, &domainSyncMap, &wg))
		if err != nil {
			log.Errorln(err)
			continue
		}
	}
	wg.Wait()
	fmt.Printf("running goroutines: %d\n", ants.Running())
	fmt.Printf("finish all tasks.\n")

	result := make(map[string]CheckSslResp, 0)
	domainSyncMap.Range(func(key, value interface{}) bool {
		vv, ok := value.(*CheckSslResp)
		if !ok {
			log.Println(fmt.Sprintf("CheckSslResp类型转换失败:%s", key))
			return false
		}
		result[fmt.Sprintf("%v", key)] = *vv
		return true
	})
	return result, nil
}

func wrapperCheckSSL(hostname string, domainSslMap *sync.Map, wg *sync.WaitGroup) func() {
	return func() {
		checkSsl(hostname, domainSslMap, wg)
	}
}

func checkSsl(hostname string, domainSslMap *sync.Map, wg *sync.WaitGroup) {
	defer wg.Done()

	log.Println(fmt.Sprintf("开始检查:%s", hostname))
	notAfter, err := CheckSSL(hostname)
	if err != nil {
		log.Errorln(err)
	}
	log.Println(fmt.Sprintf("完成检查:%s,notAfter:%v:", hostname, notAfter))
	ri := new(CheckSslResp)
	ri.Domain = hostname
	if err != nil {
		ri.Success = false
		ri.Error = err
		ri.NotAfter = nil
	} else {
		ri.Success = true
		ri.NotAfter = notAfter
	}
	domainSslMap.Store(hostname, ri)
	return
}

func CheckSSL(url string) (*time.Time, error) {
	//client := &http.Client{
	//	Transport: &http.Transport{
	//		// 注意如果证书已过期,那么只有在关闭证书校验的情况下链接才能建立成功
	//		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	//	},
	//	// 10s 超时后认为服务挂了
	//	Timeout: 10 * time.Second,
	//}

	url = strings.ReplaceAll(url, "http://", "https://")
	if !strings.HasPrefix(url, "https://") {
		url = fmt.Sprintf("https://%s", url)
	}
	log.Println(url)
	resp, err := client.Get(url)
	if err != nil {
		log.Errorln(err)
		return nil, err
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.TLS == nil {
		return nil, errors.New("未获取到SSL信息")
	}
	// 遍历所有证书
	for _, cert := range resp.TLS.PeerCertificates {
		if cert.Issuer.CommonName == "Kubernetes Ingress Controller Fake Certificate" {
			return nil, errors.New("Kubernetes Ingress Controller Fake Certificate")
		}
		// 检测证书是否已经过期
		if !cert.NotAfter.After(time.Now()) {
			log.Warningln(fmt.Sprintf("Website [%s] certificate has expired: %s", url, cert.NotAfter.Local().Format("2006-01-02 15:04:05")))
		}
		return &cert.NotAfter, nil
	}
	return nil, err
}

type CheckSslResp struct {
	Domain   string
	Error    error
	Success  bool
	NotAfter *time.Time
}
相关推荐
tekin21 分钟前
Go、Java、Python、C/C++、PHP、Rust 语言全方位对比分析
java·c++·golang·编程语言对比·python 语言·php 语言·编程适用场景
anddddoooo8 小时前
域内证书维权
服务器·网络·网络协议·安全·网络安全·https·ssl
zhoupenghui1689 小时前
golang时间相关函数总结
服务器·前端·golang·time
孤雪心殇9 小时前
简单易懂,解析Go语言中的Map
开发语言·数据结构·后端·golang·go
Nerd Nirvana9 小时前
OpenSSL crt & key (生成一套用于TLS双向认证的证书密钥)
linux·ssl·shell·认证·加密·tls·oepnssl
闲猫11 小时前
go 反射 interface{} 判断类型 获取值 设置值 指针才可以设置值
开发语言·后端·golang·反射
Ciderw13 小时前
LLVM编译器简介
c++·golang·编译·编译器·gcc·llvm·基础设施
朗迹 - 张伟16 小时前
Golang连接使用SqlCipher
开发语言·后端·golang
闲猫18 小时前
go 网络编程 websocket gorilla/websocket
开发语言·websocket·golang
Ciderw18 小时前
MySQL日志undo log、redo log和binlog详解
数据库·c++·redis·后端·mysql·面试·golang