allow leading dots in secret keys

This commit is contained in:
deads2k 2015-05-11 15:03:10 -04:00
parent efb42b302d
commit d404a17f0a
8 changed files with 50 additions and 26 deletions

View File

@ -1825,7 +1825,8 @@ type Secret struct {
TypeMeta `json:",inline"` TypeMeta `json:",inline"`
ObjectMeta `json:"metadata,omitempty"` 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, // The serialized form of the secret data is a base64 encoded string,
// representing the arbitrary (possibly non-string) data value here. // representing the arbitrary (possibly non-string) data value here.
Data map[string][]byte `json:"data,omitempty"` Data map[string][]byte `json:"data,omitempty"`

View File

@ -1722,10 +1722,11 @@ type Secret struct {
TypeMeta `json:",inline"` TypeMeta `json:",inline"`
ObjectMeta `json:"metadata,omitempty" description:"standard object metadata; see http://docs.k8s.io/api-conventions.md#metadata"` 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, // The serialized form of the secret data is a base64 encoded string,
// representing the arbitrary (possibly non-string) data value here. // 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. // Used to facilitate programmatic handling of secret data.
Type SecretType `json:"type,omitempty" description:"type facilitates programmatic handling of secret data"` Type SecretType `json:"type,omitempty" description:"type facilitates programmatic handling of secret data"`

View File

@ -1625,10 +1625,11 @@ type NFSVolumeSource struct {
type Secret struct { type Secret struct {
TypeMeta `json:",inline"` 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, // The serialized form of the secret data is a base64 encoded string,
// representing the arbitrary (possibly non-string) data value here. // 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. // Used to facilitate programmatic handling of secret data.
Type SecretType `json:"type,omitempty" description:"type facilitates programmatic handling of secret data"` Type SecretType `json:"type,omitempty" description:"type facilitates programmatic handling of secret data"`

View File

@ -1702,10 +1702,11 @@ type NFSVolumeSource struct {
type Secret struct { type Secret struct {
TypeMeta `json:",inline"` 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, // The serialized form of the secret data is a base64 encoded string,
// representing the arbitrary (possibly non-string) data value here. // 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. // Used to facilitate programmatic handling of secret data.
Type SecretType `json:"type,omitempty" description:"type facilitates programmatic handling of secret data"` Type SecretType `json:"type,omitempty" description:"type facilitates programmatic handling of secret data"`

View File

@ -1722,10 +1722,11 @@ type Secret struct {
TypeMeta `json:",inline"` TypeMeta `json:",inline"`
ObjectMeta `json:"metadata,omitempty" description:"standard object metadata; see http://docs.k8s.io/api-conventions.md#metadata"` 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, // The serialized form of the secret data is a base64 encoded string,
// representing the arbitrary (possibly non-string) data value here. // 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. // Used to facilitate programmatic handling of secret data.
Type SecretType `json:"type,omitempty" description:"type facilitates programmatic handling of secret data"` Type SecretType `json:"type,omitempty" description:"type facilitates programmatic handling of secret data"`

View File

@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"net" "net"
"path" "path"
"regexp"
"strings" "strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
@ -1257,6 +1258,16 @@ func ValidateServiceAccountUpdate(oldServiceAccount, newServiceAccount *api.Serv
return allErrs 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. // ValidateSecret tests if required fields in the Secret are set.
func ValidateSecret(secret *api.Secret) errs.ValidationErrorList { func ValidateSecret(secret *api.Secret) errs.ValidationErrorList {
allErrs := errs.ValidationErrorList{} allErrs := errs.ValidationErrorList{}
@ -1264,8 +1275,8 @@ func ValidateSecret(secret *api.Secret) errs.ValidationErrorList {
totalSize := 0 totalSize := 0
for key, value := range secret.Data { for key, value := range secret.Data {
if !util.IsDNS1123Subdomain(key) { if !IsSecretKey(key) {
allErrs = append(allErrs, errs.NewFieldInvalid(fmt.Sprintf("data[%s]", key), key, dnsSubdomainErrorMsg)) 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) totalSize += len(value)

View File

@ -2869,12 +2869,15 @@ func TestValidateSecret(t *testing.T) {
} }
var ( var (
emptyName = validSecret() emptyName = validSecret()
invalidName = validSecret() invalidName = validSecret()
emptyNs = validSecret() emptyNs = validSecret()
invalidNs = validSecret() invalidNs = validSecret()
overMaxSize = validSecret() overMaxSize = validSecret()
invalidKey = validSecret() invalidKey = validSecret()
leadingDotKey = validSecret()
dotKey = validSecret()
doubleDotKey = validSecret()
) )
emptyName.Name = "" emptyName.Name = ""
@ -2885,6 +2888,9 @@ func TestValidateSecret(t *testing.T) {
"over": make([]byte, api.MaxSecretSize+1), "over": make([]byte, api.MaxSecretSize+1),
} }
invalidKey.Data["a..b"] = []byte("whoops") 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 // kubernetes.io/service-account-token secret validation
validServiceAccountTokenSecret := func() api.Secret { validServiceAccountTokenSecret := func() api.Secret {
@ -2916,18 +2922,20 @@ func TestValidateSecret(t *testing.T) {
secret api.Secret secret api.Secret
valid bool valid bool
}{ }{
"valid": {validSecret(), true}, "valid": {validSecret(), true},
"empty name": {emptyName, false}, "empty name": {emptyName, false},
"invalid name": {invalidName, false}, "invalid name": {invalidName, false},
"empty namespace": {emptyNs, false}, "empty namespace": {emptyNs, false},
"invalid namespace": {invalidNs, false}, "invalid namespace": {invalidNs, false},
"over max size": {overMaxSize, false}, "over max size": {overMaxSize, false},
"invalid key": {invalidKey, false}, "invalid key": {invalidKey, false},
"valid service-account-token secret": {validServiceAccountTokenSecret(), true}, "valid service-account-token secret": {validServiceAccountTokenSecret(), true},
"empty service-account-token annotation": {emptyTokenAnnotation, false}, "empty service-account-token annotation": {emptyTokenAnnotation, false},
"missing service-account-token annotation": {missingTokenAnnotation, false}, "missing service-account-token annotation": {missingTokenAnnotation, false},
"missing service-account-token annotations": {missingTokenAnnotations, 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 { for name, tc := range tests {

View File

@ -63,7 +63,7 @@ func TestCreate(t *testing.T) {
}, },
&api.Secret{ &api.Secret{
ObjectMeta: api.ObjectMeta{Name: "name"}, ObjectMeta: api.ObjectMeta{Name: "name"},
Data: map[string][]byte{".dotfile": []byte("")}, Data: map[string][]byte{"~.dotfile": []byte("")},
}, },
) )
} }