From d404a17f0aeabd78eedd4258a9c2662c0197a731 Mon Sep 17 00:00:00 2001 From: deads2k Date: Mon, 11 May 2015 15:03:10 -0400 Subject: [PATCH] allow leading dots in secret keys --- pkg/api/types.go | 3 ++- pkg/api/v1/types.go | 5 ++-- pkg/api/v1beta1/types.go | 5 ++-- pkg/api/v1beta2/types.go | 5 ++-- pkg/api/v1beta3/types.go | 5 ++-- pkg/api/validation/validation.go | 15 +++++++++-- pkg/api/validation/validation_test.go | 36 ++++++++++++++++----------- pkg/registry/secret/etcd/etcd_test.go | 2 +- 8 files changed, 50 insertions(+), 26 deletions(-) diff --git a/pkg/api/types.go b/pkg/api/types.go index 760060fc215..53c3c55826c 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -1825,7 +1825,8 @@ type Secret struct { TypeMeta `json:",inline"` ObjectMeta `json:"metadata,omitempty"` - // Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN. + // Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN + // or leading dot followed by valid DNS_SUBDOMAIN. // The serialized form of the secret data is a base64 encoded string, // representing the arbitrary (possibly non-string) data value here. Data map[string][]byte `json:"data,omitempty"` diff --git a/pkg/api/v1/types.go b/pkg/api/v1/types.go index e76d86ab921..ed6a6fc3732 100644 --- a/pkg/api/v1/types.go +++ b/pkg/api/v1/types.go @@ -1722,10 +1722,11 @@ type Secret struct { TypeMeta `json:",inline"` ObjectMeta `json:"metadata,omitempty" description:"standard object metadata; see http://docs.k8s.io/api-conventions.md#metadata"` - // Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN. + // Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN + // or leading dot followed by valid DNS_SUBDOMAIN. // The serialized form of the secret data is a base64 encoded string, // representing the arbitrary (possibly non-string) data value here. - Data map[string][]byte `json:"data,omitempty" description:"data contains the secret data. Each key must be a valid DNS_SUBDOMAIN. Each value must be a base64 encoded string"` + Data map[string][]byte `json:"data,omitempty" description:"data contains the secret data. Each key must be a valid DNS_SUBDOMAIN or leading dot followed by valid DNS_SUBDOMAIN. Each value must be a base64 encoded string as described in https://tools.ietf.org/html/rfc4648#section-4"` // Used to facilitate programmatic handling of secret data. Type SecretType `json:"type,omitempty" description:"type facilitates programmatic handling of secret data"` diff --git a/pkg/api/v1beta1/types.go b/pkg/api/v1beta1/types.go index e833f7c933f..07dfd30a8de 100644 --- a/pkg/api/v1beta1/types.go +++ b/pkg/api/v1beta1/types.go @@ -1625,10 +1625,11 @@ type NFSVolumeSource struct { type Secret struct { TypeMeta `json:",inline"` - // Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN. + // Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN + // or leading dot followed by valid DNS_SUBDOMAIN. // The serialized form of the secret data is a base64 encoded string, // representing the arbitrary (possibly non-string) data value here. - Data map[string][]byte `json:"data,omitempty" description:"data contains the secret data. Each key must be a valid DNS_SUBDOMAIN. Each value must be a base64 encoded string"` + Data map[string][]byte `json:"data,omitempty" description:"data contains the secret data. Each key must be a valid DNS_SUBDOMAIN or leading dot followed by valid DNS_SUBDOMAIN. Each value must be a base64 encoded string as described in https://tools.ietf.org/html/rfc4648#section-4"` // Used to facilitate programmatic handling of secret data. Type SecretType `json:"type,omitempty" description:"type facilitates programmatic handling of secret data"` diff --git a/pkg/api/v1beta2/types.go b/pkg/api/v1beta2/types.go index 9fb2bf319dc..39374245112 100644 --- a/pkg/api/v1beta2/types.go +++ b/pkg/api/v1beta2/types.go @@ -1702,10 +1702,11 @@ type NFSVolumeSource struct { type Secret struct { TypeMeta `json:",inline"` - // Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN. + // Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN + // or leading dot followed by valid DNS_SUBDOMAIN. // The serialized form of the secret data is a base64 encoded string, // representing the arbitrary (possibly non-string) data value here. - Data map[string][]byte `json:"data,omitempty" description:"data contains the secret data. Each key must be a valid DNS_SUBDOMAIN. Each value must be a base64 encoded string"` + Data map[string][]byte `json:"data,omitempty" description:"data contains the secret data. Each key must be a valid DNS_SUBDOMAIN or leading dot followed by valid DNS_SUBDOMAIN. Each value must be a base64 encoded string as described in https://tools.ietf.org/html/rfc4648#section-4"` // Used to facilitate programmatic handling of secret data. Type SecretType `json:"type,omitempty" description:"type facilitates programmatic handling of secret data"` diff --git a/pkg/api/v1beta3/types.go b/pkg/api/v1beta3/types.go index 88505563981..e2efa44b082 100644 --- a/pkg/api/v1beta3/types.go +++ b/pkg/api/v1beta3/types.go @@ -1722,10 +1722,11 @@ type Secret struct { TypeMeta `json:",inline"` ObjectMeta `json:"metadata,omitempty" description:"standard object metadata; see http://docs.k8s.io/api-conventions.md#metadata"` - // Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN. + // Data contains the secret data. Each key must be a valid DNS_SUBDOMAIN + // or leading dot followed by valid DNS_SUBDOMAIN. // The serialized form of the secret data is a base64 encoded string, // representing the arbitrary (possibly non-string) data value here. - Data map[string][]byte `json:"data,omitempty" description:"data contains the secret data. Each key must be a valid DNS_SUBDOMAIN. Each value must be a base64 encoded string"` + Data map[string][]byte `json:"data,omitempty" description:"data contains the secret data. Each key must be a valid DNS_SUBDOMAIN or leading dot followed by valid DNS_SUBDOMAIN. Each value must be a base64 encoded string as described in https://tools.ietf.org/html/rfc4648#section-4"` // Used to facilitate programmatic handling of secret data. Type SecretType `json:"type,omitempty" description:"type facilitates programmatic handling of secret data"` diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index bab284dcfd3..5afc37d3dc5 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -20,6 +20,7 @@ import ( "fmt" "net" "path" + "regexp" "strings" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" @@ -1257,6 +1258,16 @@ func ValidateServiceAccountUpdate(oldServiceAccount, newServiceAccount *api.Serv return allErrs } +const SecretKeyFmt string = "\\.?" + util.DNS1123LabelFmt + "(\\." + util.DNS1123LabelFmt + ")*" + +var secretKeyRegexp = regexp.MustCompile("^" + SecretKeyFmt + "$") + +// IsSecretKey tests for a string that conforms to the definition of a +// subdomain in DNS (RFC 1123), except that a leading dot is allowed +func IsSecretKey(value string) bool { + return len(value) <= util.DNS1123SubdomainMaxLength && secretKeyRegexp.MatchString(value) +} + // ValidateSecret tests if required fields in the Secret are set. func ValidateSecret(secret *api.Secret) errs.ValidationErrorList { allErrs := errs.ValidationErrorList{} @@ -1264,8 +1275,8 @@ func ValidateSecret(secret *api.Secret) errs.ValidationErrorList { totalSize := 0 for key, value := range secret.Data { - if !util.IsDNS1123Subdomain(key) { - allErrs = append(allErrs, errs.NewFieldInvalid(fmt.Sprintf("data[%s]", key), key, dnsSubdomainErrorMsg)) + if !IsSecretKey(key) { + allErrs = append(allErrs, errs.NewFieldInvalid(fmt.Sprintf("data[%s]", key), key, fmt.Sprintf("must have at most %d characters and match regex %s", util.DNS1123SubdomainMaxLength, SecretKeyFmt))) } totalSize += len(value) diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index ef1ea40ae3c..b233b08f97b 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -2869,12 +2869,15 @@ func TestValidateSecret(t *testing.T) { } var ( - emptyName = validSecret() - invalidName = validSecret() - emptyNs = validSecret() - invalidNs = validSecret() - overMaxSize = validSecret() - invalidKey = validSecret() + emptyName = validSecret() + invalidName = validSecret() + emptyNs = validSecret() + invalidNs = validSecret() + overMaxSize = validSecret() + invalidKey = validSecret() + leadingDotKey = validSecret() + dotKey = validSecret() + doubleDotKey = validSecret() ) emptyName.Name = "" @@ -2885,6 +2888,9 @@ func TestValidateSecret(t *testing.T) { "over": make([]byte, api.MaxSecretSize+1), } invalidKey.Data["a..b"] = []byte("whoops") + leadingDotKey.Data[".key"] = []byte("bar") + dotKey.Data["."] = []byte("bar") + doubleDotKey.Data[".."] = []byte("bar") // kubernetes.io/service-account-token secret validation validServiceAccountTokenSecret := func() api.Secret { @@ -2916,18 +2922,20 @@ func TestValidateSecret(t *testing.T) { secret api.Secret valid bool }{ - "valid": {validSecret(), true}, - "empty name": {emptyName, false}, - "invalid name": {invalidName, false}, - "empty namespace": {emptyNs, false}, - "invalid namespace": {invalidNs, false}, - "over max size": {overMaxSize, false}, - "invalid key": {invalidKey, false}, - + "valid": {validSecret(), true}, + "empty name": {emptyName, false}, + "invalid name": {invalidName, false}, + "empty namespace": {emptyNs, false}, + "invalid namespace": {invalidNs, false}, + "over max size": {overMaxSize, false}, + "invalid key": {invalidKey, false}, "valid service-account-token secret": {validServiceAccountTokenSecret(), true}, "empty service-account-token annotation": {emptyTokenAnnotation, false}, "missing service-account-token annotation": {missingTokenAnnotation, false}, "missing service-account-token annotations": {missingTokenAnnotations, false}, + "leading dot key": {leadingDotKey, true}, + "dot key": {dotKey, false}, + "double dot key": {doubleDotKey, false}, } for name, tc := range tests { diff --git a/pkg/registry/secret/etcd/etcd_test.go b/pkg/registry/secret/etcd/etcd_test.go index 04574f43886..dc8933c268f 100644 --- a/pkg/registry/secret/etcd/etcd_test.go +++ b/pkg/registry/secret/etcd/etcd_test.go @@ -63,7 +63,7 @@ func TestCreate(t *testing.T) { }, &api.Secret{ ObjectMeta: api.ObjectMeta{Name: "name"}, - Data: map[string][]byte{".dotfile": []byte("")}, + Data: map[string][]byte{"~.dotfile": []byte("")}, }, ) }