Merge pull request #93258 from zshihang/token

mv TokenRequest and TokenRequestProjection to GA
This commit is contained in:
Kubernetes Prow Robot 2020-10-30 16:36:51 -07:00 committed by GitHub
commit bf67247124
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 254 additions and 327 deletions

View File

@ -18905,6 +18905,32 @@
"version": "unversioned" "version": "unversioned"
}, },
"paths": { "paths": {
"/.well-known/openid-configuration/": {
"get": {
"description": "get service account issuer OpenID configuration, also known as the 'OIDC discovery doc'",
"operationId": "getServiceAccountIssuerOpenIDConfiguration",
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"401": {
"description": "Unauthorized"
}
},
"schemes": [
"https"
],
"tags": [
"WellKnown"
]
}
},
"/api/": { "/api/": {
"get": { "get": {
"consumes": [ "consumes": [
@ -105104,6 +105130,32 @@
} }
] ]
}, },
"/openid/v1/jwks/": {
"get": {
"description": "get service account issuer OpenID JSON Web Key Set (contains public token verification keys)",
"operationId": "getServiceAccountIssuerOpenIDKeyset",
"produces": [
"application/jwk-set+json"
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"401": {
"description": "Unauthorized"
}
},
"schemes": [
"https"
],
"tags": [
"openid"
]
}
},
"/version/": { "/version/": {
"get": { "get": {
"consumes": [ "consumes": [

View File

@ -270,7 +270,7 @@ func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) {
"Turns on aggregator routing requests to endpoints IP rather than cluster IP.") "Turns on aggregator routing requests to endpoints IP rather than cluster IP.")
fs.StringVar(&s.ServiceAccountSigningKeyFile, "service-account-signing-key-file", s.ServiceAccountSigningKeyFile, ""+ fs.StringVar(&s.ServiceAccountSigningKeyFile, "service-account-signing-key-file", s.ServiceAccountSigningKeyFile, ""+
"Path to the file that contains the current private key of the service account token issuer. The issuer will sign issued ID tokens with this private key. (Requires the 'TokenRequest' feature gate.)") "Path to the file that contains the current private key of the service account token issuer. The issuer will sign issued ID tokens with this private key.")
return fss return fss
} }

View File

@ -120,14 +120,6 @@ func validateTokenRequest(options *ServerRunOptions) []error {
enableSucceeded := options.ServiceAccountIssuer != nil enableSucceeded := options.ServiceAccountIssuer != nil
if enableAttempted && !utilfeature.DefaultFeatureGate.Enabled(features.TokenRequest) {
errs = append(errs, errors.New("the TokenRequest feature is not enabled but --service-account-signing-key-file, --service-account-issuer and/or --api-audiences flags were passed"))
}
if utilfeature.DefaultFeatureGate.Enabled(features.BoundServiceAccountTokenVolume) && !utilfeature.DefaultFeatureGate.Enabled(features.TokenRequest) {
errs = append(errs, errors.New("the BoundServiceAccountTokenVolume feature depends on the TokenRequest feature, but the TokenRequest features is not enabled"))
}
if !enableAttempted && utilfeature.DefaultFeatureGate.Enabled(features.BoundServiceAccountTokenVolume) { if !enableAttempted && utilfeature.DefaultFeatureGate.Enabled(features.BoundServiceAccountTokenVolume) {
errs = append(errs, errors.New("--service-account-signing-key-file and --service-account-issuer are required flags")) errs = append(errs, errors.New("--service-account-signing-key-file and --service-account-issuer are required flags"))
} }

View File

@ -39,7 +39,6 @@ import (
"k8s.io/apiserver/pkg/server" "k8s.io/apiserver/pkg/server"
"k8s.io/apiserver/pkg/server/healthz" "k8s.io/apiserver/pkg/server/healthz"
"k8s.io/apiserver/pkg/server/mux" "k8s.io/apiserver/pkg/server/mux"
utilfeature "k8s.io/apiserver/pkg/util/feature"
cacheddiscovery "k8s.io/client-go/discovery/cached" cacheddiscovery "k8s.io/client-go/discovery/cached"
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
@ -67,7 +66,6 @@ import (
"k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller"
kubectrlmgrconfig "k8s.io/kubernetes/pkg/controller/apis/config" kubectrlmgrconfig "k8s.io/kubernetes/pkg/controller/apis/config"
serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount" serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/serviceaccount" "k8s.io/kubernetes/pkg/serviceaccount"
) )
@ -621,9 +619,6 @@ func readCA(file string) ([]byte, error) {
} }
func shouldTurnOnDynamicClient(client clientset.Interface) bool { func shouldTurnOnDynamicClient(client clientset.Interface) bool {
if !utilfeature.DefaultFeatureGate.Enabled(features.TokenRequest) {
return false
}
apiResourceList, err := client.Discovery().ServerResourcesForGroupVersion(v1.SchemeGroupVersion.String()) apiResourceList, err := client.Discovery().ServerResourcesForGroupVersion(v1.SchemeGroupVersion.String())
if err != nil { if err != nil {
klog.Warningf("fetch api resource lists failed, use legacy client builder: %v", err) klog.Warningf("fetch api resource lists failed, use legacy client builder: %v", err)

View File

@ -137,6 +137,8 @@ func getAPIServerCommand(cfg *kubeadmapi.ClusterConfiguration, localAPIEndpoint
"enable-admission-plugins": "NodeRestriction", "enable-admission-plugins": "NodeRestriction",
"service-cluster-ip-range": cfg.Networking.ServiceSubnet, "service-cluster-ip-range": cfg.Networking.ServiceSubnet,
"service-account-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPublicKeyName), "service-account-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPublicKeyName),
"service-account-signing-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.ServiceAccountPrivateKeyName),
"service-account-issuer": fmt.Sprintf("https://kubernetes.default.svc.%s", cfg.Networking.DNSDomain),
"client-ca-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.CACertName), "client-ca-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.CACertName),
"tls-cert-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerCertName), "tls-cert-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerCertName),
"tls-private-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerKeyName), "tls-private-key-file": filepath.Join(cfg.CertificatesDir, kubeadmconstants.APIServerKeyName),

View File

@ -197,7 +197,7 @@ func TestGetAPIServerCommand(t *testing.T) {
{ {
name: "testing defaults", name: "testing defaults",
cfg: &kubeadmapi.ClusterConfiguration{ cfg: &kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{ServiceSubnet: "bar"}, Networking: kubeadmapi.Networking{ServiceSubnet: "bar", DNSDomain: "cluster.local"},
CertificatesDir: testCertsDir, CertificatesDir: testCertsDir,
}, },
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"}, endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "1.2.3.4"},
@ -207,6 +207,8 @@ func TestGetAPIServerCommand(t *testing.T) {
"--enable-admission-plugins=NodeRestriction", "--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar", "--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub", "--service-account-key-file=" + testCertsDir + "/sa.pub",
"--service-account-signing-key-file=" + testCertsDir + "/sa.key",
"--service-account-issuer=https://kubernetes.default.svc.cluster.local",
"--client-ca-file=" + testCertsDir + "/ca.crt", "--client-ca-file=" + testCertsDir + "/ca.crt",
"--tls-cert-file=" + testCertsDir + "/apiserver.crt", "--tls-cert-file=" + testCertsDir + "/apiserver.crt",
"--tls-private-key-file=" + testCertsDir + "/apiserver.key", "--tls-private-key-file=" + testCertsDir + "/apiserver.key",
@ -234,7 +236,7 @@ func TestGetAPIServerCommand(t *testing.T) {
{ {
name: "ipv6 advertise address", name: "ipv6 advertise address",
cfg: &kubeadmapi.ClusterConfiguration{ cfg: &kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{ServiceSubnet: "bar"}, Networking: kubeadmapi.Networking{ServiceSubnet: "bar", DNSDomain: "cluster.local"},
CertificatesDir: testCertsDir, CertificatesDir: testCertsDir,
}, },
endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "2001:db8::1"}, endpoint: &kubeadmapi.APIEndpoint{BindPort: 123, AdvertiseAddress: "2001:db8::1"},
@ -244,6 +246,8 @@ func TestGetAPIServerCommand(t *testing.T) {
"--enable-admission-plugins=NodeRestriction", "--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar", "--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub", "--service-account-key-file=" + testCertsDir + "/sa.pub",
"--service-account-signing-key-file=" + testCertsDir + "/sa.key",
"--service-account-issuer=https://kubernetes.default.svc.cluster.local",
"--client-ca-file=" + testCertsDir + "/ca.crt", "--client-ca-file=" + testCertsDir + "/ca.crt",
"--tls-cert-file=" + testCertsDir + "/apiserver.crt", "--tls-cert-file=" + testCertsDir + "/apiserver.crt",
"--tls-private-key-file=" + testCertsDir + "/apiserver.key", "--tls-private-key-file=" + testCertsDir + "/apiserver.key",
@ -271,7 +275,7 @@ func TestGetAPIServerCommand(t *testing.T) {
{ {
name: "an external etcd with custom ca, certs and keys", name: "an external etcd with custom ca, certs and keys",
cfg: &kubeadmapi.ClusterConfiguration{ cfg: &kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{ServiceSubnet: "bar"}, Networking: kubeadmapi.Networking{ServiceSubnet: "bar", DNSDomain: "cluster.local"},
Etcd: kubeadmapi.Etcd{ Etcd: kubeadmapi.Etcd{
External: &kubeadmapi.ExternalEtcd{ External: &kubeadmapi.ExternalEtcd{
Endpoints: []string{"https://[2001:abcd:bcda::1]:2379", "https://[2001:abcd:bcda::2]:2379"}, Endpoints: []string{"https://[2001:abcd:bcda::1]:2379", "https://[2001:abcd:bcda::2]:2379"},
@ -289,6 +293,8 @@ func TestGetAPIServerCommand(t *testing.T) {
"--enable-admission-plugins=NodeRestriction", "--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar", "--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub", "--service-account-key-file=" + testCertsDir + "/sa.pub",
"--service-account-signing-key-file=" + testCertsDir + "/sa.key",
"--service-account-issuer=https://kubernetes.default.svc.cluster.local",
"--client-ca-file=" + testCertsDir + "/ca.crt", "--client-ca-file=" + testCertsDir + "/ca.crt",
"--tls-cert-file=" + testCertsDir + "/apiserver.crt", "--tls-cert-file=" + testCertsDir + "/apiserver.crt",
"--tls-private-key-file=" + testCertsDir + "/apiserver.key", "--tls-private-key-file=" + testCertsDir + "/apiserver.key",
@ -316,7 +322,7 @@ func TestGetAPIServerCommand(t *testing.T) {
{ {
name: "an insecure etcd", name: "an insecure etcd",
cfg: &kubeadmapi.ClusterConfiguration{ cfg: &kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{ServiceSubnet: "bar"}, Networking: kubeadmapi.Networking{ServiceSubnet: "bar", DNSDomain: "cluster.local"},
Etcd: kubeadmapi.Etcd{ Etcd: kubeadmapi.Etcd{
External: &kubeadmapi.ExternalEtcd{ External: &kubeadmapi.ExternalEtcd{
Endpoints: []string{"http://[::1]:2379", "http://[::1]:2380"}, Endpoints: []string{"http://[::1]:2379", "http://[::1]:2380"},
@ -331,6 +337,8 @@ func TestGetAPIServerCommand(t *testing.T) {
"--enable-admission-plugins=NodeRestriction", "--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar", "--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub", "--service-account-key-file=" + testCertsDir + "/sa.pub",
"--service-account-signing-key-file=" + testCertsDir + "/sa.key",
"--service-account-issuer=https://kubernetes.default.svc.cluster.local",
"--client-ca-file=" + testCertsDir + "/ca.crt", "--client-ca-file=" + testCertsDir + "/ca.crt",
"--tls-cert-file=" + testCertsDir + "/apiserver.crt", "--tls-cert-file=" + testCertsDir + "/apiserver.crt",
"--tls-private-key-file=" + testCertsDir + "/apiserver.key", "--tls-private-key-file=" + testCertsDir + "/apiserver.key",
@ -355,7 +363,7 @@ func TestGetAPIServerCommand(t *testing.T) {
{ {
name: "test APIServer.ExtraArgs works as expected", name: "test APIServer.ExtraArgs works as expected",
cfg: &kubeadmapi.ClusterConfiguration{ cfg: &kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{ServiceSubnet: "bar"}, Networking: kubeadmapi.Networking{ServiceSubnet: "bar", DNSDomain: "cluster.local"},
CertificatesDir: testCertsDir, CertificatesDir: testCertsDir,
APIServer: kubeadmapi.APIServer{ APIServer: kubeadmapi.APIServer{
ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{ ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{
@ -375,6 +383,8 @@ func TestGetAPIServerCommand(t *testing.T) {
"--enable-admission-plugins=NodeRestriction", "--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=baz", "--service-cluster-ip-range=baz",
"--service-account-key-file=" + testCertsDir + "/sa.pub", "--service-account-key-file=" + testCertsDir + "/sa.pub",
"--service-account-signing-key-file=" + testCertsDir + "/sa.key",
"--service-account-issuer=https://kubernetes.default.svc.cluster.local",
"--client-ca-file=" + testCertsDir + "/ca.crt", "--client-ca-file=" + testCertsDir + "/ca.crt",
"--tls-cert-file=" + testCertsDir + "/apiserver.crt", "--tls-cert-file=" + testCertsDir + "/apiserver.crt",
"--tls-private-key-file=" + testCertsDir + "/apiserver.key", "--tls-private-key-file=" + testCertsDir + "/apiserver.key",
@ -404,7 +414,7 @@ func TestGetAPIServerCommand(t *testing.T) {
{ {
name: "authorization-mode extra-args ABAC", name: "authorization-mode extra-args ABAC",
cfg: &kubeadmapi.ClusterConfiguration{ cfg: &kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{ServiceSubnet: "bar"}, Networking: kubeadmapi.Networking{ServiceSubnet: "bar", DNSDomain: "cluster.local"},
CertificatesDir: testCertsDir, CertificatesDir: testCertsDir,
APIServer: kubeadmapi.APIServer{ APIServer: kubeadmapi.APIServer{
ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{ ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{
@ -421,6 +431,8 @@ func TestGetAPIServerCommand(t *testing.T) {
"--enable-admission-plugins=NodeRestriction", "--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar", "--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub", "--service-account-key-file=" + testCertsDir + "/sa.pub",
"--service-account-signing-key-file=" + testCertsDir + "/sa.key",
"--service-account-issuer=https://kubernetes.default.svc.cluster.local",
"--client-ca-file=" + testCertsDir + "/ca.crt", "--client-ca-file=" + testCertsDir + "/ca.crt",
"--tls-cert-file=" + testCertsDir + "/apiserver.crt", "--tls-cert-file=" + testCertsDir + "/apiserver.crt",
"--tls-private-key-file=" + testCertsDir + "/apiserver.key", "--tls-private-key-file=" + testCertsDir + "/apiserver.key",
@ -448,7 +460,7 @@ func TestGetAPIServerCommand(t *testing.T) {
{ {
name: "insecure-port extra-args", name: "insecure-port extra-args",
cfg: &kubeadmapi.ClusterConfiguration{ cfg: &kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{ServiceSubnet: "bar"}, Networking: kubeadmapi.Networking{ServiceSubnet: "bar", DNSDomain: "cluster.local"},
CertificatesDir: testCertsDir, CertificatesDir: testCertsDir,
APIServer: kubeadmapi.APIServer{ APIServer: kubeadmapi.APIServer{
ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{ ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{
@ -465,6 +477,8 @@ func TestGetAPIServerCommand(t *testing.T) {
"--enable-admission-plugins=NodeRestriction", "--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar", "--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub", "--service-account-key-file=" + testCertsDir + "/sa.pub",
"--service-account-signing-key-file=" + testCertsDir + "/sa.key",
"--service-account-issuer=https://kubernetes.default.svc.cluster.local",
"--client-ca-file=" + testCertsDir + "/ca.crt", "--client-ca-file=" + testCertsDir + "/ca.crt",
"--tls-cert-file=" + testCertsDir + "/apiserver.crt", "--tls-cert-file=" + testCertsDir + "/apiserver.crt",
"--tls-private-key-file=" + testCertsDir + "/apiserver.key", "--tls-private-key-file=" + testCertsDir + "/apiserver.key",
@ -492,7 +506,7 @@ func TestGetAPIServerCommand(t *testing.T) {
{ {
name: "authorization-mode extra-args Webhook", name: "authorization-mode extra-args Webhook",
cfg: &kubeadmapi.ClusterConfiguration{ cfg: &kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{ServiceSubnet: "bar"}, Networking: kubeadmapi.Networking{ServiceSubnet: "bar", DNSDomain: "cluster.local"},
CertificatesDir: testCertsDir, CertificatesDir: testCertsDir,
APIServer: kubeadmapi.APIServer{ APIServer: kubeadmapi.APIServer{
ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{ ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{
@ -513,6 +527,8 @@ func TestGetAPIServerCommand(t *testing.T) {
"--enable-admission-plugins=NodeRestriction", "--enable-admission-plugins=NodeRestriction",
"--service-cluster-ip-range=bar", "--service-cluster-ip-range=bar",
"--service-account-key-file=" + testCertsDir + "/sa.pub", "--service-account-key-file=" + testCertsDir + "/sa.pub",
"--service-account-signing-key-file=" + testCertsDir + "/sa.key",
"--service-account-issuer=https://kubernetes.default.svc.cluster.local",
"--client-ca-file=" + testCertsDir + "/ca.crt", "--client-ca-file=" + testCertsDir + "/ca.crt",
"--tls-cert-file=" + testCertsDir + "/apiserver.crt", "--tls-cert-file=" + testCertsDir + "/apiserver.crt",
"--tls-private-key-file=" + testCertsDir + "/apiserver.key", "--tls-private-key-file=" + testCertsDir + "/apiserver.key",
@ -628,7 +644,7 @@ func TestGetControllerManagerCommand(t *testing.T) {
{ {
name: "custom cluster-cidr for " + cpVersion, name: "custom cluster-cidr for " + cpVersion,
cfg: &kubeadmapi.ClusterConfiguration{ cfg: &kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{PodSubnet: "10.0.1.15/16"}, Networking: kubeadmapi.Networking{PodSubnet: "10.0.1.15/16", DNSDomain: "cluster.local"},
CertificatesDir: testCertsDir, CertificatesDir: testCertsDir,
KubernetesVersion: cpVersion, KubernetesVersion: cpVersion,
}, },
@ -657,7 +673,9 @@ func TestGetControllerManagerCommand(t *testing.T) {
cfg: &kubeadmapi.ClusterConfiguration{ cfg: &kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{ Networking: kubeadmapi.Networking{
PodSubnet: "10.0.1.15/16", PodSubnet: "10.0.1.15/16",
ServiceSubnet: "172.20.0.0/24"}, ServiceSubnet: "172.20.0.0/24",
DNSDomain: "cluster.local",
},
CertificatesDir: testCertsDir, CertificatesDir: testCertsDir,
KubernetesVersion: cpVersion, KubernetesVersion: cpVersion,
}, },
@ -685,7 +703,7 @@ func TestGetControllerManagerCommand(t *testing.T) {
{ {
name: "custom extra-args for " + cpVersion, name: "custom extra-args for " + cpVersion,
cfg: &kubeadmapi.ClusterConfiguration{ cfg: &kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{PodSubnet: "10.0.1.15/16"}, Networking: kubeadmapi.Networking{PodSubnet: "10.0.1.15/16", DNSDomain: "cluster.local"},
ControllerManager: kubeadmapi.ControlPlaneComponent{ ControllerManager: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{"node-cidr-mask-size": "20"}, ExtraArgs: map[string]string{"node-cidr-mask-size": "20"},
}, },
@ -719,7 +737,9 @@ func TestGetControllerManagerCommand(t *testing.T) {
Networking: kubeadmapi.Networking{ Networking: kubeadmapi.Networking{
PodSubnet: "2001:db8::/64", PodSubnet: "2001:db8::/64",
ServiceSubnet: "fd03::/112", ServiceSubnet: "fd03::/112",
DNSDomain: "cluster.local",
}, },
CertificatesDir: testCertsDir, CertificatesDir: testCertsDir,
KubernetesVersion: cpVersion, KubernetesVersion: cpVersion,
}, },
@ -750,6 +770,7 @@ func TestGetControllerManagerCommand(t *testing.T) {
Networking: kubeadmapi.Networking{ Networking: kubeadmapi.Networking{
PodSubnet: "2001:db8::/64,10.1.0.0/16", PodSubnet: "2001:db8::/64,10.1.0.0/16",
ServiceSubnet: "fd03::/112,192.168.0.0/16", ServiceSubnet: "fd03::/112,192.168.0.0/16",
DNSDomain: "cluster.local",
}, },
CertificatesDir: testCertsDir, CertificatesDir: testCertsDir,
KubernetesVersion: cpVersion, KubernetesVersion: cpVersion,
@ -780,7 +801,10 @@ func TestGetControllerManagerCommand(t *testing.T) {
{ {
name: "dual-stack networking custom extra-args for " + cpVersion, name: "dual-stack networking custom extra-args for " + cpVersion,
cfg: &kubeadmapi.ClusterConfiguration{ cfg: &kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{PodSubnet: "10.0.1.15/16,2001:db8::/64"}, Networking: kubeadmapi.Networking{
PodSubnet: "10.0.1.15/16,2001:db8::/64",
DNSDomain: "cluster.local",
},
ControllerManager: kubeadmapi.ControlPlaneComponent{ ControllerManager: kubeadmapi.ControlPlaneComponent{
ExtraArgs: map[string]string{"node-cidr-mask-size-ipv4": "20", "node-cidr-mask-size-ipv6": "80"}, ExtraArgs: map[string]string{"node-cidr-mask-size-ipv4": "20", "node-cidr-mask-size-ipv6": "80"},
}, },

View File

@ -34,6 +34,15 @@ source "${KUBE_ROOT}/hack/lib/init.sh"
source "${KUBE_ROOT}/hack/lib/test.sh" source "${KUBE_ROOT}/hack/lib/test.sh"
source "${KUBE_ROOT}/test/cmd/legacy-script.sh" source "${KUBE_ROOT}/test/cmd/legacy-script.sh"
# setup envs for TokenRequest required flags
SERVICE_ACCOUNT_LOOKUP=${SERVICE_ACCOUNT_LOOKUP:-true}
SERVICE_ACCOUNT_KEY=${SERVICE_ACCOUNT_KEY:-/tmp/kube-serviceaccount.key}
# Generate ServiceAccount key if needed
if [[ ! -f "${SERVICE_ACCOUNT_KEY}" ]]; then
mkdir -p "$(dirname "${SERVICE_ACCOUNT_KEY}")"
openssl genrsa -out "${SERVICE_ACCOUNT_KEY}" 2048 2>/dev/null
fi
# Runs kube-apiserver # Runs kube-apiserver
# #
# Exports: # Exports:
@ -64,6 +73,10 @@ function run_kube_apiserver() {
--disable-admission-plugins="${DISABLE_ADMISSION_PLUGINS}" \ --disable-admission-plugins="${DISABLE_ADMISSION_PLUGINS}" \
--etcd-servers="http://${ETCD_HOST}:${ETCD_PORT}" \ --etcd-servers="http://${ETCD_HOST}:${ETCD_PORT}" \
--runtime-config=api/v1 \ --runtime-config=api/v1 \
--service-account-key-file="${SERVICE_ACCOUNT_KEY}" \
--service-account-lookup="${SERVICE_ACCOUNT_LOOKUP}" \
--service-account-issuer="https://kubernetes.default.svc" \
--service-account-signing-key-file="${SERVICE_ACCOUNT_KEY}" \
--storage-media-type="${KUBE_TEST_API_STORAGE_TYPE-}" \ --storage-media-type="${KUBE_TEST_API_STORAGE_TYPE-}" \
--cert-dir="${TMPDIR:-/tmp/}" \ --cert-dir="${TMPDIR:-/tmp/}" \
--service-cluster-ip-range="10.0.0.0/24" \ --service-cluster-ip-range="10.0.0.0/24" \

View File

@ -58,6 +58,15 @@ kube::etcd::start
echo "dummy_token,admin,admin" > "${TMP_DIR}/tokenauth.csv" echo "dummy_token,admin,admin" > "${TMP_DIR}/tokenauth.csv"
# setup envs for TokenRequest required flags
SERVICE_ACCOUNT_LOOKUP=${SERVICE_ACCOUNT_LOOKUP:-true}
SERVICE_ACCOUNT_KEY=${SERVICE_ACCOUNT_KEY:-/tmp/kube-serviceaccount.key}
# Generate ServiceAccount key if needed
if [[ ! -f "${SERVICE_ACCOUNT_KEY}" ]]; then
mkdir -p "$(dirname "${SERVICE_ACCOUNT_KEY}")"
openssl genrsa -out "${SERVICE_ACCOUNT_KEY}" 2048 2>/dev/null
fi
# Start kube-apiserver # Start kube-apiserver
kube::log::status "Starting kube-apiserver" kube::log::status "Starting kube-apiserver"
"${KUBE_OUTPUT_HOSTBIN}/kube-apiserver" \ "${KUBE_OUTPUT_HOSTBIN}/kube-apiserver" \
@ -69,8 +78,10 @@ kube::log::status "Starting kube-apiserver"
--runtime-config="api/all=true" \ --runtime-config="api/all=true" \
--token-auth-file="${TMP_DIR}/tokenauth.csv" \ --token-auth-file="${TMP_DIR}/tokenauth.csv" \
--authorization-mode=RBAC \ --authorization-mode=RBAC \
--service-account-issuer="https://kubernetes.default.svc/" \ --service-account-key-file="${SERVICE_ACCOUNT_KEY}" \
--service-account-signing-key-file="${KUBE_ROOT}/staging/src/k8s.io/client-go/util/cert/testdata/dontUseThisKey.pem" \ --service-account-lookup="${SERVICE_ACCOUNT_LOOKUP}" \
--service-account-issuer="https://kubernetes.default.svc" \
--service-account-signing-key-file="${SERVICE_ACCOUNT_KEY}" \
--logtostderr \ --logtostderr \
--v=2 \ --v=2 \
--service-cluster-ip-range="10.0.0.0/24" >"${API_LOGFILE}" 2>&1 & --service-cluster-ip-range="10.0.0.0/24" >"${API_LOGFILE}" 2>&1 &

View File

@ -19,7 +19,7 @@ package pod
import ( import (
"strings" "strings"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
@ -349,18 +349,6 @@ func dropDisabledFields(
podSpec = &api.PodSpec{} podSpec = &api.PodSpec{}
} }
if !utilfeature.DefaultFeatureGate.Enabled(features.TokenRequestProjection) &&
!tokenRequestProjectionInUse(oldPodSpec) {
for i := range podSpec.Volumes {
if podSpec.Volumes[i].Projected != nil {
for j := range podSpec.Volumes[i].Projected.Sources {
podSpec.Volumes[i].Projected.Sources[j].ServiceAccountToken = nil
}
}
}
}
if !utilfeature.DefaultFeatureGate.Enabled(features.AppArmor) && !appArmorInUse(oldPodAnnotations) { if !utilfeature.DefaultFeatureGate.Enabled(features.AppArmor) && !appArmorInUse(oldPodAnnotations) {
for k := range podAnnotations { for k := range podAnnotations {
if strings.HasPrefix(k, v1.AppArmorBetaContainerAnnotationKeyPrefix) { if strings.HasPrefix(k, v1.AppArmorBetaContainerAnnotationKeyPrefix) {
@ -593,23 +581,6 @@ func appArmorInUse(podAnnotations map[string]string) bool {
return false return false
} }
func tokenRequestProjectionInUse(podSpec *api.PodSpec) bool {
if podSpec == nil {
return false
}
for _, v := range podSpec.Volumes {
if v.Projected == nil {
continue
}
for _, s := range v.Projected.Sources {
if s.ServiceAccountToken != nil {
return true
}
}
}
return false
}
// podPriorityInUse returns true if the pod spec is non-nil and has Priority or PriorityClassName set. // podPriorityInUse returns true if the pod spec is non-nil and has Priority or PriorityClassName set.
func podPriorityInUse(podSpec *api.PodSpec) bool { func podPriorityInUse(podSpec *api.PodSpec) bool {
if podSpec == nil { if podSpec == nil {

View File

@ -24,7 +24,7 @@ import (
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/diff" "k8s.io/apimachinery/pkg/util/diff"
@ -1106,122 +1106,6 @@ func TestDropAppArmor(t *testing.T) {
} }
} }
func TestDropTokenRequestProjection(t *testing.T) {
podWithoutTRProjection := func() *api.Pod {
return &api.Pod{
Spec: api.PodSpec{
Volumes: []api.Volume{{
VolumeSource: api.VolumeSource{
Projected: &api.ProjectedVolumeSource{
Sources: []api.VolumeProjection{{
ServiceAccountToken: nil,
}},
}}},
},
},
}
}
podWithoutProjectedVolumeSource := func() *api.Pod {
return &api.Pod{
Spec: api.PodSpec{
Volumes: []api.Volume{
{VolumeSource: api.VolumeSource{
ConfigMap: &api.ConfigMapVolumeSource{},
}},
},
},
}
}
podWithTRProjection := func() *api.Pod {
return &api.Pod{
Spec: api.PodSpec{
Volumes: []api.Volume{{
VolumeSource: api.VolumeSource{
Projected: &api.ProjectedVolumeSource{
Sources: []api.VolumeProjection{{
ServiceAccountToken: &api.ServiceAccountTokenProjection{
Audience: "api",
ExpirationSeconds: 3600,
Path: "token",
}},
}},
},
},
},
}}
}
podInfo := []struct {
description string
hasTRProjection bool
pod func() *api.Pod
}{
{
description: "has TokenRequestProjection",
hasTRProjection: true,
pod: podWithTRProjection,
},
{
description: "does not have TokenRequestProjection",
hasTRProjection: false,
pod: podWithoutTRProjection,
},
{
description: "does not have ProjectedVolumeSource",
hasTRProjection: false,
pod: podWithoutProjectedVolumeSource,
},
{
description: "is nil",
hasTRProjection: false,
pod: func() *api.Pod { return nil },
},
}
for _, enabled := range []bool{true, false} {
for _, oldPodInfo := range podInfo {
for _, newPodInfo := range podInfo {
oldPodhasTRProjection, oldPod := oldPodInfo.hasTRProjection, oldPodInfo.pod()
newPodhasTRProjection, newPod := newPodInfo.hasTRProjection, newPodInfo.pod()
if newPod == nil {
continue
}
t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TokenRequestProjection, enabled)()
var oldPodSpec *api.PodSpec
if oldPod != nil {
oldPodSpec = &oldPod.Spec
}
dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
// old pod should never be changed
if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
t.Errorf("old pod changed: %v", diff.ObjectReflectDiff(oldPod, oldPodInfo.pod()))
}
switch {
case enabled || oldPodhasTRProjection:
if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
}
case newPodhasTRProjection:
// new pod should be changed
if reflect.DeepEqual(newPod, newPodInfo.pod()) {
t.Errorf("%v", oldPod)
t.Errorf("%v", newPod)
t.Errorf("new pod was not changed")
}
if !reflect.DeepEqual(newPod, podWithoutTRProjection()) {
t.Errorf("new pod had Tokenrequestprojection: %v", diff.ObjectReflectDiff(newPod, podWithoutTRProjection()))
}
default:
// new pod should not need to be changed
if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
t.Errorf("new pod changed: %v", diff.ObjectReflectDiff(newPod, newPodInfo.pod()))
}
}
})
}
}
}
}
func TestDropRunAsGroup(t *testing.T) { func TestDropRunAsGroup(t *testing.T) {
group := func() *int64 { group := func() *int64 {
testGroup := int64(1000) testGroup := int64(1000)

View File

@ -211,12 +211,14 @@ const (
// owner: @mikedanese // owner: @mikedanese
// beta: v1.12 // beta: v1.12
// ga: v1.20
// //
// Implement TokenRequest endpoint on service account resources. // Implement TokenRequest endpoint on service account resources.
TokenRequest featuregate.Feature = "TokenRequest" TokenRequest featuregate.Feature = "TokenRequest"
// owner: @mikedanese // owner: @mikedanese
// beta: v1.12 // beta: v1.12
// ga: v1.20
// //
// Enable ServiceAccountTokenVolumeProjection support in ProjectedVolumes. // Enable ServiceAccountTokenVolumeProjection support in ProjectedVolumes.
TokenRequestProjection featuregate.Feature = "TokenRequestProjection" TokenRequestProjection featuregate.Feature = "TokenRequestProjection"
@ -686,8 +688,8 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
SupportPodPidsLimit: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.21 SupportPodPidsLimit: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.21
SupportNodePidsLimit: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.21 SupportNodePidsLimit: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.21
HyperVContainer: {Default: false, PreRelease: featuregate.Deprecated}, HyperVContainer: {Default: false, PreRelease: featuregate.Deprecated},
TokenRequest: {Default: true, PreRelease: featuregate.Beta}, TokenRequest: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.21
TokenRequestProjection: {Default: true, PreRelease: featuregate.Beta}, TokenRequestProjection: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.21
BoundServiceAccountTokenVolume: {Default: false, PreRelease: featuregate.Alpha}, BoundServiceAccountTokenVolume: {Default: false, PreRelease: featuregate.Alpha},
ServiceAccountIssuerDiscovery: {Default: true, PreRelease: featuregate.Beta}, ServiceAccountIssuerDiscovery: {Default: true, PreRelease: featuregate.Beta},
CRIContainerLogRotation: {Default: true, PreRelease: featuregate.Beta}, CRIContainerLogRotation: {Default: true, PreRelease: featuregate.Beta},

View File

@ -10,7 +10,6 @@ go_library(
srcs = ["config.go"], srcs = ["config.go"],
importpath = "k8s.io/kubernetes/pkg/kubeapiserver/authenticator", importpath = "k8s.io/kubernetes/pkg/kubeapiserver/authenticator",
deps = [ deps = [
"//pkg/features:go_default_library",
"//pkg/serviceaccount:go_default_library", "//pkg/serviceaccount:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library", "//staging/src/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
@ -26,7 +25,6 @@ go_library(
"//staging/src/k8s.io/apiserver/pkg/authentication/token/tokenfile:go_default_library", "//staging/src/k8s.io/apiserver/pkg/authentication/token/tokenfile:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/token/union:go_default_library", "//staging/src/k8s.io/apiserver/pkg/authentication/token/union:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/server/dynamiccertificates:go_default_library", "//staging/src/k8s.io/apiserver/pkg/server/dynamiccertificates:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc:go_default_library", "//staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc:go_default_library",
"//staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook:go_default_library", "//staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook:go_default_library",
"//staging/src/k8s.io/client-go/plugin/pkg/client/auth:go_default_library", "//staging/src/k8s.io/client-go/plugin/pkg/client/auth:go_default_library",

View File

@ -35,14 +35,12 @@ import (
"k8s.io/apiserver/pkg/authentication/token/tokenfile" "k8s.io/apiserver/pkg/authentication/token/tokenfile"
tokenunion "k8s.io/apiserver/pkg/authentication/token/union" tokenunion "k8s.io/apiserver/pkg/authentication/token/union"
"k8s.io/apiserver/pkg/server/dynamiccertificates" "k8s.io/apiserver/pkg/server/dynamiccertificates"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/apiserver/plugin/pkg/authenticator/token/oidc" "k8s.io/apiserver/plugin/pkg/authenticator/token/oidc"
"k8s.io/apiserver/plugin/pkg/authenticator/token/webhook" "k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
// Initialize all known client auth plugins. // Initialize all known client auth plugins.
_ "k8s.io/client-go/plugin/pkg/client/auth" _ "k8s.io/client-go/plugin/pkg/client/auth"
"k8s.io/client-go/util/keyutil" "k8s.io/client-go/util/keyutil"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/serviceaccount" "k8s.io/kubernetes/pkg/serviceaccount"
) )
@ -127,7 +125,7 @@ func (config Config) New() (authenticator.Request, *spec.SecurityDefinitions, er
} }
tokenAuthenticators = append(tokenAuthenticators, serviceAccountAuth) tokenAuthenticators = append(tokenAuthenticators, serviceAccountAuth)
} }
if utilfeature.DefaultFeatureGate.Enabled(features.TokenRequest) && config.ServiceAccountIssuer != "" { if config.ServiceAccountIssuer != "" {
serviceAccountAuth, err := newServiceAccountAuthenticator(config.ServiceAccountIssuer, config.ServiceAccountKeyFiles, config.APIAudiences, config.ServiceAccountTokenGetter) serviceAccountAuth, err := newServiceAccountAuthenticator(config.ServiceAccountIssuer, config.ServiceAccountKeyFiles, config.APIAudiences, config.ServiceAccountTokenGetter)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@ -192,10 +192,6 @@ func (o *BuiltInAuthenticationOptions) Validate() []error {
} }
} }
if o.ServiceAccounts != nil && utilfeature.DefaultFeatureGate.Enabled(features.BoundServiceAccountTokenVolume) { if o.ServiceAccounts != nil && utilfeature.DefaultFeatureGate.Enabled(features.BoundServiceAccountTokenVolume) {
if !utilfeature.DefaultFeatureGate.Enabled(features.TokenRequest) || !utilfeature.DefaultFeatureGate.Enabled(features.TokenRequestProjection) {
allErrors = append(allErrors, errors.New("if the BoundServiceAccountTokenVolume feature is enabled,"+
" the TokenRequest and TokenRequestProjection features must also be enabled"))
}
if len(o.ServiceAccounts.Issuer) == 0 { if len(o.ServiceAccounts.Issuer) == 0 {
allErrors = append(allErrors, errors.New("service-account-issuer is a required flag when BoundServiceAccountTokenVolume is enabled")) allErrors = append(allErrors, errors.New("service-account-issuer is a required flag when BoundServiceAccountTokenVolume is enabled"))
} }
@ -313,7 +309,7 @@ func (o *BuiltInAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
"that this value comply with the OpenID spec: https://openid.net/specs/openid-connect-discovery-1_0.html. "+ "that this value comply with the OpenID spec: https://openid.net/specs/openid-connect-discovery-1_0.html. "+
"In practice, this means that service-account-issuer must be an https URL. It is also highly "+ "In practice, this means that service-account-issuer must be an https URL. It is also highly "+
"recommended that this URL be capable of serving OpenID discovery documents at "+ "recommended that this URL be capable of serving OpenID discovery documents at "+
"`{service-account-issuer}/.well-known/openid-configuration`.") "{service-account-issuer}/.well-known/openid-configuration.")
fs.StringVar(&o.ServiceAccounts.JWKSURI, "service-account-jwks-uri", o.ServiceAccounts.JWKSURI, ""+ fs.StringVar(&o.ServiceAccounts.JWKSURI, "service-account-jwks-uri", o.ServiceAccounts.JWKSURI, ""+
"Overrides the URI for the JSON Web Key Set in the discovery doc served at "+ "Overrides the URI for the JSON Web Key Set in the discovery doc served at "+
@ -464,14 +460,13 @@ func (o *BuiltInAuthenticationOptions) ApplyTo(authInfo *genericapiserver.Authen
authInfo.APIAudiences = authenticator.Audiences{o.ServiceAccounts.Issuer} authInfo.APIAudiences = authenticator.Audiences{o.ServiceAccounts.Issuer}
} }
if o.ServiceAccounts.Lookup || utilfeature.DefaultFeatureGate.Enabled(features.TokenRequest) {
authenticatorConfig.ServiceAccountTokenGetter = serviceaccountcontroller.NewGetterFromClient( authenticatorConfig.ServiceAccountTokenGetter = serviceaccountcontroller.NewGetterFromClient(
extclient, extclient,
versionedInformer.Core().V1().Secrets().Lister(), versionedInformer.Core().V1().Secrets().Lister(),
versionedInformer.Core().V1().ServiceAccounts().Lister(), versionedInformer.Core().V1().ServiceAccounts().Lister(),
versionedInformer.Core().V1().Pods().Lister(), versionedInformer.Core().V1().Pods().Lister(),
) )
}
authenticatorConfig.BootstrapTokenAuthenticator = bootstrap.NewTokenAuthenticator( authenticatorConfig.BootstrapTokenAuthenticator = bootstrap.NewTokenAuthenticator(
versionedInformer.Core().V1().Secrets().Lister().Secrets(metav1.NamespaceSystem), versionedInformer.Core().V1().Secrets().Lister().Secrets(metav1.NamespaceSystem),
) )

View File

@ -181,7 +181,7 @@ func (c LegacyRESTStorageProvider) NewLegacyRESTStorage(restOptionsGetter generi
} }
var serviceAccountStorage *serviceaccountstore.REST var serviceAccountStorage *serviceaccountstore.REST
if c.ServiceAccountIssuer != nil && utilfeature.DefaultFeatureGate.Enabled(features.TokenRequest) { if c.ServiceAccountIssuer != nil {
serviceAccountStorage, err = serviceaccountstore.NewREST(restOptionsGetter, c.ServiceAccountIssuer, c.APIAudiences, c.ServiceAccountMaxExpiration, podStorage.Pod.Store, secretStorage.Store, c.ExtendExpiration) serviceAccountStorage, err = serviceaccountstore.NewREST(restOptionsGetter, c.ServiceAccountIssuer, c.APIAudiences, c.ServiceAccountMaxExpiration, podStorage.Pod.Store, secretStorage.Store, c.ExtendExpiration)
} else { } else {
serviceAccountStorage, err = serviceaccountstore.NewREST(restOptionsGetter, nil, nil, 0, nil, nil, false) serviceAccountStorage, err = serviceaccountstore.NewREST(restOptionsGetter, nil, nil, 0, nil, nil, false)

View File

@ -36,7 +36,6 @@ go_library(
srcs = ["projected.go"], srcs = ["projected.go"],
importpath = "k8s.io/kubernetes/pkg/volume/projected", importpath = "k8s.io/kubernetes/pkg/volume/projected",
deps = [ deps = [
"//pkg/features:go_default_library",
"//pkg/volume:go_default_library", "//pkg/volume:go_default_library",
"//pkg/volume/configmap:go_default_library", "//pkg/volume/configmap:go_default_library",
"//pkg/volume/downwardapi:go_default_library", "//pkg/volume/downwardapi:go_default_library",
@ -48,7 +47,6 @@ go_library(
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//vendor/k8s.io/klog/v2:go_default_library", "//vendor/k8s.io/klog/v2:go_default_library",
"//vendor/k8s.io/utils/strings:go_default_library", "//vendor/k8s.io/utils/strings:go_default_library",
], ],

View File

@ -25,9 +25,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
"k8s.io/kubernetes/pkg/volume/configmap" "k8s.io/kubernetes/pkg/volume/configmap"
"k8s.io/kubernetes/pkg/volume/downwardapi" "k8s.io/kubernetes/pkg/volume/downwardapi"
@ -322,10 +320,6 @@ func (s *projectedVolumeMounter) collectData(mounterArgs volume.MounterArgs) (ma
payload[k] = v payload[k] = v
} }
case source.ServiceAccountToken != nil: case source.ServiceAccountToken != nil:
if !utilfeature.DefaultFeatureGate.Enabled(features.TokenRequestProjection) {
errlist = append(errlist, fmt.Errorf("pod request ServiceAccountToken projection but the TokenRequestProjection feature was not enabled"))
continue
}
tp := source.ServiceAccountToken tp := source.ServiceAccountToken
// When FsGroup is set, we depend on SetVolumeOwnership to // When FsGroup is set, we depend on SetVolumeOwnership to

View File

@ -71,7 +71,6 @@ type Plugin struct {
podsGetter corev1lister.PodLister podsGetter corev1lister.PodLister
nodesGetter corev1lister.NodeLister nodesGetter corev1lister.NodeLister
tokenRequestEnabled bool
csiNodeInfoEnabled bool csiNodeInfoEnabled bool
expandPersistentVolumesEnabled bool expandPersistentVolumesEnabled bool
} }
@ -84,7 +83,6 @@ var (
// InspectFeatureGates allows setting bools without taking a dep on a global variable // InspectFeatureGates allows setting bools without taking a dep on a global variable
func (p *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) { func (p *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) {
p.tokenRequestEnabled = featureGates.Enabled(features.TokenRequest)
p.csiNodeInfoEnabled = featureGates.Enabled(features.CSINodeInfo) p.csiNodeInfoEnabled = featureGates.Enabled(features.CSINodeInfo)
p.expandPersistentVolumesEnabled = featureGates.Enabled(features.ExpandPersistentVolumes) p.expandPersistentVolumesEnabled = featureGates.Enabled(features.ExpandPersistentVolumes)
} }
@ -159,10 +157,7 @@ func (p *Plugin) Admit(ctx context.Context, a admission.Attributes, o admission.
} }
case svcacctResource: case svcacctResource:
if p.tokenRequestEnabled {
return p.admitServiceAccount(nodeName, a) return p.admitServiceAccount(nodeName, a)
}
return nil
case leaseResource: case leaseResource:
return p.admitLease(nodeName, a) return p.admitLease(nodeName, a)

View File

@ -48,7 +48,6 @@ import (
) )
var ( var (
trEnabledFeature = featuregate.NewFeatureGate()
csiNodeInfoEnabledFeature = featuregate.NewFeatureGate() csiNodeInfoEnabledFeature = featuregate.NewFeatureGate()
csiNodeInfoDisabledFeature = featuregate.NewFeatureGate() csiNodeInfoDisabledFeature = featuregate.NewFeatureGate()
) )
@ -56,15 +55,12 @@ var (
func init() { func init() {
// all features need to be set on all featuregates for the tests. We set everything and then then the if's below override it. // all features need to be set on all featuregates for the tests. We set everything and then then the if's below override it.
relevantFeatures := map[featuregate.Feature]featuregate.FeatureSpec{ relevantFeatures := map[featuregate.Feature]featuregate.FeatureSpec{
features.TokenRequest: {Default: false},
features.CSINodeInfo: {Default: false}, features.CSINodeInfo: {Default: false},
features.ExpandPersistentVolumes: {Default: false}, features.ExpandPersistentVolumes: {Default: false},
} }
utilruntime.Must(trEnabledFeature.Add(relevantFeatures))
utilruntime.Must(csiNodeInfoEnabledFeature.Add(relevantFeatures)) utilruntime.Must(csiNodeInfoEnabledFeature.Add(relevantFeatures))
utilruntime.Must(csiNodeInfoDisabledFeature.Add(relevantFeatures)) utilruntime.Must(csiNodeInfoDisabledFeature.Add(relevantFeatures))
utilruntime.Must(trEnabledFeature.SetFromMap(map[string]bool{string(features.TokenRequest): true}))
utilruntime.Must(csiNodeInfoEnabledFeature.SetFromMap(map[string]bool{string(features.CSINodeInfo): true})) utilruntime.Must(csiNodeInfoEnabledFeature.SetFromMap(map[string]bool{string(features.CSINodeInfo): true}))
} }
@ -1086,35 +1082,30 @@ func Test_nodePlugin_Admit(t *testing.T) {
{ {
name: "forbid create of unbound token", name: "forbid create of unbound token",
podsGetter: noExistingPods, podsGetter: noExistingPods,
features: trEnabledFeature,
attributes: admission.NewAttributesRecord(makeTokenRequest("", ""), nil, tokenrequestKind, "ns", "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode), attributes: admission.NewAttributesRecord(makeTokenRequest("", ""), nil, tokenrequestKind, "ns", "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode),
err: "not bound to a pod", err: "not bound to a pod",
}, },
{ {
name: "forbid create of token bound to nonexistant pod", name: "forbid create of token bound to nonexistant pod",
podsGetter: noExistingPods, podsGetter: noExistingPods,
features: trEnabledFeature,
attributes: admission.NewAttributesRecord(makeTokenRequest("nopod", "someuid"), nil, tokenrequestKind, "ns", "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode), attributes: admission.NewAttributesRecord(makeTokenRequest("nopod", "someuid"), nil, tokenrequestKind, "ns", "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode),
err: "not found", err: "not found",
}, },
{ {
name: "forbid create of token bound to pod without uid", name: "forbid create of token bound to pod without uid",
podsGetter: existingPods, podsGetter: existingPods,
features: trEnabledFeature,
attributes: admission.NewAttributesRecord(makeTokenRequest(coremypod.Name, ""), nil, tokenrequestKind, "ns", "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode), attributes: admission.NewAttributesRecord(makeTokenRequest(coremypod.Name, ""), nil, tokenrequestKind, "ns", "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode),
err: "pod binding without a uid", err: "pod binding without a uid",
}, },
{ {
name: "forbid create of token bound to pod scheduled on another node", name: "forbid create of token bound to pod scheduled on another node",
podsGetter: existingPods, podsGetter: existingPods,
features: trEnabledFeature,
attributes: admission.NewAttributesRecord(makeTokenRequest(coreotherpod.Name, coreotherpod.UID), nil, tokenrequestKind, coreotherpod.Namespace, "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode), attributes: admission.NewAttributesRecord(makeTokenRequest(coreotherpod.Name, coreotherpod.UID), nil, tokenrequestKind, coreotherpod.Namespace, "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode),
err: "pod scheduled on a different node", err: "pod scheduled on a different node",
}, },
{ {
name: "allow create of token bound to pod scheduled this node", name: "allow create of token bound to pod scheduled this node",
podsGetter: existingPods, podsGetter: existingPods,
features: trEnabledFeature,
attributes: admission.NewAttributesRecord(makeTokenRequest(coremypod.Name, coremypod.UID), nil, tokenrequestKind, coremypod.Namespace, "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode), attributes: admission.NewAttributesRecord(makeTokenRequest(coremypod.Name, coremypod.UID), nil, tokenrequestKind, coremypod.Namespace, "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode),
}, },

View File

@ -123,10 +123,7 @@ func (r *NodeAuthorizer) Authorize(ctx context.Context, attrs authorizer.Attribu
case vaResource: case vaResource:
return r.authorizeGet(nodeName, vaVertexType, attrs) return r.authorizeGet(nodeName, vaVertexType, attrs)
case svcAcctResource: case svcAcctResource:
if r.features.Enabled(features.TokenRequest) {
return r.authorizeCreateToken(nodeName, serviceAccountVertexType, attrs) return r.authorizeCreateToken(nodeName, serviceAccountVertexType, attrs)
}
return authorizer.DecisionNoOpinion, fmt.Sprintf("disabled by feature gate %s", features.TokenRequest), nil
case leaseResource: case leaseResource:
return r.authorizeLease(nodeName, attrs) return r.authorizeLease(nodeName, attrs)
case csiNodeResource: case csiNodeResource:

View File

@ -42,19 +42,11 @@ import (
) )
var ( var (
trEnabledFeature = featuregate.NewFeatureGate()
trDisabledFeature = featuregate.NewFeatureGate()
csiNodeInfoEnabledFeature = featuregate.NewFeatureGate() csiNodeInfoEnabledFeature = featuregate.NewFeatureGate()
csiNodeInfoDisabledFeature = featuregate.NewFeatureGate() csiNodeInfoDisabledFeature = featuregate.NewFeatureGate()
) )
func init() { func init() {
if err := trEnabledFeature.Add(map[featuregate.Feature]featuregate.FeatureSpec{features.TokenRequest: {Default: true}}); err != nil {
panic(err)
}
if err := trDisabledFeature.Add(map[featuregate.Feature]featuregate.FeatureSpec{features.TokenRequest: {Default: false}}); err != nil {
panic(err)
}
if err := csiNodeInfoEnabledFeature.Add(map[featuregate.Feature]featuregate.FeatureSpec{features.CSINodeInfo: {Default: true}}); err != nil { if err := csiNodeInfoEnabledFeature.Add(map[featuregate.Feature]featuregate.FeatureSpec{features.CSINodeInfo: {Default: true}}); err != nil {
panic(err) panic(err)
} }
@ -189,33 +181,23 @@ func TestAuthorizer(t *testing.T) {
expect: authorizer.DecisionAllow, expect: authorizer.DecisionAllow,
}, },
{ {
name: "allowed svcacct token create - feature enabled", name: "allowed svcacct token create",
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "serviceaccounts", Subresource: "token", Name: "svcacct0-node0", Namespace: "ns0"}, attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "serviceaccounts", Subresource: "token", Name: "svcacct0-node0", Namespace: "ns0"},
features: trEnabledFeature,
expect: authorizer.DecisionAllow, expect: authorizer.DecisionAllow,
}, },
{ {
name: "disallowed svcacct token create - serviceaccount not attached to node", name: "disallowed svcacct token create - serviceaccount not attached to node",
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "serviceaccounts", Subresource: "token", Name: "svcacct0-node1", Namespace: "ns0"}, attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "serviceaccounts", Subresource: "token", Name: "svcacct0-node1", Namespace: "ns0"},
features: trEnabledFeature,
expect: authorizer.DecisionNoOpinion,
},
{
name: "disallowed svcacct token create - feature disabled",
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "serviceaccounts", Subresource: "token", Name: "svcacct0-node0", Namespace: "ns0"},
features: trDisabledFeature,
expect: authorizer.DecisionNoOpinion, expect: authorizer.DecisionNoOpinion,
}, },
{ {
name: "disallowed svcacct token create - no subresource", name: "disallowed svcacct token create - no subresource",
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "serviceaccounts", Name: "svcacct0-node0", Namespace: "ns0"}, attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "serviceaccounts", Name: "svcacct0-node0", Namespace: "ns0"},
features: trEnabledFeature,
expect: authorizer.DecisionNoOpinion, expect: authorizer.DecisionNoOpinion,
}, },
{ {
name: "disallowed svcacct token create - non create", name: "disallowed svcacct token create - non create",
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "serviceaccounts", Subresource: "token", Name: "svcacct0-node0", Namespace: "ns0"}, attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "serviceaccounts", Subresource: "token", Name: "svcacct0-node0", Namespace: "ns0"},
features: trEnabledFeature,
expect: authorizer.DecisionNoOpinion, expect: authorizer.DecisionNoOpinion,
}, },
{ {

View File

@ -153,6 +153,10 @@ func NodeRules() []rbacv1.PolicyRule {
// CSI // CSI
rbacv1helpers.NewRule("get").Groups(storageGroup).Resources("volumeattachments").RuleOrDie(), rbacv1helpers.NewRule("get").Groups(storageGroup).Resources("volumeattachments").RuleOrDie(),
// Use the Node authorization to limit a node to create tokens for service accounts running on that node
// Use the NodeRestriction admission plugin to limit a node to create tokens bound to pods on that node
rbacv1helpers.NewRule("create").Groups(legacyGroup).Resources("serviceaccounts/token").RuleOrDie(),
} }
if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) { if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
@ -162,13 +166,6 @@ func NodeRules() []rbacv1.PolicyRule {
nodePolicyRules = append(nodePolicyRules, pvcStatusPolicyRule) nodePolicyRules = append(nodePolicyRules, pvcStatusPolicyRule)
} }
if utilfeature.DefaultFeatureGate.Enabled(features.TokenRequest) {
// Use the Node authorization to limit a node to create tokens for service accounts running on that node
// Use the NodeRestriction admission plugin to limit a node to create tokens bound to pods on that node
tokenRequestRule := rbacv1helpers.NewRule("create").Groups(legacyGroup).Resources("serviceaccounts/token").RuleOrDie()
nodePolicyRules = append(nodePolicyRules, tokenRequestRule)
}
// CSI // CSI
csiDriverRule := rbacv1helpers.NewRule("get", "watch", "list").Groups("storage.k8s.io").Resources("csidrivers").RuleOrDie() csiDriverRule := rbacv1helpers.NewRule("get", "watch", "list").Groups("storage.k8s.io").Resources("csidrivers").RuleOrDie()
nodePolicyRules = append(nodePolicyRules, csiDriverRule) nodePolicyRules = append(nodePolicyRules, csiDriverRule)

View File

@ -1018,6 +1018,12 @@ items:
- volumeattachments - volumeattachments
verbs: verbs:
- get - get
- apiGroups:
- ""
resources:
- serviceaccounts/token
verbs:
- create
- apiGroups: - apiGroups:
- "" - ""
resources: resources:
@ -1026,12 +1032,6 @@ items:
- get - get
- patch - patch
- update - update
- apiGroups:
- ""
resources:
- serviceaccounts/token
verbs:
- create
- apiGroups: - apiGroups:
- storage.k8s.io - storage.k8s.io
resources: resources:

View File

@ -41,6 +41,7 @@ import (
e2epod "k8s.io/kubernetes/test/e2e/framework/pod" e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
imageutils "k8s.io/kubernetes/test/utils/image" imageutils "k8s.io/kubernetes/test/utils/image"
utilptr "k8s.io/utils/pointer"
"github.com/onsi/ginkgo" "github.com/onsi/ginkgo"
) )
@ -418,6 +419,59 @@ var _ = SIGDescribe("ServiceAccounts", func() {
} }
}) })
/*
Release : v1.20
Testname: TokenRequestProjection should mount a projected volume with token using TokenRequest API.
Description: Ensure that projected service account token is mounted.
*/
ginkgo.It("should mount projected service account token when requested", func() {
var (
podName = "test-pod-" + string(uuid.NewUUID())
volumeName = "test-volume"
volumeMountPath = "/test-volume"
tokenVolumePath = "/test-volume/token"
)
volumes := []v1.Volume{
{
Name: volumeName,
VolumeSource: v1.VolumeSource{
Projected: &v1.ProjectedVolumeSource{
Sources: []v1.VolumeProjection{
{
ServiceAccountToken: &v1.ServiceAccountTokenProjection{
Path: "token",
ExpirationSeconds: utilptr.Int64Ptr(60 * 60),
},
},
},
},
},
},
}
volumeMounts := []v1.VolumeMount{
{
Name: volumeName,
MountPath: volumeMountPath,
ReadOnly: true,
},
}
mounttestArgs := []string{
"mounttest",
fmt.Sprintf("--file_content=%v", tokenVolumePath),
}
pod := e2epod.NewAgnhostPod(f.Namespace.Name, podName, volumes, volumeMounts, nil, mounttestArgs...)
pod.Spec.RestartPolicy = v1.RestartPolicyNever
output := []string{
fmt.Sprintf("content of file \"%v\": %s", tokenVolumePath, `[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]*`),
}
f.TestContainerOutputRegexp("service account token: ", pod, 0, output)
})
/* /*
Testname: Projected service account token file ownership and permission. Testname: Projected service account token file ownership and permission.
Description: Ensure that Projected Service Account Token is mounted with Description: Ensure that Projected Service Account Token is mounted with
@ -431,24 +485,17 @@ var _ = SIGDescribe("ServiceAccounts", func() {
Containers MUST verify that the projected service account token can be Containers MUST verify that the projected service account token can be
read and has correct file mode set including ownership and permission. read and has correct file mode set including ownership and permission.
*/ */
ginkgo.It("should set ownership and permission when RunAsUser or FsGroup is present [LinuxOnly] [NodeFeature:FSGroup] [Feature:TokenRequestProjection]", func() { ginkgo.It("should set ownership and permission when RunAsUser or FsGroup is present [LinuxOnly] [NodeFeature:FSGroup]", func() {
e2eskipper.SkipIfNodeOSDistroIs("windows") e2eskipper.SkipIfNodeOSDistroIs("windows")
var ( var (
podName = "test-pod-" + string(uuid.NewUUID()) podName = "test-pod-" + string(uuid.NewUUID())
containerName = "test-container"
volumeName = "test-volume" volumeName = "test-volume"
volumeMountPath = "/test-volume" volumeMountPath = "/test-volume"
tokenVolumePath = "/test-volume/token" tokenVolumePath = "/test-volume/token"
int64p = func(i int64) *int64 { return &i }
) )
pod := &v1.Pod{ volumes := []v1.Volume{
ObjectMeta: metav1.ObjectMeta{
Name: podName,
},
Spec: v1.PodSpec{
Volumes: []v1.Volume{
{ {
Name: volumeName, Name: volumeName,
VolumeSource: v1.VolumeSource{ VolumeSource: v1.VolumeSource{
@ -457,36 +504,31 @@ var _ = SIGDescribe("ServiceAccounts", func() {
{ {
ServiceAccountToken: &v1.ServiceAccountTokenProjection{ ServiceAccountToken: &v1.ServiceAccountTokenProjection{
Path: "token", Path: "token",
ExpirationSeconds: int64p(60 * 60), ExpirationSeconds: utilptr.Int64Ptr(60 * 60),
}, },
}, },
}, },
}, },
}, },
}, },
}, }
Containers: []v1.Container{ volumeMounts := []v1.VolumeMount{
{ {
Name: containerName, Name: volumeName,
Image: imageutils.GetE2EImage(imageutils.Agnhost), MountPath: volumeMountPath,
Args: []string{ ReadOnly: true,
},
}
mounttestArgs := []string{
"mounttest", "mounttest",
fmt.Sprintf("--file_perm=%v", tokenVolumePath), fmt.Sprintf("--file_perm=%v", tokenVolumePath),
fmt.Sprintf("--file_owner=%v", tokenVolumePath), fmt.Sprintf("--file_owner=%v", tokenVolumePath),
fmt.Sprintf("--file_content=%v", tokenVolumePath), fmt.Sprintf("--file_content=%v", tokenVolumePath),
},
VolumeMounts: []v1.VolumeMount{
{
Name: volumeName,
MountPath: volumeMountPath,
},
},
},
},
RestartPolicy: v1.RestartPolicyNever,
},
} }
pod := e2epod.NewAgnhostPod(f.Namespace.Name, podName, volumes, volumeMounts, nil, mounttestArgs...)
pod.Spec.RestartPolicy = v1.RestartPolicyNever
testcases := []struct { testcases := []struct {
runAsUser bool runAsUser bool
fsGroup bool fsGroup bool
@ -531,7 +573,7 @@ var _ = SIGDescribe("ServiceAccounts", func() {
output := []string{ output := []string{
fmt.Sprintf("perms of file \"%v\": %s", tokenVolumePath, tc.wantPerm), fmt.Sprintf("perms of file \"%v\": %s", tokenVolumePath, tc.wantPerm),
fmt.Sprintf("content of file \"%v\": %s", tokenVolumePath, ".+"), fmt.Sprintf("content of file \"%v\": %s", tokenVolumePath, `[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]*`),
fmt.Sprintf("owner UID of \"%v\": %d", tokenVolumePath, tc.wantUID), fmt.Sprintf("owner UID of \"%v\": %d", tokenVolumePath, tc.wantUID),
fmt.Sprintf("owner GID of \"%v\": %d", tokenVolumePath, tc.wantGID), fmt.Sprintf("owner GID of \"%v\": %d", tokenVolumePath, tc.wantGID),
} }
@ -539,7 +581,7 @@ var _ = SIGDescribe("ServiceAccounts", func() {
} }
}) })
ginkgo.It("should support InClusterConfig with token rotation [Slow] [Feature:TokenRequestProjection]", func() { ginkgo.It("should support InClusterConfig with token rotation [Slow]", func() {
cfg, err := framework.LoadConfig() cfg, err := framework.LoadConfig()
framework.ExpectNoError(err) framework.ExpectNoError(err)

View File

@ -26,21 +26,16 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authorization/authorizerfactory" "k8s.io/apiserver/pkg/authorization/authorizerfactory"
utilfeature "k8s.io/apiserver/pkg/util/feature"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubernetes/cmd/kube-apiserver/app/options" "k8s.io/kubernetes/cmd/kube-apiserver/app/options"
"k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/controlplane" "k8s.io/kubernetes/pkg/controlplane"
"k8s.io/kubernetes/pkg/features"
kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options" kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
"k8s.io/kubernetes/test/integration/framework" "k8s.io/kubernetes/test/integration/framework"
) )
func TestDynamicClientBuilder(t *testing.T) { func TestDynamicClientBuilder(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TokenRequest, true)()
tmpfile, err := ioutil.TempFile("/tmp", "key") tmpfile, err := ioutil.TempFile("/tmp", "key")
if err != nil { if err != nil {
t.Fatalf("create temp file failed: %v", err) t.Fatalf("create temp file failed: %v", err)

View File

@ -64,7 +64,6 @@ AwEHoUQDQgAEH6cuzP8XuD5wal6wf9M6xDljTOPLX2i8uIp/C/ASqiIGUeeKQtX0
-----END EC PRIVATE KEY-----` -----END EC PRIVATE KEY-----`
func TestServiceAccountTokenCreate(t *testing.T) { func TestServiceAccountTokenCreate(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TokenRequest, true)()
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountIssuerDiscovery, true)() defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountIssuerDiscovery, true)()
// Build client config, clientset, and informers // Build client config, clientset, and informers