Skip to content

Commit

Permalink
Merge pull request #125 from EnriqueJoseSanjuaneloRobles-TomTom/master
Browse files Browse the repository at this point in the history
Support for PKCS12/PFX certificates
  • Loading branch information
joe-elliott authored Nov 17, 2022
2 parents 3038e95 + ef1c7a7 commit 233f1dd
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 22 deletions.
10 changes: 5 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ require (
github.com/prometheus/common v0.15.0 // indirect
github.com/prometheus/procfs v0.2.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f // indirect
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect
golang.org/x/crypto v0.1.0 // indirect
golang.org/x/net v0.1.0 // indirect
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e // indirect
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 // indirect
golang.org/x/text v0.3.4 // indirect
golang.org/x/sys v0.1.0 // indirect
golang.org/x/term v0.1.0 // indirect
golang.org/x/text v0.4.0 // indirect
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
google.golang.org/appengine v1.6.5 // indirect
google.golang.org/protobuf v1.25.0 // indirect
Expand Down
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f h1:aZp0e2vLN4MToVqnjNEYEtrEA8RH8U8FN1CU7JgqsPU=
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand Down Expand Up @@ -472,6 +474,8 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down Expand Up @@ -524,15 +528,21 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs=
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Kubernetes uses PKI certificates for authentication between all major components

cert-exporter can publish metrics about

- x509 certificates on disk encoded in the [PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail)
- x509 certificates on disk encoded in the [PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail) and [PKCS12 format](https://en.wikipedia.org/wiki/PKCS_12)
- Certs embedded or referenced from kubeconfig files.
- Certs stored in Kubernetes
- secrets
Expand Down
76 changes: 60 additions & 16 deletions src/exporters/certHelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"encoding/base64"
"encoding/pem"
"io/ioutil"

"golang.org/x/crypto/pkcs12"
)

type certMetric struct {
Expand Down Expand Up @@ -37,40 +39,82 @@ func secondsToExpiryFromCertAsBase64String(s string) ([]certMetric, error) {

func secondsToExpiryFromCertAsBytes(certBytes []byte) ([]certMetric, error) {
var metrics []certMetric

parsed, metrics, err := parseAsPEM(certBytes)
if parsed {
return metrics, err
}
// Parse as PKCS ?
parsed, metrics, err = parseAsPKCS(certBytes)
if parsed {
return metrics, nil
}
return nil, fmt.Errorf("failed to parse as pem and pkcs12: %w", err)
}

func getCertificateMetrics(cert *x509.Certificate)(certMetric) {
var metric certMetric
metric.notAfter = float64(cert.NotAfter.Unix())
metric.durationUntilExpiry = time.Until(cert.NotAfter).Seconds()
metric.issuer = cert.Issuer.CommonName
metric.cn = cert.Subject.CommonName
return metric
}

func parseAsPKCS(certBytes []byte) (bool, []certMetric, error) {
var metrics []certMetric
var blocks []*pem.Block
var last_err error

pfx_blocks, err := pkcs12.ToPEM(certBytes, "")
if err != nil {
return false, nil, err
}
for _ , b := range pfx_blocks {
if b.Type == "CERTIFICATE" {
blocks = append(blocks, b)
}
}

// Export the first certificates in the certificate chain
for _, block := range blocks {
cert, err := x509.ParseCertificate(block.Bytes)
if err == nil {
var metric = getCertificateMetrics(cert)
metrics = append(metrics, metric)
} else {
last_err = err
}
}
return true, metrics, last_err
}

func parseAsPEM(certBytes []byte)(bool, []certMetric, error) {
var metrics []certMetric
var blocks []*pem.Block

block, rest := pem.Decode(certBytes)
if block == nil {
return metrics, fmt.Errorf("Failed to parse as a pem")
}
return false, metrics, fmt.Errorf("Failed to parse as a pem")
}
blocks = append(blocks, block)

// Export the remaining certificates in the certificate chain
for len(rest) != 0 {
block, rest = pem.Decode(rest)
if block == nil {
return metrics, fmt.Errorf("Failed to parse intermediate as a pem")
return true, metrics, fmt.Errorf("Failed to parse intermediate as a pem")
}
if block.Type == "CERTIFICATE" {
blocks = append(blocks, block)
}
}

for _, block := range blocks {
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return metrics, err
return true, metrics ,err
}

var metric certMetric
metric.notAfter = float64(cert.NotAfter.Unix())
metric.durationUntilExpiry = time.Until(cert.NotAfter).Seconds()
metric.issuer = cert.Issuer.CommonName
metric.cn = cert.Subject.CommonName

var metric = getCertificateMetrics(cert)
metrics = append(metrics, metric)
}

return metrics, nil
return true, metrics, nil
}

3 changes: 3 additions & 0 deletions test/files/genCerts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,7 @@ openssl x509 -req -in bundle_server.csr -CA bundle_root.cert -CAkey bundle_root.
# create bundle
cat bundle_server.cert bundle_root.cert > bundle.crt

# generate pfx
openssl pkcs12 -export -out bundle_pfx.crt -in bundle.crt -inkey bundle_root.key -passout pass:

popd
3 changes: 3 additions & 0 deletions test/files/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ mkdir certs
./genCerts.sh certs $days >/dev/null 2>&1
./genKubeConfig.sh certs ./ >/dev/null 2>&1


# run exporter
$CERT_EXPORTER_PATH -include-cert-glob=certs/*.crt -include-kubeconfig-glob=certs/kubeconfig &

Expand All @@ -59,6 +60,8 @@ validateMetrics 'cert_exporter_cert_expires_in_seconds{cn="root",filename="certs
validateMetrics 'cert_exporter_cert_expires_in_seconds{cn="example.com",filename="certs/server.crt",issuer="root",nodename="master0"}' $days
validateMetrics 'cert_exporter_cert_expires_in_seconds{cn="bundle-root",filename="certs/bundle.crt",issuer="bundle-root",nodename="master0"}' $days
validateMetrics 'cert_exporter_cert_expires_in_seconds{cn="example-bundle.be",filename="certs/bundle.crt",issuer="bundle-root",nodename="master0"}' $days
validateMetrics 'cert_exporter_cert_expires_in_seconds{cn="bundle-root",filename="certs/bundle_pfx.crt",issuer="bundle-root",nodename="master0"}' $days


validateMetrics 'cert_exporter_kubeconfig_expires_in_seconds{cn="root",filename="certs/kubeconfig",issuer="root",name="cluster1",nodename="master0",type="cluster"}' $days
validateMetrics 'cert_exporter_kubeconfig_expires_in_seconds{cn="root",filename="certs/kubeconfig",issuer="root",name="cluster2",nodename="master0",type="cluster"}' $days
Expand Down

0 comments on commit 233f1dd

Please sign in to comment.