preamble
Since the cloud vendor's free ssl certificates were changed to 3 months, and the number of certificates is still 20, the ssl certificates of my own website were changed to other free programs. But the free program will not remind the certificate expiration, so write a tool to check the certificate every day at regular intervals the number of days remaining valid, if the certificate is about to expire, send an e-mail reminder.
fundamental realization
The most basic code function is to detect the number of days a site's ssl certificate is valid, and you can specify the site's domain name by passing a parameter on the command line.
package main
import (
"crypto/tls"
"flag"
"fmt"
"net"
"os"
"sync"
"time"
)
var (
port int
wg
)
func checkssl(domain string, port int) {
defer ()
host := ("%s:%d", domain, port)
conn, err := (&{
Timeout: * 5,
Deadline: ().Add( * 5),
}, "tcp", host, &{InsecureSkipVerify: true})
if err != nil {
(err)
return
}
defer ()
stats := ()
certs := [0]
localtz, _ := ("Asia/Shanghai")
issueTime := (localtz)
expireTime := (localtz)
today := ().In(localtz)
dayLeft := int((today).Hours() / 24)
("%s, issue time: %v, expire time: %v, days left: %v\n", domain, issueTime, expireTime, dayLeft)
}
func main() {
(&port, "p", 443, "port, example: ./checkssl -p 1443 <domain name>")
()
positionArgs := ()
if len(positionArgs) == 0 {
("Error: Missing domain name")
("Usage: ./checkssl <domain name>")
(1)
}
(len(positionArgs))
for _, arg := range positionArgs {
go checkssl(arg, port)
}
()
}
usage example
# 1. compiling
go build
# 2. Specify the domain name by passing a parameter on the command line
./check-ssl
# exports
, issue time: 2024-01-30 08:00:00 +0800 CST, expire time: 2025-03-02 07:59:59 +0800 CST, days left: 187
, issue time: 2024-01-22 08:00:00 +0800 CST, expire time: 2025-02-22 07:59:59 +0800 CST, days left: 179
, issue time: 2024-06-04 08:00:00 +0800 CST, expire time: 2025-06-11 07:59:59 +0800 CST, days left: 288
improve functionality
The main feature that needs to be perfected is to send emails, here we use SMTP protocol to send emails. If you are using 163 email like me, you need to get an SMTP authorization code first.
Because you need to configure the SMTP connection information, you have changed to use a file to pass in the configuration, which is also easy to modify later. Configuration fileExample:
domains.
-
-
email: - - - - - - - - - - - - - - - - - - - - - -
smtp.
host: "smtp." # address of smtp server
port: 465 # Because the cloud server blocks port 25, only tls-encrypted port 465 can be used.
from: "" # Sender's email address
token: "" # Authorization code
sendto.
- "qq@" # Receiver's e-mail address
expire: 7 # Remaining valid days of certificate, send email reminder if less than 7 days.
Read the configured code fileUse
viper
to read the configuration file.
package main
import "/spf13/viper"
var (
v *
)
type SMTPServer struct {
Host string
Port int
Token string
From string
}
func initViper() {
v = ()
(".")
("yaml")
(configfile)
err := ()
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: (""),
Port: (""),
Token: (""),
From: (""),
}
}
func (c configer) GetDomains() []string {
return ("domains")
}
func (c configer) GetSendTos() []string {
return ("")
}
func (c configer) GetExpiry() int {
return ("")
}
Code files related to sending emails:
package main
import (
"crypto/tls"
"fmt"
"net/smtp"
"/jordan-wright/email"
)
type Postman struct {
SmtpServer SMTPServer
SendTos []string
}
func (p Postman) SendEmail(domain string, dayleft int) {
auth := ("", , , )
e := &{
To: ,
From: ("YXHYW <%s>", ),
Subject: ("domain name %s SSLCertificate expiration reminder", domain),
Text: []byte(("domain name %s (used form a nominal expression)SSLCertificates are about to expire, Remaining validity period %d sky", domain, dayleft)),
}
// err := (("%s:%d", , ), auth)
addr := ("%s:%d", , )
("SMTP Server addr: ", addr)
err := (addr, auth, &{
InsecureSkipVerify: false,
ServerName: ,
})
if err != nil {
("Send email failed, %v\n", err)
}
}
Master Code FileThe main modification is that the method for sending emails is called after detecting that the certificate is about to expire.
package main
import (
"crypto/tls"
"flag"
"fmt"
"net"
"sync"
"time"
)
var (
port int
configfile string
wg
c configer = NewConfiger()
)
func checkssl(domain string, port int) {
defer ()
host := ("%s:%d", domain, port)
conn, err := (&{
Timeout: * 5,
Deadline: ().Add( * 5),
}, "tcp", host, &{InsecureSkipVerify: true})
if err != nil {
(err)
return
}
defer ()
stats := ()
certs := [0]
localtz, _ := ("Asia/Shanghai")
issueTime := (localtz)
expireTime := (localtz)
today := ().In(localtz)
dayLeft := int((today).Hours() / 24)
("%s, issue time: %v, expire time: %v, days left: %v\n", domain, issueTime, expireTime, dayLeft)
// c := NewConfiger()
if dayLeft < () {
p := Postman{SmtpServer: (), SendTos: ()}
(domain, dayLeft)
}
}
func main() {
(&port, "p", 443, "port, example: ./check-ssl -p 1443 <domain name>")
(&configfile, "c", "", "config file")
()
conf := NewConfiger()
domains := ()
(len(domains))
for _, arg := range domains {
go checkssl(arg, port)
}
()
}
After the local test passes, it can be dispatched to the server'scrontab
Implemented on a daily basis.