Skip to content

Commit

Permalink
kdecode: Add -pem flag to decode secret contents as a PEM certificate
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidGamba committed Mar 23, 2024
1 parent c3e6713 commit ef6979c
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 4 deletions.
58 changes: 57 additions & 1 deletion kdecode/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,63 @@

Decodes K8s secret's data block.

Cluster selection can be done using the `--cluster` flag to avoid having to change the current context when comparing multiple clusters.
Change namespace with the `--namespace` or `-n` flag.

Cluster selection can be done using the `--cluster` or `-c` flag to avoid having to change the current context when comparing multiple clusters.

You can limit the output to a single key within the secret with the `--key` or `-k` flag.

Additionally, if the secret is a PEM encoded certificate, you can the `--pem` flag to decode the certificate and print the details.

== Examples

.Decode a secret as a PEM certificate and pass the namespace, the cluster and filter to a single key
----
kdecode argo-server-tls -n argocd -c prod-cluster -k tls.crt -pem
tls.crt=
Certificate 0
Issuer: CN=R3,O=Let's Encrypt,C=US
Subject: CN=cd.argo.example.com
NotBefore: 2024-03-22 18:03:13 +0000 UTC
NotAfter: 2024-06-20 18:03:12 +0000 UTC
SignatureAlgorithm: SHA256-RSA
PublicKeyAlgorithm: RSA
SerialNumber: 111111111111111111111111111111111111111111
KeyUsage:
OCSPServer: [http://r3.o.lencr.org]
IssuingCertificateURL: [http://r3.i.lencr.org/]
DNSNames: [cd.argo.example.com]
Certificate 1
Issuer: CN=ISRG Root X1,O=Internet Security Research Group,C=US
Subject: CN=R3,O=Let's Encrypt,C=US
NotBefore: 2020-09-04 00:00:00 +0000 UTC
NotAfter: 2025-09-15 16:00:00 +0000 UTC
SignatureAlgorithm: SHA256-RSA
PublicKeyAlgorithm: RSA
SerialNumber: 222222222222222222222222222222222222222
KeyUsage:
IssuingCertificateURL: [http://x1.i.lencr.org/]
CRLDistributionPoints: [http://x1.c.lencr.org/]
Certificate 2
Issuer: CN=DST Root CA X3,O=Digital Signature Trust Co.
Subject: CN=ISRG Root X1,O=Internet Security Research Group,C=US
NotBefore: 2021-01-20 19:14:03 +0000 UTC
NotAfter: 2024-09-30 18:14:03 +0000 UTC
SignatureAlgorithm: SHA256-RSA
PublicKeyAlgorithm: RSA
SerialNumber: 33333333333333333333333333333333333333
KeyUsage:
IssuingCertificateURL: [http://apps.identrust.com/roots/dstrootcax3.p7c]
CRLDistributionPoints: [http://crl.identrust.com/DSTROOTCAX3CRL.crl]
----

.Decode a secret as a PEM certificate and pass the namespace, use the current context and filter to a single key
----
kdecode argo-server-tls -n argocd -k tls.crt -pem # current context
tls.crt=
Certificate 0
...
----

== Installation

Expand Down
111 changes: 108 additions & 3 deletions kdecode/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package main

import (
"context"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io"
Expand All @@ -26,8 +28,9 @@ func program(args []string) int {
If a secret is not given, lists all secrets in the current namespace.
Source: https://github.com/DavidGamba/dgtools`)
opt.Bool("quiet", false, opt.GetEnv("QUIET"))
opt.SetCommandFn(Run)
opt.Bool("quiet", false, opt.GetEnv("QUIET"))
opt.Bool("pem", false, opt.Description("Parse the secret as a PEM encoded certificate"))
opt.String("namespace", "")
opt.String("key", "", opt.Description("limit output to the given key"))
opt.String("cluster", "", opt.Description(`Allows targeting a different cluster from the current context.
Expand Down Expand Up @@ -64,6 +67,7 @@ func Run(ctx context.Context, opt *getoptions.GetOpt, args []string) error {
namespace := opt.Value("namespace").(string)
cluster := opt.Value("cluster").(string)
key := opt.Value("key").(string)
pem := opt.Value("pem").(bool)
secret := ""
if len(args) >= 1 {
secret = args[0]
Expand Down Expand Up @@ -99,14 +103,115 @@ func Run(ctx context.Context, opt *getoptions.GetOpt, args []string) error {
}
for k, v := range k8sSecret.Data {
if (key != "" && k == key) || key == "" {
fmt.Printf("%s=%s\n", k, string(v))
fmt.Printf("%s=", k)
if pem {
info, err := ParseCert(string(v))
if err != nil {
fmt.Printf("%s\n", string(v))
Logger.Printf("%s\n", err)
} else {
fmt.Printf("\n%s\n", info)
}
} else {
fmt.Printf("%s=%s\n", k, string(v))
}
}
}
for k, v := range k8sSecret.StringData {
if (key != "" && k == key) || key == "" {
fmt.Printf("%s=%s\n", k, v)
fmt.Printf("%s=", k)
if pem {
info, err := ParseCert(string(v))
if err != nil {
fmt.Printf("%s\n", string(v))
Logger.Printf("%s\n", err)
} else {
fmt.Printf("\n%s\n", info)
}
} else {
fmt.Printf("%s=%s\n", k, v)
}
}
}

return nil
}

func ParseCert(data string) (string, error) {
info := ""
blocks := []*pem.Block{}
rest := []byte(data)
for {
block, newRest := pem.Decode(rest)
if block == nil {
return info, fmt.Errorf("failed to parse certificate PEM")
}
blocks = append(blocks, block)
if len(newRest) == 0 {
break
}
rest = newRest
}

for i, block := range blocks {
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return info, fmt.Errorf("failed to parse certificate: %w", err)
}
info += fmt.Sprintf("Certificate %d\n", i)
info += fmt.Sprintf("\tIssuer: %s\n", cert.Issuer)
info += fmt.Sprintf("\tSubject: %s\n", cert.Subject)
info += fmt.Sprintf("\tNotBefore: %s\n", cert.NotBefore)
info += fmt.Sprintf("\tNotAfter: %s\n", cert.NotAfter)
info += fmt.Sprintf("\tSignatureAlgorithm: %s\n", cert.SignatureAlgorithm)
info += fmt.Sprintf("\tPublicKeyAlgorithm: %s\n", cert.PublicKeyAlgorithm)
info += fmt.Sprintf("\tSerialNumber: %s\n", cert.SerialNumber)
info += fmt.Sprintf("\tKeyUsage: %s\n", KeyUsage(cert.KeyUsage))
if len(cert.OCSPServer) > 0 {
info += fmt.Sprintf("\tOCSPServer: %v\n", cert.OCSPServer)
}
if len(cert.IssuingCertificateURL) > 0 {
info += fmt.Sprintf("\tIssuingCertificateURL: %v\n", cert.IssuingCertificateURL)
}
if len(cert.DNSNames) > 0 {
info += fmt.Sprintf("\tDNSNames: %v\n", cert.DNSNames)
}
if len(cert.EmailAddresses) > 0 {
info += fmt.Sprintf("\tEmailAddresses: %v\n", cert.EmailAddresses)
}
if len(cert.IPAddresses) > 0 {
info += fmt.Sprintf("\tIPAddresses: %v\n", cert.IPAddresses)
}
if len(cert.URIs) > 0 {
info += fmt.Sprintf("\tURIs: %v\n", cert.URIs)
}
if len(cert.CRLDistributionPoints) > 0 {
info += fmt.Sprintf("\tCRLDistributionPoints: %v\n", cert.CRLDistributionPoints)
}
}
return info, nil
}

func KeyUsage(k x509.KeyUsage) string {
switch k {
case x509.KeyUsageDigitalSignature:
return "DigitalSignature"
case x509.KeyUsageContentCommitment:
return "ContentCommitment"
case x509.KeyUsageKeyEncipherment:
return "KeyEncipherment"
case x509.KeyUsageDataEncipherment:
return "DataEncipherment"
case x509.KeyUsageKeyAgreement:
return "KeyAgreement"
case x509.KeyUsageCertSign:
return "CertSign"
case x509.KeyUsageCRLSign:
return "CRLSign"
case x509.KeyUsageEncipherOnly:
return "EncipherOnly"
case x509.KeyUsageDecipherOnly:
return "DecipherOnly"
}
return ""
}

0 comments on commit ef6979c

Please sign in to comment.