k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/plugin/pkg/admission/certificates/ctbattest/admission.go (about) 1 /* 2 Copyright 2022 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package ctbattest 18 19 import ( 20 "context" 21 "fmt" 22 "io" 23 24 "k8s.io/apiserver/pkg/admission" 25 genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer" 26 "k8s.io/apiserver/pkg/authorization/authorizer" 27 "k8s.io/component-base/featuregate" 28 "k8s.io/klog/v2" 29 api "k8s.io/kubernetes/pkg/apis/certificates" 30 kapihelper "k8s.io/kubernetes/pkg/apis/core/helper" 31 "k8s.io/kubernetes/pkg/features" 32 "k8s.io/kubernetes/pkg/registry/rbac" 33 "k8s.io/kubernetes/plugin/pkg/admission/certificates" 34 ) 35 36 const PluginName = "ClusterTrustBundleAttest" 37 38 func Register(plugins *admission.Plugins) { 39 plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { 40 return NewPlugin(), nil 41 }) 42 } 43 44 // Plugin is the ClusterTrustBundle attest plugin. 45 // 46 // In order to create or update a ClusterTrustBundle that sets signerName, 47 // you must have the following permission: group=certificates.k8s.io 48 // resource=signers resourceName=<the signer name> verb=attest. 49 type Plugin struct { 50 *admission.Handler 51 authz authorizer.Authorizer 52 53 inspectedFeatureGates bool 54 enabled bool 55 } 56 57 var _ admission.ValidationInterface = &Plugin{} 58 var _ admission.InitializationValidator = &Plugin{} 59 var _ genericadmissioninit.WantsAuthorizer = &Plugin{} 60 var _ genericadmissioninit.WantsFeatures = &Plugin{} 61 62 func NewPlugin() *Plugin { 63 return &Plugin{ 64 Handler: admission.NewHandler(admission.Create, admission.Update), 65 } 66 } 67 68 // SetAuthorizer sets the plugin's authorizer. 69 func (p *Plugin) SetAuthorizer(authz authorizer.Authorizer) { 70 p.authz = authz 71 } 72 73 // InspectFeatureGates implements WantsFeatures. 74 func (p *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) { 75 p.enabled = featureGates.Enabled(features.ClusterTrustBundle) 76 p.inspectedFeatureGates = true 77 } 78 79 // ValidateInitialization checks that the plugin was initialized correctly. 80 func (p *Plugin) ValidateInitialization() error { 81 if p.authz == nil { 82 return fmt.Errorf("%s requires an authorizer", PluginName) 83 } 84 if !p.inspectedFeatureGates { 85 return fmt.Errorf("%s did not see feature gates", PluginName) 86 } 87 return nil 88 } 89 90 var clusterTrustBundleGroupResource = api.Resource("clustertrustbundles") 91 92 func (p *Plugin) Validate(ctx context.Context, a admission.Attributes, _ admission.ObjectInterfaces) error { 93 if !p.enabled { 94 return nil 95 } 96 if a.GetResource().GroupResource() != clusterTrustBundleGroupResource { 97 return nil 98 } 99 100 newBundle, ok := a.GetObject().(*api.ClusterTrustBundle) 101 if !ok { 102 return admission.NewForbidden(a, fmt.Errorf("expected type ClusterTrustBundle, got: %T", a.GetOldObject())) 103 } 104 105 // Unlike CSRs, it's OK to validate against the *new* object, because 106 // updates to signer name will be rejected during validation. 107 108 // If signer name isn't specified, we don't need to perform the 109 // attest check. 110 if newBundle.Spec.SignerName == "" { 111 return nil 112 } 113 114 // Skip the attest check when the semantics of the bundle are unchanged to support storage migration and GC workflows 115 if a.GetOperation() == admission.Update && rbac.IsOnlyMutatingGCFields(a.GetObject(), a.GetOldObject(), kapihelper.Semantic) { 116 return nil 117 } 118 119 if !certificates.IsAuthorizedForSignerName(ctx, p.authz, a.GetUserInfo(), "attest", newBundle.Spec.SignerName) { 120 klog.V(4).Infof("user not permitted to attest ClusterTrustBundle %q with signerName %q", newBundle.Name, newBundle.Spec.SignerName) 121 return admission.NewForbidden(a, fmt.Errorf("user not permitted to attest for signerName %q", newBundle.Spec.SignerName)) 122 } 123 124 return nil 125 }