mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
Graduate ServiceAccountIssuerDiscovery to GA
Waiting on KEP updates first: https://github.com/kubernetes/enhancements/pull/2363
This commit is contained in:
parent
f384925847
commit
6aa80d9172
@ -17,7 +17,6 @@ go_library(
|
|||||||
"//pkg/controlplane/controller/crdregistration:go_default_library",
|
"//pkg/controlplane/controller/crdregistration:go_default_library",
|
||||||
"//pkg/controlplane/reconcilers:go_default_library",
|
"//pkg/controlplane/reconcilers:go_default_library",
|
||||||
"//pkg/controlplane/tunneler:go_default_library",
|
"//pkg/controlplane/tunneler:go_default_library",
|
||||||
"//pkg/features:go_default_library",
|
|
||||||
"//pkg/generated/openapi:go_default_library",
|
"//pkg/generated/openapi:go_default_library",
|
||||||
"//pkg/kubeapiserver:go_default_library",
|
"//pkg/kubeapiserver:go_default_library",
|
||||||
"//pkg/kubeapiserver/admission:go_default_library",
|
"//pkg/kubeapiserver/admission:go_default_library",
|
||||||
|
@ -73,7 +73,6 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/controlplane"
|
"k8s.io/kubernetes/pkg/controlplane"
|
||||||
"k8s.io/kubernetes/pkg/controlplane/reconcilers"
|
"k8s.io/kubernetes/pkg/controlplane/reconcilers"
|
||||||
"k8s.io/kubernetes/pkg/controlplane/tunneler"
|
"k8s.io/kubernetes/pkg/controlplane/tunneler"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
generatedopenapi "k8s.io/kubernetes/pkg/generated/openapi"
|
generatedopenapi "k8s.io/kubernetes/pkg/generated/openapi"
|
||||||
"k8s.io/kubernetes/pkg/kubeapiserver"
|
"k8s.io/kubernetes/pkg/kubeapiserver"
|
||||||
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
|
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
|
||||||
@ -422,21 +421,19 @@ func CreateKubeAPIServerConfig(
|
|||||||
config.ExtraConfig.ProxyTransport = c
|
config.ExtraConfig.ProxyTransport = c
|
||||||
}
|
}
|
||||||
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.ServiceAccountIssuerDiscovery) {
|
// Load the public keys.
|
||||||
// Load the public keys.
|
var pubKeys []interface{}
|
||||||
var pubKeys []interface{}
|
for _, f := range s.Authentication.ServiceAccounts.KeyFiles {
|
||||||
for _, f := range s.Authentication.ServiceAccounts.KeyFiles {
|
keys, err := keyutil.PublicKeysFromFile(f)
|
||||||
keys, err := keyutil.PublicKeysFromFile(f)
|
if err != nil {
|
||||||
if err != nil {
|
return nil, nil, nil, fmt.Errorf("failed to parse key file %q: %v", f, err)
|
||||||
return nil, nil, nil, fmt.Errorf("failed to parse key file %q: %v", f, err)
|
|
||||||
}
|
|
||||||
pubKeys = append(pubKeys, keys...)
|
|
||||||
}
|
}
|
||||||
// Plumb the required metadata through ExtraConfig.
|
pubKeys = append(pubKeys, keys...)
|
||||||
config.ExtraConfig.ServiceAccountIssuerURL = s.Authentication.ServiceAccounts.Issuer
|
|
||||||
config.ExtraConfig.ServiceAccountJWKSURI = s.Authentication.ServiceAccounts.JWKSURI
|
|
||||||
config.ExtraConfig.ServiceAccountPublicKeys = pubKeys
|
|
||||||
}
|
}
|
||||||
|
// Plumb the required metadata through ExtraConfig.
|
||||||
|
config.ExtraConfig.ServiceAccountIssuerURL = s.Authentication.ServiceAccounts.Issuer
|
||||||
|
config.ExtraConfig.ServiceAccountJWKSURI = s.Authentication.ServiceAccounts.JWKSURI
|
||||||
|
config.ExtraConfig.ServiceAccountPublicKeys = pubKeys
|
||||||
|
|
||||||
return config, serviceResolver, pluginInitializers, nil
|
return config, serviceResolver, pluginInitializers, nil
|
||||||
}
|
}
|
||||||
|
@ -366,37 +366,35 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
|
|||||||
routes.Logs{}.Install(s.Handler.GoRestfulContainer)
|
routes.Logs{}.Install(s.Handler.GoRestfulContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.ServiceAccountIssuerDiscovery) {
|
// Metadata and keys are expected to only change across restarts at present,
|
||||||
// Metadata and keys are expected to only change across restarts at present,
|
// so we just marshal immediately and serve the cached JSON bytes.
|
||||||
// so we just marshal immediately and serve the cached JSON bytes.
|
md, err := serviceaccount.NewOpenIDMetadata(
|
||||||
md, err := serviceaccount.NewOpenIDMetadata(
|
c.ExtraConfig.ServiceAccountIssuerURL,
|
||||||
c.ExtraConfig.ServiceAccountIssuerURL,
|
c.ExtraConfig.ServiceAccountJWKSURI,
|
||||||
c.ExtraConfig.ServiceAccountJWKSURI,
|
c.GenericConfig.ExternalAddress,
|
||||||
c.GenericConfig.ExternalAddress,
|
c.ExtraConfig.ServiceAccountPublicKeys,
|
||||||
c.ExtraConfig.ServiceAccountPublicKeys,
|
)
|
||||||
)
|
if err != nil {
|
||||||
if err != nil {
|
// If there was an error, skip installing the endpoints and log the
|
||||||
// If there was an error, skip installing the endpoints and log the
|
// error, but continue on. We don't return the error because the
|
||||||
// error, but continue on. We don't return the error because the
|
// metadata responses require additional, backwards incompatible
|
||||||
// metadata responses require additional, backwards incompatible
|
// validation of command-line options.
|
||||||
// validation of command-line options.
|
msg := fmt.Sprintf("Could not construct pre-rendered responses for"+
|
||||||
msg := fmt.Sprintf("Could not construct pre-rendered responses for"+
|
" ServiceAccountIssuerDiscovery endpoints. Endpoints will not be"+
|
||||||
" ServiceAccountIssuerDiscovery endpoints. Endpoints will not be"+
|
" enabled. Error: %v", err)
|
||||||
" enabled. Error: %v", err)
|
if c.ExtraConfig.ServiceAccountIssuerURL != "" {
|
||||||
if c.ExtraConfig.ServiceAccountIssuerURL != "" {
|
// The user likely expects this feature to be enabled if issuer URL is
|
||||||
// The user likely expects this feature to be enabled if issuer URL is
|
// set and the feature gate is enabled. In the future, if there is no
|
||||||
// set and the feature gate is enabled. In the future, if there is no
|
// longer a feature gate and issuer URL is not set, the user may not
|
||||||
// longer a feature gate and issuer URL is not set, the user may not
|
// expect this feature to be enabled. We log the former case as an Error
|
||||||
// expect this feature to be enabled. We log the former case as an Error
|
// and the latter case as an Info.
|
||||||
// and the latter case as an Info.
|
klog.Error(msg)
|
||||||
klog.Error(msg)
|
|
||||||
} else {
|
|
||||||
klog.Info(msg)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
routes.NewOpenIDMetadataServer(md.ConfigJSON, md.PublicKeysetJSON).
|
klog.Info(msg)
|
||||||
Install(s.Handler.GoRestfulContainer)
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
routes.NewOpenIDMetadataServer(md.ConfigJSON, md.PublicKeysetJSON).
|
||||||
|
Install(s.Handler.GoRestfulContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &Instance{
|
m := &Instance{
|
||||||
|
@ -194,6 +194,7 @@ const (
|
|||||||
// owner: @mtaufen
|
// owner: @mtaufen
|
||||||
// alpha: v1.18
|
// alpha: v1.18
|
||||||
// beta: v1.20
|
// beta: v1.20
|
||||||
|
// stable: v1.21
|
||||||
//
|
//
|
||||||
// Enable OIDC discovery endpoints (issuer and JWKS URLs) for the service
|
// Enable OIDC discovery endpoints (issuer and JWKS URLs) for the service
|
||||||
// account issuer in the API server.
|
// account issuer in the API server.
|
||||||
@ -699,7 +700,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
SupportPodPidsLimit: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.23
|
SupportPodPidsLimit: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.23
|
||||||
SupportNodePidsLimit: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.23
|
SupportNodePidsLimit: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.23
|
||||||
BoundServiceAccountTokenVolume: {Default: false, PreRelease: featuregate.Alpha},
|
BoundServiceAccountTokenVolume: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
ServiceAccountIssuerDiscovery: {Default: true, PreRelease: featuregate.Beta},
|
ServiceAccountIssuerDiscovery: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.22
|
||||||
CRIContainerLogRotation: {Default: true, PreRelease: featuregate.Beta},
|
CRIContainerLogRotation: {Default: true, PreRelease: featuregate.Beta},
|
||||||
CSIMigration: {Default: true, PreRelease: featuregate.Beta},
|
CSIMigration: {Default: true, PreRelease: featuregate.Beta},
|
||||||
CSIMigrationGCE: {Default: false, PreRelease: featuregate.Beta}, // Off by default (requires GCE PD CSI Driver)
|
CSIMigrationGCE: {Default: false, PreRelease: featuregate.Beta}, // Off by default (requires GCE PD CSI Driver)
|
||||||
|
@ -213,18 +213,14 @@ func (o *BuiltInAuthenticationOptions) Validate() []error {
|
|||||||
allErrors = append(allErrors, errors.New("service-account-key-file is a required flag"))
|
allErrors = append(allErrors, errors.New("service-account-key-file is a required flag"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.ServiceAccountIssuerDiscovery) {
|
// Validate the JWKS URI when it is explicitly set.
|
||||||
// Validate the JWKS URI when it is explicitly set.
|
// When unset, it is later derived from ExternalHost.
|
||||||
// When unset, it is later derived from ExternalHost.
|
if o.ServiceAccounts.JWKSURI != "" {
|
||||||
if o.ServiceAccounts.JWKSURI != "" {
|
if u, err := url.Parse(o.ServiceAccounts.JWKSURI); err != nil {
|
||||||
if u, err := url.Parse(o.ServiceAccounts.JWKSURI); err != nil {
|
allErrors = append(allErrors, fmt.Errorf("service-account-jwks-uri must be a valid URL: %v", err))
|
||||||
allErrors = append(allErrors, fmt.Errorf("service-account-jwks-uri must be a valid URL: %v", err))
|
} else if u.Scheme != "https" {
|
||||||
} else if u.Scheme != "https" {
|
allErrors = append(allErrors, fmt.Errorf("service-account-jwks-uri requires https scheme, parsed as: %v", u.String()))
|
||||||
allErrors = append(allErrors, fmt.Errorf("service-account-jwks-uri requires https scheme, parsed as: %v", u.String()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if len(o.ServiceAccounts.JWKSURI) > 0 {
|
|
||||||
allErrors = append(allErrors, fmt.Errorf("service-account-jwks-uri may only be set when the ServiceAccountIssuerDiscovery feature gate is enabled"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,18 +494,16 @@ func ClusterRoles() []rbacv1.ClusterRole {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.ServiceAccountIssuerDiscovery) {
|
// Add the cluster role for reading the ServiceAccountIssuerDiscovery endpoints
|
||||||
// Add the cluster role for reading the ServiceAccountIssuerDiscovery endpoints
|
roles = append(roles, rbacv1.ClusterRole{
|
||||||
roles = append(roles, rbacv1.ClusterRole{
|
ObjectMeta: metav1.ObjectMeta{Name: "system:service-account-issuer-discovery"},
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "system:service-account-issuer-discovery"},
|
Rules: []rbacv1.PolicyRule{
|
||||||
Rules: []rbacv1.PolicyRule{
|
rbacv1helpers.NewRule("get").URLs(
|
||||||
rbacv1helpers.NewRule("get").URLs(
|
"/.well-known/openid-configuration",
|
||||||
"/.well-known/openid-configuration",
|
"/openid/v1/jwks",
|
||||||
"/openid/v1/jwks",
|
).RuleOrDie(),
|
||||||
).RuleOrDie(),
|
},
|
||||||
},
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// node-proxier role is used by kube-proxy.
|
// node-proxier role is used by kube-proxy.
|
||||||
nodeProxierRules := []rbacv1.PolicyRule{
|
nodeProxierRules := []rbacv1.PolicyRule{
|
||||||
@ -590,19 +588,17 @@ func ClusterRoleBindings() []rbacv1.ClusterRoleBinding {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.ServiceAccountIssuerDiscovery) {
|
// Allow all in-cluster workloads (via their service accounts) to read the OIDC discovery endpoints.
|
||||||
// Allow all in-cluster workloads (via their service accounts) to read the OIDC discovery endpoints.
|
// Users with certain forms of write access (create pods, create secrets, create service accounts, etc)
|
||||||
// Users with certain forms of write access (create pods, create secrets, create service accounts, etc)
|
// can gain access to a service account identity which would allow them to access this information.
|
||||||
// can gain access to a service account identity which would allow them to access this information.
|
// This includes the issuer URL, which is already present in the SA token JWT. Similarly, SAs can
|
||||||
// This includes the issuer URL, which is already present in the SA token JWT. Similarly, SAs can
|
// already gain this same info via introspection of their own token. Since this discovery endpoint
|
||||||
// already gain this same info via introspection of their own token. Since this discovery endpoint
|
// points to what issued all service account tokens, it seems fitting for SAs to have this access.
|
||||||
// points to what issued all service account tokens, it seems fitting for SAs to have this access.
|
// Defer to the cluster admin with regard to binding directly to all authenticated and/or
|
||||||
// Defer to the cluster admin with regard to binding directly to all authenticated and/or
|
// unauthenticated users.
|
||||||
// unauthenticated users.
|
rolebindings = append(rolebindings,
|
||||||
rolebindings = append(rolebindings,
|
rbacv1helpers.NewClusterBinding("system:service-account-issuer-discovery").Groups(serviceaccount.AllServiceAccountsGroup).BindingOrDie(),
|
||||||
rbacv1helpers.NewClusterBinding("system:service-account-issuer-discovery").Groups(serviceaccount.AllServiceAccountsGroup).BindingOrDie(),
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
addClusterRoleBindingLabel(rolebindings)
|
addClusterRoleBindingLabel(rolebindings)
|
||||||
|
|
||||||
|
@ -43,16 +43,13 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/authentication/request/bearertoken"
|
"k8s.io/apiserver/pkg/authentication/request/bearertoken"
|
||||||
apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount"
|
apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount"
|
||||||
"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"
|
||||||
v1listers "k8s.io/client-go/listers/core/v1"
|
v1listers "k8s.io/client-go/listers/core/v1"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
"k8s.io/client-go/util/keyutil"
|
"k8s.io/client-go/util/keyutil"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
"k8s.io/kubernetes/pkg/apis/core"
|
"k8s.io/kubernetes/pkg/apis/core"
|
||||||
serviceaccountgetter "k8s.io/kubernetes/pkg/controller/serviceaccount"
|
serviceaccountgetter "k8s.io/kubernetes/pkg/controller/serviceaccount"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/pkg/serviceaccount"
|
"k8s.io/kubernetes/pkg/serviceaccount"
|
||||||
"k8s.io/kubernetes/test/integration/framework"
|
"k8s.io/kubernetes/test/integration/framework"
|
||||||
)
|
)
|
||||||
@ -64,7 +61,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.ServiceAccountIssuerDiscovery, true)()
|
|
||||||
|
|
||||||
// Build client config, clientset, and informers
|
// Build client config, clientset, and informers
|
||||||
sk, err := keyutil.ParsePrivateKeyPEM([]byte(ecdsaPrivateKey))
|
sk, err := keyutil.ParsePrivateKeyPEM([]byte(ecdsaPrivateKey))
|
||||||
|
Loading…
Reference in New Issue
Block a user