1
16
17 package renewal
18
19 import (
20 "crypto"
21 "crypto/x509"
22 "crypto/x509/pkix"
23 "fmt"
24 "net"
25 "os"
26 "path/filepath"
27 "reflect"
28 "testing"
29 "time"
30
31 certutil "k8s.io/client-go/util/cert"
32 netutils "k8s.io/utils/net"
33
34 kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
35 certtestutil "k8s.io/kubernetes/cmd/kubeadm/app/util/certs"
36 "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
37 testutil "k8s.io/kubernetes/cmd/kubeadm/test"
38 )
39
40 var (
41 testCACertCfg = &pkiutil.CertConfig{
42 Config: certutil.Config{CommonName: "kubernetes"},
43 }
44
45 testCACert, testCAKey, _ = pkiutil.NewCertificateAuthority(testCACertCfg)
46
47 testCertOrganization = []string{"sig-cluster-lifecycle"}
48
49 testCertCfg = makeTestCertConfig(testCertOrganization)
50 )
51
52 type fakecertificateReadWriter struct {
53 exist bool
54 cert *x509.Certificate
55 }
56
57 func (cr fakecertificateReadWriter) Exists() (bool, error) {
58 return cr.exist, nil
59 }
60
61 func (cr fakecertificateReadWriter) Read() (*x509.Certificate, error) {
62 return cr.cert, nil
63 }
64
65 func (cr fakecertificateReadWriter) Write(*x509.Certificate, crypto.Signer) error {
66 return nil
67 }
68
69 func TestNewManager(t *testing.T) {
70 tests := []struct {
71 name string
72 cfg *kubeadmapi.ClusterConfiguration
73 expectedCertificates int
74 }{
75 {
76 name: "cluster with local etcd",
77 cfg: &kubeadmapi.ClusterConfiguration{},
78 expectedCertificates: 11,
79 },
80 {
81 name: "cluster with external etcd",
82 cfg: &kubeadmapi.ClusterConfiguration{
83 Etcd: kubeadmapi.Etcd{
84 External: &kubeadmapi.ExternalEtcd{},
85 },
86 },
87 expectedCertificates: 7,
88 },
89 }
90
91 for _, test := range tests {
92 t.Run(test.name, func(t *testing.T) {
93 rm, err := NewManager(test.cfg, "")
94 if err != nil {
95 t.Fatalf("Failed to create the certificate renewal manager: %v", err)
96 }
97
98 if len(rm.Certificates()) != test.expectedCertificates {
99 t.Errorf("Expected %d certificates, saw %d", test.expectedCertificates, len(rm.Certificates()))
100 }
101 })
102 }
103 }
104
105 func TestRenewUsingLocalCA(t *testing.T) {
106 dir := testutil.SetupTempDir(t)
107 defer os.RemoveAll(dir)
108
109 if err := pkiutil.WriteCertAndKey(dir, "ca", testCACert, testCAKey); err != nil {
110 t.Fatalf("couldn't write out CA certificate to %s", dir)
111 }
112
113 etcdDir := filepath.Join(dir, "etcd")
114 if err := pkiutil.WriteCertAndKey(etcdDir, "ca", testCACert, testCAKey); err != nil {
115 t.Fatalf("couldn't write out CA certificate to %s", etcdDir)
116 }
117
118 cfg := &kubeadmapi.ClusterConfiguration{
119 CertificatesDir: dir,
120 }
121 rm, err := NewManager(cfg, dir)
122 if err != nil {
123 t.Fatalf("Failed to create the certificate renewal manager: %v", err)
124 }
125
126 tests := []struct {
127 name string
128 certName string
129 createCertFunc func() *x509.Certificate
130 expectedOrganization []string
131 }{
132 {
133 name: "Certificate renewal for a PKI certificate",
134 certName: "apiserver",
135 createCertFunc: func() *x509.Certificate {
136 return writeTestCertificate(t, dir, "apiserver", testCACert, testCAKey, testCertOrganization)
137 },
138 expectedOrganization: testCertOrganization,
139 },
140 {
141 name: "Certificate renewal for a certificate embedded in a kubeconfig file",
142 certName: "admin.conf",
143 createCertFunc: func() *x509.Certificate {
144 return writeTestKubeconfig(t, dir, "admin.conf", testCACert, testCAKey)
145 },
146 expectedOrganization: testCertOrganization,
147 },
148 }
149
150 for _, test := range tests {
151 t.Run(test.name, func(t *testing.T) {
152 cert := test.createCertFunc()
153
154 time.Sleep(1 * time.Second)
155
156 _, err := rm.RenewUsingLocalCA(test.certName)
157 if err != nil {
158 t.Fatalf("error renewing certificate: %v", err)
159 }
160
161 newCert, err := rm.certificates[test.certName].readwriter.Read()
162 if err != nil {
163 t.Fatalf("error reading renewed certificate: %v", err)
164 }
165
166 if newCert.SerialNumber.Cmp(cert.SerialNumber) == 0 {
167 t.Fatal("expected new certificate, but renewed certificate has same serial number")
168 }
169
170 if !newCert.NotAfter.After(cert.NotAfter) {
171 t.Fatalf("expected new certificate with updated expiration, but renewed certificate has same NotAfter value: saw %s, expected greather than %s", newCert.NotAfter, cert.NotAfter)
172 }
173
174 certtestutil.AssertCertificateIsSignedByCa(t, newCert, testCACert)
175 certtestutil.AssertCertificateHasClientAuthUsage(t, newCert)
176 certtestutil.AssertCertificateHasOrganizations(t, newCert, test.expectedOrganization...)
177 certtestutil.AssertCertificateHasCommonName(t, newCert, testCertCfg.CommonName)
178 certtestutil.AssertCertificateHasDNSNames(t, newCert, testCertCfg.AltNames.DNSNames...)
179 certtestutil.AssertCertificateHasIPAddresses(t, newCert, testCertCfg.AltNames.IPs...)
180 })
181 }
182 }
183
184 func TestCreateRenewCSR(t *testing.T) {
185 dir := testutil.SetupTempDir(t)
186 defer os.RemoveAll(dir)
187
188 outdir := filepath.Join(dir, "out")
189
190 if err := os.MkdirAll(outdir, 0755); err != nil {
191 t.Fatalf("couldn't create %s", outdir)
192 }
193
194 if err := pkiutil.WriteCertAndKey(dir, "ca", testCACert, testCAKey); err != nil {
195 t.Fatalf("couldn't write out CA certificate to %s", dir)
196 }
197
198 cfg := &kubeadmapi.ClusterConfiguration{
199 CertificatesDir: dir,
200 }
201 rm, err := NewManager(cfg, dir)
202 if err != nil {
203 t.Fatalf("Failed to create the certificate renewal manager: %v", err)
204 }
205
206 tests := []struct {
207 name string
208 certName string
209 createCertFunc func() *x509.Certificate
210 }{
211 {
212 name: "Creation of a CSR request for renewal of a PKI certificate",
213 certName: "apiserver",
214 createCertFunc: func() *x509.Certificate {
215 return writeTestCertificate(t, dir, "apiserver", testCACert, testCAKey, testCertOrganization)
216 },
217 },
218 {
219 name: "Creation of a CSR request for renewal of a certificate embedded in a kubeconfig file",
220 certName: "admin.conf",
221 createCertFunc: func() *x509.Certificate {
222 return writeTestKubeconfig(t, dir, "admin.conf", testCACert, testCAKey)
223 },
224 },
225 }
226
227 for _, test := range tests {
228 t.Run(test.name, func(t *testing.T) {
229 test.createCertFunc()
230
231 time.Sleep(1 * time.Second)
232
233 err := rm.CreateRenewCSR(test.certName, outdir)
234 if err != nil {
235 t.Fatalf("error renewing certificate: %v", err)
236 }
237
238 file := fmt.Sprintf("%s.key", test.certName)
239 if _, err := os.Stat(filepath.Join(outdir, file)); os.IsNotExist(err) {
240 t.Errorf("Expected file %s does not exist", file)
241 }
242
243 file = fmt.Sprintf("%s.csr", test.certName)
244 if _, err := os.Stat(filepath.Join(outdir, file)); os.IsNotExist(err) {
245 t.Errorf("Expected file %s does not exist", file)
246 }
247 })
248 }
249
250 }
251
252 func TestCertToConfig(t *testing.T) {
253 expectedConfig := &certutil.Config{
254 CommonName: "test-common-name",
255 Organization: testCertOrganization,
256 AltNames: certutil.AltNames{
257 IPs: []net.IP{netutils.ParseIPSloppy("10.100.0.1")},
258 DNSNames: []string{"test-domain.space"},
259 },
260 Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
261 }
262
263 cert := &x509.Certificate{
264 Subject: pkix.Name{
265 CommonName: "test-common-name",
266 Organization: testCertOrganization,
267 },
268 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
269 DNSNames: []string{"test-domain.space"},
270 IPAddresses: []net.IP{netutils.ParseIPSloppy("10.100.0.1")},
271 }
272
273 cfg := certToConfig(cert)
274
275 if cfg.CommonName != expectedConfig.CommonName {
276 t.Errorf("expected common name %q, got %q", expectedConfig.CommonName, cfg.CommonName)
277 }
278
279 if len(cfg.Organization) != 1 || cfg.Organization[0] != expectedConfig.Organization[0] {
280 t.Errorf("expected organization %v, got %v", expectedConfig.Organization, cfg.Organization)
281
282 }
283
284 if len(cfg.Usages) != 1 || cfg.Usages[0] != expectedConfig.Usages[0] {
285 t.Errorf("expected ext key usage %v, got %v", expectedConfig.Usages, cfg.Usages)
286 }
287
288 if len(cfg.AltNames.IPs) != 1 || cfg.AltNames.IPs[0].String() != expectedConfig.AltNames.IPs[0].String() {
289 t.Errorf("expected SAN IPs %v, got %v", expectedConfig.AltNames.IPs, cfg.AltNames.IPs)
290 }
291
292 if len(cfg.AltNames.DNSNames) != 1 || cfg.AltNames.DNSNames[0] != expectedConfig.AltNames.DNSNames[0] {
293 t.Errorf("expected SAN DNSNames %v, got %v", expectedConfig.AltNames.DNSNames, cfg.AltNames.DNSNames)
294 }
295 }
296
297 func makeTestCertConfig(organization []string) *pkiutil.CertConfig {
298 return &pkiutil.CertConfig{
299 Config: certutil.Config{
300 CommonName: "test-common-name",
301 Organization: organization,
302 AltNames: certutil.AltNames{
303 IPs: []net.IP{netutils.ParseIPSloppy("10.100.0.1")},
304 DNSNames: []string{"test-domain.space"},
305 },
306 Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
307 },
308 }
309 }
310
311 func TestManagerCAs(t *testing.T) {
312 tests := []struct {
313 name string
314 cas map[string]*CAExpirationHandler
315 want []*CAExpirationHandler
316 }{
317 {
318 name: "CAExpirationHandler is sequential",
319 cas: map[string]*CAExpirationHandler{
320 "foo": {
321 Name: "1",
322 },
323 "bar": {
324 Name: "2",
325 },
326 },
327 want: []*CAExpirationHandler{
328 {
329 Name: "1",
330 },
331 {
332 Name: "2",
333 },
334 },
335 },
336 {
337 name: "CAExpirationHandler is in reverse order",
338 cas: map[string]*CAExpirationHandler{
339 "foo": {
340 Name: "2",
341 },
342 "bar": {
343 Name: "1",
344 },
345 },
346 want: []*CAExpirationHandler{
347 {
348 Name: "1",
349 },
350 {
351 Name: "2",
352 },
353 },
354 },
355 }
356 for _, tt := range tests {
357 t.Run(tt.name, func(t *testing.T) {
358 rm := &Manager{
359 cas: tt.cas,
360 }
361 if got := rm.CAs(); !reflect.DeepEqual(got, tt.want) {
362 t.Errorf("Manager.CAs() = %v, want %v", got, tt.want)
363 }
364 })
365 }
366 }
367
368 func TestManagerCAExists(t *testing.T) {
369 certificateReadWriterExist := fakecertificateReadWriter{
370 exist: true,
371 }
372 certificateReadWriterMissing := fakecertificateReadWriter{
373 exist: false,
374 }
375 tests := []struct {
376 name string
377 cas map[string]*CAExpirationHandler
378 caName string
379 want bool
380 wantErr bool
381 }{
382 {
383 name: "caName does not exist in cas list",
384 cas: map[string]*CAExpirationHandler{},
385 caName: "foo",
386 want: false,
387 wantErr: true,
388 },
389 {
390 name: "ca exists",
391 cas: map[string]*CAExpirationHandler{
392 "foo": {
393 Name: "foo",
394 FileName: "test",
395 readwriter: certificateReadWriterExist,
396 },
397 },
398 caName: "foo",
399 want: true,
400 wantErr: false,
401 },
402 {
403 name: "ca does not exist",
404 cas: map[string]*CAExpirationHandler{
405 "foo": {
406 Name: "foo",
407 FileName: "test",
408 readwriter: certificateReadWriterMissing,
409 },
410 },
411 caName: "foo",
412 want: false,
413 wantErr: false,
414 },
415 }
416 for _, tt := range tests {
417 t.Run(tt.name, func(t *testing.T) {
418 rm := &Manager{
419 cas: tt.cas,
420 }
421 got, err := rm.CAExists(tt.caName)
422 if (err != nil) != tt.wantErr {
423 t.Errorf("Manager.CAExists() error = %v, wantErr %v", err, tt.wantErr)
424 return
425 }
426 if got != tt.want {
427 t.Errorf("Manager.CAExists() = %v, want %v", got, tt.want)
428 }
429 })
430 }
431 }
432
433 func TestManagerCertificateExists(t *testing.T) {
434 certificateReadWriterExist := fakecertificateReadWriter{
435 exist: true,
436 }
437 certificateReadWriterMissing := fakecertificateReadWriter{
438 exist: false,
439 }
440 tests := []struct {
441 name string
442 certificates map[string]*CertificateRenewHandler
443 certName string
444 want bool
445 wantErr bool
446 }{
447 {
448 name: "certName does not exist in certificate list",
449 certificates: map[string]*CertificateRenewHandler{},
450 certName: "foo",
451 want: false,
452 wantErr: true,
453 },
454 {
455 name: "certificate exists",
456 certificates: map[string]*CertificateRenewHandler{
457 "foo": {
458 Name: "foo",
459 readwriter: certificateReadWriterExist,
460 },
461 },
462 certName: "foo",
463 want: true,
464 wantErr: false,
465 },
466 {
467 name: "certificate does not exist",
468 certificates: map[string]*CertificateRenewHandler{
469 "foo": {
470 Name: "foo",
471 readwriter: certificateReadWriterMissing,
472 },
473 },
474 certName: "foo",
475 want: false,
476 wantErr: false,
477 },
478 }
479 for _, tt := range tests {
480 t.Run(tt.name, func(t *testing.T) {
481 rm := &Manager{
482 certificates: tt.certificates,
483 }
484 got, err := rm.CertificateExists(tt.certName)
485 if (err != nil) != tt.wantErr {
486 t.Errorf("Manager.CertificateExists() error = %v, wantErr %v", err, tt.wantErr)
487 return
488 }
489 if got != tt.want {
490 t.Errorf("Manager.CertificateExists() = %v, want %v", got, tt.want)
491 }
492 })
493 }
494 }
495
View as plain text