...

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

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

     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 authority
    18  
    19  import (
    20  	"crypto/ecdsa"
    21  	"crypto/elliptic"
    22  	"crypto/rand"
    23  	"crypto/x509"
    24  	"crypto/x509/pkix"
    25  	"math/big"
    26  	"net/url"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/google/go-cmp/cmp"
    31  	"github.com/google/go-cmp/cmp/cmpopts"
    32  
    33  	capi "k8s.io/api/certificates/v1"
    34  	"k8s.io/apimachinery/pkg/util/diff"
    35  )
    36  
    37  func TestCertificateAuthority(t *testing.T) {
    38  	caKey, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
    39  	if err != nil {
    40  		t.Fatal(err)
    41  	}
    42  	now := time.Now()
    43  	nowFunc := func() time.Time { return now }
    44  	tmpl := &x509.Certificate{
    45  		SerialNumber: big.NewInt(42),
    46  		Subject: pkix.Name{
    47  			CommonName: "test-ca",
    48  		},
    49  		NotBefore:             now.Add(-24 * time.Hour),
    50  		NotAfter:              now.Add(24 * time.Hour),
    51  		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
    52  		BasicConstraintsValid: true,
    53  		IsCA:                  true,
    54  	}
    55  	der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, caKey.Public(), caKey)
    56  	if err != nil {
    57  		t.Fatal(err)
    58  	}
    59  	caCert, err := x509.ParseCertificate(der)
    60  	if err != nil {
    61  		t.Fatal(err)
    62  	}
    63  
    64  	uri, err := url.Parse("help://me@what:8080/where/when?why=true")
    65  	if err != nil {
    66  		t.Fatal(err)
    67  	}
    68  
    69  	tests := []struct {
    70  		name     string
    71  		cr       x509.CertificateRequest
    72  		policy   SigningPolicy
    73  		mutateCA func(ca *CertificateAuthority)
    74  
    75  		want    x509.Certificate
    76  		wantErr string
    77  	}{
    78  		{
    79  			name:   "ca info",
    80  			policy: PermissiveSigningPolicy{TTL: time.Hour, Now: nowFunc},
    81  			want: x509.Certificate{
    82  				Issuer:                caCert.Subject,
    83  				AuthorityKeyId:        caCert.SubjectKeyId,
    84  				NotBefore:             now,
    85  				NotAfter:              now.Add(1 * time.Hour),
    86  				BasicConstraintsValid: true,
    87  			},
    88  		},
    89  		{
    90  			name:   "key usage",
    91  			policy: PermissiveSigningPolicy{TTL: time.Hour, Usages: []capi.KeyUsage{"signing"}, Now: nowFunc},
    92  			want: x509.Certificate{
    93  				NotBefore:             now,
    94  				NotAfter:              now.Add(1 * time.Hour),
    95  				BasicConstraintsValid: true,
    96  				KeyUsage:              x509.KeyUsageDigitalSignature,
    97  			},
    98  		},
    99  		{
   100  			name:   "ext key usage",
   101  			policy: PermissiveSigningPolicy{TTL: time.Hour, Usages: []capi.KeyUsage{"client auth"}, Now: nowFunc},
   102  			want: x509.Certificate{
   103  				NotBefore:             now,
   104  				NotAfter:              now.Add(1 * time.Hour),
   105  				BasicConstraintsValid: true,
   106  				ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
   107  			},
   108  		},
   109  		{
   110  			name:   "backdate without short",
   111  			policy: PermissiveSigningPolicy{TTL: time.Hour, Backdate: 5 * time.Minute, Now: nowFunc},
   112  			want: x509.Certificate{
   113  				NotBefore:             now.Add(-5 * time.Minute),
   114  				NotAfter:              now.Add(55 * time.Minute),
   115  				BasicConstraintsValid: true,
   116  			},
   117  		},
   118  		{
   119  			name:   "backdate without short and super small ttl",
   120  			policy: PermissiveSigningPolicy{TTL: time.Minute, Backdate: 5 * time.Minute, Now: nowFunc},
   121  			want: x509.Certificate{
   122  				NotBefore:             now.Add(-5 * time.Minute),
   123  				NotAfter:              now.Add(-4 * time.Minute),
   124  				BasicConstraintsValid: true,
   125  			},
   126  		},
   127  		{
   128  			name:   "backdate with short",
   129  			policy: PermissiveSigningPolicy{TTL: time.Hour, Backdate: 5 * time.Minute, Short: 8 * time.Hour, Now: nowFunc},
   130  			want: x509.Certificate{
   131  				NotBefore:             now.Add(-5 * time.Minute),
   132  				NotAfter:              now.Add(time.Hour),
   133  				BasicConstraintsValid: true,
   134  			},
   135  		},
   136  		{
   137  			name:   "backdate with short and super small ttl",
   138  			policy: PermissiveSigningPolicy{TTL: time.Minute, Backdate: 5 * time.Minute, Short: 8 * time.Hour, Now: nowFunc},
   139  			want: x509.Certificate{
   140  				NotBefore:             now.Add(-5 * time.Minute),
   141  				NotAfter:              now.Add(time.Minute),
   142  				BasicConstraintsValid: true,
   143  			},
   144  		},
   145  		{
   146  			name:   "backdate with short but longer ttl",
   147  			policy: PermissiveSigningPolicy{TTL: 24 * time.Hour, Backdate: 5 * time.Minute, Short: 8 * time.Hour, Now: nowFunc},
   148  			want: x509.Certificate{
   149  				NotBefore:             now.Add(-5 * time.Minute),
   150  				NotAfter:              now.Add(24*time.Hour - 5*time.Minute),
   151  				BasicConstraintsValid: true,
   152  			},
   153  		},
   154  		{
   155  			name:   "truncate expiration",
   156  			policy: PermissiveSigningPolicy{TTL: 48 * time.Hour, Now: nowFunc},
   157  			want: x509.Certificate{
   158  				NotBefore:             now,
   159  				NotAfter:              now.Add(24 * time.Hour),
   160  				BasicConstraintsValid: true,
   161  			},
   162  		},
   163  		{
   164  			name:   "uri sans",
   165  			policy: PermissiveSigningPolicy{TTL: time.Hour, Now: nowFunc},
   166  			cr: x509.CertificateRequest{
   167  				URIs: []*url.URL{uri},
   168  			},
   169  			want: x509.Certificate{
   170  				URIs:                  []*url.URL{uri},
   171  				NotBefore:             now,
   172  				NotAfter:              now.Add(1 * time.Hour),
   173  				BasicConstraintsValid: true,
   174  			},
   175  		},
   176  		{
   177  			name:   "expired ca",
   178  			policy: PermissiveSigningPolicy{TTL: time.Hour, Now: nowFunc},
   179  			mutateCA: func(ca *CertificateAuthority) {
   180  				ca.Certificate.NotAfter = now // pretend that the CA has expired
   181  			},
   182  			wantErr: "the signer has expired: NotAfter=" + now.String(),
   183  		},
   184  		{
   185  			name:   "expired ca with backdate",
   186  			policy: PermissiveSigningPolicy{TTL: time.Hour, Backdate: 5 * time.Minute, Now: nowFunc},
   187  			mutateCA: func(ca *CertificateAuthority) {
   188  				ca.Certificate.NotAfter = now // pretend that the CA has expired
   189  			},
   190  			wantErr: "refusing to sign a certificate that expired in the past: NotAfter=" + now.String(),
   191  		},
   192  	}
   193  
   194  	crKey, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
   195  	if err != nil {
   196  		t.Fatal(err)
   197  	}
   198  
   199  	for _, test := range tests {
   200  		t.Run(test.name, func(t *testing.T) {
   201  			caCertShallowCopy := *caCert
   202  
   203  			ca := &CertificateAuthority{
   204  				Certificate: &caCertShallowCopy,
   205  				PrivateKey:  caKey,
   206  			}
   207  
   208  			if test.mutateCA != nil {
   209  				test.mutateCA(ca)
   210  			}
   211  
   212  			csr, err := x509.CreateCertificateRequest(rand.Reader, &test.cr, crKey)
   213  			if err != nil {
   214  				t.Fatal(err)
   215  			}
   216  
   217  			certDER, err := ca.Sign(csr, test.policy)
   218  			if len(test.wantErr) > 0 {
   219  				if errStr := errString(err); test.wantErr != errStr {
   220  					t.Fatalf("expected error %s but got %s", test.wantErr, errStr)
   221  				}
   222  				return
   223  			}
   224  			if err != nil {
   225  				t.Fatal(err)
   226  			}
   227  
   228  			cert, err := x509.ParseCertificate(certDER)
   229  			if err != nil {
   230  				t.Fatal(err)
   231  			}
   232  
   233  			opts := cmp.Options{
   234  				cmpopts.IgnoreFields(x509.Certificate{},
   235  					"SignatureAlgorithm",
   236  					"PublicKeyAlgorithm",
   237  					"Version",
   238  					"MaxPathLen",
   239  				),
   240  				diff.IgnoreUnset(),
   241  				cmp.Transformer("RoundTime", func(x time.Time) time.Time {
   242  					return x.Truncate(time.Second)
   243  				}),
   244  				cmp.Comparer(func(x, y *url.URL) bool {
   245  					return ((x == nil) && (y == nil)) || x.String() == y.String()
   246  				}),
   247  			}
   248  			if !cmp.Equal(*cert, test.want, opts) {
   249  				t.Errorf("unexpected diff: %v", cmp.Diff(*cert, test.want, opts))
   250  			}
   251  		})
   252  	}
   253  }
   254  
   255  func errString(err error) string {
   256  	if err == nil {
   257  		return ""
   258  	}
   259  
   260  	return err.Error()
   261  }
   262  

View as plain text