From 4940c32c39295bd2bc433c20ca97316ff09bbb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Tue, 14 Feb 2017 20:29:23 +0200 Subject: [PATCH] Expose the constants in pkg/controller/bootstrap and add a validate token method --- pkg/bootstrap/api/types.go | 9 +++++++++ pkg/controller/bootstrap/bootstrapsigner.go | 18 ++++++------------ .../bootstrap/bootstrapsigner_test.go | 7 ++++--- pkg/controller/bootstrap/jws.go | 17 +++++++++++++---- pkg/controller/bootstrap/jws_test.go | 14 ++++++++++++++ 5 files changed, 46 insertions(+), 19 deletions(-) diff --git a/pkg/bootstrap/api/types.go b/pkg/bootstrap/api/types.go index 8f046792a56..de5dfd75c2f 100644 --- a/pkg/bootstrap/api/types.go +++ b/pkg/bootstrap/api/types.go @@ -45,4 +45,13 @@ const ( // sign configs as part of the bootstrap process. Value must be "true". Any // other value is assumed to be false. Optional. BootstrapTokenUsageSigningKey = "usage-bootstrap-signing" + + // ConfigMapClusterInfo defines the name for the ConfigMap where the information how to connect and trust the cluster exist + ConfigMapClusterInfo = "cluster-info" + + // KubeConfigKey defines at which key in the Data object of the ConfigMap the KubeConfig object is stored + KubeConfigKey = "kubeconfig" + + // JWSSignatureKeyPrefix defines what key prefix the JWS-signed tokens have + JWSSignatureKeyPrefix = "jws-kubeconfig-" ) diff --git a/pkg/controller/bootstrap/bootstrapsigner.go b/pkg/controller/bootstrap/bootstrapsigner.go index 64218511672..6851d1009de 100644 --- a/pkg/controller/bootstrap/bootstrapsigner.go +++ b/pkg/controller/bootstrap/bootstrapsigner.go @@ -38,12 +38,6 @@ import ( "k8s.io/kubernetes/pkg/util/metrics" ) -const ( - configMapClusterInfo = "cluster-info" - kubeConfigKey = "kubeconfig" - signaturePrefix = "jws-kubeconfig-" -) - // BootstrapSignerOptions contains options for the BootstrapSigner type BootstrapSignerOptions struct { @@ -70,7 +64,7 @@ type BootstrapSignerOptions struct { func DefaultBootstrapSignerOptions() BootstrapSignerOptions { return BootstrapSignerOptions{ ConfigMapNamespace: api.NamespacePublic, - ConfigMapName: configMapClusterInfo, + ConfigMapName: bootstrapapi.ConfigMapClusterInfo, TokenSecretNamespace: api.NamespaceSystem, } } @@ -191,17 +185,17 @@ func (e *BootstrapSigner) signConfigMap() { } // First capture the config we are signing - content, ok := newCM.Data[kubeConfigKey] + content, ok := newCM.Data[bootstrapapi.KubeConfigKey] if !ok { - glog.V(3).Infof("No %s key in %s/%s ConfigMap", kubeConfigKey, origCM.Namespace, origCM.Name) + glog.V(3).Infof("No %s key in %s/%s ConfigMap", bootstrapapi.KubeConfigKey, origCM.Namespace, origCM.Name) return } // Next remove and save all existing signatures sigs := map[string]string{} for key, value := range newCM.Data { - if strings.HasPrefix(key, signaturePrefix) { - tokenID := strings.TrimPrefix(key, signaturePrefix) + if strings.HasPrefix(key, bootstrapapi.JWSSignatureKeyPrefix) { + tokenID := strings.TrimPrefix(key, bootstrapapi.JWSSignatureKeyPrefix) sigs[tokenID] = value delete(newCM.Data, key) } @@ -222,7 +216,7 @@ func (e *BootstrapSigner) signConfigMap() { } delete(sigs, tokenID) - newCM.Data[signaturePrefix+tokenID] = sig + newCM.Data[bootstrapapi.JWSSignatureKeyPrefix+tokenID] = sig } // If we have signatures left over we know that some signatures were diff --git a/pkg/controller/bootstrap/bootstrapsigner_test.go b/pkg/controller/bootstrap/bootstrapsigner_test.go index f196e3e2519..b590ae900d4 100644 --- a/pkg/controller/bootstrap/bootstrapsigner_test.go +++ b/pkg/controller/bootstrap/bootstrapsigner_test.go @@ -27,6 +27,7 @@ import ( "k8s.io/client-go/pkg/api" "k8s.io/client-go/pkg/api/v1" core "k8s.io/client-go/testing" + bootstrapapi "k8s.io/kubernetes/pkg/bootstrap/api" ) func init() { @@ -43,15 +44,15 @@ func newConfigMap(tokenID, signature string) *v1.ConfigMap { ret := &v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ Namespace: metav1.NamespacePublic, - Name: configMapClusterInfo, + Name: bootstrapapi.ConfigMapClusterInfo, ResourceVersion: "1", }, Data: map[string]string{ - kubeConfigKey: "payload", + bootstrapapi.KubeConfigKey: "payload", }, } if len(tokenID) > 0 { - ret.Data[signaturePrefix+tokenID] = signature + ret.Data[bootstrapapi.JWSSignatureKeyPrefix+tokenID] = signature } return ret } diff --git a/pkg/controller/bootstrap/jws.go b/pkg/controller/bootstrap/jws.go index e9d7e099511..ec73ceb4887 100644 --- a/pkg/controller/bootstrap/jws.go +++ b/pkg/controller/bootstrap/jws.go @@ -34,17 +34,17 @@ func computeDetachedSig(content, tokenID, tokenSecret string) (string, error) { signer, err := jose.NewSigner(jose.HS256, jwk) if err != nil { - return "", nil + return "", fmt.Errorf("can't make a HS256 signer from the given token: %v", err) } jws, err := signer.Sign([]byte(content)) if err != nil { - return "", nil + return "", fmt.Errorf("can't HS256-sign the given token: %v", err) } fullSig, err := jws.CompactSerialize() if err != nil { - return "", nil + return "", fmt.Errorf("can't serialize the given token: %v", err) } return stripContent(fullSig) } @@ -57,8 +57,17 @@ func computeDetachedSig(content, tokenID, tokenSecret string) (string, error) { func stripContent(fullSig string) (string, error) { parts := strings.Split(fullSig, ".") if len(parts) != 3 { - return "", fmt.Errorf("Compact JWS format must have three parts") + return "", fmt.Errorf("compact JWS format must have three parts") } return parts[0] + ".." + parts[2], nil } + +// DetachedTokenIsValid checks whether a given detached JWS-encoded token matches JWS output of the given content and token +func DetachedTokenIsValid(detachedToken, content, tokenID, tokenSecret string) bool { + newToken, err := computeDetachedSig(content, tokenID, tokenSecret) + if err != nil { + return false + } + return detachedToken == newToken +} diff --git a/pkg/controller/bootstrap/jws_test.go b/pkg/controller/bootstrap/jws_test.go index 3add9d4c835..690ec7b0cda 100644 --- a/pkg/controller/bootstrap/jws_test.go +++ b/pkg/controller/bootstrap/jws_test.go @@ -51,3 +51,17 @@ func TestComputeDetachedSig(t *testing.T) { t.Errorf("Wrong signature. Got: %v", sig) } } + +func TestDetachedTokenIsValid(t *testing.T) { + // Valid detached JWS token and valid inputs should succeed + sig := "eyJhbGciOiJIUzI1NiIsImtpZCI6Impvc2h1YSJ9..VShe2taLd-YTrmWuRkcL_8QTNDHYxQIEBsAYYiIj1_8" + if !DetachedTokenIsValid(sig, content, id, secret) { + t.Errorf("Content %q and token \"%s:%s\" should equal signature: %q", content, id, secret, sig) + } + + // Invalid detached JWS token and valid inputs should fail + sig2 := sig + "foo" + if DetachedTokenIsValid(sig2, content, id, secret) { + t.Errorf("Content %q and token \"%s:%s\" should not equal signature: %q", content, id, secret, sig) + } +}