媒介
自从云厂商的免费ssl证书改成3个月,而且证书数量还是20个之后,自己网站的ssl证书就换成了别的免费方案。但是免费方案不会提醒证书过期,所以写个工具每天定时查询证书剩余有效天数,如果证书即将过期,就发送邮件提醒。
基本实现
最基本的代码功能就是检测网站ssl证书的有效天数,可以用下令行传参的方式指定网站域名。- 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中每天执行。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |