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
| 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 | } |