...

Source file src/k8s.io/kubernetes/pkg/controller/certificates/approver/sarapprove.go

Documentation: k8s.io/kubernetes/pkg/controller/certificates/approver

     1  /*
     2  Copyright 2016 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 approver implements an automated approver for kubelet certificates.
    18  package approver
    19  
    20  import (
    21  	"context"
    22  	"crypto/x509"
    23  	"fmt"
    24  
    25  	authorization "k8s.io/api/authorization/v1"
    26  	capi "k8s.io/api/certificates/v1"
    27  	corev1 "k8s.io/api/core/v1"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/util/sets"
    30  	certificatesinformers "k8s.io/client-go/informers/certificates/v1"
    31  	clientset "k8s.io/client-go/kubernetes"
    32  	capihelper "k8s.io/kubernetes/pkg/apis/certificates"
    33  	"k8s.io/kubernetes/pkg/controller/certificates"
    34  )
    35  
    36  type csrRecognizer struct {
    37  	recognize      func(csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) bool
    38  	permission     authorization.ResourceAttributes
    39  	successMessage string
    40  }
    41  
    42  type sarApprover struct {
    43  	client      clientset.Interface
    44  	recognizers []csrRecognizer
    45  }
    46  
    47  // NewCSRApprovingController creates a new CSRApprovingController.
    48  func NewCSRApprovingController(ctx context.Context, client clientset.Interface, csrInformer certificatesinformers.CertificateSigningRequestInformer) *certificates.CertificateController {
    49  	approver := &sarApprover{
    50  		client:      client,
    51  		recognizers: recognizers(),
    52  	}
    53  	return certificates.NewCertificateController(
    54  		ctx,
    55  		"csrapproving",
    56  		client,
    57  		csrInformer,
    58  		approver.handle,
    59  	)
    60  }
    61  
    62  func recognizers() []csrRecognizer {
    63  	recognizers := []csrRecognizer{
    64  		{
    65  			recognize:      isSelfNodeClientCert,
    66  			permission:     authorization.ResourceAttributes{Group: "certificates.k8s.io", Resource: "certificatesigningrequests", Verb: "create", Subresource: "selfnodeclient", Version: "*"},
    67  			successMessage: "Auto approving self kubelet client certificate after SubjectAccessReview.",
    68  		},
    69  		{
    70  			recognize:      isNodeClientCert,
    71  			permission:     authorization.ResourceAttributes{Group: "certificates.k8s.io", Resource: "certificatesigningrequests", Verb: "create", Subresource: "nodeclient", Version: "*"},
    72  			successMessage: "Auto approving kubelet client certificate after SubjectAccessReview.",
    73  		},
    74  	}
    75  	return recognizers
    76  }
    77  
    78  func (a *sarApprover) handle(ctx context.Context, csr *capi.CertificateSigningRequest) error {
    79  	if len(csr.Status.Certificate) != 0 {
    80  		return nil
    81  	}
    82  	if approved, denied := certificates.GetCertApprovalCondition(&csr.Status); approved || denied {
    83  		return nil
    84  	}
    85  	x509cr, err := capihelper.ParseCSR(csr.Spec.Request)
    86  	if err != nil {
    87  		return fmt.Errorf("unable to parse csr %q: %v", csr.Name, err)
    88  	}
    89  
    90  	tried := []string{}
    91  
    92  	for _, r := range a.recognizers {
    93  		if !r.recognize(csr, x509cr) {
    94  			continue
    95  		}
    96  
    97  		tried = append(tried, r.permission.Subresource)
    98  
    99  		approved, err := a.authorize(ctx, csr, r.permission)
   100  		if err != nil {
   101  			return err
   102  		}
   103  		if approved {
   104  			appendApprovalCondition(csr, r.successMessage)
   105  			_, err = a.client.CertificatesV1().CertificateSigningRequests().UpdateApproval(ctx, csr.Name, csr, metav1.UpdateOptions{})
   106  			if err != nil {
   107  				return fmt.Errorf("error updating approval for csr: %v", err)
   108  			}
   109  			return nil
   110  		}
   111  	}
   112  
   113  	if len(tried) != 0 {
   114  		return certificates.IgnorableError("recognized csr %q as %v but subject access review was not approved", csr.Name, tried)
   115  	}
   116  
   117  	return nil
   118  }
   119  
   120  func (a *sarApprover) authorize(ctx context.Context, csr *capi.CertificateSigningRequest, rattrs authorization.ResourceAttributes) (bool, error) {
   121  	extra := make(map[string]authorization.ExtraValue)
   122  	for k, v := range csr.Spec.Extra {
   123  		extra[k] = authorization.ExtraValue(v)
   124  	}
   125  
   126  	sar := &authorization.SubjectAccessReview{
   127  		Spec: authorization.SubjectAccessReviewSpec{
   128  			User:               csr.Spec.Username,
   129  			UID:                csr.Spec.UID,
   130  			Groups:             csr.Spec.Groups,
   131  			Extra:              extra,
   132  			ResourceAttributes: &rattrs,
   133  		},
   134  	}
   135  	sar, err := a.client.AuthorizationV1().SubjectAccessReviews().Create(ctx, sar, metav1.CreateOptions{})
   136  	if err != nil {
   137  		return false, err
   138  	}
   139  	return sar.Status.Allowed, nil
   140  }
   141  
   142  func appendApprovalCondition(csr *capi.CertificateSigningRequest, message string) {
   143  	csr.Status.Conditions = append(csr.Status.Conditions, capi.CertificateSigningRequestCondition{
   144  		Type:    capi.CertificateApproved,
   145  		Status:  corev1.ConditionTrue,
   146  		Reason:  "AutoApproved",
   147  		Message: message,
   148  	})
   149  }
   150  
   151  func isNodeClientCert(csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) bool {
   152  	if csr.Spec.SignerName != capi.KubeAPIServerClientKubeletSignerName {
   153  		return false
   154  	}
   155  	return capihelper.IsKubeletClientCSR(x509cr, usagesToSet(csr.Spec.Usages))
   156  }
   157  
   158  func isSelfNodeClientCert(csr *capi.CertificateSigningRequest, x509cr *x509.CertificateRequest) bool {
   159  	if csr.Spec.Username != x509cr.Subject.CommonName {
   160  		return false
   161  	}
   162  	return isNodeClientCert(csr, x509cr)
   163  }
   164  
   165  func usagesToSet(usages []capi.KeyUsage) sets.String {
   166  	result := sets.NewString()
   167  	for _, usage := range usages {
   168  		result.Insert(string(usage))
   169  	}
   170  	return result
   171  }
   172  

View as plain text