利用GO快速扫描acme.sh证书过期数脚本

package main

import (
	"context"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"log"
	"net"
	"os"
	"path"
	"strings"
	"sync"
	"time"
)

func main() {
	root := os.Getenv("ROOT")
	if len(root) == 0 {
		// acme域名证书目录
		root = "/root/.acme.sh"
	}
	source := os.Getenv("SOURCE")
	if len(source) == 0 {
		// cname源地址
		source = "dns.xxx.cn."
	}
	dirs, err := os.ReadDir(root)
	if err != nil {
		log.Panicln(err)
	}

	output, err := os.OpenFile("output.txt", os.O_RDWR|os.O_CREATE, os.ModePerm)
	if err != nil {
		log.Panic(err)
	}

	defer output.Close()
	num := 0
	cnameErr := 0
	p := &sync.WaitGroup{}
	now := time.Now()
	for _, dir := range dirs {
		if dir.IsDir() && len(strings.Split(dir.Name(), ".")) > 2 {
			di, err := dir.Info()
			if err != nil {
				log.Fatal(err)
			}
			p.Add(1)
			go func() {
				data, err := os.ReadFile(path.Join(root, di.Name(), "fullchain.cer"))
				if err != nil {
					data, err = os.ReadFile(path.Join(root, di.Name(), fmt.Sprintf("%s.cer", di.Name())))
					if err != nil {
						log.Println(err.Error())
						p.Done()
						return
					}
				}
				pe, _ := pem.Decode(data)
				if pe == nil || len(pe.Bytes) == 0 {
					printStr := fmt.Sprintf("domain: %s; cert not vaild \r\n", di.Name())
					log.Println(printStr)
					_, _ = output.WriteString(printStr)
					p.Done()
					return
				}
				cer, err := x509.ParseCertificate(pe.Bytes)
				if err != nil {
					log.Panic(err)

				}
				if cer.NotAfter.Before(now) {
					cnameRes := cnameCheck(di.Name(), source)
					if !cnameRes {
						cnameErr += 1
					}
					printStr := fmt.Sprintf("domain: %s; cname: %s; expired: %s; \r\n", di.Name(), boolToStr(cnameRes), cer.NotAfter.String())
					log.Println(printStr)
					_, _ = output.WriteString(printStr)
					num += 1
					p.Done()
					return
				}

				// 即将过期
				if cer.NotAfter.Before(now.Add(time.Hour * 24 * 30)) {
					printStr := fmt.Sprintf("domain: %s; will expired: %s; \r\n", di.Name(), cer.NotAfter.String())
					log.Println(printStr)
					_, _ = output.WriteString(printStr)
					p.Done()
					return
				}
				
				p.Done()
			}()
		}
	}

	p.Wait()
	endStr := fmt.Sprintf("域名过期总数:%d; cname转移总数:%d", num, cnameErr)
	log.Printf(endStr)
	_, _ = output.WriteString(endStr)
}

func cnameCheck(domain string, source string) bool {

	ctx, _ := context.WithTimeout(context.Background(), time.Second*10)

	cname, err := net.DefaultResolver.LookupCNAME(ctx, domain)
	if err != nil {
		return false
	}
	return cname == source
	//client := &http.Client{
	//	Timeout: 10,
	//}
	//resp, err := client.Get(domain)
	//if err != nil {
	//	log.Println(err)
	//	return false
	//}
	//return resp.StatusCode >= 200 || resp.StatusCode <= 210
}

func boolToStr(b bool) string {
	if b {
		return "true"
	}
	return "false"
}


# golang 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×