mirror of
https://github.com/distribution/distribution
synced 2024-12-25 15:05:51 +01:00
feat(configuration): support mtls auth mod (#4537)
This commit is contained in:
commit
1c62898144
@ -109,6 +109,10 @@ type Configuration struct {
|
||||
// A file may contain multiple CA certificates encoded as PEM
|
||||
ClientCAs []string `yaml:"clientcas,omitempty"`
|
||||
|
||||
// Client certificate authentication mode
|
||||
// One of: request-client-cert, require-any-client-cert, verify-client-cert-if-given, require-and-verify-client-cert
|
||||
ClientAuth ClientAuth `yaml:"clientauth,omitempty"`
|
||||
|
||||
// Specifies the lowest TLS version allowed
|
||||
MinimumTLS string `yaml:"minimumtls,omitempty"`
|
||||
|
||||
@ -899,3 +903,35 @@ func setFieldValue(field reflect.Value, value interface{}) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
ClientAuthRequestClientCert = "request-client-cert"
|
||||
ClientAuthRequireAnyClientCert = "require-any-client-cert"
|
||||
ClientAuthVerifyClientCertIfGiven = "verify-client-cert-if-given"
|
||||
ClientAuthRequireAndVerifyClientCert = "require-and-verify-client-cert"
|
||||
)
|
||||
|
||||
type ClientAuth string
|
||||
|
||||
// UnmarshalYAML implements the yaml.Umarshaler interface
|
||||
// Unmarshals a string into a ClientAuth, validating that it represents a valid ClientAuth mod
|
||||
func (clientAuth *ClientAuth) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var clientAuthString string
|
||||
err := unmarshal(&clientAuthString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch clientAuthString {
|
||||
case ClientAuthRequestClientCert:
|
||||
case ClientAuthRequireAnyClientCert:
|
||||
case ClientAuthVerifyClientCertIfGiven:
|
||||
case ClientAuthRequireAndVerifyClientCert:
|
||||
default:
|
||||
return fmt.Errorf("invalid ClientAuth %s Must be one of: %s, %s, %s, %s", clientAuthString, ClientAuthRequestClientCert, ClientAuthRequireAnyClientCert, ClientAuthVerifyClientCertIfGiven, ClientAuthRequireAndVerifyClientCert)
|
||||
}
|
||||
|
||||
*clientAuth = ClientAuth(clientAuthString)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -78,11 +78,12 @@ var configStruct = Configuration{
|
||||
RelativeURLs bool `yaml:"relativeurls,omitempty"`
|
||||
DrainTimeout time.Duration `yaml:"draintimeout,omitempty"`
|
||||
TLS struct {
|
||||
Certificate string `yaml:"certificate,omitempty"`
|
||||
Key string `yaml:"key,omitempty"`
|
||||
ClientCAs []string `yaml:"clientcas,omitempty"`
|
||||
MinimumTLS string `yaml:"minimumtls,omitempty"`
|
||||
CipherSuites []string `yaml:"ciphersuites,omitempty"`
|
||||
Certificate string `yaml:"certificate,omitempty"`
|
||||
Key string `yaml:"key,omitempty"`
|
||||
ClientCAs []string `yaml:"clientcas,omitempty"`
|
||||
ClientAuth ClientAuth `yaml:"clientauth,omitempty"`
|
||||
MinimumTLS string `yaml:"minimumtls,omitempty"`
|
||||
CipherSuites []string `yaml:"ciphersuites,omitempty"`
|
||||
LetsEncrypt struct {
|
||||
CacheFile string `yaml:"cachefile,omitempty"`
|
||||
Email string `yaml:"email,omitempty"`
|
||||
@ -106,11 +107,12 @@ var configStruct = Configuration{
|
||||
} `yaml:"h2c,omitempty"`
|
||||
}{
|
||||
TLS: struct {
|
||||
Certificate string `yaml:"certificate,omitempty"`
|
||||
Key string `yaml:"key,omitempty"`
|
||||
ClientCAs []string `yaml:"clientcas,omitempty"`
|
||||
MinimumTLS string `yaml:"minimumtls,omitempty"`
|
||||
CipherSuites []string `yaml:"ciphersuites,omitempty"`
|
||||
Certificate string `yaml:"certificate,omitempty"`
|
||||
Key string `yaml:"key,omitempty"`
|
||||
ClientCAs []string `yaml:"clientcas,omitempty"`
|
||||
ClientAuth ClientAuth `yaml:"clientauth,omitempty"`
|
||||
MinimumTLS string `yaml:"minimumtls,omitempty"`
|
||||
CipherSuites []string `yaml:"ciphersuites,omitempty"`
|
||||
LetsEncrypt struct {
|
||||
CacheFile string `yaml:"cachefile,omitempty"`
|
||||
Email string `yaml:"email,omitempty"`
|
||||
@ -118,7 +120,8 @@ var configStruct = Configuration{
|
||||
DirectoryURL string `yaml:"directoryurl,omitempty"`
|
||||
} `yaml:"letsencrypt,omitempty"`
|
||||
}{
|
||||
ClientCAs: []string{"/path/to/ca.pem"},
|
||||
ClientCAs: []string{"/path/to/ca.pem"},
|
||||
ClientAuth: ClientAuthVerifyClientCertIfGiven,
|
||||
},
|
||||
Headers: http.Header{
|
||||
"X-Content-Type-Options": []string{"nosniff"},
|
||||
@ -202,6 +205,7 @@ http:
|
||||
tls:
|
||||
clientcas:
|
||||
- /path/to/ca.pem
|
||||
clientauth: verify-client-cert-if-given
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
redis:
|
||||
@ -297,6 +301,7 @@ func (suite *ConfigSuite) TestParseInmemory() {
|
||||
suite.expectedConfig.Storage = Storage{"inmemory": Parameters{}}
|
||||
suite.expectedConfig.Log.Fields = nil
|
||||
suite.expectedConfig.HTTP.TLS.ClientCAs = nil
|
||||
suite.expectedConfig.HTTP.TLS.ClientAuth = ""
|
||||
suite.expectedConfig.Redis = Redis{}
|
||||
|
||||
config, err := Parse(bytes.NewReader([]byte(inmemoryConfigYamlV0_1)))
|
||||
@ -318,6 +323,7 @@ func (suite *ConfigSuite) TestParseIncomplete() {
|
||||
suite.expectedConfig.Notifications = Notifications{}
|
||||
suite.expectedConfig.HTTP.Headers = nil
|
||||
suite.expectedConfig.HTTP.TLS.ClientCAs = nil
|
||||
suite.expectedConfig.HTTP.TLS.ClientAuth = ""
|
||||
suite.expectedConfig.Redis = Redis{}
|
||||
suite.expectedConfig.Validation.Manifests.Indexes.Platforms = ""
|
||||
|
||||
@ -590,6 +596,7 @@ func copyConfig(config Configuration) *Configuration {
|
||||
}
|
||||
configCopy.HTTP.TLS.ClientCAs = make([]string, 0, len(config.HTTP.TLS.ClientCAs))
|
||||
configCopy.HTTP.TLS.ClientCAs = append(configCopy.HTTP.TLS.ClientCAs, config.HTTP.TLS.ClientCAs...)
|
||||
configCopy.HTTP.TLS.ClientAuth = config.HTTP.TLS.ClientAuth
|
||||
|
||||
configCopy.Redis = config.Redis
|
||||
configCopy.Redis.TLS.Certificate = config.Redis.TLS.Certificate
|
||||
|
@ -229,6 +229,7 @@ http:
|
||||
clientcas:
|
||||
- /path/to/ca.pem
|
||||
- /path/to/another/ca.pem
|
||||
clientauth: require-and-verify-client-cert
|
||||
letsencrypt:
|
||||
cachefile: /path/to/cache-file
|
||||
email: emailused@letsencrypt.com
|
||||
@ -808,6 +809,7 @@ http:
|
||||
clientcas:
|
||||
- /path/to/ca.pem
|
||||
- /path/to/another/ca.pem
|
||||
clientauth: require-and-verify-client-cert
|
||||
minimumtls: tls1.2
|
||||
ciphersuites:
|
||||
- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
|
||||
@ -848,13 +850,14 @@ for the server. If you already have a web server running on
|
||||
the same host as the registry, you may prefer to configure TLS on that web server
|
||||
and proxy connections to the registry server.
|
||||
|
||||
| Parameter | Required | Description |
|
||||
|-----------|----------|-------------------------------------------------------|
|
||||
| `certificate` | yes | Absolute path to the x509 certificate file. |
|
||||
| `key` | yes | Absolute path to the x509 private key file. |
|
||||
| `clientcas` | no | An array of absolute paths to x509 CA files. |
|
||||
| `minimumtls` | no | Minimum TLS version allowed (tls1.0, tls1.1, tls1.2, tls1.3). Defaults to tls1.2 |
|
||||
| `ciphersuites` | no | Cipher suites allowed. Please see below for allowed values and default. |
|
||||
| Parameter | Required | Description |
|
||||
|----------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `certificate` | yes | Absolute path to the x509 certificate file. |
|
||||
| `key` | yes | Absolute path to the x509 private key file. |
|
||||
| `clientcas` | no | An array of absolute paths to x509 CA files. |
|
||||
| `clientauth` | no | Client certificate authentication mode. This setting determines how the server handles client certificates during the TLS handshake. If clientcas is not provided, TLS Client Authentication is disabled, and the mode is ignored. Allowed (request-client-cert, require-any-client-cert, verify-client-cert-if-given, require-and-verify-client-cert). Defaults to require-and-verify-client-cert |
|
||||
| `minimumtls` | no | Minimum TLS version allowed (tls1.0, tls1.1, tls1.2, tls1.3). Defaults to tls1.2 |
|
||||
| `ciphersuites` | no | Cipher suites allowed. Please see below for allowed values and default. |
|
||||
|
||||
Available cipher suites:
|
||||
- TLS_RSA_WITH_RC4_128_SHA
|
||||
|
@ -79,6 +79,14 @@ var tlsVersions = map[string]uint16{
|
||||
"tls1.3": tls.VersionTLS13,
|
||||
}
|
||||
|
||||
// tlsClientAuth maps user-specified values to TLS Client Authentication constants.
|
||||
var tlsClientAuth = map[string]tls.ClientAuthType{
|
||||
configuration.ClientAuthRequestClientCert: tls.RequestClientCert,
|
||||
configuration.ClientAuthRequireAnyClientCert: tls.RequireAnyClientCert,
|
||||
configuration.ClientAuthVerifyClientCertIfGiven: tls.VerifyClientCertIfGiven,
|
||||
configuration.ClientAuthRequireAndVerifyClientCert: tls.RequireAndVerifyClientCert,
|
||||
}
|
||||
|
||||
// defaultLogFormatter is the default formatter to use for logs.
|
||||
const defaultLogFormatter = "text"
|
||||
|
||||
@ -298,7 +306,18 @@ func (registry *Registry) ListenAndServe() error {
|
||||
dcontext.GetLogger(registry.app).Debugf("CA Subject: %s", string(subj))
|
||||
}
|
||||
|
||||
tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
if config.HTTP.TLS.ClientAuth != "" {
|
||||
tlsClientAuthMod, ok := tlsClientAuth[string(config.HTTP.TLS.ClientAuth)]
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown client auth mod '%s' specified for http.tls.clientauth", config.HTTP.TLS.ClientAuth)
|
||||
}
|
||||
|
||||
tlsConf.ClientAuth = tlsClientAuthMod
|
||||
} else {
|
||||
tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
|
||||
tlsConf.ClientCAs = pool
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user