summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNWHirschfeld <nwhirschfeld@users.noreply.github.com>2020-06-05 20:19:36 +0200
committerGitHub <noreply@github.com>2020-06-05 12:19:36 -0600
commit1dfb11486eacc32af1003242023ddc4544823a31 (patch)
tree6aea6cb19fb6b9e87a8d35fb861c70205c57fd0c
parent11a132d48b574ef113e411aa22c0801a5a3190bd (diff)
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>
-rw-r--r--caddyconfig/httpcaddyfile/builtins.go63
-rw-r--r--caddytest/caddy.ca.cer20
-rw-r--r--caddytest/integration/caddyfile_adapt/tls_client_auth_cert_file.txt66
-rw-r--r--caddytest/integration/caddyfile_adapt/tls_client_auth_inline_cert.txt66
-rw-r--r--modules/caddytls/connpolicy.go8
5 files changed, 215 insertions, 8 deletions
diff --git a/caddyconfig/httpcaddyfile/builtins.go b/caddyconfig/httpcaddyfile/builtins.go
index 90ce3a4..fde5601 100644
--- a/caddyconfig/httpcaddyfile/builtins.go
+++ b/caddyconfig/httpcaddyfile/builtins.go
@@ -15,8 +15,11 @@
package httpcaddyfile
import (
+ "encoding/base64"
+ "encoding/pem"
"fmt"
"html"
+ "io/ioutil"
"net/http"
"reflect"
"strings"
@@ -59,6 +62,13 @@ func parseBind(h Helper) ([]ConfigValue, error) {
// protocols <min> [<max>]
// ciphers <cipher_suites...>
// curves <curves...>
+// client_auth {
+// mode [request|require|verify_if_given|require_and_verify]
+// trusted_ca_cert <base64_der>
+// trusted_ca_cert_file <filename>
+// trusted_leaf_cert <base64_der>
+// trusted_leaf_cert_file <filename>
+// }
// alpn <values...>
// load <paths...>
// ca <acme_ca_endpoint>
@@ -143,7 +153,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
}
var hasBlock bool
- for h.NextBlock(0) {
+ for nesting := h.Nesting(); h.NextBlock(nesting); {
hasBlock = true
switch h.Val() {
@@ -181,6 +191,57 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
cp.Curves = append(cp.Curves, h.Val())
}
+ case "client_auth":
+ cp.ClientAuthentication = &caddytls.ClientAuthentication{}
+ for nesting := h.Nesting(); h.NextBlock(nesting); {
+ subdir := h.Val()
+ switch subdir {
+ case "mode":
+ if !h.Args(&cp.ClientAuthentication.Mode) {
+ return nil, h.ArgErr()
+ }
+ if h.NextArg() {
+ return nil, h.ArgErr()
+ }
+
+ case "trusted_ca_cert",
+ "trusted_leaf_cert":
+ if !h.NextArg() {
+ return nil, h.ArgErr()
+ }
+ if subdir == "trusted_ca_cert" {
+ cp.ClientAuthentication.TrustedCACerts = append(cp.ClientAuthentication.TrustedCACerts, h.Val())
+ } else {
+ cp.ClientAuthentication.TrustedLeafCerts = append(cp.ClientAuthentication.TrustedLeafCerts, h.Val())
+ }
+
+ case "trusted_ca_cert_file",
+ "trusted_leaf_cert_file":
+ if !h.NextArg() {
+ return nil, h.ArgErr()
+ }
+ filename := h.Val()
+ certDataPEM, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return nil, err
+ }
+ block, _ := pem.Decode(certDataPEM)
+ if block == nil || block.Type != "CERTIFICATE" {
+ return nil, h.Errf("no CERTIFICATE pem block found in %s", h.Val())
+ }
+ if subdir == "trusted_ca_cert_file" {
+ cp.ClientAuthentication.TrustedCACerts = append(cp.ClientAuthentication.TrustedCACerts,
+ base64.StdEncoding.EncodeToString(block.Bytes))
+ } else {
+ cp.ClientAuthentication.TrustedLeafCerts = append(cp.ClientAuthentication.TrustedLeafCerts,
+ base64.StdEncoding.EncodeToString(block.Bytes))
+ }
+
+ default:
+ return nil, h.Errf("unknown subdirective for client_auth: %s", subdir)
+ }
+ }
+
case "alpn":
args := h.RemainingArgs()
if len(args) == 0 {
diff --git a/caddytest/caddy.ca.cer b/caddytest/caddy.ca.cer
new file mode 100644
index 0000000..00a9a1c
--- /dev/null
+++ b/caddytest/caddy.ca.cer
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDSzCCAjOgAwIBAgIUfIRObjWNUA4jxQ/0x8BOCvE2Vw4wDQYJKoZIhvcNAQEL
+BQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMTkwODI4MTYyNTU5WhcNMjkw
+ODI1MTYyNTU5WjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBAK5m5elxhQfMp/3aVJ4JnpN9PUSz6LlP6LePAPFU
+7gqohVVFVtDkChJAG3FNkNQNlieVTja/bgH9IcC6oKbROwdY1h0MvNV8AHHigvl0
+3WuJD8g2ReVFXXwsnrPmKXCFzQyMI6TYk3m2gYrXsZOU1GLnfMRC3KAMRgE2F45t
+wOs9hqG169YJ6mM2eQjzjCHWI6S2/iUYvYxRkCOlYUbLsMD/AhgAf1plzg6LPqNx
+tdlwxZnA0ytgkmhK67HtzJu0+ovUCsMv0RwcMhsEo9T8nyFAGt9XLZ63X5WpBCTU
+ApaAUhnG0XnerjmUWb6eUWw4zev54sEfY5F3x002iQaW6cECAwEAAaOBkDCBjTAd
+BgNVHQ4EFgQU4CBUbZsS2GaNIkGRz/cBsD5ivjswUQYDVR0jBEowSIAU4CBUbZsS
+2GaNIkGRz/cBsD5ivjuhGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghR8hE5u
+NY1QDiPFD/THwE4K8TZXDjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkq
+hkiG9w0BAQsFAAOCAQEAKB3V4HIzoiO/Ch6WMj9bLJ2FGbpkMrcb/Eq01hT5zcfK
+D66lVS1MlK+cRL446Z2b2KDP1oFyVs+qmrmtdwrWgD+nfe2sBmmIHo9m9KygMkEO
+fG3MghGTEcS+0cTKEcoHYWYyOqQh6jnedXY8Cdm4GM1hAc9MiL3/sqV8YCVSLNnk
+oNysmr06/rZ0MCUZPGUtRmfd0heWhrfzAKw2HLgX+RAmpOE2MZqWcjvqKGyaRiaZ
+ks4nJkP6521aC2Lgp0HhCz1j8/uQ5ldoDszCnu/iro0NAsNtudTMD+YoLQxLqdle
+Ih6CW+illc2VdXwj7mn6J04yns9jfE2jRjW/yTLFuQ==
+-----END CERTIFICATE----- \ No newline at end of file
diff --git a/caddytest/integration/caddyfile_adapt/tls_client_auth_cert_file.txt b/caddytest/integration/caddyfile_adapt/tls_client_auth_cert_file.txt
new file mode 100644
index 0000000..1e68a4d
--- /dev/null
+++ b/caddytest/integration/caddyfile_adapt/tls_client_auth_cert_file.txt
@@ -0,0 +1,66 @@
+localhost
+
+respond "hello from localhost"
+tls {
+ client_auth {
+ mode request
+ trusted_ca_cert_file ../caddy.ca.cer
+ }
+}
+----------
+{
+ "apps": {
+ "http": {
+ "servers": {
+ "srv0": {
+ "listen": [
+ ":443"
+ ],
+ "routes": [
+ {
+ "match": [
+ {
+ "host": [
+ "localhost"
+ ]
+ }
+ ],
+ "handle": [
+ {
+ "handler": "subroute",
+ "routes": [
+ {
+ "handle": [
+ {
+ "body": "hello from localhost",
+ "handler": "static_response"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "terminal": true
+ }
+ ],
+ "tls_connection_policies": [
+ {
+ "match": {
+ "sni": [
+ "localhost"
+ ]
+ },
+ "client_authentication": {
+ "trusted_ca_certs": [
+ "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=="
+ ],
+ "mode": "request"
+ }
+ },
+ {}
+ ]
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/caddytest/integration/caddyfile_adapt/tls_client_auth_inline_cert.txt b/caddytest/integration/caddyfile_adapt/tls_client_auth_inline_cert.txt
new file mode 100644
index 0000000..028d3b1
--- /dev/null
+++ b/caddytest/integration/caddyfile_adapt/tls_client_auth_inline_cert.txt
@@ -0,0 +1,66 @@
+localhost
+
+respond "hello from localhost"
+tls {
+ client_auth {
+ mode request
+ 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==
+ }
+}
+----------
+{
+ "apps": {
+ "http": {
+ "servers": {
+ "srv0": {
+ "listen": [
+ ":443"
+ ],
+ "routes": [
+ {
+ "match": [
+ {
+ "host": [
+ "localhost"
+ ]
+ }
+ ],
+ "handle": [
+ {
+ "handler": "subroute",
+ "routes": [
+ {
+ "handle": [
+ {
+ "body": "hello from localhost",
+ "handler": "static_response"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "terminal": true
+ }
+ ],
+ "tls_connection_policies": [
+ {
+ "match": {
+ "sni": [
+ "localhost"
+ ]
+ },
+ "client_authentication": {
+ "trusted_ca_certs": [
+ "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=="
+ ],
+ "mode": "request"
+ }
+ },
+ {}
+ ]
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/modules/caddytls/connpolicy.go b/modules/caddytls/connpolicy.go
index 3a18061..fec1fe2 100644
--- a/modules/caddytls/connpolicy.go
+++ b/modules/caddytls/connpolicy.go
@@ -334,7 +334,7 @@ func (clientauth *ClientAuthentication) ConfigureTLSConfig(cfg *tls.Config) erro
case "require_and_verify":
cfg.ClientAuth = tls.RequireAndVerifyClientCert
default:
- return fmt.Errorf("client auth mode %s not allowed", clientauth.Mode)
+ return fmt.Errorf("client auth mode not recognized: %s", clientauth.Mode)
}
} else {
// otherwise, set a safe default mode
@@ -361,7 +361,6 @@ func (clientauth *ClientAuthentication) ConfigureTLSConfig(cfg *tls.Config) erro
// enforce leaf verification by writing our own verify function
if len(clientauth.TrustedLeafCerts) > 0 {
clientauth.trustedLeafCerts = []*x509.Certificate{}
-
for _, clientCertString := range clientauth.TrustedLeafCerts {
clientCert, err := decodeBase64DERCert(clientCertString)
if err != nil {
@@ -369,10 +368,8 @@ func (clientauth *ClientAuthentication) ConfigureTLSConfig(cfg *tls.Config) erro
}
clientauth.trustedLeafCerts = append(clientauth.trustedLeafCerts, clientCert)
}
-
// if a custom verification function already exists, wrap it
clientauth.existingVerifyPeerCert = cfg.VerifyPeerCertificate
-
cfg.VerifyPeerCertificate = clientauth.verifyPeerCertificate
}
@@ -411,13 +408,10 @@ func (clientauth ClientAuthentication) verifyPeerCertificate(rawCerts [][]byte,
// decodeBase64DERCert base64-decodes, then DER-decodes, certStr.
func decodeBase64DERCert(certStr string) (*x509.Certificate, error) {
- // decode base64
derBytes, err := base64.StdEncoding.DecodeString(certStr)
if err != nil {
return nil, err
}
-
- // parse the DER-encoded certificate
return x509.ParseCertificate(derBytes)
}