caddytls: Configure trusted CAs from PEM files (#3882)

Closes #3563
This commit is contained in:
Matt Holt 2020-11-25 10:53:00 -07:00 committed by GitHub
parent 03d853e2ec
commit b0f8fc7aae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -19,6 +19,7 @@ import (
"crypto/x509" "crypto/x509"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"io/ioutil"
"strings" "strings"
"github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2"
@ -286,6 +287,12 @@ type ClientAuthentication struct {
// these CAs will be rejected. // these CAs will be rejected.
TrustedCACerts []string `json:"trusted_ca_certs,omitempty"` TrustedCACerts []string `json:"trusted_ca_certs,omitempty"`
// TrustedCACertPEMFiles is a list of PEM file names
// from which to load certificates of trusted CAs.
// Client certificates which are not signed by any of
// these CA certificates will be rejected.
TrustedCACertPEMFiles []string `json:"trusted_ca_certs_pem_files,omitempty"`
// A list of base64 DER-encoded client leaf certs // A list of base64 DER-encoded client leaf certs
// to accept. If this list is not empty, client certs // to accept. If this list is not empty, client certs
// which are not in this list will be rejected. // which are not in this list will be rejected.
@ -301,8 +308,8 @@ type ClientAuthentication struct {
// `require_and_verify` | Require clients to present a valid certificate that is verified // `require_and_verify` | Require clients to present a valid certificate that is verified
// //
// The default mode is `require_and_verify` if any // The default mode is `require_and_verify` if any
// TrustedCACerts or TrustedLeafCerts are provided; // TrustedCACerts or TrustedCACertPEMFiles or TrustedLeafCerts
// otherwise, the default mode is `require`. // are provided; otherwise, the default mode is `require`.
Mode string `json:"mode,omitempty"` Mode string `json:"mode,omitempty"`
// state established with the last call to ConfigureTLSConfig // state established with the last call to ConfigureTLSConfig
@ -312,7 +319,10 @@ type ClientAuthentication struct {
// Active returns true if clientauth has an actionable configuration. // Active returns true if clientauth has an actionable configuration.
func (clientauth ClientAuthentication) Active() bool { func (clientauth ClientAuthentication) Active() bool {
return len(clientauth.TrustedCACerts) > 0 || len(clientauth.TrustedLeafCerts) > 0 || len(clientauth.Mode) > 0 return len(clientauth.TrustedCACerts) > 0 ||
len(clientauth.TrustedCACertPEMFiles) > 0 ||
len(clientauth.TrustedLeafCerts) > 0 ||
len(clientauth.Mode) > 0
} }
// ConfigureTLSConfig sets up cfg to enforce clientauth's configuration. // ConfigureTLSConfig sets up cfg to enforce clientauth's configuration.
@ -339,7 +349,9 @@ func (clientauth *ClientAuthentication) ConfigureTLSConfig(cfg *tls.Config) erro
} }
} else { } else {
// otherwise, set a safe default mode // otherwise, set a safe default mode
if len(clientauth.TrustedCACerts) > 0 || len(clientauth.TrustedLeafCerts) > 0 { if len(clientauth.TrustedCACerts) > 0 ||
len(clientauth.TrustedCACertPEMFiles) > 0 ||
len(clientauth.TrustedLeafCerts) > 0 {
cfg.ClientAuth = tls.RequireAndVerifyClientCert cfg.ClientAuth = tls.RequireAndVerifyClientCert
} else { } else {
cfg.ClientAuth = tls.RequireAnyClientCert cfg.ClientAuth = tls.RequireAnyClientCert
@ -347,7 +359,7 @@ func (clientauth *ClientAuthentication) ConfigureTLSConfig(cfg *tls.Config) erro
} }
// enforce CA verification by adding CA certs to the ClientCAs pool // enforce CA verification by adding CA certs to the ClientCAs pool
if len(clientauth.TrustedCACerts) > 0 { if len(clientauth.TrustedCACerts) > 0 || len(clientauth.TrustedCACertPEMFiles) > 0 {
caPool := x509.NewCertPool() caPool := x509.NewCertPool()
for _, clientCAString := range clientauth.TrustedCACerts { for _, clientCAString := range clientauth.TrustedCACerts {
clientCA, err := decodeBase64DERCert(clientCAString) clientCA, err := decodeBase64DERCert(clientCAString)
@ -356,6 +368,13 @@ func (clientauth *ClientAuthentication) ConfigureTLSConfig(cfg *tls.Config) erro
} }
caPool.AddCert(clientCA) caPool.AddCert(clientCA)
} }
for _, pemFile := range clientauth.TrustedCACertPEMFiles {
pemContents, err := ioutil.ReadFile(pemFile)
if err != nil {
return fmt.Errorf("reading %s: %v", pemFile, err)
}
caPool.AppendCertsFromPEM(pemContents)
}
cfg.ClientCAs = caPool cfg.ClientCAs = caPool
} }