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)
|
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:
|
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")
|
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
|
ProtocolMinVersion uint16
|
||||||
ProtocolMaxVersion uint16
|
ProtocolMaxVersion uint16
|
||||||
PreferServerCipherSuites bool
|
PreferServerCipherSuites bool
|
||||||
|
ClientCerts []string
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,9 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -137,15 +139,53 @@ func ListenAndServeTLSWithSNI(srv *http.Server, tlsConfigs []TLSConfig) error {
|
||||||
config.CipherSuites = tlsConfigs[0].Ciphers
|
config.CipherSuites = tlsConfigs[0].Ciphers
|
||||||
config.PreferServerCipherSuites = tlsConfigs[0].PreferServerCipherSuites
|
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 {
|
if err != nil {
|
||||||
return err
|
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)
|
tlsListener := tls.NewListener(conn, config)
|
||||||
|
|
||||||
return srv.Serve(tlsListener)
|
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
|
// 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
|
// is bound to. It acts as a multiplexer for the requests hostname as
|
||||||
// defined in the Host header so that the correct virtualhost
|
// defined in the Host header so that the correct virtualhost
|
||||||
|
|
Loading…
Reference in a new issue