@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 467 lines 15 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package caddytls
16
17 import (
18 "context"
19 "crypto/tls"
20 "encoding/json"
21 "fmt"
22 "reflect"
23 "testing"
24
25 "github.com/caddyserver/caddy/v2"
26 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
27 )
28
29 func TestConnectionPolicyIDNSNIMatcherFastPath(t *testing.T) {
30 ctx, cancel := caddy.NewContext(caddy.Context{Context: context.Background()})
31 defer cancel()
32
33 targetTLSConfig := &tls.Config{ClientAuth: tls.RequireAnyClientCert}
34 policies := ConnectionPolicies{
35 {
36 matchers: []ConnectionMatcher{MatchServerName{"つ.Localhost"}},
37 TLSConfig: targetTLSConfig,
38 },
39 }
40
41 const sniFastPathThreshold = 30
42 for i := len(policies); i < sniFastPathThreshold; i++ {
43 policies = append(policies, &ConnectionPolicy{
44 matchers: []ConnectionMatcher{MatchServerName{fmt.Sprintf("example-%d.localhost", i)}},
45 TLSConfig: &tls.Config{},
46 })
47 }
48 policies = append(policies, &ConnectionPolicy{
49 matchers: []ConnectionMatcher{MatchServerName{"xn--k9j.localhost"}},
50 TLSConfig: &tls.Config{ClientAuth: tls.NoClientCert},
51 })
52
53 tlsConfig := policies.TLSConfig(ctx)
54 got, err := tlsConfig.GetConfigForClient(&tls.ClientHelloInfo{ServerName: "XN--K9J.LOCALHOST"})
55 if err != nil {
56 t.Fatalf("GetConfigForClient() error = %v", err)
57 }
58 if got != targetTLSConfig {
59 t.Fatalf("expected Unicode IDN policy to match before later punycode policy")
60 }
61 }
62
63 func TestClientAuthenticationUnmarshalCaddyfileWithDirectiveName(t *testing.T) {
64 const 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==`
65 const test_cert_file_1 = "../../caddytest/caddy.ca.cer"
66 type args struct {
67 d *caddyfile.Dispenser
68 }
69 tests := []struct {
70 name string
71 args args
72 expected ClientAuthentication
73 wantErr bool
74 }{
75 {
76 name: "empty client_auth block does not error",
77 args: args{
78 d: caddyfile.NewTestDispenser(
79 `client_auth {
80 }`,
81 ),
82 },
83 wantErr: false,
84 },
85 {
86 name: "providing both 'trust_pool' and 'trusted_ca_cert' returns an error",
87 args: args{
88 d: caddyfile.NewTestDispenser(
89 `client_auth {
90 trust_pool inline 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==
91 trusted_ca_cert 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==
92 }`),
93 },
94 wantErr: true,
95 },
96 {
97 name: "trust_pool without a module argument returns an error",
98 args: args{
99 d: caddyfile.NewTestDispenser(
100 `client_auth {
101 trust_pool
102 }`),
103 },
104 wantErr: true,
105 },
106 {
107 name: "providing more than 1 mode produces an error",
108 args: args{
109 d: caddyfile.NewTestDispenser(`
110 client_auth {
111 mode require request
112 }
113 `),
114 },
115 wantErr: true,
116 },
117 {
118 name: "not providing 'mode' argument produces an error",
119 args: args{d: caddyfile.NewTestDispenser(`
120 client_auth {
121 mode
122 }
123 `)},
124 wantErr: true,
125 },
126 {
127 name: "providing a single 'mode' argument sets the mode",
128 args: args{
129 d: caddyfile.NewTestDispenser(`
130 client_auth {
131 mode require
132 }
133 `),
134 },
135 expected: ClientAuthentication{
136 Mode: "require",
137 },
138 wantErr: false,
139 },
140 {
141 name: "not providing an argument to 'trusted_ca_cert' produces an error",
142 args: args{
143 d: caddyfile.NewTestDispenser(`
144 client_auth {
145 trusted_ca_cert
146 }
147 `),
148 },
149 wantErr: true,
150 },
151 {
152 name: "not providing an argument to 'trusted_leaf_cert' produces an error",
153 args: args{
154 d: caddyfile.NewTestDispenser(`
155 client_auth {
156 trusted_leaf_cert
157 }
158 `),
159 },
160 wantErr: true,
161 },
162 {
163 name: "not providing an argument to 'trusted_ca_cert_file' produces an error",
164 args: args{
165 d: caddyfile.NewTestDispenser(`
166 client_auth {
167 trusted_ca_cert_file
168 }
169 `),
170 },
171 wantErr: true,
172 },
173 {
174 name: "not providing an argument to 'trusted_leaf_cert_file' produces an error",
175 args: args{
176 d: caddyfile.NewTestDispenser(`
177 client_auth {
178 trusted_leaf_cert_file
179 }
180 `),
181 },
182 wantErr: true,
183 },
184 {
185 name: "using 'trusted_ca_cert' adapts successfully",
186 args: args{
187 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
188 client_auth {
189 trusted_ca_cert %s
190 }`, test_der_1)),
191 },
192 expected: ClientAuthentication{
193 CARaw: json.RawMessage(fmt.Sprintf(`{"provider":"inline","trusted_ca_certs":["%s"]}`, test_der_1)),
194 },
195 },
196 {
197 name: "using 'inline' trust_pool loads the module successfully",
198 args: args{
199 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
200 client_auth {
201 trust_pool inline {
202 trust_der %s
203 }
204 }
205 `, test_der_1)),
206 },
207 expected: ClientAuthentication{
208 CARaw: json.RawMessage(fmt.Sprintf(`{"provider":"inline","trusted_ca_certs":["%s"]}`, test_der_1)),
209 },
210 },
211 {
212 name: "setting 'trusted_ca_cert' and 'trust_pool' produces an error",
213 args: args{
214 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
215 client_auth {
216 trusted_ca_cert %s
217 trust_pool inline {
218 trust_der %s
219 }
220 }`, test_der_1, test_der_1)),
221 },
222 wantErr: true,
223 },
224 {
225 name: "setting 'trust_pool' and 'trusted_ca_cert' produces an error",
226 args: args{
227 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
228 client_auth {
229 trust_pool inline {
230 trust_der %s
231 }
232 trusted_ca_cert %s
233 }`, test_der_1, test_der_1)),
234 },
235 wantErr: true,
236 },
237 {
238 name: "setting 'trust_pool' and 'trusted_ca_cert' produces an error",
239 args: args{
240 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
241 client_auth {
242 trust_pool inline {
243 trust_der %s
244 }
245 trusted_ca_cert_file %s
246 }`, test_der_1, test_cert_file_1)),
247 },
248 wantErr: true,
249 },
250 {
251 name: "configuring 'trusted_ca_cert_file' without an argument is an error",
252 args: args{
253 d: caddyfile.NewTestDispenser(`
254 client_auth {
255 trusted_ca_cert_file
256 }
257 `),
258 },
259 wantErr: true,
260 },
261 {
262 name: "configuring 'trusted_ca_cert_file' produces config with 'inline' provider",
263 args: args{
264 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
265 client_auth {
266 trusted_ca_cert_file %s
267 }`, test_cert_file_1),
268 ),
269 },
270 expected: ClientAuthentication{
271 CARaw: json.RawMessage(fmt.Sprintf(`{"provider":"inline","trusted_ca_certs":["%s"]}`, test_der_1)),
272 },
273 wantErr: false,
274 },
275 {
276 name: "configuring leaf certs does not conflict with 'trust_pool'",
277 args: args{
278 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
279 client_auth {
280 trust_pool inline {
281 trust_der %s
282 }
283 trusted_leaf_cert %s
284 }`, test_der_1, test_der_1)),
285 },
286 expected: ClientAuthentication{
287 CARaw: json.RawMessage(fmt.Sprintf(`{"provider":"inline","trusted_ca_certs":["%s"]}`, test_der_1)),
288 TrustedLeafCerts: []string{test_der_1},
289 },
290 },
291 {
292 name: "providing trusted leaf certificate file loads the cert successfully",
293 args: args{
294 d: caddyfile.NewTestDispenser(fmt.Sprintf(`
295 client_auth {
296 trusted_leaf_cert_file %s
297 }`, test_cert_file_1)),
298 },
299 expected: ClientAuthentication{
300 TrustedLeafCerts: []string{test_der_1},
301 },
302 wantErr: false,
303 },
304 }
305 for _, tt := range tests {
306 t.Run(tt.name, func(t *testing.T) {
307 ca := &ClientAuthentication{}
308 if err := ca.UnmarshalCaddyfile(tt.args.d); (err != nil) != tt.wantErr {
309 t.Errorf("ClientAuthentication.UnmarshalCaddyfile() error = %v, wantErr %v", err, tt.wantErr)
310 return
311 }
312 if !tt.wantErr && !reflect.DeepEqual(&tt.expected, ca) {
313 t.Errorf("ClientAuthentication.UnmarshalCaddyfile() = %v, want %v", ca, tt.expected)
314 }
315 })
316 }
317 }
318
319 func TestClientAuthenticationProvision(t *testing.T) {
320 tests := []struct {
321 name string
322 ca ClientAuthentication
323 wantErr bool
324 }{
325 {
326 name: "specifying both 'CARaw' and 'TrustedCACerts' produces an error",
327 ca: ClientAuthentication{
328 CARaw: json.RawMessage(`{"provider":"inline","trusted_ca_certs":["foo"]}`),
329 TrustedCACerts: []string{"foo"},
330 },
331 wantErr: true,
332 },
333 {
334 name: "specifying both 'CARaw' and 'TrustedCACertPEMFiles' produces an error",
335 ca: ClientAuthentication{
336 CARaw: json.RawMessage(`{"provider":"inline","trusted_ca_certs":["foo"]}`),
337 TrustedCACertPEMFiles: []string{"foo"},
338 },
339 wantErr: true,
340 },
341 {
342 name: "setting 'TrustedCACerts' provisions the cert pool",
343 ca: ClientAuthentication{
344 TrustedCACerts: []string{test_der_1},
345 },
346 },
347 }
348
349 for _, tt := range tests {
350 t.Run(tt.name, func(t *testing.T) {
351 err := tt.ca.provision(caddy.Context{})
352 if (err != nil) != tt.wantErr {
353 t.Errorf("ClientAuthentication.provision() error = %v, wantErr %v", err, tt.wantErr)
354 return
355 }
356 if !tt.wantErr {
357 if tt.ca.ca.CertPool() == nil {
358 t.Error("CertPool is nil, expected non-nil value")
359 }
360 }
361 })
362 }
363 }
364
365 func TestParseCaddyfileNestedMatcherSet(t *testing.T) {
366 for i, tc := range []struct {
367 input string
368 expect string
369 wantErr bool
370 }{
371 {
372 input: `match sni example.com`,
373 expect: `{"sni":["example.com"]}`,
374 },
375 {
376 input: `match {
377 sni example.com
378 sni example.net
379 }`,
380 expect: `{"sni":["example.com","example.net"]}`,
381 },
382 {
383 input: `match {
384 remote_ip 10.0.0.0/8
385 sni example.com
386 remote_ip !10.1.2.3
387 }`,
388 expect: `{"remote_ip":{"ranges":["10.0.0.0/8"],"not_ranges":["10.1.2.3"]},"sni":["example.com"]}`,
389 },
390 {
391 input: `match {
392 sni_regexp ^a
393 sni_regexp ^b
394 }`,
395 wantErr: true,
396 },
397 } {
398 d := caddyfile.NewTestDispenser(tc.input)
399 d.Next() // consume "match"
400 matcherSet, err := ParseCaddyfileNestedMatcherSet(d)
401 if tc.wantErr {
402 if err == nil {
403 t.Errorf("Test %d: expected error but got none", i)
404 }
405 continue
406 }
407 if err != nil {
408 t.Errorf("Test %d: unexpected error: %v", i, err)
409 continue
410 }
411
412 actual, err := json.Marshal(matcherSet)
413 if err != nil {
414 t.Fatalf("Test %d: marshaling matcher set: %v", i, err)
415 }
416 if string(actual) != tc.expect {
417 t.Errorf("Test %d: expected %s, got %s", i, tc.expect, actual)
418 }
419 }
420 }
421
422 func TestConnectionPolicyRejectsUnknownNames(t *testing.T) {
423 for _, tc := range []struct {
424 name string
425 policy ConnectionPolicy
426 }{
427 {"unknown curve", ConnectionPolicy{Curves: []string{"X25519"}}},
428 {"unknown protocol_min", ConnectionPolicy{ProtocolMin: "TLS1.3"}},
429 {"unknown protocol_max", ConnectionPolicy{ProtocolMax: "tls13"}},
430 {"protocol_min greater than protocol_max", ConnectionPolicy{ProtocolMin: "tls1.3", ProtocolMax: "tls1.2"}},
431 } {
432 t.Run(tc.name, func(t *testing.T) {
433 ctx, err := caddy.ProvisionContext(&caddy.Config{})
434 if err != nil {
435 t.Fatal(err)
436 }
437 if err := (ConnectionPolicies{&tc.policy}).Provision(ctx); err == nil {
438 t.Fatal("expected an error, got nil")
439 }
440 })
441 }
442 }
443
444 func TestConnectionPolicyProtocolDefaults(t *testing.T) {
445 for _, tc := range []struct {
446 name string
447 policy ConnectionPolicy
448 min, max uint16
449 }{
450 {"both empty", ConnectionPolicy{}, tls.VersionTLS12, tls.VersionTLS13},
451 {"min only", ConnectionPolicy{ProtocolMin: "tls1.3"}, tls.VersionTLS13, tls.VersionTLS13},
452 {"max only", ConnectionPolicy{ProtocolMax: "tls1.2"}, tls.VersionTLS12, tls.VersionTLS12},
453 } {
454 t.Run(tc.name, func(t *testing.T) {
455 ctx, err := caddy.ProvisionContext(&caddy.Config{})
456 if err != nil {
457 t.Fatal(err)
458 }
459 if err := (ConnectionPolicies{&tc.policy}).Provision(ctx); err != nil {
460 t.Fatalf("unexpected error: %v", err)
461 }
462 if got := tc.policy.TLSConfig; got.MinVersion != tc.min || got.MaxVersion != tc.max {
463 t.Fatalf("got min=%x max=%x, want min=%x max=%x", got.MinVersion, got.MaxVersion, tc.min, tc.max)
464 }
465 })
466 }
467 }