Initial import of lib + certificate validity checker
This commit is contained in:
commit
66a1b017be
6 changed files with 403 additions and 0 deletions
cmd/check_ssl_certificate
122
cmd/check_ssl_certificate/main.go
Normal file
122
cmd/check_ssl_certificate/main.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"nocternity.net/monitoring/perfdata"
|
||||
"nocternity.net/monitoring/plugin"
|
||||
)
|
||||
|
||||
type cliFlags struct {
|
||||
hostname string
|
||||
port int
|
||||
warn int
|
||||
crit int
|
||||
ignoreCnOnly bool
|
||||
}
|
||||
|
||||
func handleCli(p *plugin.Plugin) (flags *cliFlags) {
|
||||
flags = &cliFlags{}
|
||||
flag.StringVar(&flags.hostname, "hostname", "", "Host name to connect to.")
|
||||
flag.StringVar(&flags.hostname, "H", "", "Host name to connect to (shorthand).")
|
||||
flag.IntVar(&flags.port, "port", -1, "Port to connect to.")
|
||||
flag.IntVar(&flags.port, "P", -1, "Port to connect to (shorthand).")
|
||||
flag.IntVar(&flags.warn, "warning", -1, "Validity threshold below which a warning state is issued, in days.")
|
||||
flag.IntVar(&flags.warn, "W", -1, "Validity threshold below which a warning state is issued, in days (shorthand).")
|
||||
flag.IntVar(&flags.crit, "critical", -1, "Validity threshold below which a critical state is issued, in days.")
|
||||
flag.IntVar(&flags.crit, "C", -1, "Validity threshold below which a critical state is issued, in days (shorthand).")
|
||||
flag.BoolVar(&flags.ignoreCnOnly, "ignore-cn-only", false,
|
||||
"Do not issue warnings regarding certificates that do not use SANs at all.")
|
||||
flag.Parse()
|
||||
return
|
||||
}
|
||||
|
||||
func checkFlags(p *plugin.Plugin, flags *cliFlags) bool {
|
||||
if flags.hostname == "" {
|
||||
p.SetState(plugin.UNKNOWN, "no hostname specified")
|
||||
return false
|
||||
}
|
||||
if flags.port < 1 || flags.port > 65535 {
|
||||
p.SetState(plugin.UNKNOWN, "invalid or missing port number")
|
||||
return false
|
||||
}
|
||||
if flags.warn != -1 && flags.crit != -1 && flags.warn <= flags.crit {
|
||||
p.SetState(plugin.UNKNOWN, "nonsensical thresholds")
|
||||
return false
|
||||
}
|
||||
flags.hostname = strings.ToLower(flags.hostname)
|
||||
return true
|
||||
}
|
||||
|
||||
func findHostname(cert *x509.Certificate, hostname string) bool {
|
||||
for _, name := range cert.DNSNames {
|
||||
if strings.ToLower(name) == hostname {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func main() {
|
||||
p := plugin.New("Certificate check")
|
||||
defer p.Done()
|
||||
flags := handleCli(p)
|
||||
if !checkFlags(p, flags) {
|
||||
return
|
||||
}
|
||||
|
||||
tls_cfg := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
MinVersion: tls.VersionTLS10,
|
||||
}
|
||||
tls_conn, tls_err := tls.Dial("tcp", fmt.Sprintf("%s:%d", flags.hostname, flags.port), tls_cfg)
|
||||
if tls_err != nil {
|
||||
p.SetState(plugin.UNKNOWN, fmt.Sprintf("connection failed: %s", tls_err))
|
||||
return
|
||||
}
|
||||
defer tls_conn.Close()
|
||||
if hsk_err := tls_conn.Handshake(); hsk_err != nil {
|
||||
p.SetState(plugin.UNKNOWN, fmt.Sprintf("handshake failed: %s", hsk_err))
|
||||
return
|
||||
}
|
||||
certificate := tls_conn.ConnectionState().PeerCertificates[0]
|
||||
|
||||
if len(certificate.DNSNames) == 0 {
|
||||
if !flags.ignoreCnOnly {
|
||||
p.SetState(plugin.WARNING, "certificate doesn't have SAN domain names")
|
||||
return
|
||||
}
|
||||
dn := strings.ToLower(certificate.Subject.String())
|
||||
if !strings.HasPrefix(dn, fmt.Sprintf("cn=%s,", flags.hostname)) {
|
||||
p.SetState(plugin.ERROR, "incorrect certificate CN")
|
||||
return
|
||||
}
|
||||
} else if !findHostname(certificate, flags.hostname) {
|
||||
p.SetState(plugin.ERROR, "host name not found in SAN domain names")
|
||||
return
|
||||
}
|
||||
timeLeft := certificate.NotAfter.Sub(time.Now())
|
||||
tlDays := int((timeLeft + 86399*time.Second) / (24 * time.Hour))
|
||||
if flags.crit > 0 && tlDays <= flags.crit {
|
||||
p.SetState(plugin.ERROR, fmt.Sprintf("certificate will expire in %d days (<= %d)", tlDays, flags.crit))
|
||||
} else if flags.warn > 0 && tlDays <= flags.warn {
|
||||
p.SetState(plugin.WARNING, fmt.Sprintf("certificate will expire in %d days (<= %d)", tlDays, flags.warn))
|
||||
} else {
|
||||
p.SetState(plugin.OK, fmt.Sprintf("certificate will expire in %d days", tlDays))
|
||||
}
|
||||
|
||||
var pdat perfdata.PerfData
|
||||
pdat = perfdata.New("validity", perfdata.UOM_NONE, fmt.Sprintf("%d", tlDays))
|
||||
if flags.crit > 0 {
|
||||
pdat.SetCrit(perfdata.PDRMax(fmt.Sprint(flags.crit)))
|
||||
}
|
||||
if flags.warn > 0 {
|
||||
pdat.SetWarn(perfdata.PDRMax(fmt.Sprint(flags.warn)))
|
||||
}
|
||||
p.AddPerfData(pdat)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue