feat: MVP phase 1 complete

This commit is contained in:
Blake Ridgway
2026-03-25 02:41:17 -05:00
parent 81ae5c6c7b
commit bfa03e6fbf
32 changed files with 3503 additions and 39 deletions

78
internal/ssl/checker.go Normal file
View File

@@ -0,0 +1,78 @@
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
}