From e438dccf1986c6ad127565fa26042ff96f6c6647 Mon Sep 17 00:00:00 2001 From: Andreas Auernhammer Date: Wed, 8 Sep 2021 04:03:48 +0200 Subject: [PATCH] sts: add support for certificate-based authentication (#12748) This commit adds a new STS API for X.509 certificate authentication. A client can make an HTTP POST request over a TLS connection and MinIO will verify the provided client certificate, map it to an S3 policy and return temp. S3 credentials to the client. So, this STS API allows clients to authenticate with X.509 certificates over TLS and obtain temp. S3 credentials. For more details and examples refer to the docs/sts/tls.md documentation. Signed-off-by: Andreas Auernhammer --- cmd/admin-handlers-config-kv.go | 2 + cmd/config-current.go | 18 ++++ cmd/globals.go | 2 + cmd/sts-datatypes.go | 12 +++ cmd/sts-errors.go | 12 +++ cmd/sts-handlers.go | 114 ++++++++++++++++++++++- cmd/stserrorcode_string.go | 10 +- docs/sts/tls.md | 107 +++++++++++++++++++++ internal/config/config.go | 3 + internal/config/identity/tls/config.go | 123 +++++++++++++++++++++++++ internal/http/server.go | 1 + 11 files changed, 396 insertions(+), 8 deletions(-) create mode 100644 docs/sts/tls.md create mode 100644 internal/config/identity/tls/config.go diff --git a/cmd/admin-handlers-config-kv.go b/cmd/admin-handlers-config-kv.go index 1813d1714..21323b966 100644 --- a/cmd/admin-handlers-config-kv.go +++ b/cmd/admin-handlers-config-kv.go @@ -417,6 +417,8 @@ func (a adminAPIHandlers) GetConfigHandler(w http.ResponseWriter, r *http.Reques off = !openid.Enabled(kv) case config.IdentityLDAPSubSys: off = !xldap.Enabled(kv) + case config.IdentityTLSSubSys: + off = !globalSTSTLSConfig.Enabled } if off { s.WriteString(config.KvComment) diff --git a/cmd/config-current.go b/cmd/config-current.go index 20c71b2a9..0e4698d67 100644 --- a/cmd/config-current.go +++ b/cmd/config-current.go @@ -34,6 +34,7 @@ import ( "github.com/minio/minio/internal/config/heal" xldap "github.com/minio/minio/internal/config/identity/ldap" "github.com/minio/minio/internal/config/identity/openid" + xtls "github.com/minio/minio/internal/config/identity/tls" "github.com/minio/minio/internal/config/notify" "github.com/minio/minio/internal/config/policy/opa" "github.com/minio/minio/internal/config/scanner" @@ -54,6 +55,7 @@ func initHelp() { config.CompressionSubSys: compress.DefaultKVS, config.IdentityLDAPSubSys: xldap.DefaultKVS, config.IdentityOpenIDSubSys: openid.DefaultKVS, + config.IdentityTLSSubSys: xtls.DefaultKVS, config.PolicyOPASubSys: opa.DefaultKVS, config.RegionSubSys: config.DefaultRegionKVS, config.APISubSys: api.DefaultKVS, @@ -98,6 +100,10 @@ func initHelp() { Key: config.IdentityLDAPSubSys, Description: "enable LDAP SSO support", }, + config.HelpKV{ + Key: config.IdentityTLSSubSys, + Description: "enable X.509 TLS certificate SSO support", + }, config.HelpKV{ Key: config.PolicyOPASubSys, Description: "[DEPRECATED] enable external OPA for policy enforcement", @@ -202,6 +208,7 @@ func initHelp() { config.ScannerSubSys: scanner.Help, config.IdentityOpenIDSubSys: openid.Help, config.IdentityLDAPSubSys: xldap.Help, + config.IdentityTLSSubSys: xtls.Help, config.PolicyOPASubSys: opa.Help, config.LoggerWebhookSubSys: logger.Help, config.AuditWebhookSubSys: logger.HelpWebhook, @@ -317,6 +324,12 @@ func validateConfig(s config.Config) error { conn.Close() } } + { + _, err := xtls.Lookup(s[config.IdentityTLSSubSys][config.Default]) + if err != nil { + return err + } + } if _, err := opa.LookupConfig(s[config.PolicyOPASubSys][config.Default], NewGatewayHTTPTransport(), xhttp.DrainBody); err != nil { @@ -469,6 +482,11 @@ func lookupConfigs(s config.Config, objAPI ObjectLayer) { logger.Fatal(errors.New("no KMS configured"), "MINIO_KMS_AUTO_ENCRYPTION requires a valid KMS configuration") } + globalSTSTLSConfig, err = xtls.Lookup(s[config.IdentityTLSSubSys][config.Default]) + if err != nil { + logger.LogIf(ctx, fmt.Errorf("Unable to initialize X.509/TLS STS API: %w", err)) + } + globalOpenIDConfig, err = openid.LookupConfig(s[config.IdentityOpenIDSubSys][config.Default], NewGatewayHTTPTransport(), xhttp.DrainBody) if err != nil { diff --git a/cmd/globals.go b/cmd/globals.go index cd6d05ec0..6a83b93fd 100644 --- a/cmd/globals.go +++ b/cmd/globals.go @@ -38,6 +38,7 @@ import ( "github.com/minio/minio/internal/config/dns" xldap "github.com/minio/minio/internal/config/identity/ldap" "github.com/minio/minio/internal/config/identity/openid" + xtls "github.com/minio/minio/internal/config/identity/tls" "github.com/minio/minio/internal/config/policy/opa" "github.com/minio/minio/internal/config/storageclass" xhttp "github.com/minio/minio/internal/http" @@ -188,6 +189,7 @@ var ( globalStorageClass storageclass.Config globalLDAPConfig xldap.Config globalOpenIDConfig openid.Config + globalSTSTLSConfig xtls.Config // CA root certificates, a nil value means system certs pool will be used globalRootCAs *x509.CertPool diff --git a/cmd/sts-datatypes.go b/cmd/sts-datatypes.go index aeb4517b7..5c20472d6 100644 --- a/cmd/sts-datatypes.go +++ b/cmd/sts-datatypes.go @@ -191,3 +191,15 @@ type AssumeRoleWithLDAPResponse struct { type LDAPIdentityResult struct { Credentials auth.Credentials `xml:",omitempty"` } + +// AssumeRoleWithCertificateResponse contains the result of +// a successful AssumeRoleWithCertificate request. +type AssumeRoleWithCertificateResponse struct { + XMLName xml.Name `xml:"https://sts.amazonaws.com/doc/2011-06-15/ AssumeRoleWithCertificateResponse" json:"-"` + Result struct { + Credentials auth.Credentials `xml:"Credentials,omitempty"` + } `xml:"AssumeRoleWithCertificateResult"` + Metadata struct { + RequestID string `xml:"RequestId,omitempty"` + } `xml:"ResponseMetadata,omitempty"` +} diff --git a/cmd/sts-errors.go b/cmd/sts-errors.go index b87f080e1..73fc1029e 100644 --- a/cmd/sts-errors.go +++ b/cmd/sts-errors.go @@ -93,6 +93,8 @@ const ( ErrSTSClientGrantsExpiredToken ErrSTSInvalidClientGrantsToken ErrSTSMalformedPolicyDocument + ErrSTSInsecureConnection + ErrSTSInvalidClientCertificate ErrSTSNotInitialized ErrSTSInternalError ) @@ -145,6 +147,16 @@ var stsErrCodes = stsErrorCodeMap{ Description: "The request was rejected because the policy document was malformed.", HTTPStatusCode: http.StatusBadRequest, }, + ErrSTSInsecureConnection: { + Code: "InsecureConnection", + Description: "The request was made over a plain HTTP connection. A TLS connection is required.", + HTTPStatusCode: http.StatusBadRequest, + }, + ErrSTSInvalidClientCertificate: { + Code: "InvalidClientCertificate", + Description: "The provided client certificate is invalid. Retry with a different certificate.", + HTTPStatusCode: http.StatusBadRequest, + }, ErrSTSNotInitialized: { Code: "STSNotInitialized", Description: "STS API not initialized, please try again.", diff --git a/cmd/sts-handlers.go b/cmd/sts-handlers.go index 3eb477da7..1633af95c 100644 --- a/cmd/sts-handlers.go +++ b/cmd/sts-handlers.go @@ -20,11 +20,13 @@ package cmd import ( "bytes" "context" + "crypto/x509" "encoding/base64" "errors" "fmt" "net/http" "strings" + "time" "github.com/gorilla/mux" "github.com/minio/minio/internal/auth" @@ -48,10 +50,11 @@ const ( stsLDAPPassword = "LDAPPassword" // STS API action constants - clientGrants = "AssumeRoleWithClientGrants" - webIdentity = "AssumeRoleWithWebIdentity" - ldapIdentity = "AssumeRoleWithLDAPIdentity" - assumeRole = "AssumeRole" + clientGrants = "AssumeRoleWithClientGrants" + webIdentity = "AssumeRoleWithWebIdentity" + ldapIdentity = "AssumeRoleWithLDAPIdentity" + clientCertificate = "AssumeRoleWithCertificate" + assumeRole = "AssumeRole" stsRequestBodyLimit = 10 * (1 << 20) // 10 MiB @@ -124,6 +127,12 @@ func registerSTSRouter(router *mux.Router) { Queries(stsVersion, stsAPIVersion). Queries(stsLDAPUsername, "{LDAPUsername:.*}"). Queries(stsLDAPPassword, "{LDAPPassword:.*}") + + // AssumeRoleWithCertificate + stsRouter.Methods(http.MethodPost).HandlerFunc(httpTraceAll(sts.AssumeRoleWithCertificate)). + Queries(stsAction, clientCertificate). + Queries(stsVersion, stsAPIVersion) + } func checkAssumeRoleAuth(ctx context.Context, r *http.Request) (user auth.Credentials, isErrCodeSTS bool, stsErr STSErrorCode) { @@ -649,3 +658,100 @@ func (sts *stsAPIHandlers) AssumeRoleWithLDAPIdentity(w http.ResponseWriter, r * writeSuccessResponseXML(w, encodedSuccessResponse) } + +// AssumeRoleWithCertificate implements user authentication with client certificates. +// It verifies the client-provided X.509 certificate, maps the certificate to an S3 policy +// and returns temp. S3 credentials to the client. +// +// API endpoint: https://minio:9000?Action=AssumeRoleWithCertificate&Version=2011-06-15 +func (sts *stsAPIHandlers) AssumeRoleWithCertificate(w http.ResponseWriter, r *http.Request) { + var ctx = newContext(r, w, "AssumeRoleWithCertificate") + + if !globalSTSTLSConfig.Enabled { + writeSTSErrorResponse(ctx, w, true, ErrSTSNotInitialized, errors.New("STS API 'AssumeRoleWithCertificate' is disabled")) + return + } + + // We have to establish a TLS connection and the + // client must provide exactly one client certificate. + // Otherwise, we don't have a certificate to verify or + // the policy lookup would ambigious. + if r.TLS == nil { + writeSTSErrorResponse(ctx, w, true, ErrSTSInsecureConnection, errors.New("No TLS connection attempt")) + return + } + if len(r.TLS.PeerCertificates) == 0 { + writeSTSErrorResponse(ctx, w, true, ErrSTSMissingParameter, errors.New("No client certificate provided")) + return + } + if len(r.TLS.PeerCertificates) > 1 { + writeSTSErrorResponse(ctx, w, true, ErrSTSInvalidParameterValue, errors.New("More than one client certificate provided")) + return + } + + var certificate = r.TLS.PeerCertificates[0] + if !globalSTSTLSConfig.InsecureSkipVerify { + _, err := certificate.Verify(x509.VerifyOptions{ + KeyUsages: []x509.ExtKeyUsage{ + x509.ExtKeyUsageClientAuth, + }, + Roots: globalRootCAs, + }) + if err != nil { + writeSTSErrorResponse(ctx, w, true, ErrSTSInvalidClientCertificate, err) + return + } + } + + // We map the X.509 subject common name to the policy. So, a client + // with the common name "foo" will be associated with the policy "foo". + // Other mapping functions - e.g. public-key hash based mapping - are + // possible but not implemented. + // + // Group mapping is not possible with standard X.509 certificates. + if certificate.Subject.CommonName == "" { + writeSTSErrorResponse(ctx, w, true, ErrSTSMissingParameter, errors.New("certificate subject CN cannot be empty")) + return + } + + expiry, err := globalSTSTLSConfig.GetExpiryDuration(r.Form.Get(stsDurationSeconds)) + if err != nil { + writeSTSErrorResponse(ctx, w, true, ErrSTSMissingParameter, err) + return + } + + // We set the expiry of the temp. credentials to the minimum of the + // configured expiry and the duration until the certificate itself + // expires. + // We must not issue credentials that out-live the certificate. + if validUntil := time.Until(certificate.NotAfter); validUntil < expiry { + expiry = validUntil + } + + // Associate any service accounts to the certificate CN + parentUser := "tls:" + certificate.Subject.CommonName + + tmpCredentials, err := auth.GetNewCredentialsWithMetadata(map[string]interface{}{ + expClaim: time.Now().UTC().Add(expiry).Unix(), + parentClaim: parentUser, + subClaim: certificate.Subject.CommonName, + audClaim: certificate.Subject.Organization, + issClaim: certificate.Issuer.CommonName, + }, globalActiveCred.SecretKey) + if err != nil { + writeSTSErrorResponse(ctx, w, true, ErrSTSInternalError, err) + return + } + + tmpCredentials.ParentUser = parentUser + err = globalIAMSys.SetTempUser(tmpCredentials.AccessKey, tmpCredentials, certificate.Subject.CommonName) + if err != nil { + writeSTSErrorResponse(ctx, w, true, ErrSTSInternalError, err) + return + } + + var response = new(AssumeRoleWithCertificateResponse) + response.Result.Credentials = tmpCredentials + response.Metadata.RequestID = w.Header().Get(xhttp.AmzRequestID) + writeSuccessResponseXML(w, encodeResponse(response)) +} diff --git a/cmd/stserrorcode_string.go b/cmd/stserrorcode_string.go index 8b96ab7a5..7a1ede2ad 100644 --- a/cmd/stserrorcode_string.go +++ b/cmd/stserrorcode_string.go @@ -16,13 +16,15 @@ func _() { _ = x[ErrSTSClientGrantsExpiredToken-5] _ = x[ErrSTSInvalidClientGrantsToken-6] _ = x[ErrSTSMalformedPolicyDocument-7] - _ = x[ErrSTSNotInitialized-8] - _ = x[ErrSTSInternalError-9] + _ = x[ErrSTSInsecureConnection-8] + _ = x[ErrSTSInvalidClientCertificate-9] + _ = x[ErrSTSNotInitialized-10] + _ = x[ErrSTSInternalError-11] } -const _STSErrorCode_name = "STSNoneSTSAccessDeniedSTSMissingParameterSTSInvalidParameterValueSTSWebIdentityExpiredTokenSTSClientGrantsExpiredTokenSTSInvalidClientGrantsTokenSTSMalformedPolicyDocumentSTSNotInitializedSTSInternalError" +const _STSErrorCode_name = "STSNoneSTSAccessDeniedSTSMissingParameterSTSInvalidParameterValueSTSWebIdentityExpiredTokenSTSClientGrantsExpiredTokenSTSInvalidClientGrantsTokenSTSMalformedPolicyDocumentSTSInsecureConnectionSTSInvalidClientCertificateSTSNotInitializedSTSInternalError" -var _STSErrorCode_index = [...]uint8{0, 7, 22, 41, 65, 91, 118, 145, 171, 188, 204} +var _STSErrorCode_index = [...]uint8{0, 7, 22, 41, 65, 91, 118, 145, 171, 192, 219, 236, 252} func (i STSErrorCode) String() string { if i < 0 || i >= STSErrorCode(len(_STSErrorCode_index)-1) { diff --git a/docs/sts/tls.md b/docs/sts/tls.md new file mode 100644 index 000000000..50dedfc35 --- /dev/null +++ b/docs/sts/tls.md @@ -0,0 +1,107 @@ +# AssumeRoleWithCertificate [![Slack](https://slack.min.io/slack?type=svg)](https://slack.min.io) + +## Introduction +MinIO provides a custom STS API that allows authentication with client X.509 / TLS certificates. + +A major advantage of certificate-based authentication compared to other STS authentication methods, like OpenID Connect or LDAP/AD, is that client authentication works without any additional/external component that must be constantly available. Therefore, certificate-based authentication may provide better availability / lower operational complexity. + +The MinIO TLS STS API can be configured via MinIO's standard configuration API (i.e. using `mc admin config set/get`). Further, it can be configured via the following environment variables: + +``` +mc admin config set myminio identity_tls --env +KEY: +identity_tls enable X.509 TLS certificate SSO support + +ARGS: +MINIO_IDENTITY_TLS_SKIP_VERIFY (on|off) trust client certificates without verification. Defaults to "off" (verify) +``` + +The MinIO TLS STS API is enabled by default. However, it can be completely *disabled* by setting: +``` +MINIO_IDENTITY_TLS_ENABLE=off +``` + +## Example +MinIO exposes a custom S3 STS API endpoint as `Action=AssumeRoleWithCertificate`. A client has to send an HTTP `POST` request to `https://:?Action=AssumeRoleWithCertificate&Version=2011-06-15`. Since the authentication and authorization happens via X.509 certificates the client has to send the request over **TLS** and has to provide +a client certificate. + +The following curl example shows how to authenticate to a MinIO server with client certificate and obtain STS access credentials. + +```curl +curl -X POST --key private.key --cert public.crt "https://minio:9000?Action=AssumeRoleWithCertificate&Version=2011-06-15&DurationSeconds=3600" +``` + +```xml + + + + + YC12ZBHUVW588BQAE5BM + Zgl9+zdE0pZ88+hLqtfh0ocLN+WQTJixHouCkZkW + 2021-07-19T20:10:45ZeyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiJZQzEyWkJIVVZXNTg4QlFBRTVCTSIsImV4cCI6MTYyNjcyNTQ0NX0.wvMUf3w_x16qpVWgua8WxnV1Sgtv1jOnSu03vbrwOMzV3cI4q3_9WZD9LwlP-34DTsvbsg7gCBGh6YNriMMiQw + + + + 169339CD8B3A6948 + + +``` + +## Authentication Flow + +A client can request temp. S3 credentials via the STS API. It can authenticate via a client certificate and obtain a access/secret key pair as well as a session token. These credentials are associated to an S3 policy at the MinIO server. + +In case of certificate-based authentication, MinIO has to map the client-provided certificate to an S3 policy. MinIO does this via the subject common name field of the X.509 certificate. So, MinIO will associate a certificate with a subject `CN = foobar` to a S3 policy named `foobar`. + +The following self-signed certificate is issued for `consoleAdmin`. So, MinIO would associate it with the pre-defined `consoleAdmin` policy. +``` +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 35:ac:60:46:ad:8d:de:18:dc:0b:f6:98:14:ee:89:e8 + Signature Algorithm: ED25519 + Issuer: CN = consoleAdmin + Validity + Not Before: Jul 19 15:08:44 2021 GMT + Not After : Aug 18 15:08:44 2021 GMT + Subject: CN = consoleAdmin + Subject Public Key Info: + Public Key Algorithm: ED25519 + ED25519 Public-Key: + pub: + 5a:91:87:b8:77:fe:d4:af:d9:c7:c7:ce:55:ae:74: + aa:f3:f1:fe:04:63:9b:cb:20:97:61:97:90:94:fa: + 12:8b + X509v3 extensions: + X509v3 Key Usage: critical + Digital Signature + X509v3 Extended Key Usage: + TLS Web Client Authentication + X509v3 Basic Constraints: critical + CA:FALSE + Signature Algorithm: ED25519 + 7e:aa:be:ed:47:4d:b9:2f:fc:ed:7f:5a:fc:6b:c0:05:5b:f5: + a0:31:fe:86:e3:8e:3f:49:af:6d:d5:ac:c7:c4:57:47:ce:97: + 7d:ab:b8:e9:75:ec:b4:39:fb:c8:cf:53:16:5b:1f:15:b6:7f: + 5a:d1:35:2d:fc:31:3a:10:e7:0c +``` +> Observe the `Subject: CN = consoleAdmin` field. + +Also, note that the certificate has to contain the `Extended Key Usage: TLS Web Client Authentication`. Otherwise, MinIO would not accept the certificate as client certificate. + +Now, the STS certificate-based authentication happens in 4 steps: + +- Client sends HTTP `POST` request over a TLS connection hitting the MinIO TLS STS API. +- MinIO verifies that the client certificate is valid. +- MinIO tries to find a policy that matches the `CN` of the client certificate. +- MinIO returns temp. S3 credentials associated to the found policy. + +The returned credentials expiry after a certain period of time that can be configured via `&DurationSeconds=3600`. By default, the STS credentials are valid for 1 hour. The minimum expiration allowed is 15 minutes. + +Further, the temp. S3 credentials will never out-live the client certificate. For example, if the `MINIO_IDENTITY_TLS_STS_EXPIRY` is 7 days but the certificate itself is only valid for the next 3 days, then MinIO will return S3 credentials that are valid for 3 days only. + +## Explore Further +- [MinIO Admin Complete Guide](https://docs.min.io/docs/minio-admin-complete-guide.html) +- [The MinIO documentation website](https://docs.min.io) diff --git a/internal/config/config.go b/internal/config/config.go index e732c5190..736719fa2 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -66,6 +66,7 @@ const ( PolicyOPASubSys = "policy_opa" IdentityOpenIDSubSys = "identity_openid" IdentityLDAPSubSys = "identity_ldap" + IdentityTLSSubSys = "identity_tls" CacheSubSys = "cache" RegionSubSys = "region" EtcdSubSys = "etcd" @@ -113,6 +114,7 @@ var SubSystems = set.CreateStringSet( PolicyOPASubSys, IdentityLDAPSubSys, IdentityOpenIDSubSys, + IdentityTLSSubSys, ScannerSubSys, HealSubSys, NotifyAMQPSubSys, @@ -147,6 +149,7 @@ var SubSystemsSingleTargets = set.CreateStringSet([]string{ PolicyOPASubSys, IdentityLDAPSubSys, IdentityOpenIDSubSys, + IdentityTLSSubSys, HealSubSys, ScannerSubSys, }...) diff --git a/internal/config/identity/tls/config.go b/internal/config/identity/tls/config.go new file mode 100644 index 000000000..ad8522863 --- /dev/null +++ b/internal/config/identity/tls/config.go @@ -0,0 +1,123 @@ +// Copyright (c) 2015-2021 MinIO, Inc. +// +// This file is part of MinIO Object Storage stack +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package tls + +import ( + "strconv" + "time" + + "github.com/minio/minio/internal/auth" + "github.com/minio/minio/internal/config" + "github.com/minio/pkg/env" +) + +const ( + // EnvEnabled is an environment variable that controls whether the X.509 + // TLS STS API is enabled. By default, if not set, it is enabled. + EnvEnabled = "MINIO_IDENTITY_TLS_ENABLE" + + // EnvSkipVerify is an environment variable that controls whether + // MinIO verifies the client certificate present by the client + // when requesting temp. credentials. + // By default, MinIO always verify the client certificate. + // + // The client certificate verification should only be skipped + // when debugging or testing a setup since it allows arbitrary + // clients to obtain temp. credentials with arbitrary policy + // permissions - including admin permissions. + EnvSkipVerify = "MINIO_IDENTITY_TLS_SKIP_VERIFY" +) + +// Config contains the STS TLS configuration for generating temp. +// credentials and mapping client certificates to S3 policies. +type Config struct { + Enabled bool `json:"enabled"` + + // InsecureSkipVerify, if set to true, disables the client + // certificate verification. It should only be set for + // debugging or testing purposes. + InsecureSkipVerify bool `json:"skip_verify"` +} + +const ( + defaultExpiry time.Duration = 1 * time.Hour + minExpiry time.Duration = 15 * time.Minute + maxExpiry time.Duration = 365 * 24 * time.Hour +) + +// GetExpiryDuration - return parsed expiry duration. +func (l Config) GetExpiryDuration(dsecs string) (time.Duration, error) { + if dsecs == "" { + return defaultExpiry, nil + } + + d, err := strconv.Atoi(dsecs) + if err != nil { + return 0, auth.ErrInvalidDuration + } + + dur := time.Duration(d) * time.Second + + if dur < minExpiry || dur > maxExpiry { + return 0, auth.ErrInvalidDuration + } + return dur, nil +} + +// Lookup returns a new Config by merging the given K/V config +// system with environment variables. +func Lookup(kvs config.KVS) (Config, error) { + if err := config.CheckValidKeys(config.IdentityTLSSubSys, kvs, DefaultKVS); err != nil { + return Config{}, err + } + insecureSkipVerify, err := config.ParseBool(env.Get(EnvSkipVerify, kvs.Get(skipVerify))) + if err != nil { + return Config{}, err + } + enabled, err := config.ParseBool(env.Get(EnvEnabled, "on")) + if err != nil { + return Config{}, err + } + return Config{ + Enabled: enabled, + InsecureSkipVerify: insecureSkipVerify, + }, nil +} + +const ( + skipVerify = "skip_verify" +) + +// DefaultKVS is the the default K/V config system for +// the STS TLS API. +var DefaultKVS = config.KVS{ + config.KV{ + Key: skipVerify, + Value: "off", + }, +} + +// Help is the help and description for the STS API K/V configuration. +var Help = config.HelpKVS{ + config.HelpKV{ + Key: skipVerify, + Description: `trust client certificates without verification. Defaults to "off" (verify)`, + Optional: true, + Type: "on|off", + }, +} diff --git a/internal/http/server.go b/internal/http/server.go index ace99a574..ad953d2ea 100644 --- a/internal/http/server.go +++ b/internal/http/server.go @@ -172,6 +172,7 @@ func NewServer(addrs []string, handler http.Handler, getCert certs.GetCertificat MinVersion: tls.VersionTLS12, NextProtos: []string{"http/1.1", "h2"}, GetCertificate: getCert, + ClientAuth: tls.RequestClientCert, } if secureCiphers || fips.Enabled { tlsConfig.CipherSuites = fips.CipherSuitesTLS()