广告位联系
返回顶部
分享到

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

Golang 来源:互联网 作者:佚名 发布时间:2024-10-02 12:16:21 人浏览
摘要

自从云厂商的免费ssl证书改成3个月,而且证书数量还是20个之后,自己网站的ssl证书就换成了其它免费方案。但是免费方案不会提醒证书过期,所以写个工具每天定时查询证书剩余有效天数,

自从云厂商的免费ssl证书改成3个月,而且证书数量还是20个之后,自己网站的ssl证书就换成了其它免费方案。但是免费方案不会提醒证书过期,所以写个工具每天定时查询证书剩余有效天数,如果证书即将过期,就发送邮件提醒。

基本实现

最基本的代码功能就是检测网站ssl证书的有效天数,可以用命令行传参的方式指定网站域名。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

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

2

3

4

5

6

7

8

9

# 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示例:

1

2

3

4

5

6

7

8

9

10

11

12

13

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来读取配置文件。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

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,主要修改地方:检测到证书即将过期后,调用发送邮件的相关方法。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

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中每天执行。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。

您可能感兴趣的文章 :

原文链接 :
    Tag :
相关文章
  • Go语言中的switch高级用法介绍
    最近翻开源代码的时候看到了一种很有意思的switch用法,分享一下。 注意这里讨论的不是typed switch,也就是case语句后面是类型的那种。 直
  • Go语言sync.Map介绍及使用场景
    在 Go 语言中,sync.Map是一个并发安全的映射结构,专门用于在高并发场景下处理键值对数据。它的并发安全是通过内部实现机制来保证的,
  • 怎么使用工具自动监测SSL证书有效期并发送提醒
    自从云厂商的免费ssl证书改成3个月,而且证书数量还是20个之后,自己网站的ssl证书就换成了其它免费方案。但是免费方案不会提醒证书过
  • Go语言字符串处理库strings包介绍

    Go语言字符串处理库strings包介绍
    Golang字符串处理库strings 在Go语言中,strings包是一个非常重要且功能丰富的标准库,它提供了一系列用于操作字符串的函数。从基本的字符串
  • Go语言中的指针的使用介绍
    1、Go 语言中指针的介绍 1.1、什么是指针? 指针是一种变量,它存储了另一个变量的内存地址。在 Go 中,你可以通过取地址操作符获取变量
  • Go环境变量配置,及GOROOT、GOPATH的区别

    Go环境变量配置,及GOROOT、GOPATH的区别
    一、安装Go go下载地址: https://golang.google.cn/dl/ windows下载安装,有两种方式。解压和直接安装 方式一:直接下载安装包。以.msi结尾的文件。
  • golang interface指针实现
    在 Go 语言中,接口(interface)是一种类型,它定义了一组方法的集合。任何实现了接口中所有方法的类型都会自动满足该接口。当涉及到指针
  • Go语言等待组sync.WaitGrou的使用
    Go语言中除了可以使用通道(channel)和互斥锁进行两个并发程序间的同步外,还可以使用等待组进行多个任务的同步,等待组可以保证在并
  • Go语言中的接口类型介绍
    接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。 1.接口类型 1.1 接口类型的说明 Go语言中
  • Go语言占位符的使用介绍
    Golang的字符串占位符在fmt包的各种打印函数中使用,如fmt.Printf、fmt.Sprintf。 变量值与类型的打印 %v: 打印变量的值 %v会根据变量的类型选择
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计