...

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

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

     1  /*
     2  Copyright 2019 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 signer implements a CA signer that uses keys stored on local disk.
    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  // Run the main goroutine responsible for watching and syncing jobs.
   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 // max TTL; individual requests may request shorter certs by setting spec.expirationSeconds
   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  	// Ignore unapproved or failed requests
   153  	if !certificates.IsCertificateRequestApproved(csr) || certificates.HasTrueCondition(csr, capi.CertificateFailed) {
   154  		return nil
   155  	}
   156  
   157  	// Fast-path to avoid any additional processing if the CSRs signerName does not match
   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  		// Ignore requests for kubernetes.io signerNames we don't recognize
   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, // this must always be less than the minimum TTL requested by a user (see sanity check requestedDuration below)
   204  		Short:    8 * time.Hour,   // 5 minutes of backdating is roughly 1% of 8 hours
   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  	// honor requested duration is if it is less than the default TTL
   219  	// use 10 min (2x hard coded backdate above) as a sanity check lower bound
   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  // getCSRVerificationFuncForSignerName is a function that provides reliable mapping of signer names to verification so that
   234  // we don't have accidents with wiring at some later date.
   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  		// TODO type this error so that a different reporting loop (one without a signing cert), can mark
   247  		//  CSRs with unknown kube signers as terminal if we wish.  This largely depends on how tightly we want to control
   248  		//  our signerNames.
   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  	// No restrictions are applied to the legacy-unknown signerName to
   279  	// maintain backward compatibility in v1.
   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  		// these usages are optional
   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