diff --git a/configuration.go b/configuration.go index 7cfdebc4a..a055bba0f 100644 --- a/configuration.go +++ b/configuration.go @@ -94,7 +94,7 @@ func (ep *EntryPoints) String() string { // Set's argument is a string to be parsed to set the flag. // It's a comma-separated list, so we split it. func (ep *EntryPoints) Set(value string) error { - regex := regexp.MustCompile("(?:Name:(?P\\S*))\\s*(?:Address:(?P
\\S*))?\\s*(?:TLS:(?P\\S*))?\\s*((?PTLS))?\\s*(?:Redirect.EntryPoint:(?P\\S*))?\\s*(?:Redirect.Regex:(?P\\S*))?\\s*(?:Redirect.Replacement:(?P\\S*))?") + regex := regexp.MustCompile("(?:Name:(?P\\S*))\\s*(?:Address:(?P
\\S*))?\\s*(?:TLS:(?P\\S*))?\\s*((?PTLS))?\\s*(?:CA:(?P\\S*))?\\s*(?:Redirect.EntryPoint:(?P\\S*))?\\s*(?:Redirect.Regex:(?P\\S*))?\\s*(?:Redirect.Replacement:(?P\\S*))?") match := regex.FindAllStringSubmatch(value, -1) if match == nil { return errors.New("Bad EntryPoints format: " + value) @@ -120,6 +120,10 @@ func (ep *EntryPoints) Set(value string) error { Certificates: Certificates{}, } } + if len(result["CA"]) > 0 { + files := strings.Split(result["CA"], ",") + tls.ClientCAFiles = files + } var redirect *Redirect if len(result["RedirectEntryPoint"]) > 0 || len(result["RedirectRegex"]) > 0 || len(result["RedirectReplacement"]) > 0 { redirect = &Redirect{ @@ -168,7 +172,8 @@ type Redirect struct { // TLS configures TLS for an entry point type TLS struct { - Certificates Certificates + Certificates Certificates + ClientCAFiles []string } // Certificates defines traefik certificates type diff --git a/docs/basics.md b/docs/basics.md index fca49afec..c6a4145ee 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -30,7 +30,7 @@ Entrypoints are the network entry points into Træfɪk. They can be defined using: - a port (80, 443...) -- SSL (Certificates. Keys...) +- SSL (Certificates, Keys, authentication with a client certificate signed by a trusted CA...) - redirection to another entrypoint (redirect `HTTP` to `HTTPS`) Here is an example of entrypoints definition: @@ -54,6 +54,23 @@ Here is an example of entrypoints definition: - We enable SSL on `https` by giving a certificate and a key. - We also redirect all the traffic from entrypoint `http` to `https`. +And here is another example with client certificate authentication: + +```toml +[entryPoints] + [entryPoints.https] + address = ":443" + [entryPoints.https.tls] + clientCAFiles = ["tests/clientca1.crt", "tests/clientca2.crt"] + [[entryPoints.https.tls.certificates]] + certFile = "tests/traefik.crt" + keyFile = "tests/traefik.key" +``` + +- We enable SSL on `https` by giving a certificate and a key. +- One or several files containing Certificate Authorities in PEM format are added. +- It is possible to have multiple CA:s in the same file or keep them in separate files. + ## Frontends A frontend is a set of rules that forwards the incoming traffic from an entrypoint to a backend. diff --git a/docs/toml.md b/docs/toml.md index f6be08a66..8028206ea 100644 --- a/docs/toml.md +++ b/docs/toml.md @@ -89,6 +89,28 @@ # [entryPoints.http.redirect] # regex = "^http://localhost/(.*)" # replacement = "http://mydomain/$1" +# +# Only accept clients that present a certificate signed by a specified +# Certificate Authority (CA) +# ClientCAFiles can be configured with multiple CA:s in the same file or +# use multiple files containing one or several CA:s. The CA:s has to be in PEM format. +# All clients will be required to present a valid cert. +# The requirement will apply to all server certs in the entrypoint +# In the example below both snitest.com and snitest.org will require client certs +# +# [entryPoints] +# [entryPoints.https] +# address = ":443" +# [entryPoints.https.tls] +# ClientCAFiles = ["tests/clientca1.crt", "tests/clientca2.crt"] +# [[entryPoints.https.tls.certificates]] +# CertFile = "integration/fixtures/https/snitest.com.cert" +# KeyFile = "integration/fixtures/https/snitest.com.key" +# [[entryPoints.https.tls.certificates]] +# CertFile = "integration/fixtures/https/snitest.org.cert" +# KeyFile = "integration/fixtures/https/snitest.org.key" +# + [entryPoints] [entryPoints.http] diff --git a/server.go b/server.go index 3411970f3..1a7be3d6c 100644 --- a/server.go +++ b/server.go @@ -5,8 +5,10 @@ package main import ( "crypto/tls" + "crypto/x509" "encoding/json" "errors" + "io/ioutil" "net/http" "net/url" "os" @@ -298,6 +300,22 @@ func (server *Server) createTLSConfig(entryPointName string, tlsOption *TLS, rou config.Certificates = append(config.Certificates, cert) } + if len(tlsOption.ClientCAFiles) > 0 { + pool := x509.NewCertPool() + for _, caFile := range tlsOption.ClientCAFiles { + data, err := ioutil.ReadFile(caFile) + if err != nil { + return nil, err + } + ok := pool.AppendCertsFromPEM(data) + if !ok { + return nil, errors.New("invalid certificate(s) in " + caFile) + } + } + config.ClientCAs = pool + config.ClientAuth = tls.RequireAndVerifyClientCert + } + if server.globalConfiguration.ACME != nil { if _, ok := server.serverEntryPoints[server.globalConfiguration.ACME.EntryPoint]; ok { if entryPointName == server.globalConfiguration.ACME.EntryPoint {