Files
migrate/internal/ssl/checker.go
2026-03-25 02:41:17 -05:00

79 lines
1.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package ssl
import (
"crypto/tls"
"fmt"
"net"
"time"
)
// Result holds the outcome of a single cert check.
type Result struct {
Domain string
ExpiresAt time.Time
DaysRemaining int
IsValid bool
Error string
}
// Severity returns a CSS class name for the expiry status.
//
// "ok" — > 30 days
// "warn" — 1430 days
// "crit" — < 14 days or invalid
func (r Result) Severity() string {
if !r.IsValid {
return "crit"
}
switch {
case r.DaysRemaining > 30:
return "ok"
case r.DaysRemaining >= 14:
return "warn"
default:
return "crit"
}
}
// Check dials domain:443, retrieves the TLS certificate chain, and returns
// the expiry of the leaf certificate.
func Check(domain string) Result {
r := Result{Domain: domain}
conn, err := tls.DialWithDialer(
&net.Dialer{Timeout: 10 * time.Second},
"tcp",
net.JoinHostPort(domain, "443"),
&tls.Config{ServerName: domain},
)
if err != nil {
r.Error = fmt.Sprintf("tls dial: %s", err)
return r
}
defer conn.Close()
certs := conn.ConnectionState().PeerCertificates
if len(certs) == 0 {
r.Error = "no certificates in chain"
return r
}
leaf := certs[0]
now := time.Now()
r.ExpiresAt = leaf.NotAfter
r.DaysRemaining = int(leaf.NotAfter.Sub(now).Hours() / 24)
if now.Before(leaf.NotBefore) {
r.Error = fmt.Sprintf("certificate not yet valid (valid from %s)", leaf.NotBefore.Format("2006-01-02"))
return r
}
if now.After(leaf.NotAfter) {
r.Error = fmt.Sprintf("certificate expired %s", leaf.NotAfter.Format("2006-01-02"))
return r
}
r.IsValid = true
return r
}