@marcel91200 / caddy Mirror · read-only
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 01:00 UTC

master
go 995 lines 23.4 KB
Raw
1 package caddytls
2
3 import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "reflect"
8 "testing"
9 "time"
10
11 "github.com/caddyserver/caddy/v2"
12 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
13 _ "github.com/caddyserver/caddy/v2/modules/filestorage"
14 )
15
16 const (
17 test_der_1 = `MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQELBQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkwODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl03WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45twOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNxtdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTUApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAdBgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5uNY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfKD66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEOfG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnkoNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdleIh6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ==`
18 test_cert_file_1 = "../../caddytest/caddy.ca.cer"
19 )
20
21 func TestInlineCAPoolUnmarshalCaddyfile(t *testing.T) {
22 type args struct {
23 d *caddyfile.Dispenser
24 }
25 tests := []struct {
26 name string
27 args args
28 expected InlineCAPool
29 wantErr bool
30 }{
31 {
32 name: "configuring no certificatest produces an error",
33 args: args{
34 d: caddyfile.NewTestDispenser(`
35 inline {
36 }
37 `),
38 },
39 wantErr: true,
40 },
41 {
42 name: "configuring certificates as arguments in-line produces an error",
43 args: args{
44 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
45 inline %s
46 `, test_der_1)),
47 },
48 wantErr: true,
49 },
50 {
51 name: "single cert",
52 args: args{
53 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
54 inline {
55 trust_der %s
56 }
57 `, test_der_1)),
58 },
59 expected: InlineCAPool{
60 TrustedCACerts: []string{test_der_1},
61 },
62 wantErr: false,
63 },
64 {
65 name: "multiple certs in one line",
66 args: args{
67 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
68 inline {
69 trust_der %s %s
70 }
71 `, test_der_1, test_der_1),
72 ),
73 },
74 expected: InlineCAPool{
75 TrustedCACerts: []string{test_der_1, test_der_1},
76 },
77 },
78 {
79 name: "multiple certs in multiple lines",
80 args: args{
81 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
82 inline {
83 trust_der %s
84 trust_der %s
85 }
86 `, test_der_1, test_der_1)),
87 },
88 expected: InlineCAPool{
89 TrustedCACerts: []string{test_der_1, test_der_1},
90 },
91 },
92 }
93 for _, tt := range tests {
94 t.Run(tt.name, func(t *testing.T) {
95 icp := &InlineCAPool{}
96 if err := icp.UnmarshalCaddyfile(tt.args.d); (err != nil) != tt.wantErr {
97 t.Errorf("InlineCAPool.UnmarshalCaddyfile() error = %v, wantErr %v", err, tt.wantErr)
98 }
99 if !tt.wantErr && !reflect.DeepEqual(&tt.expected, icp) {
100 t.Errorf("InlineCAPool.UnmarshalCaddyfile() = %v, want %v", icp, tt.expected)
101 }
102 })
103 }
104 }
105
106 func TestFileCAPoolUnmarshalCaddyfile(t *testing.T) {
107 type args struct {
108 d *caddyfile.Dispenser
109 }
110 tests := []struct {
111 name string
112 expected FileCAPool
113 args args
114 wantErr bool
115 }{
116 {
117 name: "configuring no certificatest produces an error",
118 args: args{
119 d: caddyfile.NewTestDispenser(`
120 file {
121 }
122 `),
123 },
124 wantErr: true,
125 },
126 {
127 name: "configuring certificates as arguments in-line produces an error",
128 args: args{
129 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
130 file %s
131 `, test_cert_file_1)),
132 },
133 expected: FileCAPool{
134 TrustedCACertPEMFiles: []string{test_cert_file_1},
135 },
136 },
137 {
138 name: "single cert",
139 args: args{
140 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
141 file {
142 pem_file %s
143 }
144 `, test_cert_file_1)),
145 },
146 expected: FileCAPool{
147 TrustedCACertPEMFiles: []string{test_cert_file_1},
148 },
149 wantErr: false,
150 },
151 {
152 name: "multiple certs inline and in-block are merged",
153 args: args{
154 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
155 file %s {
156 pem_file %s
157 }
158 `, test_cert_file_1, test_cert_file_1)),
159 },
160 expected: FileCAPool{
161 TrustedCACertPEMFiles: []string{test_cert_file_1, test_cert_file_1},
162 },
163 wantErr: false,
164 },
165 {
166 name: "multiple certs in one line",
167 args: args{
168 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
169 file {
170 pem_file %s %s
171 }
172 `, test_der_1, test_der_1),
173 ),
174 },
175 expected: FileCAPool{
176 TrustedCACertPEMFiles: []string{test_der_1, test_der_1},
177 },
178 },
179 {
180 name: "multiple certs in multiple lines",
181 args: args{
182 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
183 file {
184 pem_file %s
185 pem_file %s
186 }
187 `, test_cert_file_1, test_cert_file_1)),
188 },
189 expected: FileCAPool{
190 TrustedCACertPEMFiles: []string{test_cert_file_1, test_cert_file_1},
191 },
192 },
193 }
194 for _, tt := range tests {
195 t.Run(tt.name, func(t *testing.T) {
196 fcap := &FileCAPool{}
197 if err := fcap.UnmarshalCaddyfile(tt.args.d); (err != nil) != tt.wantErr {
198 t.Errorf("FileCAPool.UnmarshalCaddyfile() error = %v, wantErr %v", err, tt.wantErr)
199 }
200 if !tt.wantErr && !reflect.DeepEqual(&tt.expected, fcap) {
201 t.Errorf("FileCAPool.UnmarshalCaddyfile() = %v, want %v", fcap, tt.expected)
202 }
203 })
204 }
205 }
206
207 func TestPKIRootCAPoolUnmarshalCaddyfile(t *testing.T) {
208 type args struct {
209 d *caddyfile.Dispenser
210 }
211 tests := []struct {
212 name string
213 expected PKIRootCAPool
214 args args
215 wantErr bool
216 }{
217 {
218 name: "configuring no certificatest produces an error",
219 args: args{
220 d: caddyfile.NewTestDispenser(`
221 pki_root {
222 }
223 `),
224 },
225 wantErr: true,
226 },
227 {
228 name: "single authority as arguments in-line",
229 args: args{
230 d: caddyfile.NewTestDispenser(`
231 pki_root ca_1
232 `),
233 },
234 expected: PKIRootCAPool{
235 Authority: []string{"ca_1"},
236 },
237 },
238 {
239 name: "multiple authorities as arguments in-line",
240 args: args{
241 d: caddyfile.NewTestDispenser(`
242 pki_root ca_1 ca_2
243 `),
244 },
245 expected: PKIRootCAPool{
246 Authority: []string{"ca_1", "ca_2"},
247 },
248 },
249 {
250 name: "single authority in block",
251 args: args{
252 d: caddyfile.NewTestDispenser(`
253 pki_root {
254 authority ca_1
255 }`),
256 },
257 expected: PKIRootCAPool{
258 Authority: []string{"ca_1"},
259 },
260 wantErr: false,
261 },
262 {
263 name: "multiple authorities in one line",
264 args: args{
265 d: caddyfile.NewTestDispenser(`
266 pki_root {
267 authority ca_1 ca_2
268 }`),
269 },
270 expected: PKIRootCAPool{
271 Authority: []string{"ca_1", "ca_2"},
272 },
273 },
274 {
275 name: "multiple authorities in multiple lines",
276 args: args{
277 d: caddyfile.NewTestDispenser(`
278 pki_root {
279 authority ca_1
280 authority ca_2
281 }`),
282 },
283 expected: PKIRootCAPool{
284 Authority: []string{"ca_1", "ca_2"},
285 },
286 },
287 }
288 for _, tt := range tests {
289 t.Run(tt.name, func(t *testing.T) {
290 pkir := &PKIRootCAPool{}
291 if err := pkir.UnmarshalCaddyfile(tt.args.d); (err != nil) != tt.wantErr {
292 t.Errorf("PKIRootCAPool.UnmarshalCaddyfile() error = %v, wantErr %v", err, tt.wantErr)
293 }
294 if !tt.wantErr && !reflect.DeepEqual(&tt.expected, pkir) {
295 t.Errorf("PKIRootCAPool.UnmarshalCaddyfile() = %v, want %v", pkir, tt.expected)
296 }
297 })
298 }
299 }
300
301 func TestPKIIntermediateCAPoolUnmarshalCaddyfile(t *testing.T) {
302 type args struct {
303 d *caddyfile.Dispenser
304 }
305 tests := []struct {
306 name string
307 expected PKIIntermediateCAPool
308 args args
309 wantErr bool
310 }{
311 {
312 name: "configuring no certificatest produces an error",
313 args: args{
314 d: caddyfile.NewTestDispenser(`
315 pki_intermediate {
316 }`),
317 },
318 wantErr: true,
319 },
320 {
321 name: "single authority as arguments in-line",
322 args: args{
323 d: caddyfile.NewTestDispenser(`pki_intermediate ca_1`),
324 },
325 expected: PKIIntermediateCAPool{
326 Authority: []string{"ca_1"},
327 },
328 },
329 {
330 name: "multiple authorities as arguments in-line",
331 args: args{
332 d: caddyfile.NewTestDispenser(`pki_intermediate ca_1 ca_2`),
333 },
334 expected: PKIIntermediateCAPool{
335 Authority: []string{"ca_1", "ca_2"},
336 },
337 },
338 {
339 name: "single authority in block",
340 args: args{
341 d: caddyfile.NewTestDispenser(`
342 pki_intermediate {
343 authority ca_1
344 }`),
345 },
346 expected: PKIIntermediateCAPool{
347 Authority: []string{"ca_1"},
348 },
349 wantErr: false,
350 },
351 {
352 name: "multiple authorities in one line",
353 args: args{
354 d: caddyfile.NewTestDispenser(`
355 pki_intermediate {
356 authority ca_1 ca_2
357 }`),
358 },
359 expected: PKIIntermediateCAPool{
360 Authority: []string{"ca_1", "ca_2"},
361 },
362 },
363 {
364 name: "multiple authorities in multiple lines",
365 args: args{
366 d: caddyfile.NewTestDispenser(`
367 pki_intermediate {
368 authority ca_1
369 authority ca_2
370 }`),
371 },
372 expected: PKIIntermediateCAPool{
373 Authority: []string{"ca_1", "ca_2"},
374 },
375 },
376 }
377 for _, tt := range tests {
378 t.Run(tt.name, func(t *testing.T) {
379 pic := &PKIIntermediateCAPool{}
380 if err := pic.UnmarshalCaddyfile(tt.args.d); (err != nil) != tt.wantErr {
381 t.Errorf("PKIIntermediateCAPool.UnmarshalCaddyfile() error = %v, wantErr %v", err, tt.wantErr)
382 }
383 if !tt.wantErr && !reflect.DeepEqual(&tt.expected, pic) {
384 t.Errorf("PKIIntermediateCAPool.UnmarshalCaddyfile() = %v, want %v", pic, tt.expected)
385 }
386 })
387 }
388 }
389
390 func TestStoragePoolUnmarshalCaddyfile(t *testing.T) {
391 type args struct {
392 d *caddyfile.Dispenser
393 }
394 tests := []struct {
395 name string
396 args args
397 expected StoragePool
398 wantErr bool
399 }{
400 {
401 name: "empty block",
402 args: args{
403 d: caddyfile.NewTestDispenser(`storage {
404 }`),
405 },
406 expected: StoragePool{},
407 wantErr: false,
408 },
409 {
410 name: "providing single storage key inline",
411 args: args{
412 d: caddyfile.NewTestDispenser(`storage key-1`),
413 },
414 expected: StoragePool{
415 PEMKeys: []string{"key-1"},
416 },
417 wantErr: false,
418 },
419 {
420 name: "providing multiple storage keys inline",
421 args: args{
422 d: caddyfile.NewTestDispenser(`storage key-1 key-2`),
423 },
424 expected: StoragePool{
425 PEMKeys: []string{"key-1", "key-2"},
426 },
427 wantErr: false,
428 },
429 {
430 name: "providing keys inside block without specifying storage type",
431 args: args{
432 d: caddyfile.NewTestDispenser(`
433 storage {
434 keys key-1 key-2
435 }
436 `),
437 },
438 expected: StoragePool{
439 PEMKeys: []string{"key-1", "key-2"},
440 },
441 wantErr: false,
442 },
443 {
444 name: "providing keys in-line and inside block merges them",
445 args: args{
446 d: caddyfile.NewTestDispenser(`storage key-1 key-2 key-3 {
447 keys key-4 key-5
448 }`),
449 },
450 expected: StoragePool{
451 PEMKeys: []string{"key-1", "key-2", "key-3", "key-4", "key-5"},
452 },
453 wantErr: false,
454 },
455 {
456 name: "specifying storage type in block",
457 args: args{
458 d: caddyfile.NewTestDispenser(`storage {
459 storage file_system /var/caddy/storage
460 }`),
461 },
462 expected: StoragePool{
463 StorageRaw: json.RawMessage(`{"module":"file_system","root":"/var/caddy/storage"}`),
464 },
465 wantErr: false,
466 },
467 }
468 for _, tt := range tests {
469 t.Run(tt.name, func(t *testing.T) {
470 sp := &StoragePool{}
471 if err := sp.UnmarshalCaddyfile(tt.args.d); (err != nil) != tt.wantErr {
472 t.Errorf("StoragePool.UnmarshalCaddyfile() error = %v, wantErr %v", err, tt.wantErr)
473 }
474 if !tt.wantErr && !reflect.DeepEqual(&tt.expected, sp) {
475 t.Errorf("StoragePool.UnmarshalCaddyfile() = %s, want %s", sp.StorageRaw, tt.expected.StorageRaw)
476 }
477 })
478 }
479 }
480
481 func TestTLSConfig_unmarshalCaddyfile(t *testing.T) {
482 type args struct {
483 d *caddyfile.Dispenser
484 }
485 tests := []struct {
486 name string
487 args args
488 expected TLSConfig
489 wantErr bool
490 }{
491 {
492 name: "no arguments is valid",
493 args: args{
494 d: caddyfile.NewTestDispenser(` {
495 }`),
496 },
497 expected: TLSConfig{},
498 },
499 {
500 name: "setting 'renegotiation' to 'never' is valid",
501 args: args{
502 d: caddyfile.NewTestDispenser(` {
503 renegotiation never
504 }`),
505 },
506 expected: TLSConfig{
507 Renegotiation: "never",
508 },
509 },
510 {
511 name: "setting 'renegotiation' to 'once' is valid",
512 args: args{
513 d: caddyfile.NewTestDispenser(` {
514 renegotiation once
515 }`),
516 },
517 expected: TLSConfig{
518 Renegotiation: "once",
519 },
520 },
521 {
522 name: "setting 'renegotiation' to 'freely' is valid",
523 args: args{
524 d: caddyfile.NewTestDispenser(` {
525 renegotiation freely
526 }`),
527 },
528 expected: TLSConfig{
529 Renegotiation: "freely",
530 },
531 },
532 {
533 name: "setting 'renegotiation' to other than 'none', 'once, or 'freely' is invalid",
534 args: args{
535 d: caddyfile.NewTestDispenser(` {
536 renegotiation foo
537 }`),
538 },
539 wantErr: true,
540 },
541 {
542 name: "setting 'renegotiation' without argument is invalid",
543 args: args{
544 d: caddyfile.NewTestDispenser(` {
545 renegotiation
546 }`),
547 },
548 wantErr: true,
549 },
550 {
551 name: "setting 'ca' without argument is an error",
552 args: args{
553 d: caddyfile.NewTestDispenser(`{
554 ca
555 }`),
556 },
557 wantErr: true,
558 },
559 {
560 name: "setting 'ca' to 'file' with in-line cert is valid",
561 args: args{
562 d: caddyfile.NewTestDispenser(`{
563 ca file /var/caddy/ca.pem
564 }`),
565 },
566 expected: TLSConfig{
567 CARaw: []byte(`{"pem_files":["/var/caddy/ca.pem"],"provider":"file"}`),
568 },
569 },
570 {
571 name: "setting 'ca' to 'file' with appropriate block is valid",
572 args: args{
573 d: caddyfile.NewTestDispenser(`{
574 ca file /var/caddy/ca.pem {
575 pem_file /var/caddy/ca.pem
576 }
577 }`),
578 },
579 expected: TLSConfig{
580 CARaw: []byte(`{"pem_files":["/var/caddy/ca.pem","/var/caddy/ca.pem"],"provider":"file"}`),
581 },
582 },
583 {
584 name: "setting 'ca' multiple times is an error",
585 args: args{
586 d: caddyfile.NewTestDispenser(fmt.Sprintf(`{
587 ca file /var/caddy/ca.pem {
588 pem_file /var/caddy/ca.pem
589 }
590 ca inline %s
591 }`, test_der_1)),
592 },
593 wantErr: true,
594 },
595 {
596 name: "setting 'handshake_timeout' without value is an error",
597 args: args{
598 d: caddyfile.NewTestDispenser(`{
599 handshake_timeout
600 }`),
601 },
602 wantErr: true,
603 },
604 {
605 name: "setting 'handshake_timeout' properly is successful",
606 args: args{
607 d: caddyfile.NewTestDispenser(`{
608 handshake_timeout 42m
609 }`),
610 },
611 expected: TLSConfig{
612 HandshakeTimeout: caddy.Duration(42 * time.Minute),
613 },
614 },
615 {
616 name: "setting 'server_name' without value is an error",
617 args: args{
618 d: caddyfile.NewTestDispenser(`{
619 server_name
620 }`),
621 },
622 wantErr: true,
623 },
624 {
625 name: "setting 'server_name' properly is successful",
626 args: args{
627 d: caddyfile.NewTestDispenser(`{
628 server_name example.com
629 }`),
630 },
631 expected: TLSConfig{
632 ServerName: "example.com",
633 },
634 },
635 {
636 name: "unsupported directives are errors",
637 args: args{
638 d: caddyfile.NewTestDispenser(`{
639 foo
640 }`),
641 },
642 wantErr: true,
643 },
644 }
645 for _, tt := range tests {
646 t.Run(tt.name, func(t *testing.T) {
647 tr := &TLSConfig{}
648 if err := tr.unmarshalCaddyfile(tt.args.d); (err != nil) != tt.wantErr {
649 t.Errorf("TLSConfig.unmarshalCaddyfile() error = %v, wantErr %v", err, tt.wantErr)
650 }
651 if !tt.wantErr && !reflect.DeepEqual(&tt.expected, tr) {
652 t.Errorf("TLSConfig.UnmarshalCaddyfile() = %v, want %v", tr, tt.expected)
653 }
654 })
655 }
656 }
657
658 func TestHTTPCertPoolUnmarshalCaddyfile(t *testing.T) {
659 type args struct {
660 d *caddyfile.Dispenser
661 }
662 tests := []struct {
663 name string
664 args args
665 expected HTTPCertPool
666 wantErr bool
667 }{
668 {
669 name: "no block, inline http endpoint",
670 args: args{
671 d: caddyfile.NewTestDispenser(`http http://localhost/ca-certs`),
672 },
673 expected: HTTPCertPool{
674 Endpoints: []string{"http://localhost/ca-certs"},
675 },
676 wantErr: false,
677 },
678 {
679 name: "no block, inline https endpoint",
680 args: args{
681 d: caddyfile.NewTestDispenser(`http https://localhost/ca-certs`),
682 },
683 expected: HTTPCertPool{
684 Endpoints: []string{"https://localhost/ca-certs"},
685 },
686 wantErr: false,
687 },
688 {
689 name: "no block, mixed http and https endpoints inline",
690 args: args{
691 d: caddyfile.NewTestDispenser(`http http://localhost/ca-certs https://localhost/ca-certs`),
692 },
693 expected: HTTPCertPool{
694 Endpoints: []string{"http://localhost/ca-certs", "https://localhost/ca-certs"},
695 },
696 wantErr: false,
697 },
698 {
699 name: "multiple endpoints in separate lines in block",
700 args: args{
701 d: caddyfile.NewTestDispenser(`
702 http {
703 endpoints http://localhost/ca-certs
704 endpoints http://remotehost/ca-certs
705 }
706 `),
707 },
708 expected: HTTPCertPool{
709 Endpoints: []string{"http://localhost/ca-certs", "http://remotehost/ca-certs"},
710 },
711 wantErr: false,
712 },
713 {
714 name: "endpoints defined inline and in block are merged",
715 args: args{
716 d: caddyfile.NewTestDispenser(`http http://localhost/ca-certs {
717 endpoints http://remotehost/ca-certs
718 }`),
719 },
720 expected: HTTPCertPool{
721 Endpoints: []string{"http://localhost/ca-certs", "http://remotehost/ca-certs"},
722 },
723 wantErr: false,
724 },
725 {
726 name: "multiple endpoints defined in block on the same line",
727 args: args{
728 d: caddyfile.NewTestDispenser(`http {
729 endpoints http://remotehost/ca-certs http://localhost/ca-certs
730 }`),
731 },
732 expected: HTTPCertPool{
733 Endpoints: []string{"http://remotehost/ca-certs", "http://localhost/ca-certs"},
734 },
735 wantErr: false,
736 },
737 {
738 name: "declaring 'endpoints' in block without argument is an error",
739 args: args{
740 d: caddyfile.NewTestDispenser(`http {
741 endpoints
742 }`),
743 },
744 wantErr: true,
745 },
746 {
747 name: "multiple endpoints in separate lines in block",
748 args: args{
749 d: caddyfile.NewTestDispenser(`
750 http {
751 endpoints http://localhost/ca-certs
752 endpoints http://remotehost/ca-certs
753 tls {
754 renegotiation freely
755 }
756 }
757 `),
758 },
759 expected: HTTPCertPool{
760 Endpoints: []string{"http://localhost/ca-certs", "http://remotehost/ca-certs"},
761 TLS: &TLSConfig{
762 Renegotiation: "freely",
763 },
764 },
765 wantErr: false,
766 },
767 }
768 for _, tt := range tests {
769 t.Run(tt.name, func(t *testing.T) {
770 hcp := &HTTPCertPool{}
771 if err := hcp.UnmarshalCaddyfile(tt.args.d); (err != nil) != tt.wantErr {
772 t.Errorf("HTTPCertPool.UnmarshalCaddyfile() error = %v, wantErr %v", err, tt.wantErr)
773 }
774 if !tt.wantErr && !reflect.DeepEqual(&tt.expected, hcp) {
775 t.Errorf("HTTPCertPool.UnmarshalCaddyfile() = %v, want %v", hcp, tt.expected)
776 }
777 })
778 }
779 }
780
781 func TestSystemCAPoolUnmarshalCaddyfile(t *testing.T) {
782 type args struct {
783 d *caddyfile.Dispenser
784 }
785 tests := []struct {
786 name string
787 args args
788 wantErr bool
789 }{
790 {
791 name: "basic system pool configuration",
792 args: args{
793 d: caddyfile.NewTestDispenser(`system`),
794 },
795 wantErr: false,
796 },
797 {
798 name: "system pool with arguments produces error",
799 args: args{
800 d: caddyfile.NewTestDispenser(`system foo`),
801 },
802 wantErr: true,
803 },
804 {
805 name: "system pool with block produces error",
806 args: args{
807 d: caddyfile.NewTestDispenser(`system {
808 foo bar
809 }`),
810 },
811 wantErr: true,
812 },
813 }
814 for _, tt := range tests {
815 t.Run(tt.name, func(t *testing.T) {
816 scp := &SystemCAPool{}
817 if err := scp.UnmarshalCaddyfile(tt.args.d); (err != nil) != tt.wantErr {
818 t.Errorf("SystemCAPool.UnmarshalCaddyfile() error = %v, wantErr %v", err, tt.wantErr)
819 }
820 })
821 }
822 }
823
824 func TestCombinedCAPoolUnmarshalCaddyfile(t *testing.T) {
825 type args struct {
826 d *caddyfile.Dispenser
827 }
828 tests := []struct {
829 name string
830 args args
831 wantErr bool
832 }{
833 {
834 name: "empty block produces error",
835 args: args{
836 d: caddyfile.NewTestDispenser(`combined {
837 }`),
838 },
839 wantErr: true,
840 },
841 {
842 name: "arguments on same line as module name produces error",
843 args: args{
844 d: caddyfile.NewTestDispenser(`combined foo`),
845 },
846 wantErr: true,
847 },
848 {
849 name: "single source - system",
850 args: args{
851 d: caddyfile.NewTestDispenser(`combined {
852 source system
853 }`),
854 },
855 wantErr: false,
856 },
857 {
858 name: "single source - inline with config",
859 args: args{
860 d: caddyfile.NewTestDispenser(fmt.Sprintf(`combined {
861 source inline {
862 trust_der %s
863 }
864 }`, test_der_1)),
865 },
866 wantErr: false,
867 },
868 {
869 name: "multiple sources produces error due to limitation",
870 args: args{
871 d: caddyfile.NewTestDispenser(fmt.Sprintf(`combined {
872 source system
873 source inline {
874 trust_der %s
875 }
876 }`, test_der_1)),
877 },
878 wantErr: false, // UnmarshalCaddyfile succeeds, but Provision will fail
879 },
880 {
881 name: "source without module name produces error",
882 args: args{
883 d: caddyfile.NewTestDispenser(`combined {
884 source
885 }`),
886 },
887 wantErr: true,
888 },
889 {
890 name: "invalid directive produces error",
891 args: args{
892 d: caddyfile.NewTestDispenser(`combined {
893 invalid_directive foo
894 }`),
895 },
896 wantErr: true,
897 },
898 }
899 for _, tt := range tests {
900 t.Run(tt.name, func(t *testing.T) {
901 ccp := &CombinedCAPool{}
902 if err := ccp.UnmarshalCaddyfile(tt.args.d); (err != nil) != tt.wantErr {
903 t.Errorf("CombinedCAPool.UnmarshalCaddyfile() error = %v, wantErr %v", err, tt.wantErr)
904 }
905 if !tt.wantErr && len(ccp.SourcesRaw) == 0 {
906 t.Errorf("CombinedCAPool.UnmarshalCaddyfile() produced no sources")
907 }
908 })
909 }
910 }
911
912 func TestSystemCAPoolProvision(t *testing.T) {
913 scp := &SystemCAPool{}
914 ctx := caddy.Context{Context: context.Background()}
915
916 err := scp.Provision(ctx)
917 if err != nil {
918 t.Errorf("SystemCAPool.Provision() error = %v", err)
919 }
920
921 if scp.pool == nil {
922 t.Error("SystemCAPool.Provision() did not create a cert pool")
923 }
924
925 pool := scp.CertPool()
926 if pool == nil {
927 t.Error("SystemCAPool.CertPool() returned nil")
928 }
929 }
930
931 func TestCombinedCAPoolProvisionWithSystemFails(t *testing.T) {
932 // Test that combining system pool fails during Provision
933 // because SystemCAPool doesn't implement CertificateProvider
934 ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
935 defer cancel()
936
937 // Create a combined pool with system source
938 ccp := &CombinedCAPool{
939 SourcesRaw: []json.RawMessage{
940 json.RawMessage(`{"provider":"system"}`),
941 },
942 }
943
944 err := ccp.Provision(ctx)
945 if err == nil {
946 t.Error("CombinedCAPool.Provision() with system source should fail, but succeeded")
947 }
948
949 // Verify error message mentions CertificateProvider
950 if err != nil && !contains(err.Error(), "CertificateProvider") {
951 t.Errorf("Expected error to mention CertificateProvider, got: %v", err)
952 }
953 }
954
955 func TestCombinedCAPoolProvisionWithInlineSucceeds(t *testing.T) {
956 // Test that combining inline pools works
957 ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
958 defer cancel()
959
960 // Create a combined pool with inline source
961 ccp := &CombinedCAPool{
962 SourcesRaw: []json.RawMessage{
963 json.RawMessage(fmt.Sprintf(`{"provider":"inline","trusted_ca_certs":["%s"]}`, test_der_1)),
964 },
965 }
966
967 err := ccp.Provision(ctx)
968 if err != nil {
969 t.Errorf("CombinedCAPool.Provision() with inline source failed: %v", err)
970 }
971
972 if ccp.pool == nil {
973 t.Error("CombinedCAPool.Provision() did not create a cert pool")
974 }
975
976 pool := ccp.CertPool()
977 if pool == nil {
978 t.Error("CombinedCAPool.CertPool() returned nil")
979 }
980 }
981
982 // Helper function for string contains check
983 func contains(s, substr string) bool {
984 return len(s) >= len(substr) && (s == substr || len(substr) == 0 ||
985 (len(s) > 0 && len(substr) > 0 && findSubstring(s, substr)))
986 }
987
988 func findSubstring(s, substr string) bool {
989 for i := 0; i <= len(s)-len(substr); i++ {
990 if s[i:i+len(substr)] == substr {
991 return true
992 }
993 }
994 return false
995 }