From 1365ed54b819547b3e32257b7c452197d6e23c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20Ecs=C3=A9di?= Date: Mon, 11 Mar 2019 21:02:07 +0100 Subject: [PATCH] Add cert hash to token output --- .../app/phases/kubeadm/token/create/create.go | 13 ++++-- cmd/pke/app/phases/kubeadm/token/list/list.go | 12 +++-- cmd/pke/app/phases/kubeadm/token/token.go | 45 +++++++++++++++---- 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/cmd/pke/app/phases/kubeadm/token/create/create.go b/cmd/pke/app/phases/kubeadm/token/create/create.go index 3546a71c..d72b1954 100644 --- a/cmd/pke/app/phases/kubeadm/token/create/create.go +++ b/cmd/pke/app/phases/kubeadm/token/create/create.go @@ -41,6 +41,7 @@ const ( cmdKubeadm = "/bin/kubeadm" kubeConfig = "/etc/kubernetes/admin.conf" + caCertFile = "/etc/kubernetes/pki/ca.crt" ) var _ phases.Runnable = (*Create)(nil) @@ -73,6 +74,11 @@ func (c *Create) Validate(cmd *cobra.Command) error { } func (c *Create) Run(out io.Writer) error { + hash, err := token.CertHash(ioutil.Discard, caCertFile) + if err != nil { + return err + } + cmd := runner.Cmd(ioutil.Discard, cmdKubeadm, "token", "create") cmd.Env = append(os.Environ(), "KUBECONFIG="+kubeConfig) o, err := cmd.CombinedOutput() @@ -97,7 +103,7 @@ func (c *Create) Run(out io.Writer) error { return errors.New("creation error: invalid token format") } - t, err = token.Get(ioutil.Discard, "bootstrap-token-"+line[:idx]) + t, err = token.Get(ioutil.Discard, "bootstrap-token-"+line[:idx], hash) if err != nil { return err } @@ -106,8 +112,9 @@ func (c *Create) Run(out io.Writer) error { switch c.o { default: tw := tabwriter.NewWriter(out, 0, 0, 2, ' ', 0) - _, _ = fmt.Fprintf(tw, "Token\tTTL\tExpires\tExpired\n") - _, _ = fmt.Fprintf(tw, "%s\t%dh\t%s\t%t\n", t.Token, t.TTL, t.Expires, t.Expired) + _, _ = fmt.Fprintf(tw, "Token\tTTL\tExpires\tExpired\tCert Hash\n") + _, _ = fmt.Fprintf(tw, "%s\t%dh\t%s\t%t\t%s\n", t.Token, t.TTL, t.Expires, t.Expired, t.CertHash) + _ = tw.Flush() _ = tw.Flush() case "yaml": diff --git a/cmd/pke/app/phases/kubeadm/token/list/list.go b/cmd/pke/app/phases/kubeadm/token/list/list.go index 92f1340f..78718a1f 100644 --- a/cmd/pke/app/phases/kubeadm/token/list/list.go +++ b/cmd/pke/app/phases/kubeadm/token/list/list.go @@ -39,6 +39,7 @@ const ( cmdKubectl = "/bin/kubectl" kubeConfig = "/etc/kubernetes/admin.conf" + caCertFile = "/etc/kubernetes/pki/ca.crt" ) var _ phases.Runnable = (*List)(nil) @@ -71,6 +72,11 @@ func (l *List) Validate(cmd *cobra.Command) error { } func (l *List) Run(out io.Writer) error { + hash, err := token.CertHash(ioutil.Discard, caCertFile) + if err != nil { + return err + } + // kubectl get secret -n kube-system -o jsonpath='{range .items[?(.type=="bootstrap.kubernetes.io/token")]}{.metadata.name}{"\n"}{end}' args := []string{"get", "secret", "-n", "kube-system", "-o", `jsonpath={range .items[?(.type=="bootstrap.kubernetes.io/token")]}{.metadata.name}{"\n"}{end}`} cmd := runner.Cmd(ioutil.Discard, cmdKubectl, args...) @@ -92,7 +98,7 @@ func (l *List) Run(out io.Writer) error { return err } - t, err := token.Get(ioutil.Discard, line) + t, err := token.Get(ioutil.Discard, line, hash) if err != nil { return err } @@ -103,9 +109,9 @@ func (l *List) Run(out io.Writer) error { switch l.o { default: tw := tabwriter.NewWriter(out, 0, 0, 2, ' ', 0) - _, _ = fmt.Fprintf(tw, "Token\tTTL\tExpires\tExpired\n") + _, _ = fmt.Fprintf(tw, "Token\tTTL\tExpires\tExpired\tCert Hash\n") for _, row := range list.Tokens { - _, _ = fmt.Fprintf(tw, "%s\t%dh\t%s\t%t\n", row.Token, row.TTL, row.Expires, row.Expired) + _, _ = fmt.Fprintf(tw, "%s\t%dh\t%s\t%t\t%s\n", row.Token, row.TTL, row.Expires, row.Expired, row.CertHash) } _ = tw.Flush() diff --git a/cmd/pke/app/phases/kubeadm/token/token.go b/cmd/pke/app/phases/kubeadm/token/token.go index 15883a46..e4c9f50d 100644 --- a/cmd/pke/app/phases/kubeadm/token/token.go +++ b/cmd/pke/app/phases/kubeadm/token/token.go @@ -15,13 +15,19 @@ package token import ( + "crypto/sha256" + "crypto/x509" "encoding/base64" + "encoding/hex" "encoding/json" + "encoding/pem" "fmt" "io" + "io/ioutil" "time" "github.com/banzaicloud/pke/cmd/pke/app/util/runner" + "github.com/pkg/errors" ) const ( @@ -29,17 +35,18 @@ const ( ) type Token struct { - Token string `json:"token"` - TTL int `json:"-"` - Expires time.Time `json:"expires"` - Expired bool `json:"expired"` + Token string `json:"token"` + TTL int `json:"-"` + Expires time.Time `json:"expires"` + Expired bool `json:"expired"` + CertHash string `json:"hash"` } type Output struct { Tokens []*Token `json:"tokens"` } -func Get(out io.Writer, secret string) (*Token, error) { +func Get(out io.Writer, secret, certHash string) (*Token, error) { // kubectl get -n kube-system secret -o json subCmd := runner.Cmd(out, cmdKubectl, []string{"get", "secret", "-n", "kube-system", "-o", "json", secret}...) cmdOut, err := subCmd.Output() @@ -72,9 +79,29 @@ func Get(out io.Writer, secret string) (*Token, error) { } return &Token{ - Token: fmt.Sprintf("%s.%s", tid, ts), - TTL: int(t.Sub(time.Now()).Hours()), - Expires: t, - Expired: t.Sub(time.Now()) <= 0, + Token: fmt.Sprintf("%s.%s", tid, ts), + TTL: int(t.Sub(time.Now()).Hours()), + Expires: t, + Expired: t.Sub(time.Now()) <= 0, + CertHash: certHash, }, nil } + +func CertHash(out io.Writer, certFile string) (string, error) { + b, err := ioutil.ReadFile(certFile) + if err != nil { + return "", errors.Wrap(err, "failed to open certificate for hashing") + } + + block, _ := pem.Decode(b) + if block == nil { + return "", errors.New("failed to parse certificate") + } + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return "", errors.Wrapf(err, "failed to parse certificate") + } + + h := sha256.Sum256(cert.RawSubjectPublicKeyInfo) + return fmt.Sprintf("sha256:%s", hex.EncodeToString(h[:])), nil +}