SoFunction
Updated on 2025-03-02

How to use tools to automatically monitor the validity period of SSL certificates and send reminder emails

Preface

Since the free ssl certificate of cloud manufacturers has been changed to 3 months, and the number of certificates is still 20, the ssl certificate of its own website has been replaced with other free solutions. However, the free plan will not remind the certificate to expire, so write a tool to check the remaining valid days of the certificate every day. If the certificate is about to expire, send an email reminder.

Basic implementation

The most basic code function is to detect the valid number of days of the website SSL certificate. You can specify the website domain name by passing parameters 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)
	}
	()
}

Example of usage

# 1. Compilationgo build
# 2. Specify the domain name by passing parameters on the command line./check-ssl   

#Output, 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 functions

The main function that needs to be improved is to send emails, and the SMTP protocol is used here to send emails. If you use 163 email like me, you need to get an SMTP authorization code first.

Because SMTP connection information needs to be configured, it has been changed to using files to pass in the configuration, which is also convenient for later modification. Configuration FileExample:

domains:
  - 
  - 

email:
  smtp:
    host: "smtp."  # smtp server address    port: 465  # Because the cloud server blocks port 25, it can only use port 465 encrypted by TLS    from: ""   # Sender's email    token: ""  # Authorization code  sendto:
    - "qq@"  # The recipient's email address  expire: 7  # The number of valid days remaining for the certificate, send an email reminder if it is less than 7 days

Read the configuration code file,useviperto 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 ("")
}

Related code files for 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 := &amp;{
		To:      ,
		From:    ("YXHYW &lt;%s&gt;", ),
		Subject: ("Domain name %s SSL certificate expiration reminder", domain),
		Text:    []byte(("The SSL certificate for domain name %s is about to expire, and the remaining validity period is %d days", domain, dayleft)),
	}
	// err := (("%s:%d", , ), auth)
	addr := ("%s:%d", , )
	("SMTP Server addr: ", addr)
	err := (addr, auth, &amp;{
		InsecureSkipVerify: false,
		ServerName:         ,
	})
	if err != nil {
		("Send email failed, %v\n", err)
	}
}

Body code file, Main modification: after detecting that the certificate is about to expire, call the relevant method of sending the email.

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 is passed, it can be configured to the server.crontabPerform every day.

This is the article about how to use tools to automatically monitor the validity period of SSL certificates and send reminder emails. For more related [golang] to query the remaining valid days of SSL certificates, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!