@marcel91200 / caddy Mirror · read-only / commits / 1dfb1148
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 14, 05:15 UTC

httpcaddyfile: Add client_auth options to tls directive (#3335)

* reading client certificate config from Caddyfile Signed-off-by: NWHirschfeld <Niclas@NWHirschfeld.de> * Update caddyconfig/httpcaddyfile/builtins.go Co-authored-by: Francis Lavoie <lavofr@gmail.com> * added adapt test for parsing client certificate configuration from Caddyfile Signed-off-by: NWHirschfeld <Niclas@NWHirschfeld.de> * read client ca and leaf certificates from file https://github.com/caddyserver/caddy/pull/3335#discussion_r421633844 Signed-off-by: NWHirschfeld <Niclas@NWHirschfeld.de> * Update modules/caddytls/connpolicy.go * Make review adjustments Co-authored-by: Francis Lavoie <lavofr@gmail.com> Co-authored-by: Matt Holt <mholt@users.noreply.github.com>

NWHirschfeld committed Jun 5, 2020 at 20:19 UTC 1dfb11486eacc32af1003242023ddc4544823a31
5 files changed +215 -8
caddyconfig/httpcaddyfile/builtins.go
+62 -1
@@ -15,8 +15,11 @@
15 package httpcaddyfile
16
17 import (
18 + "encoding/base64"
19 + "encoding/pem"
20 "fmt"
21 "html"
22 + "io/ioutil"
23 "net/http"
24 "reflect"
25 "strings"
@@ -59,6 +62,13 @@ func parseBind(h Helper) ([]ConfigValue, error) {
62 // protocols <min> [<max>]
63 // ciphers <cipher_suites...>
64 // curves <curves...>
65 +// client_auth {
66 +// mode [request|require|verify_if_given|require_and_verify]
67 +// trusted_ca_cert <base64_der>
68 +// trusted_ca_cert_file <filename>
69 +// trusted_leaf_cert <base64_der>
70 +// trusted_leaf_cert_file <filename>
71 +// }
72 // alpn <values...>
73 // load <paths...>
74 // ca <acme_ca_endpoint>
@@ -143,7 +153,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
153 }
154
155 var hasBlock bool
146 - for h.NextBlock(0) {
156 + for nesting := h.Nesting(); h.NextBlock(nesting); {
157 hasBlock = true
158
159 switch h.Val() {
@@ -181,6 +191,57 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
191 cp.Curves = append(cp.Curves, h.Val())
192 }
193
194 + case "client_auth":
195 + cp.ClientAuthentication = &caddytls.ClientAuthentication{}
196 + for nesting := h.Nesting(); h.NextBlock(nesting); {
197 + subdir := h.Val()
198 + switch subdir {
199 + case "mode":
200 + if !h.Args(&cp.ClientAuthentication.Mode) {
201 + return nil, h.ArgErr()
202 + }
203 + if h.NextArg() {
204 + return nil, h.ArgErr()
205 + }
206 +
207 + case "trusted_ca_cert",
208 + "trusted_leaf_cert":
209 + if !h.NextArg() {
210 + return nil, h.ArgErr()
211 + }
212 + if subdir == "trusted_ca_cert" {
213 + cp.ClientAuthentication.TrustedCACerts = append(cp.ClientAuthentication.TrustedCACerts, h.Val())
214 + } else {
215 + cp.ClientAuthentication.TrustedLeafCerts = append(cp.ClientAuthentication.TrustedLeafCerts, h.Val())
216 + }
217 +
218 + case "trusted_ca_cert_file",
219 + "trusted_leaf_cert_file":
220 + if !h.NextArg() {
221 + return nil, h.ArgErr()
222 + }
223 + filename := h.Val()
224 + certDataPEM, err := ioutil.ReadFile(filename)
225 + if err != nil {
226 + return nil, err
227 + }
228 + block, _ := pem.Decode(certDataPEM)
229 + if block == nil || block.Type != "CERTIFICATE" {
230 + return nil, h.Errf("no CERTIFICATE pem block found in %s", h.Val())
231 + }
232 + if subdir == "trusted_ca_cert_file" {
233 + cp.ClientAuthentication.TrustedCACerts = append(cp.ClientAuthentication.TrustedCACerts,
234 + base64.StdEncoding.EncodeToString(block.Bytes))
235 + } else {
236 + cp.ClientAuthentication.TrustedLeafCerts = append(cp.ClientAuthentication.TrustedLeafCerts,
237 + base64.StdEncoding.EncodeToString(block.Bytes))
238 + }
239 +
240 + default:
241 + return nil, h.Errf("unknown subdirective for client_auth: %s", subdir)
242 + }
243 + }
244 +
245 case "alpn":
246 args := h.RemainingArgs()
247 if len(args) == 0 {
caddytest/caddy.ca.cer new
+20
@@ -0,0 +1,20 @@
1 +-----BEGIN CERTIFICATE-----
2 +MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQEL
3 +BQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkw
4 +ODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcN
5 +AQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU
6 +7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl0
7 +3WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45t
8 +wOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNx
9 +tdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTU
10 +ApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAd
11 +BgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS
12 +2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5u
13 +NY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkq
14 +hkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfK
15 +D66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEO
16 +fG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnk
17 +oNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZ
18 +ks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdle
19 +Ih6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ==
20 +-----END CERTIFICATE-----
\ No newline at end of file
caddytest/integration/caddyfile_adapt/tls_client_auth_cert_file.txt new
+66
@@ -0,0 +1,66 @@
1 +localhost
2 +
3 +respond "hello from localhost"
4 +tls {
5 + client_auth {
6 + mode request
7 + trusted_ca_cert_file ../caddy.ca.cer
8 + }
9 +}
10 +----------
11 +{
12 + "apps": {
13 + "http": {
14 + "servers": {
15 + "srv0": {
16 + "listen": [
17 + ":443"
18 + ],
19 + "routes": [
20 + {
21 + "match": [
22 + {
23 + "host": [
24 + "localhost"
25 + ]
26 + }
27 + ],
28 + "handle": [
29 + {
30 + "handler": "subroute",
31 + "routes": [
32 + {
33 + "handle": [
34 + {
35 + "body": "hello from localhost",
36 + "handler": "static_response"
37 + }
38 + ]
39 + }
40 + ]
41 + }
42 + ],
43 + "terminal": true
44 + }
45 + ],
46 + "tls_connection_policies": [
47 + {
48 + "match": {
49 + "sni": [
50 + "localhost"
51 + ]
52 + },
53 + "client_authentication": {
54 + "trusted_ca_certs": [
55 + "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=="
56 + ],
57 + "mode": "request"
58 + }
59 + },
60 + {}
61 + ]
62 + }
63 + }
64 + }
65 + }
66 +}
\ No newline at end of file
caddytest/integration/caddyfile_adapt/tls_client_auth_inline_cert.txt new
+66
@@ -0,0 +1,66 @@
1 +localhost
2 +
3 +respond "hello from localhost"
4 +tls {
5 + client_auth {
6 + mode request
7 + 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==
8 + }
9 +}
10 +----------
11 +{
12 + "apps": {
13 + "http": {
14 + "servers": {
15 + "srv0": {
16 + "listen": [
17 + ":443"
18 + ],
19 + "routes": [
20 + {
21 + "match": [
22 + {
23 + "host": [
24 + "localhost"
25 + ]
26 + }
27 + ],
28 + "handle": [
29 + {
30 + "handler": "subroute",
31 + "routes": [
32 + {
33 + "handle": [
34 + {
35 + "body": "hello from localhost",
36 + "handler": "static_response"
37 + }
38 + ]
39 + }
40 + ]
41 + }
42 + ],
43 + "terminal": true
44 + }
45 + ],
46 + "tls_connection_policies": [
47 + {
48 + "match": {
49 + "sni": [
50 + "localhost"
51 + ]
52 + },
53 + "client_authentication": {
54 + "trusted_ca_certs": [
55 + "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=="
56 + ],
57 + "mode": "request"
58 + }
59 + },
60 + {}
61 + ]
62 + }
63 + }
64 + }
65 + }
66 +}
\ No newline at end of file
modules/caddytls/connpolicy.go
+1 -7
@@ -334,7 +334,7 @@ func (clientauth *ClientAuthentication) ConfigureTLSConfig(cfg *tls.Config) erro
334 case "require_and_verify":
335 cfg.ClientAuth = tls.RequireAndVerifyClientCert
336 default:
337 - return fmt.Errorf("client auth mode %s not allowed", clientauth.Mode)
337 + return fmt.Errorf("client auth mode not recognized: %s", clientauth.Mode)
338 }
339 } else {
340 // otherwise, set a safe default mode
@@ -361,7 +361,6 @@ func (clientauth *ClientAuthentication) ConfigureTLSConfig(cfg *tls.Config) erro
361 // enforce leaf verification by writing our own verify function
362 if len(clientauth.TrustedLeafCerts) > 0 {
363 clientauth.trustedLeafCerts = []*x509.Certificate{}
364 -
364 for _, clientCertString := range clientauth.TrustedLeafCerts {
365 clientCert, err := decodeBase64DERCert(clientCertString)
366 if err != nil {
@@ -369,10 +368,8 @@ func (clientauth *ClientAuthentication) ConfigureTLSConfig(cfg *tls.Config) erro
368 }
369 clientauth.trustedLeafCerts = append(clientauth.trustedLeafCerts, clientCert)
370 }
372 -
371 // if a custom verification function already exists, wrap it
372 clientauth.existingVerifyPeerCert = cfg.VerifyPeerCertificate
375 -
373 cfg.VerifyPeerCertificate = clientauth.verifyPeerCertificate
374 }
375
@@ -411,13 +408,10 @@ func (clientauth ClientAuthentication) verifyPeerCertificate(rawCerts [][]byte,
408
409 // decodeBase64DERCert base64-decodes, then DER-decodes, certStr.
410 func decodeBase64DERCert(certStr string) (*x509.Certificate, error) {
414 - // decode base64
411 derBytes, err := base64.StdEncoding.DecodeString(certStr)
412 if err != nil {
413 return nil, err
414 }
419 -
420 - // parse the DER-encoded certificate
415 return x509.ParseCertificate(derBytes)
416 }
417