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 // sign configs as part of the bootstrap process. Value must be "true". Any
// other value is assumed to be false. Optional. // other value is assumed to be false. Optional.
BootstrapTokenUsageSigningKey = "usage-bootstrap-signing" 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" "k8s.io/kubernetes/pkg/util/metrics"
) )
const (
configMapClusterInfo = "cluster-info"
kubeConfigKey = "kubeconfig"
signaturePrefix = "jws-kubeconfig-"
)
// BootstrapSignerOptions contains options for the BootstrapSigner // BootstrapSignerOptions contains options for the BootstrapSigner
type BootstrapSignerOptions struct { type BootstrapSignerOptions struct {
@ -70,7 +64,7 @@ type BootstrapSignerOptions struct {
func DefaultBootstrapSignerOptions() BootstrapSignerOptions { func DefaultBootstrapSignerOptions() BootstrapSignerOptions {
return BootstrapSignerOptions{ return BootstrapSignerOptions{
ConfigMapNamespace: api.NamespacePublic, ConfigMapNamespace: api.NamespacePublic,
ConfigMapName: configMapClusterInfo, ConfigMapName: bootstrapapi.ConfigMapClusterInfo,
TokenSecretNamespace: api.NamespaceSystem, TokenSecretNamespace: api.NamespaceSystem,
} }
} }
@ -191,17 +185,17 @@ func (e *BootstrapSigner) signConfigMap() {
} }
// First capture the config we are signing // First capture the config we are signing
content, ok := newCM.Data[kubeConfigKey] content, ok := newCM.Data[bootstrapapi.KubeConfigKey]
if !ok { 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 return
} }
// Next remove and save all existing signatures // Next remove and save all existing signatures
sigs := map[string]string{} sigs := map[string]string{}
for key, value := range newCM.Data { for key, value := range newCM.Data {
if strings.HasPrefix(key, signaturePrefix) { if strings.HasPrefix(key, bootstrapapi.JWSSignatureKeyPrefix) {
tokenID := strings.TrimPrefix(key, signaturePrefix) tokenID := strings.TrimPrefix(key, bootstrapapi.JWSSignatureKeyPrefix)
sigs[tokenID] = value sigs[tokenID] = value
delete(newCM.Data, key) delete(newCM.Data, key)
} }
@ -222,7 +216,7 @@ func (e *BootstrapSigner) signConfigMap() {
} }
delete(sigs, tokenID) 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 // 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"
"k8s.io/client-go/pkg/api/v1" "k8s.io/client-go/pkg/api/v1"
core "k8s.io/client-go/testing" core "k8s.io/client-go/testing"
bootstrapapi "k8s.io/kubernetes/pkg/bootstrap/api"
) )
func init() { func init() {
@ -43,15 +44,15 @@ func newConfigMap(tokenID, signature string) *v1.ConfigMap {
ret := &v1.ConfigMap{ ret := &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Namespace: metav1.NamespacePublic, Namespace: metav1.NamespacePublic,
Name: configMapClusterInfo, Name: bootstrapapi.ConfigMapClusterInfo,
ResourceVersion: "1", ResourceVersion: "1",
}, },
Data: map[string]string{ Data: map[string]string{
kubeConfigKey: "payload", bootstrapapi.KubeConfigKey: "payload",
}, },
} }
if len(tokenID) > 0 { if len(tokenID) > 0 {
ret.Data[signaturePrefix+tokenID] = signature ret.Data[bootstrapapi.JWSSignatureKeyPrefix+tokenID] = signature
} }
return ret return ret
} }

View File

@ -34,17 +34,17 @@ func computeDetachedSig(content, tokenID, tokenSecret string) (string, error) {
signer, err := jose.NewSigner(jose.HS256, jwk) signer, err := jose.NewSigner(jose.HS256, jwk)
if err != nil { 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)) jws, err := signer.Sign([]byte(content))
if err != nil { if err != nil {
return "", nil return "", fmt.Errorf("can't HS256-sign the given token: %v", err)
} }
fullSig, err := jws.CompactSerialize() fullSig, err := jws.CompactSerialize()
if err != nil { if err != nil {
return "", nil return "", fmt.Errorf("can't serialize the given token: %v", err)
} }
return stripContent(fullSig) return stripContent(fullSig)
} }
@ -57,8 +57,17 @@ func computeDetachedSig(content, tokenID, tokenSecret string) (string, error) {
func stripContent(fullSig string) (string, error) { func stripContent(fullSig string) (string, error) {
parts := strings.Split(fullSig, ".") parts := strings.Split(fullSig, ".")
if len(parts) != 3 { 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 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) 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)
}
}