cmd: start linting fixes.
This commit is contained in:
@@ -1,20 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"flag"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.wntrmute.dev/kyle/goutils/certlib"
|
||||
hosts "git.wntrmute.dev/kyle/goutils/certlib/hosts"
|
||||
"git.wntrmute.dev/kyle/goutils/certlib/revoke"
|
||||
"git.wntrmute.dev/kyle/goutils/fileutil"
|
||||
"git.wntrmute.dev/kyle/goutils/certlib"
|
||||
hosts "git.wntrmute.dev/kyle/goutils/certlib/hosts"
|
||||
"git.wntrmute.dev/kyle/goutils/certlib/revoke"
|
||||
"git.wntrmute.dev/kyle/goutils/fileutil"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -23,6 +23,13 @@ var (
|
||||
verbose bool
|
||||
)
|
||||
|
||||
var (
|
||||
strOK = "OK"
|
||||
strExpired = "EXPIRED"
|
||||
strRevoked = "REVOKED"
|
||||
strUnknown = "UNKNOWN"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.BoolVar(&hardfail, "hardfail", false, "treat revocation check failures as fatal")
|
||||
flag.DurationVar(&timeout, "timeout", 10*time.Second, "network timeout for OCSP/CRL fetches and TLS site connects")
|
||||
@@ -42,16 +49,16 @@ func main() {
|
||||
for _, target := range flag.Args() {
|
||||
status, err := processTarget(target)
|
||||
switch status {
|
||||
case "OK":
|
||||
fmt.Printf("%s: OK\n", target)
|
||||
case "EXPIRED":
|
||||
fmt.Printf("%s: EXPIRED: %v\n", target, err)
|
||||
case strOK:
|
||||
fmt.Printf("%s: %s\n", target, strOK)
|
||||
case strExpired:
|
||||
fmt.Printf("%s: %s: %v\n", target, strExpired, err)
|
||||
exitCode = 1
|
||||
case "REVOKED":
|
||||
fmt.Printf("%s: REVOKED\n", target)
|
||||
case strRevoked:
|
||||
fmt.Printf("%s: %s\n", target, strRevoked)
|
||||
exitCode = 1
|
||||
case "UNKNOWN":
|
||||
fmt.Printf("%s: UNKNOWN: %v\n", target, err)
|
||||
case strUnknown:
|
||||
fmt.Printf("%s: %s: %v\n", target, strUnknown, err)
|
||||
if hardfail {
|
||||
// In hardfail, treat unknown as failure
|
||||
exitCode = 1
|
||||
@@ -67,74 +74,67 @@ func processTarget(target string) (string, error) {
|
||||
return checkFile(target)
|
||||
}
|
||||
|
||||
// Not a file; treat as site
|
||||
return checkSite(target)
|
||||
}
|
||||
|
||||
func checkFile(path string) (string, error) {
|
||||
in, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return "UNKNOWN", err
|
||||
}
|
||||
// Prefer high-level helpers from certlib to load certificates from disk
|
||||
if certs, err := certlib.LoadCertificates(path); err == nil && len(certs) > 0 {
|
||||
// Evaluate the first certificate (leaf) by default
|
||||
return evaluateCert(certs[0])
|
||||
}
|
||||
|
||||
// Try PEM first; if that fails, try single DER cert
|
||||
certs, err := certlib.ReadCertificates(in)
|
||||
if err != nil || len(certs) == 0 {
|
||||
cert, _, derr := certlib.ReadCertificate(in)
|
||||
if derr != nil || cert == nil {
|
||||
if err == nil {
|
||||
err = derr
|
||||
}
|
||||
return "UNKNOWN", err
|
||||
}
|
||||
return evaluateCert(cert)
|
||||
}
|
||||
|
||||
// Evaluate the first certificate (leaf) by default
|
||||
return evaluateCert(certs[0])
|
||||
cert, err := certlib.LoadCertificate(path)
|
||||
if err != nil || cert == nil {
|
||||
return strUnknown, err
|
||||
}
|
||||
return evaluateCert(cert)
|
||||
}
|
||||
|
||||
func checkSite(hostport string) (string, error) {
|
||||
// Use certlib/hosts to parse host/port (supports https URLs and host:port)
|
||||
target, err := hosts.ParseHost(hostport)
|
||||
if err != nil {
|
||||
return "UNKNOWN", err
|
||||
return strUnknown, err
|
||||
}
|
||||
|
||||
d := &net.Dialer{Timeout: timeout}
|
||||
conn, err := tls.DialWithDialer(d, "tcp", target.String(), &tls.Config{InsecureSkipVerify: true, ServerName: target.Host})
|
||||
conn, err := tls.DialWithDialer(
|
||||
d,
|
||||
"tcp",
|
||||
target.String(),
|
||||
&tls.Config{InsecureSkipVerify: true, ServerName: target.Host}, // #nosec G402
|
||||
)
|
||||
if err != nil {
|
||||
return "UNKNOWN", err
|
||||
return strUnknown, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
state := conn.ConnectionState()
|
||||
if len(state.PeerCertificates) == 0 {
|
||||
return "UNKNOWN", errors.New("no peer certificates presented")
|
||||
return strUnknown, errors.New("no peer certificates presented")
|
||||
}
|
||||
return evaluateCert(state.PeerCertificates[0])
|
||||
}
|
||||
|
||||
func evaluateCert(cert *x509.Certificate) (string, error) {
|
||||
// Expiry check
|
||||
now := time.Now()
|
||||
if !now.Before(cert.NotAfter) {
|
||||
return "EXPIRED", fmt.Errorf("expired at %s", cert.NotAfter)
|
||||
}
|
||||
if !now.After(cert.NotBefore) {
|
||||
return "EXPIRED", fmt.Errorf("not valid until %s", cert.NotBefore)
|
||||
}
|
||||
// Delegate validity and revocation checks to certlib/revoke helper.
|
||||
// It returns revoked=true for both revoked and expired/not-yet-valid.
|
||||
// Map those cases back to our statuses using the returned error text.
|
||||
revoked, ok, err := revoke.VerifyCertificateError(cert)
|
||||
if revoked {
|
||||
if err != nil {
|
||||
msg := err.Error()
|
||||
if strings.Contains(msg, "expired") || strings.Contains(msg, "isn't valid until") || strings.Contains(msg, "not valid until") {
|
||||
return strExpired, err
|
||||
}
|
||||
}
|
||||
return strRevoked, err
|
||||
}
|
||||
if !ok {
|
||||
// Revocation status could not be determined
|
||||
return strUnknown, err
|
||||
}
|
||||
|
||||
// Revocation check using certlib/revoke
|
||||
revoked, ok, err := revoke.VerifyCertificateError(cert)
|
||||
if revoked {
|
||||
// If revoked is true, ok will be true per implementation, err may describe why
|
||||
return "REVOKED", err
|
||||
}
|
||||
if !ok {
|
||||
// Revocation status could not be determined
|
||||
return "UNKNOWN", err
|
||||
}
|
||||
|
||||
return "OK", nil
|
||||
return strOK, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user