当前位置: 代码网 > it编程>前端脚本>Golang > 如何使用工具自动监测SSL证书有效期并发送提醒邮件

如何使用工具自动监测SSL证书有效期并发送提醒邮件

2024年11月03日 Golang 我要评论
前言自从云厂商的免费ssl证书改成3个月,而且证书数量还是20个之后,自己网站的ssl证书就换成了其它免费方案。但是免费方案不会提醒证书过期,所以写个工具每天定时查询证书剩余有效天数,如果证书即将过期

前言

自从云厂商的免费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中每天执行。

到此这篇关于如何使用工具自动监测ssl证书有效期并发送提醒邮件的文章就介绍到这了,更多相关[golang]查询ssl证书剩余有效天数内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

  • 使用Go语言实现简单聊天系统

    使用Go语言实现简单聊天系统

    在互联网时代,聊天系统是常见的应用场景之一。无论是即时通讯、在线客服还是多人游戏中的消息系统,聊天功能的实现都是必不可少的。本文将使用 go 语言,结合 web... [阅读全文]
  • Go语言字符串处理库strings包详解

    golang字符串处理库strings在go语言中,strings包是一个非常重要且功能丰富的标准库,它提供了一系列用于操作字符串的函数。从基本的字符串查找、替换、分割到高级的比较…

    2024年11月03日 前端脚本
  • go语言中的数组指针和指针数组的区别小结

    1.介绍大家知道c语言之所以强大,就是因为c语言支持指针,而且权限特别大,c语言可以对计算机中任何内存的指针进行操作,这样自然而然也会带来一些不安全的因素,所以在golang中,「…

    2024年11月03日 前端脚本
  • golang实现枚举的几种方式

    golang实现枚举的几种方式

    在 go 语言中并没有像其他语言那样内置的枚举类型,但我们可以通过一些方式来实现枚举的功能。本文将详细介绍在 go 语言中如何实现枚举,并提供代码示例。枚举的概... [阅读全文]
  • 深入理解Go语言的容器包

    深入理解Go语言的容器包

    在go语言中,container标准包为开发者提供了三个非常有用的数据结构:堆(heap)、链表(list)和环(ring)。这些数据结构的实现分别位于cont... [阅读全文]
  • golang实现RPC模块的示例

    golang实现RPC模块的示例

    引言rpc(remote procedure call)远程过程调用,它允许不同的进程在网络上进行通信,就像调用本地函数一样。在 go 语言中,实现 rpc 模... [阅读全文]

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com