Expose the constants in pkg/controller/bootstrap and add a validate token method

This commit is contained in:
Lucas Käldström 2017-02-14 20:29:23 +02:00
parent 8db5ca1fbb
commit 4940c32c39
No known key found for this signature in database
GPG Key ID: 3FA3783D77751514
5 changed files with 46 additions and 19 deletions

View File

@ -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-"
)

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}