Location>code7788 >text

[golang] Query ssl certificate remaining valid days and email alerts

Popularity:219 ℃/2024-08-28 23:33:39

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 fileUseviperto 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'scrontabImplemented on a daily basis.