如何使用工具自動監(jiān)測SSL證書有效期并發(fā)送提醒郵件
前言
自從云廠商的免費(fèi)ssl證書改成3個月,而且證書數(shù)量還是20個之后,自己網(wǎng)站的ssl證書就換成了其它免費(fèi)方案。但是免費(fèi)方案不會提醒證書過期,所以寫個工具每天定時查詢證書剩余有效天數(shù),如果證書即將過期,就發(fā)送郵件提醒。
基本實現(xiàn)
最基本的代碼功能就是檢測網(wǎng)站ssl證書的有效天數(shù),可以用命令行傳參的方式指定網(wǎng)站域名。
package main import ( "crypto/tls" "flag" "fmt" "net" "os" "sync" "time" ) var ( port int wg sync.WaitGroup ) func checkssl(domain string, port int) { defer wg.Done() host := fmt.Sprintf("%s:%d", domain, port) conn, err := tls.DialWithDialer(&net.Dialer{ Timeout: time.Second * 5, Deadline: time.Now().Add(time.Second * 5), }, "tcp", host, &tls.Config{InsecureSkipVerify: true}) if err != nil { fmt.Println(err) return } defer conn.Close() stats := conn.ConnectionState() certs := stats.PeerCertificates[0] localtz, _ := time.LoadLocation("Asia/Shanghai") issueTime := certs.NotBefore.In(localtz) expireTime := certs.NotAfter.In(localtz) today := time.Now().In(localtz) dayLeft := int(expireTime.Sub(today).Hours() / 24) fmt.Printf("%s, issue time: %v, expire time: %v, days left: %v\n", domain, issueTime, expireTime, dayLeft) } func main() { flag.IntVar(&port, "p", 443, "port, example: ./checkssl -p 1443 <domain name>") flag.Parse() positionArgs := flag.Args() if len(positionArgs) == 0 { fmt.Println("Error: Missing domain name") fmt.Println("Usage: ./checkssl <domain name>") os.Exit(1) } wg.Add(len(positionArgs)) for _, arg := range positionArgs { go checkssl(arg, port) } wg.Wait() }
使用示例
# 1. 編譯 go build # 2. 命令行傳參的方式指定域名 ./check-ssl baidu.com ithome.com qq.com # 輸出 baidu.com, issue time: 2024-01-30 08:00:00 +0800 CST, expire time: 2025-03-02 07:59:59 +0800 CST, days left: 187 ithome.com, issue time: 2024-01-22 08:00:00 +0800 CST, expire time: 2025-02-22 07:59:59 +0800 CST, days left: 179 qq.com, issue time: 2024-06-04 08:00:00 +0800 CST, expire time: 2025-06-11 07:59:59 +0800 CST, days left: 288
完善功能
需要完善的功能主要是發(fā)送郵件,這里使用SMTP協(xié)議來發(fā)送郵件。如果跟我一樣用的是163郵箱,則需要先去獲取一個SMTP的授權(quán)碼。
因為需要配置SMTP的連接信息,所以改成了用文件來傳入配置,也方便后期修改。配置文件config.yaml
示例:
domains: - baidu.com - qq.com email: smtp: host: "smtp.163.com" # smtp服務(wù)器的地址 port: 465 # 因為云服務(wù)器屏蔽了25端口, 只能使用tls加密的465端口 from: "" # 發(fā)送方郵箱 token: "" # 授權(quán)碼 sendto: - "qq@qq.com" # 接收方的郵箱地址 expire: 7 # 證書剩余有效天數(shù), 小于7天時發(fā)送郵件提醒
讀取配置的代碼文件config.go
,使用viper
來讀取配置文件。
package main import "github.com/spf13/viper" var ( v *viper.Viper ) type SMTPServer struct { Host string Port int Token string From string } func initViper() { v = viper.New() v.AddConfigPath(".") v.SetConfigType("yaml") v.SetConfigFile(configfile) err := v.ReadInConfig() if err != nil { panic(err) } } type configer struct{} func NewConfiger() configer { if v == nil { initViper() } return configer{} } func (c configer) GetSMTPServer() SMTPServer { return SMTPServer{ Host: v.GetString("email.smtp.host"), Port: v.GetInt("email.smtp.port"), Token: v.GetString("email.smtp.token"), From: v.GetString("email.smtp.from"), } } func (c configer) GetDomains() []string { return v.GetStringSlice("domains") } func (c configer) GetSendTos() []string { return v.GetStringSlice("email.sendto") } func (c configer) GetExpiry() int { return v.GetInt("email.expire") }
發(fā)送郵件的相關(guān)代碼文件:notify.go
package main import ( "crypto/tls" "fmt" "net/smtp" "github.com/jordan-wright/email" ) type Postman struct { SmtpServer SMTPServer SendTos []string } func (p Postman) SendEmail(domain string, dayleft int) { auth := smtp.PlainAuth("", p.SmtpServer.From, p.SmtpServer.Token, p.SmtpServer.Host) e := &email.Email{ To: p.SendTos, From: fmt.Sprintf("YXHYW <%s>", p.SmtpServer.From), Subject: fmt.Sprintf("域名 %s SSL證書過期提醒", domain), Text: []byte(fmt.Sprintf("域名 %s 的SSL證書即將過期, 剩余有效期 %d 天", domain, dayleft)), } // err := e.Send(fmt.Sprintf("%s:%d", p.SmtpServer.Host, p.SmtpServer.Port), auth) addr := fmt.Sprintf("%s:%d", p.SmtpServer.Host, p.SmtpServer.Port) fmt.Println("SMTP Server addr: ", addr) err := e.SendWithTLS(addr, auth, &tls.Config{ InsecureSkipVerify: false, ServerName: p.SmtpServer.Host, }) if err != nil { fmt.Printf("Send email failed, %v\n", err) } }
主體代碼文件main.go
,主要修改地方:檢測到證書即將過期后,調(diào)用發(fā)送郵件的相關(guān)方法。
package main import ( "crypto/tls" "flag" "fmt" "net" "sync" "time" ) var ( port int configfile string wg sync.WaitGroup c configer = NewConfiger() ) func checkssl(domain string, port int) { defer wg.Done() host := fmt.Sprintf("%s:%d", domain, port) conn, err := tls.DialWithDialer(&net.Dialer{ Timeout: time.Second * 5, Deadline: time.Now().Add(time.Second * 5), }, "tcp", host, &tls.Config{InsecureSkipVerify: true}) if err != nil { fmt.Println(err) return } defer conn.Close() stats := conn.ConnectionState() certs := stats.PeerCertificates[0] localtz, _ := time.LoadLocation("Asia/Shanghai") issueTime := certs.NotBefore.In(localtz) expireTime := certs.NotAfter.In(localtz) today := time.Now().In(localtz) dayLeft := int(expireTime.Sub(today).Hours() / 24) fmt.Printf("%s, issue time: %v, expire time: %v, days left: %v\n", domain, issueTime, expireTime, dayLeft) // c := NewConfiger() if dayLeft < c.GetExpiry() { p := Postman{SmtpServer: c.GetSMTPServer(), SendTos: c.GetSendTos()} p.SendEmail(domain, dayLeft) } } func main() { flag.IntVar(&port, "p", 443, "port, example: ./check-ssl -p 1443 <domain name>") flag.StringVar(&configfile, "c", "config.yaml", "config file") flag.Parse() conf := NewConfiger() domains := conf.GetDomains() wg.Add(len(domains)) for _, arg := range domains { go checkssl(arg, port) } wg.Wait() }
本地測試通過后,可以配到服務(wù)器的crontab
中每天執(zhí)行。
到此這篇關(guān)于如何使用工具自動監(jiān)測SSL證書有效期并發(fā)送提醒郵件的文章就介紹到這了,更多相關(guān)[golang]查詢ssl證書剩余有效天數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang小數(shù)操作指南之判斷小數(shù)點位數(shù)與四舍五入
這篇文章主要給大家介紹了關(guān)于Golang小數(shù)操作指南之判斷小數(shù)點位數(shù)與四舍五入的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2022-03-03golang bad file descriptor問題的解決方法
這篇文章主要給大家介紹了golang bad file descriptor問題的解決方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02golang中按照結(jié)構(gòu)體的某個字段排序?qū)嵗a
在任何編程語言中,關(guān)乎到數(shù)據(jù)的排序都會有對應(yīng)的策略,下面這篇文章主要給大家介紹了關(guān)于golang中按照結(jié)構(gòu)體的某個字段排序的相關(guān)資料,需要的朋友可以參考下2022-05-05詳解golang channel有無緩沖區(qū)的區(qū)別
這篇文章主要給大家介紹了golang channel有無緩沖區(qū)的區(qū)別,無緩沖是同步的,有緩沖是異步的,文中通過代碼示例給大家講解的非常詳細(xì),需要的朋友可以參考下2024-01-01Qt6.5 grpc組件使用 + golang grpc server
這篇文章主要介紹了Qt6.5 grpc組件使用+golang grpc server示例,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-05-05