From b12b412a892198cc3958b2632520589e19acdb5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Doma=C5=84ski?= Date: Thu, 25 Jul 2024 17:03:53 +0200 Subject: [PATCH] first commit --- .gitignore | 25 ++++++++++++++++++++++++ Readme.md | 0 certchecker.go | 36 ++++++++++++++++++++++++++++++++++ cmd/main.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 3 +++ 5 files changed, 116 insertions(+) create mode 100644 .gitignore create mode 100644 Readme.md create mode 100644 certchecker.go create mode 100644 cmd/main.go create mode 100644 go.mod diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f72f89 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work +go.work.sum + +# env file +.env diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..e69de29 diff --git a/certchecker.go b/certchecker.go new file mode 100644 index 0000000..6f82e7d --- /dev/null +++ b/certchecker.go @@ -0,0 +1,36 @@ +package certchecker + +import ( + "crypto/tls" + "fmt" + "time" +) + +// Check checks if the certificate of the domain expires in less than notifyExpirationDays +// and returns an error if it does +func Check(domain string, notifyExpirationDays int) error { + conn, err := tls.Dial("tcp", domain+":443", nil) + if err != nil { + return err + } + defer conn.Close() + + err = conn.Handshake() + if err != nil { + return err + } + + err = conn.VerifyHostname(domain) + if err != nil { + return err + } + + for _, certificate := range conn.ConnectionState().PeerCertificates { + expiresInDays := int(certificate.NotAfter.Sub(time.Now()).Hours() / 24) + if expiresInDays < notifyExpirationDays { + return fmt.Errorf("certificate expires in %d days", expiresInDays) + } + } + + return nil +} diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..e9a0622 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,52 @@ +package main + +import ( + "doman/certchecker" + "bufio" + "flag" + "fmt" + "os" + "sync" +) + +func main() { + notifyExpirationDays := *flag.Int("notify", 7, "Notify when the certificate expires in less than this number of days") + flag.Parse() + domains := flag.Args() + + if len(domains) == 0 { + stat, _ := os.Stdin.Stat() + scanner := bufio.NewScanner(os.Stdin) + + if (stat.Mode() & os.ModeCharDevice) == 0 { + for scanner.Scan() { + domains = append(domains, scanner.Text()) + } + } else { + fmt.Println("Enter a domain to check:") + scanner.Scan() + domains = append(domains, scanner.Text()) + } + + err := scanner.Err() + if err != nil { + fmt.Fprintln(os.Stderr, "reading standard input:", err) + } + } + + var wg sync.WaitGroup + defer wg.Wait() + + for _, domain := range domains { + wg.Add(1) + go func() { + err := certchecker.Check(domain, notifyExpirationDays) + if err != nil { + fmt.Printf("%s: %v\n", domain, err) + } else{ + fmt.Printf("%s: OK\n", domain) + } + wg.Done() + } () + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..cb54960 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module doman/certchecker + +go 1.22.5