1
16
17
18 package signer
19
20 import (
21 "context"
22 "crypto/x509"
23 "encoding/pem"
24 "fmt"
25 "time"
26
27 capi "k8s.io/api/certificates/v1"
28 capiv1beta1 "k8s.io/api/certificates/v1beta1"
29 v1 "k8s.io/api/core/v1"
30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31 "k8s.io/apimachinery/pkg/util/sets"
32 "k8s.io/apiserver/pkg/server/dynamiccertificates"
33 certificatesinformers "k8s.io/client-go/informers/certificates/v1"
34 clientset "k8s.io/client-go/kubernetes"
35 "k8s.io/client-go/util/certificate/csr"
36 capihelper "k8s.io/kubernetes/pkg/apis/certificates"
37 "k8s.io/kubernetes/pkg/controller/certificates"
38 "k8s.io/kubernetes/pkg/controller/certificates/authority"
39 )
40
41 type CSRSigningController struct {
42 certificateController *certificates.CertificateController
43 dynamicCertReloader dynamiccertificates.ControllerRunner
44 }
45
46 func NewKubeletServingCSRSigningController(
47 ctx context.Context,
48 client clientset.Interface,
49 csrInformer certificatesinformers.CertificateSigningRequestInformer,
50 caFile, caKeyFile string,
51 certTTL time.Duration,
52 ) (*CSRSigningController, error) {
53 return NewCSRSigningController(ctx, "csrsigning-kubelet-serving", capi.KubeletServingSignerName, client, csrInformer, caFile, caKeyFile, certTTL)
54 }
55
56 func NewKubeletClientCSRSigningController(
57 ctx context.Context,
58 client clientset.Interface,
59 csrInformer certificatesinformers.CertificateSigningRequestInformer,
60 caFile, caKeyFile string,
61 certTTL time.Duration,
62 ) (*CSRSigningController, error) {
63 return NewCSRSigningController(ctx, "csrsigning-kubelet-client", capi.KubeAPIServerClientKubeletSignerName, client, csrInformer, caFile, caKeyFile, certTTL)
64 }
65
66 func NewKubeAPIServerClientCSRSigningController(
67 ctx context.Context,
68 client clientset.Interface,
69 csrInformer certificatesinformers.CertificateSigningRequestInformer,
70 caFile, caKeyFile string,
71 certTTL time.Duration,
72 ) (*CSRSigningController, error) {
73 return NewCSRSigningController(ctx, "csrsigning-kube-apiserver-client", capi.KubeAPIServerClientSignerName, client, csrInformer, caFile, caKeyFile, certTTL)
74 }
75
76 func NewLegacyUnknownCSRSigningController(
77 ctx context.Context,
78 client clientset.Interface,
79 csrInformer certificatesinformers.CertificateSigningRequestInformer,
80 caFile, caKeyFile string,
81 certTTL time.Duration,
82 ) (*CSRSigningController, error) {
83 return NewCSRSigningController(ctx, "csrsigning-legacy-unknown", capiv1beta1.LegacyUnknownSignerName, client, csrInformer, caFile, caKeyFile, certTTL)
84 }
85
86 func NewCSRSigningController(
87 ctx context.Context,
88 controllerName string,
89 signerName string,
90 client clientset.Interface,
91 csrInformer certificatesinformers.CertificateSigningRequestInformer,
92 caFile, caKeyFile string,
93 certTTL time.Duration,
94 ) (*CSRSigningController, error) {
95 signer, err := newSigner(signerName, caFile, caKeyFile, client, certTTL)
96 if err != nil {
97 return nil, err
98 }
99
100 return &CSRSigningController{
101 certificateController: certificates.NewCertificateController(
102 ctx,
103 controllerName,
104 client,
105 csrInformer,
106 signer.handle,
107 ),
108 dynamicCertReloader: signer.caProvider.caLoader,
109 }, nil
110 }
111
112
113 func (c *CSRSigningController) Run(ctx context.Context, workers int) {
114 go c.dynamicCertReloader.Run(ctx, workers)
115
116 c.certificateController.Run(ctx, workers)
117 }
118
119 type isRequestForSignerFunc func(req *x509.CertificateRequest, usages []capi.KeyUsage, signerName string) (bool, error)
120
121 type signer struct {
122 caProvider *caProvider
123
124 client clientset.Interface
125 certTTL time.Duration
126
127 signerName string
128 isRequestForSignerFn isRequestForSignerFunc
129 }
130
131 func newSigner(signerName, caFile, caKeyFile string, client clientset.Interface, certificateDuration time.Duration) (*signer, error) {
132 isRequestForSignerFn, err := getCSRVerificationFuncForSignerName(signerName)
133 if err != nil {
134 return nil, err
135 }
136 caProvider, err := newCAProvider(caFile, caKeyFile)
137 if err != nil {
138 return nil, err
139 }
140
141 ret := &signer{
142 caProvider: caProvider,
143 client: client,
144 certTTL: certificateDuration,
145 signerName: signerName,
146 isRequestForSignerFn: isRequestForSignerFn,
147 }
148 return ret, nil
149 }
150
151 func (s *signer) handle(ctx context.Context, csr *capi.CertificateSigningRequest) error {
152
153 if !certificates.IsCertificateRequestApproved(csr) || certificates.HasTrueCondition(csr, capi.CertificateFailed) {
154 return nil
155 }
156
157
158 if csr.Spec.SignerName != s.signerName {
159 return nil
160 }
161
162 x509cr, err := capihelper.ParseCSR(csr.Spec.Request)
163 if err != nil {
164 return fmt.Errorf("unable to parse csr %q: %v", csr.Name, err)
165 }
166 if recognized, err := s.isRequestForSignerFn(x509cr, csr.Spec.Usages, csr.Spec.SignerName); err != nil {
167 csr.Status.Conditions = append(csr.Status.Conditions, capi.CertificateSigningRequestCondition{
168 Type: capi.CertificateFailed,
169 Status: v1.ConditionTrue,
170 Reason: "SignerValidationFailure",
171 Message: err.Error(),
172 LastUpdateTime: metav1.Now(),
173 })
174 _, err = s.client.CertificatesV1().CertificateSigningRequests().UpdateStatus(ctx, csr, metav1.UpdateOptions{})
175 if err != nil {
176 return fmt.Errorf("error adding failure condition for csr: %v", err)
177 }
178 return nil
179 } else if !recognized {
180
181 return nil
182 }
183 cert, err := s.sign(x509cr, csr.Spec.Usages, csr.Spec.ExpirationSeconds, nil)
184 if err != nil {
185 return fmt.Errorf("error auto signing csr: %v", err)
186 }
187 csr.Status.Certificate = cert
188 _, err = s.client.CertificatesV1().CertificateSigningRequests().UpdateStatus(ctx, csr, metav1.UpdateOptions{})
189 if err != nil {
190 return fmt.Errorf("error updating signature for csr: %v", err)
191 }
192 return nil
193 }
194
195 func (s *signer) sign(x509cr *x509.CertificateRequest, usages []capi.KeyUsage, expirationSeconds *int32, now func() time.Time) ([]byte, error) {
196 currCA, err := s.caProvider.currentCA()
197 if err != nil {
198 return nil, err
199 }
200 der, err := currCA.Sign(x509cr.Raw, authority.PermissiveSigningPolicy{
201 TTL: s.duration(expirationSeconds),
202 Usages: usages,
203 Backdate: 5 * time.Minute,
204 Short: 8 * time.Hour,
205 Now: now,
206 })
207 if err != nil {
208 return nil, err
209 }
210 return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}), nil
211 }
212
213 func (s *signer) duration(expirationSeconds *int32) time.Duration {
214 if expirationSeconds == nil {
215 return s.certTTL
216 }
217
218
219
220 const min = 10 * time.Minute
221 switch requestedDuration := csr.ExpirationSecondsToDuration(*expirationSeconds); {
222 case requestedDuration > s.certTTL:
223 return s.certTTL
224
225 case requestedDuration < min:
226 return min
227
228 default:
229 return requestedDuration
230 }
231 }
232
233
234
235 func getCSRVerificationFuncForSignerName(signerName string) (isRequestForSignerFunc, error) {
236 switch signerName {
237 case capi.KubeletServingSignerName:
238 return isKubeletServing, nil
239 case capi.KubeAPIServerClientKubeletSignerName:
240 return isKubeletClient, nil
241 case capi.KubeAPIServerClientSignerName:
242 return isKubeAPIServerClient, nil
243 case capiv1beta1.LegacyUnknownSignerName:
244 return isLegacyUnknown, nil
245 default:
246
247
248
249 return nil, fmt.Errorf("unrecognized signerName: %q", signerName)
250 }
251 }
252
253 func isKubeletServing(req *x509.CertificateRequest, usages []capi.KeyUsage, signerName string) (bool, error) {
254 if signerName != capi.KubeletServingSignerName {
255 return false, nil
256 }
257 return true, capihelper.ValidateKubeletServingCSR(req, usagesToSet(usages))
258 }
259
260 func isKubeletClient(req *x509.CertificateRequest, usages []capi.KeyUsage, signerName string) (bool, error) {
261 if signerName != capi.KubeAPIServerClientKubeletSignerName {
262 return false, nil
263 }
264 return true, capihelper.ValidateKubeletClientCSR(req, usagesToSet(usages))
265 }
266
267 func isKubeAPIServerClient(req *x509.CertificateRequest, usages []capi.KeyUsage, signerName string) (bool, error) {
268 if signerName != capi.KubeAPIServerClientSignerName {
269 return false, nil
270 }
271 return true, validAPIServerClientUsages(usages)
272 }
273
274 func isLegacyUnknown(req *x509.CertificateRequest, usages []capi.KeyUsage, signerName string) (bool, error) {
275 if signerName != capiv1beta1.LegacyUnknownSignerName {
276 return false, nil
277 }
278
279
280 return true, nil
281 }
282
283 func validAPIServerClientUsages(usages []capi.KeyUsage) error {
284 hasClientAuth := false
285 for _, u := range usages {
286 switch u {
287
288 case capi.UsageDigitalSignature, capi.UsageKeyEncipherment:
289 case capi.UsageClientAuth:
290 hasClientAuth = true
291 default:
292 return fmt.Errorf("invalid usage for client certificate: %s", u)
293 }
294 }
295 if !hasClientAuth {
296 return fmt.Errorf("missing required usage for client certificate: %s", capi.UsageClientAuth)
297 }
298 return nil
299 }
300
301 func usagesToSet(usages []capi.KeyUsage) sets.String {
302 result := sets.NewString()
303 for _, usage := range usages {
304 result.Insert(string(usage))
305 }
306 return result
307 }
308
View as plain text