mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-23 22:27:38 -05:00
Merge pull request #98 from mholt/clientauth
tls: Client authentication
This commit is contained in:
commit
9d7639a9d3
4 changed files with 79 additions and 2 deletions
|
@ -53,8 +53,13 @@ func TLS(c *Controller) (middleware.Middleware, error) {
|
|||
}
|
||||
c.TLS.Ciphers = append(c.TLS.Ciphers, value)
|
||||
}
|
||||
case "clients":
|
||||
c.TLS.ClientCerts = c.RemainingArgs()
|
||||
if len(c.TLS.ClientCerts) == 0 {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
default:
|
||||
return nil, c.Errf("Unknown keyword '%s'")
|
||||
return nil, c.Errf("Unknown keyword '%s'", c.Val())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,3 +127,34 @@ func TestTLSParseWithWrongOptionalParams(t *testing.T) {
|
|||
t.Errorf("Expected errors, but no error returned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTLSParseWithClientAuth(t *testing.T) {
|
||||
params := `tls cert.crt cert.key {
|
||||
clients client_ca.crt client2_ca.crt
|
||||
}`
|
||||
c := newTestController(params)
|
||||
_, err := TLS(c)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no errors, got: %v", err)
|
||||
}
|
||||
|
||||
if count := len(c.TLS.ClientCerts); count != 2 {
|
||||
t.Fatalf("Expected two client certs, had %d", count)
|
||||
}
|
||||
if actual := c.TLS.ClientCerts[0]; actual != "client_ca.crt" {
|
||||
t.Errorf("Expected first client cert file to be '%s', but was '%s'", "client_ca.crt", actual)
|
||||
}
|
||||
if actual := c.TLS.ClientCerts[1]; actual != "client2_ca.crt" {
|
||||
t.Errorf("Expected second client cert file to be '%s', but was '%s'", "client2_ca.crt", actual)
|
||||
}
|
||||
|
||||
// Test missing client cert file
|
||||
params = `tls cert.crt cert.key {
|
||||
clients
|
||||
}`
|
||||
c = newTestController(params)
|
||||
_, err = TLS(c)
|
||||
if err == nil {
|
||||
t.Errorf("Expected an error, but no error returned")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,4 +64,5 @@ type TLSConfig struct {
|
|||
ProtocolMinVersion uint16
|
||||
ProtocolMaxVersion uint16
|
||||
PreferServerCipherSuites bool
|
||||
ClientCerts []string
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@ package server
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
|
@ -137,15 +139,53 @@ func ListenAndServeTLSWithSNI(srv *http.Server, tlsConfigs []TLSConfig) error {
|
|||
config.CipherSuites = tlsConfigs[0].Ciphers
|
||||
config.PreferServerCipherSuites = tlsConfigs[0].PreferServerCipherSuites
|
||||
|
||||
conn, err := net.Listen("tcp", addr)
|
||||
// TLS client authentication, if user enabled it
|
||||
err = setupClientAuth(tlsConfigs, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create listener and we're on our way
|
||||
conn, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tlsListener := tls.NewListener(conn, config)
|
||||
|
||||
return srv.Serve(tlsListener)
|
||||
}
|
||||
|
||||
// setupClientAuth sets up TLS client authentication only if
|
||||
// any of the TLS configs specified at least one cert file.
|
||||
func setupClientAuth(tlsConfigs []TLSConfig, config *tls.Config) error {
|
||||
var clientAuth bool
|
||||
for _, cfg := range tlsConfigs {
|
||||
if len(cfg.ClientCerts) > 0 {
|
||||
clientAuth = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if clientAuth {
|
||||
pool := x509.NewCertPool()
|
||||
for _, cfg := range tlsConfigs {
|
||||
for _, caFile := range cfg.ClientCerts {
|
||||
caCrt, err := ioutil.ReadFile(caFile) // Anyone that gets a cert from Matt Holt can connect
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !pool.AppendCertsFromPEM(caCrt) {
|
||||
return fmt.Errorf("Error loading client certificate '%s': no certificates were successfully parsed", caFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
config.ClientCAs = pool
|
||||
config.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ServeHTTP is the entry point for every request to the address that s
|
||||
// is bound to. It acts as a multiplexer for the requests hostname as
|
||||
// defined in the Host header so that the correct virtualhost
|
||||
|
|
Loading…
Reference in a new issue