[golang]查询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
中每天执行。
[golang]查询ssl证书剩余有效天数并邮件提醒的更多相关文章
- SSL证书/TLS证书是什么
https://blog.csdn.net/donghaixiaolongwang/article/details/79193695 A. SSL协议与TLS是什么?它们的功能是什么? 答:SSL(S ...
- 检查阿里云ssl证书到期情况
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2019-06-10 16:00 # @Author : Anthony.long # ...
- 为你的Android App实现自签名的 SSL 证书(转)
介绍 网络安全已成为大家最关心的问题. 如果你利用服务器存储客户资料, 那你应该考虑使用 SSL 加密客户跟服务器之间的通讯. 随着这几年手机应用迅速崛起. 黑客也开始向手机应用转移, 原因有下列3点 ...
- StartCom 申请 SSL 证书及 Nginx HTTPS 支持配置全攻略
来源:https://www.williamyao.com/index.php/archives/1397/ 前言 最近收到 StartCom 的邮件,数字证书即将过期,想到去年在 StartSSL ...
- 微信小程序免费SSL证书https、TLS版本问题的解决方案
微信小程序与第三方服务器通讯的域名5个必要条件1.一个已备案的域名,不是localhost.也不是127.0.0.1,域名不能加端口2.加ssl证书,也就是https://~~~4.HTTPS 服务器 ...
- Let’s encrypt 计划为网站提供免费的基本 SSL 证书,以加速互联网从 HTTP 向 HTTPS 过渡。
Let’s encrypt 计划为网站提供免费的基本 SSL 证书,以加速互联网从 HTTP 向 HTTPS 过渡. 该项目由Mozilla.Cisco.Akamai.IdenTrust.EFF 和密 ...
- 购买SSL证书到部署网站遇到的若干问题
作为一个菜鸟,对于SSL证书,我了解不多,只知道用了它网站更安全,所以这次使用SSL证书途中遇到了各方面的各种问题,到今天为止终于全部解决. 一.证书格式 前两天在那什么云上面买了个SSL证书,是Wo ...
- openSSL命令、PKI、CA、SSL证书原理
相关学习资料 http://baike.baidu.com/view/7615.htm?fr=aladdin http://www.ibm.com/developerworks/cn/security ...
- linux nginx 配置ssl证书访问
http://www.linuxidc.com/Linux/2013-08/88271.htm 一.什么是 SSL 证书,什么是 HTTPSSSL 证书是一种数字证书,它使用 Secure Socke ...
- SSL证书:Web加密使互联网更安全
在网络安全的圈子里,我们都会隔三差五的听到某某公司遭受恶意软件攻击.或大量的隐私信息被泄露等等事件,此类安全事件往往都有影响范围广.涉及用户众多等特点.不免让人质疑联网的世界越来越危险.然而,在安全专 ...
随机推荐
- 【深度学习 有效炼丹】多GPU使用教程, DP与DDP对比, ray多线程并行处理等 [GPU利用率低的分析]
️ 前言 更新日志: 20220404:新增一个DDP 加载模型时显存分布不均问题,见目录遇到的问题及解决处 主要是上次server12 被自己一个train 直接线程全部拉满了(没错 ... ser ...
- 如何在不能求逆的时候做子集卷积 exp(即便能求逆也比常见方法优雅)
为什么要求逆?正常做子集卷积 exp 的时候递推求 \(G=\exp(F)\) 的系数时要用. 什么情况下不能求逆?模 \(2^{64}\),或者压根不取模. 我们可能会想,算出来肯定除得尽啊,因为组 ...
- Qt中全局变量的定义和使用
全局变量的定义 现在需要将自定义的STRParameter变量定义为全局变量,就需要新建两个文件,分别命名为"global.h"和"global.cpp" 在g ...
- podman安装mysql容器
前言 mysql如果正式安装,卸载起来比较麻烦.如果是自己测试用的话,可以用podman拉取一个镜像来使用. 这里使用的是mysql5.7版本,对应的docker镜像是mysql:5.7 (如果拉取较 ...
- 构建基于Java Spring Boot和Uniapp的心理小程序:从零到一的完整指南
构建基于Java Spring Boot和Uniapp的心理小程序:从零到一的完整指南 前言 大家好,今天我们来聊聊如何使用Java Spring Boot和Uniapp构建一个心理小程序.这个项目不 ...
- odoo 通过Javascript显示或隐藏form自带按钮
实践环境 Odoo 14.0-20221212 (Community Edition) 需求描述 如下,根据条件对form视图自带按钮的显示.隐藏进行控制 代码实现 隐藏.显示编辑和创建按钮为例 od ...
- python virtualenv虚拟环境配置与使用
python virtualenv虚拟环境配置与使用 By:赖富玉 QQ:1033553122 概述 python开发过程中,我们可能需要同时开发多款应用,这些应用可能公用同一个版本的Python程序 ...
- 使用git(ee的一个小总结
我先把后端项目推进了gitee,后面为了方便又想把前端也放在同一个项目里,结果出问题了: 一开始是因为前端项目内包含了.git的文件(这个玩意还是隐藏文件,就是你在idea里其实是看不到他的),导致只 ...
- Linux网络问题排查
Linux网络问题排查 用于排查Linux系统的网络故障. 网络排查一般是有一定的思路和顺序的,其实排查的思路就是根据具体的问题逐段排除故障可能发生的地方,最终确定问题. 所以首先要问一问,网络问题是 ...
- argparse学习笔记
argparse是 Python 的一个内置模块,用于编写用户友好的命令行接口.使用 argparse,你可以很容易地为 Python 脚本添加参数解析功能,使得脚本可以接受命令行选项和参数.学起来也 ...