...
1
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
45
46
47
48
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
69 func (p *Plugin) SetAuthorizer(authz authorizer.Authorizer) {
70 p.authz = authz
71 }
72
73
74 func (p *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) {
75 p.enabled = featureGates.Enabled(features.ClusterTrustBundle)
76 p.inspectedFeatureGates = true
77 }
78
79
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
106
107
108
109
110 if newBundle.Spec.SignerName == "" {
111 return nil
112 }
113
114
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 }
126
View as plain text