mirror of https://github.com/k3s-io/k3s
commit
39a9043b8e
|
@ -2250,6 +2250,8 @@ const (
|
|||
ServiceAccountKubeconfigKey = "kubernetes.kubeconfig"
|
||||
// ServiceAccountRootCAKey is the key of the optional root certificate authority for SecretTypeServiceAccountToken secrets
|
||||
ServiceAccountRootCAKey = "ca.crt"
|
||||
// ServiceAccountNamespaceKey is the key of the optional namespace to use as the default for namespaced API calls
|
||||
ServiceAccountNamespaceKey = "namespace"
|
||||
|
||||
// SecretTypeDockercfg contains a dockercfg file that follows the same format rules as ~/.dockercfg
|
||||
//
|
||||
|
|
|
@ -2721,6 +2721,8 @@ const (
|
|||
ServiceAccountKubeconfigKey = "kubernetes.kubeconfig"
|
||||
// ServiceAccountRootCAKey is the key of the optional root certificate authority for SecretTypeServiceAccountToken secrets
|
||||
ServiceAccountRootCAKey = "ca.crt"
|
||||
// ServiceAccountNamespaceKey is the key of the optional namespace to use as the default for namespaced API calls
|
||||
ServiceAccountNamespaceKey = "namespace"
|
||||
|
||||
// SecretTypeDockercfg contains a dockercfg file that follows the same format rules as ~/.dockercfg
|
||||
//
|
||||
|
|
|
@ -19,8 +19,10 @@ package clientcmd
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/imdario/mergo"
|
||||
|
@ -325,12 +327,19 @@ func (inClusterClientConfig) ClientConfig() (*client.Config, error) {
|
|||
}
|
||||
|
||||
func (inClusterClientConfig) Namespace() (string, error) {
|
||||
// TODO: generic way to figure out what namespace you are running in?
|
||||
// This way assumes you've set the POD_NAMESPACE environment variable
|
||||
// using the downward API.
|
||||
// This way assumes you've set the POD_NAMESPACE environment variable using the downward API.
|
||||
// This check has to be done first for backwards compatibility with the way InClusterConfig was originally set up
|
||||
if ns := os.Getenv("POD_NAMESPACE"); ns != "" {
|
||||
return ns, nil
|
||||
}
|
||||
|
||||
// Fall back to the namespace associated with the service account token, if available
|
||||
if data, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil {
|
||||
if ns := strings.TrimSpace(string(data)); len(ns) > 0 {
|
||||
return ns, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "default", nil
|
||||
}
|
||||
|
||||
|
|
|
@ -325,6 +325,7 @@ func (e *TokensController) createSecret(serviceAccount *api.ServiceAccount) erro
|
|||
return err
|
||||
}
|
||||
secret.Data[api.ServiceAccountTokenKey] = []byte(token)
|
||||
secret.Data[api.ServiceAccountNamespaceKey] = []byte(serviceAccount.Namespace)
|
||||
if e.rootCA != nil && len(e.rootCA) > 0 {
|
||||
secret.Data[api.ServiceAccountRootCAKey] = e.rootCA
|
||||
}
|
||||
|
@ -364,10 +365,12 @@ func (e *TokensController) generateTokenIfNeeded(serviceAccount *api.ServiceAcco
|
|||
caData := secret.Data[api.ServiceAccountRootCAKey]
|
||||
needsCA := len(e.rootCA) > 0 && bytes.Compare(caData, e.rootCA) != 0
|
||||
|
||||
needsNamespace := len(secret.Data[api.ServiceAccountNamespaceKey]) == 0
|
||||
|
||||
tokenData := secret.Data[api.ServiceAccountTokenKey]
|
||||
needsToken := len(tokenData) == 0
|
||||
|
||||
if !needsCA && !needsToken {
|
||||
if !needsCA && !needsToken && !needsNamespace {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -375,6 +378,10 @@ func (e *TokensController) generateTokenIfNeeded(serviceAccount *api.ServiceAcco
|
|||
if needsCA {
|
||||
secret.Data[api.ServiceAccountRootCAKey] = e.rootCA
|
||||
}
|
||||
// Set the namespace
|
||||
if needsNamespace {
|
||||
secret.Data[api.ServiceAccountNamespaceKey] = []byte(secret.Namespace)
|
||||
}
|
||||
|
||||
// Generate the token
|
||||
if needsToken {
|
||||
|
|
|
@ -115,8 +115,9 @@ func createdTokenSecret() *api.Secret {
|
|||
},
|
||||
Type: api.SecretTypeServiceAccountToken,
|
||||
Data: map[string][]byte{
|
||||
"token": []byte("ABC"),
|
||||
"ca.crt": []byte("CA Data"),
|
||||
"token": []byte("ABC"),
|
||||
"ca.crt": []byte("CA Data"),
|
||||
"namespace": []byte("default"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -136,8 +137,9 @@ func serviceAccountTokenSecret() *api.Secret {
|
|||
},
|
||||
Type: api.SecretTypeServiceAccountToken,
|
||||
Data: map[string][]byte{
|
||||
"token": []byte("ABC"),
|
||||
"ca.crt": []byte("CA Data"),
|
||||
"token": []byte("ABC"),
|
||||
"ca.crt": []byte("CA Data"),
|
||||
"namespace": []byte("default"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -163,6 +165,20 @@ func serviceAccountTokenSecretWithCAData(data []byte) *api.Secret {
|
|||
return secret
|
||||
}
|
||||
|
||||
// serviceAccountTokenSecretWithoutNamespaceData returns an existing ServiceAccountToken secret that lacks namespace data
|
||||
func serviceAccountTokenSecretWithoutNamespaceData() *api.Secret {
|
||||
secret := serviceAccountTokenSecret()
|
||||
delete(secret.Data, api.ServiceAccountNamespaceKey)
|
||||
return secret
|
||||
}
|
||||
|
||||
// serviceAccountTokenSecretWithNamespaceData returns an existing ServiceAccountToken secret with the specified namespace data
|
||||
func serviceAccountTokenSecretWithNamespaceData(data []byte) *api.Secret {
|
||||
secret := serviceAccountTokenSecret()
|
||||
secret.Data[api.ServiceAccountNamespaceKey] = data
|
||||
return secret
|
||||
}
|
||||
|
||||
func TestTokenCreation(t *testing.T) {
|
||||
testcases := map[string]struct {
|
||||
ClientObjects []runtime.Object
|
||||
|
@ -379,6 +395,24 @@ func TestTokenCreation(t *testing.T) {
|
|||
core.NewUpdateAction("secrets", api.NamespaceDefault, serviceAccountTokenSecret()),
|
||||
},
|
||||
},
|
||||
"added token secret without namespace data": {
|
||||
ClientObjects: []runtime.Object{serviceAccountTokenSecretWithoutNamespaceData()},
|
||||
ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
|
||||
|
||||
AddedSecret: serviceAccountTokenSecretWithoutNamespaceData(),
|
||||
ExpectedActions: []core.Action{
|
||||
core.NewUpdateAction("secrets", api.NamespaceDefault, serviceAccountTokenSecret()),
|
||||
},
|
||||
},
|
||||
"added token secret with custom namespace data": {
|
||||
ClientObjects: []runtime.Object{serviceAccountTokenSecretWithNamespaceData([]byte("custom"))},
|
||||
ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
|
||||
|
||||
AddedSecret: serviceAccountTokenSecretWithNamespaceData([]byte("custom")),
|
||||
ExpectedActions: []core.Action{
|
||||
// no update is performed... the custom namespace is preserved
|
||||
},
|
||||
},
|
||||
|
||||
"updated secret without serviceaccount": {
|
||||
ClientObjects: []runtime.Object{serviceAccountTokenSecret()},
|
||||
|
@ -422,6 +456,24 @@ func TestTokenCreation(t *testing.T) {
|
|||
core.NewUpdateAction("secrets", api.NamespaceDefault, serviceAccountTokenSecret()),
|
||||
},
|
||||
},
|
||||
"updated token secret without namespace data": {
|
||||
ClientObjects: []runtime.Object{serviceAccountTokenSecretWithoutNamespaceData()},
|
||||
ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
|
||||
|
||||
UpdatedSecret: serviceAccountTokenSecretWithoutNamespaceData(),
|
||||
ExpectedActions: []core.Action{
|
||||
core.NewUpdateAction("secrets", api.NamespaceDefault, serviceAccountTokenSecret()),
|
||||
},
|
||||
},
|
||||
"updated token secret with custom namespace data": {
|
||||
ClientObjects: []runtime.Object{serviceAccountTokenSecretWithNamespaceData([]byte("custom"))},
|
||||
ExistingServiceAccount: serviceAccount(tokenSecretReferences()),
|
||||
|
||||
UpdatedSecret: serviceAccountTokenSecretWithNamespaceData([]byte("custom")),
|
||||
ExpectedActions: []core.Action{
|
||||
// no update is performed... the custom namespace is preserved
|
||||
},
|
||||
},
|
||||
|
||||
"deleted secret without serviceaccount": {
|
||||
DeletedSecret: serviceAccountTokenSecret(),
|
||||
|
|
|
@ -24,11 +24,14 @@ import (
|
|||
apierrors "k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
"k8s.io/kubernetes/pkg/util/wait"
|
||||
"k8s.io/kubernetes/pkg/version"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
var serviceAccountTokenNamespaceVersion = version.MustParse("v1.2.0")
|
||||
|
||||
var _ = Describe("ServiceAccounts", func() {
|
||||
f := NewFramework("svcaccounts")
|
||||
|
||||
|
@ -94,11 +97,28 @@ var _ = Describe("ServiceAccounts", func() {
|
|||
},
|
||||
}
|
||||
|
||||
supportsTokenNamespace, _ := serverVersionGTE(serviceAccountTokenNamespaceVersion, f.Client)
|
||||
if supportsTokenNamespace {
|
||||
pod.Spec.Containers = append(pod.Spec.Containers, api.Container{
|
||||
Name: "namespace-test",
|
||||
Image: "gcr.io/google_containers/mounttest:0.2",
|
||||
Args: []string{
|
||||
fmt.Sprintf("--file_content=%s/%s", serviceaccount.DefaultAPITokenMountPath, api.ServiceAccountNamespaceKey),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
f.TestContainerOutput("consume service account token", pod, 0, []string{
|
||||
fmt.Sprintf(`content of file "%s/%s": %s`, serviceaccount.DefaultAPITokenMountPath, api.ServiceAccountTokenKey, tokenContent),
|
||||
})
|
||||
f.TestContainerOutput("consume service account root CA", pod, 1, []string{
|
||||
fmt.Sprintf(`content of file "%s/%s": %s`, serviceaccount.DefaultAPITokenMountPath, api.ServiceAccountRootCAKey, rootCAContent),
|
||||
})
|
||||
|
||||
if supportsTokenNamespace {
|
||||
f.TestContainerOutput("consume service account namespace", pod, 2, []string{
|
||||
fmt.Sprintf(`content of file "%s/%s": %s`, serviceaccount.DefaultAPITokenMountPath, api.ServiceAccountNamespaceKey, f.Namespace.Name),
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue