引言
最近我的主站编程时光证书过期了,导致用户访问网站时出错,后面续上免费的证书,由于我是使用的免费的证书,所以证书的有效期只有一年。为了避免证书过期导致网站无法访问,我决定写一个小程序来自动检查证书的过期时间,并在证书快过期时通知我及时更换证书。
自动巡检SSL证书过期时间
我们首先定义了一个checkCertificates
函数,该函数的主要任务是读取一个名为domain.txt
的文件,该文件中列出了需要检查的域名和对应的IP地址。
func checkCertificates() { file, err := os.Open("domain.txt") if err != nil { panic(err) } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() parts := strings.Split(line, ":") domain := parts[0] ipPool := strings.Split(strings.ReplaceAll(parts[1], ",", " "), " ") // ipPool [43.138.235.240] fmt.Println("ipPool", ipPool) for _, ip := range ipPool { conn, err := tls.Dial("tcp", ip+":443", &tls.Config{ ServerName: domain, InsecureSkipVerify: true, // We're only checking expiration, not validity }) if err != nil { fmt.Printf("Error! %s\n", ip) fmt.Println(domain) continue } defer conn.Close() cert := conn.ConnectionState().PeerCertificates[0] endDate := cert.NotAfter currentTime := time.Now() remainingDays := endDate.Sub(currentTime).Hours() / 24 fmt.Printf("ip: %s\ndomain: %s\n", ip, domain) sendEmail("SSL证书过期提醒", fmt.Sprintf("The certificate for domain %s (IP: %s) will expire in less than %f days!", domain, ip, remainingDays)) if remainingDays < 7 && remainingDays >= 0 { fmt.Println("剩余时间小于七天!请及时更换证书!") fmt.Printf("ip: %s, %s\n", ip, domain) } else if remainingDays < 0 { fmt.Println("证书已过期!请及时更换证书!") } else { fmt.Printf("剩余天数为:%f\n", remainingDays) } } } if err := scanner.Err(); err != nil { panic(err) } }
邮件通知
当我们知道了证书的剩余有效期后,下一步是通知相关人员。在
sendEmail
函数中,我们使用了gomail
库来发送邮件。
func sendEmail(subject, body string) { from := "linwu.hi@gmail.com" pass := "xxx" to := "linwu.hi@gmail.com" m := gomail.NewMessage() m.SetHeader("From", from) m.SetHeader("To", to) m.SetHeader("Subject", subject) m.SetBody("text/plain", body) d := gomail.NewDialer("smtp.gmail.com", 587, from, pass) if err := d.DialAndSend(m); err != nil { log.Fatal(err) } }
定时任务
为了确保我们能够及时地检测到证书的状态,我们需要定期执行上述的检查任务。在
main
函数中,我们使用了cron
库来创建一个定时任务。
func main() { c := cron.New(cron.WithSeconds()) c.AddFunc("0 0 0 * * ?", checkCertificates) // 每日凌晨0点执行, 检查证书过期时间,通知到老板 c.Start() select {} // 阻止主goroutine退出 }
完整代码
package main import ( "bufio" "crypto/tls" "fmt" gomail "gopkg.in/gomail.v2" "log" "os" "strings" "time" ) func sendEmail(subject, body string) { from := "linwu.hi@gmail.com" pass := "lbqhjuvomebvwtox" to := "linwu.hi@gmail.com" m := gomail.NewMessage() m.SetHeader("From", from) m.SetHeader("To", to) m.SetHeader("Subject", subject) m.SetBody("text/plain", body) d := gomail.NewDialer("smtp.gmail.com", 587, from, pass) // Send the email if err := d.DialAndSend(m); err != nil { log.Fatal(err) } } func checkCertificates() { file, err := os.Open("domain.txt") if err != nil { panic(err) } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() parts := strings.Split(line, ":") domain := parts[0] ipPool := strings.Split(strings.ReplaceAll(parts[1], ",", " "), " ") // ipPool [43.138.235.240] fmt.Println("ipPool", ipPool) for _, ip := range ipPool { conn, err := tls.Dial("tcp", ip+":443", &tls.Config{ ServerName: domain, InsecureSkipVerify: true, // We're only checking expiration, not validity }) if err != nil { fmt.Printf("Error! %s\n", ip) fmt.Println(domain) continue } defer conn.Close() cert := conn.ConnectionState().PeerCertificates[0] endDate := cert.NotAfter currentTime := time.Now() remainingDays := endDate.Sub(currentTime).Hours() / 24 fmt.Printf("ip: %s\ndomain: %s\n", ip, domain) sendEmail("SSL证书过期提醒", fmt.Sprintf("The certificate for domain %s (IP: %s) will expire in less than %f days!", domain, ip, remainingDays)) if remainingDays < 7 && remainingDays >= 0 { fmt.Println("剩余时间小于七天!请及时更换证书!") fmt.Printf("ip: %s, %s\n", ip, domain) } else if remainingDays < 0 { fmt.Println("证书已过期!请及时更换证书!") } else { fmt.Printf("剩余天数为:%f\n", remainingDays) } } } if err := scanner.Err(); err != nil { panic(err) } } func main() { c := cron.New(cron.WithSeconds()) // 创建一个新的cron实例 c.AddFunc("0 0 0 * * ?", checkCertificates) // 每日凌晨0点执行 c.Start() // 开始执行定时任务 select {} // 阻止主goroutine退出 }