Add certexpiry.
This commit is contained in:
parent
858c9cb4e8
commit
9b18cc18d3
|
@ -11,6 +11,9 @@ Contents:
|
|||
certchain/ Display the certificate chain from a
|
||||
TLS connection.
|
||||
certdump/ Dump certificate information.
|
||||
certexpiry/ Print a list of certificate subjects and expiry times
|
||||
or warn about certificates expiring within a certain
|
||||
window.
|
||||
certverify/ Verify a TLS X.509 certificate, optionally printing
|
||||
the time to expiry and checking for revocations.
|
||||
clustersh/ Run commands or transfer files across multiple
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
certexpiry
|
||||
|
||||
Print a list of certificates and their expiry, or only warn about
|
||||
upcoming expiries.
|
||||
|
||||
It takes a list of PEM-encoded certificates, and compares the NotAfter
|
||||
value to the window given by the -t flag (which defaults to 2160 hours,
|
||||
or 90 days). Alternatively, given the -q flag, it will only warn about
|
||||
certificates expiring in the window.
|
||||
|
||||
Example, run on the cfssl-trust[1] CA bundle:
|
||||
|
||||
$ certexpiry -q ca-bundle.crt
|
||||
/GPKIRootCA/C=KR/O=Government of Korea/OU=GPKI/SN=93008982654396041992798201139454296355 expires on 2017-03-15 06:00:04 +0000 UTC (in 1633h0m44.686144136s)
|
||||
/CA DATEV BT 01/C=DE/O=DATEV eG/SN=139288328771231810070797444106717912243 expires on 2017-01-09 13:42:30 +0000 UTC (in 80h43m10.685385355s)
|
||||
/CA DATEV STD 01/C=DE/O=DATEV eG/SN=142389455970744957119921172249094394891 expires on 2017-01-09 13:42:30 +0000 UTC (in 80h43m10.685236793s)
|
||||
/CA DATEV INT 01/C=DE/O=DATEV eG/SN=169035066479776292612803392462688126470 expires on 2017-01-09 13:42:30 +0000 UTC (in 80h43m10.685208087s)
|
||||
$ certexpiry ca-bundle.crt | head -5
|
||||
/http://www.valicert.com//O=ValiCert, Inc./OU=ValiCert Class 3 Policy Validation Authority/L=ValiCert Validation Network/SN=1 expires on 2019-06-26 00:22:33 +0000 UTC (in 21619h22m44.060898709s)
|
||||
/QuoVadis Root CA 2/C=BM/O=QuoVadis Limited/SN=1289 expires on 2031-11-24 18:23:33 +0000 UTC (in 130453h23m44.060878817s)
|
||||
/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority/SN=149843929435818692848040365716851702463 expires on 2028-08-01 23:59:59 +0000 UTC (in 101419h0m10.06087362s)
|
||||
/Equifax Secure Global eBusiness CA-1/C=US/O=Equifax Secure Inc./SN=1 expires on 2020-06-21 04:00:00 +0000 UTC (in 30287h0m11.060869101s)
|
||||
/thawte Primary Root CA/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/SN=69529181992039203566298953787712940909 expires on 2036-07-16 23:59:59 +0000 UTC (in 171163h0m10.060864304s)
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cloudflare/cfssl/helpers"
|
||||
"github.com/kisom/goutils/die"
|
||||
"github.com/kisom/goutils/lib"
|
||||
)
|
||||
|
||||
var warnOnly bool
|
||||
var leeway = 2160 * time.Hour // three months
|
||||
|
||||
func displayName(name pkix.Name) string {
|
||||
var ns []string
|
||||
|
||||
if name.CommonName != "" {
|
||||
ns = append(ns, name.CommonName)
|
||||
}
|
||||
|
||||
for i := range name.Country {
|
||||
ns = append(ns, fmt.Sprintf("C=%s", name.Country[i]))
|
||||
}
|
||||
|
||||
for i := range name.Organization {
|
||||
ns = append(ns, fmt.Sprintf("O=%s", name.Organization[i]))
|
||||
}
|
||||
|
||||
for i := range name.OrganizationalUnit {
|
||||
ns = append(ns, fmt.Sprintf("OU=%s", name.OrganizationalUnit[i]))
|
||||
}
|
||||
|
||||
for i := range name.Locality {
|
||||
ns = append(ns, fmt.Sprintf("L=%s", name.Locality[i]))
|
||||
}
|
||||
|
||||
for i := range name.Province {
|
||||
ns = append(ns, fmt.Sprintf("ST=%s", name.Province[i]))
|
||||
}
|
||||
|
||||
if len(ns) > 0 {
|
||||
return "/" + strings.Join(ns, "/")
|
||||
}
|
||||
|
||||
die.With("no subject information in root")
|
||||
return ""
|
||||
}
|
||||
|
||||
func expires(cert *x509.Certificate) time.Duration {
|
||||
return cert.NotAfter.Sub(time.Now())
|
||||
}
|
||||
|
||||
func inDanger(cert *x509.Certificate) bool {
|
||||
return expires(cert) < leeway
|
||||
}
|
||||
|
||||
func checkCert(cert *x509.Certificate) {
|
||||
warn := inDanger(cert)
|
||||
name := displayName(cert.Subject)
|
||||
name = fmt.Sprintf("%s/SN=%s", name, cert.SerialNumber)
|
||||
expiry := expires(cert)
|
||||
if warnOnly {
|
||||
if warn {
|
||||
fmt.Fprintf(os.Stderr, "%s expires on %s (in %s)\n", name, cert.NotAfter, expiry)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("%s expires on %s (in %s)\n", name, cert.NotAfter, expiry)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.BoolVar(&warnOnly, "q", false, "only warn about expiring certs")
|
||||
flag.DurationVar(&leeway, "t", leeway, "warn if certificates are closer than this to expiring")
|
||||
flag.Parse()
|
||||
|
||||
for _, file := range flag.Args() {
|
||||
in, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
lib.Warn(err, "failed to read file")
|
||||
continue
|
||||
}
|
||||
|
||||
certs, err := helpers.ParseCertificatesPEM(in)
|
||||
if err != nil {
|
||||
lib.Warn(err, "while parsing certificates")
|
||||
continue
|
||||
}
|
||||
|
||||
for _, cert := range certs {
|
||||
checkCert(cert)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue