[golang]查询ssl证书剩余有效天数并邮件提醒

cnblogs 2024-08-29 08:39:00 阅读 54

使用go语言编写检测ssl证书剩余有效天数的工具,并邮件提醒。

前言

自从云厂商的免费ssl证书改成3个月,而且证书数量还是20个之后,自己网站的ssl证书就换成了其它免费方案。但是免费方案不会提醒证书过期,所以写个工具每天定时查询证书剩余有效天数,如果证书即将过期,就发送邮件提醒。

基本实现

最基本的代码功能就是检测网站ssl证书的有效天数,可以用命令行传参的方式指定网站域名。

<code>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

完善功能

需要完善的功能主要是发送邮件,这里使用SMTP协议来发送邮件。如果跟我一样用的是163邮箱,则需要先去获取一个SMTP的授权码。

因为需要配置SMTP的连接信息,所以改成了用文件来传入配置,也方便后期修改。配置文件config.yaml示例:

domains:

- baidu.com

- qq.com

email:

smtp:

host: "smtp.163.com" # smtp服务器的地址

port: 465 # 因为云服务器屏蔽了25端口, 只能使用tls加密的465端口

from: "" # 发送方邮箱

token: "" # 授权码

sendto:

- "qq@qq.com" # 接收方的邮箱地址

expire: 7 # 证书剩余有效天数, 小于7天时发送邮件提醒

读取配置的代码文件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")

}

发送邮件的相关代码文件: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,主要修改地方:检测到证书即将过期后,调用发送邮件的相关方法。

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()

}

本地测试通过后,可以配到服务器的crontab中每天执行。


上一篇: Java——递归

下一篇: 算法【Java】—— 二分查找

本文标签

golang   


声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。