mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-30 18:17:55 +00:00
Add an option to allow cert-auth to return metadata about client cert that fails login (#29044)
* Add an option to allow cert-auth to return metadata about client certs that fail login * Add cl * Update SPDX header for sdk/logical/response_test.go
This commit is contained in:
@@ -1317,6 +1317,8 @@ func TestBackend_ext_singleCert(t *testing.T) {
|
|||||||
testAccStepLoginWithMetadata(t, connState, "web", map[string]string{"2-1-1-1": "A UTF8String Extension"}, true),
|
testAccStepLoginWithMetadata(t, connState, "web", map[string]string{"2-1-1-1": "A UTF8String Extension"}, true),
|
||||||
testAccStepCert(t, "web", ca, "foo", allowed{metadata_ext: "1.2.3.45"}, false),
|
testAccStepCert(t, "web", ca, "foo", allowed{metadata_ext: "1.2.3.45"}, false),
|
||||||
testAccStepLoginWithMetadata(t, connState, "web", map[string]string{}, true),
|
testAccStepLoginWithMetadata(t, connState, "web", map[string]string{}, true),
|
||||||
|
testAccStepSetConfig(t, config{EnableMetadataOnFailures: true}, connState),
|
||||||
|
testAccStepReadConfig(t, config{EnableMetadataOnFailures: true}, connState),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1728,6 +1730,7 @@ func testAccStepSetConfig(t *testing.T, conf config, connState tls.ConnectionSta
|
|||||||
ConnState: &connState,
|
ConnState: &connState,
|
||||||
Data: map[string]interface{}{
|
Data: map[string]interface{}{
|
||||||
"enable_identity_alias_metadata": conf.EnableIdentityAliasMetadata,
|
"enable_identity_alias_metadata": conf.EnableIdentityAliasMetadata,
|
||||||
|
"enable_metadata_on_failures": conf.EnableMetadataOnFailures,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1752,6 +1755,20 @@ func testAccStepReadConfig(t *testing.T, conf config, connState tls.ConnectionSt
|
|||||||
t.Fatalf("bad: expected enable_identity_alias_metadata to be %t, got %t", conf.EnableIdentityAliasMetadata, b)
|
t.Fatalf("bad: expected enable_identity_alias_metadata to be %t, got %t", conf.EnableIdentityAliasMetadata, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
metaValueRaw, ok := resp.Data["enable_metadata_on_failures"]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("enable_metadata_on_failures not found in response")
|
||||||
|
}
|
||||||
|
|
||||||
|
metaValue, ok := metaValueRaw.(bool)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("bad: expected enable_metadata_on_failures to be a bool")
|
||||||
|
}
|
||||||
|
|
||||||
|
if metaValue != conf.EnableMetadataOnFailures {
|
||||||
|
t.Fatalf("bad: expected enable_metadata_on_failures to be %t, got %t", conf.EnableMetadataOnFailures, metaValue)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -1936,6 +1953,23 @@ func testAccStepCert(t *testing.T, name string, cert []byte, policies string, te
|
|||||||
return testAccStepCertWithExtraParams(t, name, cert, policies, testData, expectError, nil)
|
return testAccStepCertWithExtraParams(t, name, cert, policies, testData, expectError, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testStepEnableMetadataFailures() logicaltest.TestStep {
|
||||||
|
return logicaltest.TestStep{
|
||||||
|
Operation: logical.UpdateOperation,
|
||||||
|
Path: "config",
|
||||||
|
ErrorOk: false,
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"enable_metadata_on_failures": true,
|
||||||
|
},
|
||||||
|
Check: func(resp *logical.Response) error {
|
||||||
|
if resp != nil && resp.IsError() {
|
||||||
|
return fmt.Errorf("expected nil response got a response error: %v", resp)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testAccStepCertWithExtraParams(t *testing.T, name string, cert []byte, policies string, testData allowed, expectError bool, extraParams map[string]interface{}) logicaltest.TestStep {
|
func testAccStepCertWithExtraParams(t *testing.T, name string, cert []byte, policies string, testData allowed, expectError bool, extraParams map[string]interface{}) logicaltest.TestStep {
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"certificate": string(cert),
|
"certificate": string(cert),
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ func pathConfig(b *backend) *framework.Path {
|
|||||||
Default: defaultRoleCacheSize,
|
Default: defaultRoleCacheSize,
|
||||||
Description: `The size of the in memory role cache`,
|
Description: `The size of the in memory role cache`,
|
||||||
},
|
},
|
||||||
|
"enable_metadata_on_failures": {
|
||||||
|
Type: framework.TypeBool,
|
||||||
|
Default: false,
|
||||||
|
Description: `If set, metadata of the client certificate will be returned on authentication failures.`,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
Operations: map[logical.Operation]framework.OperationHandler{
|
Operations: map[logical.Operation]framework.OperationHandler{
|
||||||
@@ -87,6 +92,9 @@ func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, dat
|
|||||||
}
|
}
|
||||||
config.RoleCacheSize = cacheSize
|
config.RoleCacheSize = cacheSize
|
||||||
}
|
}
|
||||||
|
if enableMetadataOnFailures, ok := data.GetOk("enable_metadata_on_failures"); ok {
|
||||||
|
config.EnableMetadataOnFailures = enableMetadataOnFailures.(bool)
|
||||||
|
}
|
||||||
if err := b.storeConfig(ctx, req.Storage, config); err != nil {
|
if err := b.storeConfig(ctx, req.Storage, config); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -104,6 +112,7 @@ func (b *backend) pathConfigRead(ctx context.Context, req *logical.Request, d *f
|
|||||||
"enable_identity_alias_metadata": cfg.EnableIdentityAliasMetadata,
|
"enable_identity_alias_metadata": cfg.EnableIdentityAliasMetadata,
|
||||||
"ocsp_cache_size": cfg.OcspCacheSize,
|
"ocsp_cache_size": cfg.OcspCacheSize,
|
||||||
"role_cache_size": cfg.RoleCacheSize,
|
"role_cache_size": cfg.RoleCacheSize,
|
||||||
|
"enable_metadata_on_failures": cfg.EnableMetadataOnFailures,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &logical.Response{
|
return &logical.Response{
|
||||||
@@ -133,4 +142,5 @@ type config struct {
|
|||||||
EnableIdentityAliasMetadata bool `json:"enable_identity_alias_metadata"`
|
EnableIdentityAliasMetadata bool `json:"enable_identity_alias_metadata"`
|
||||||
OcspCacheSize int `json:"ocsp_cache_size"`
|
OcspCacheSize int `json:"ocsp_cache_size"`
|
||||||
RoleCacheSize int `json:"role_cache_size"`
|
RoleCacheSize int `json:"role_cache_size"`
|
||||||
|
EnableMetadataOnFailures bool `json:"enable_metadata_on_failures"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,12 +70,20 @@ func (b *backend) loginPathWrapper(wrappedOp func(ctx context.Context, req *logi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *backend) pathLoginResolveRole(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
func (b *backend) pathLoginResolveRole(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
config, err := b.Config(ctx, req.Storage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if b.configUpdated.Load() {
|
||||||
|
b.updatedConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
var matched *ParsedCert
|
var matched *ParsedCert
|
||||||
|
|
||||||
if verifyResp, resp, err := b.verifyCredentials(ctx, req, data); err != nil {
|
if verifyResp, resp, err := b.verifyCredentials(ctx, req, data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if resp != nil {
|
} else if resp != nil {
|
||||||
return resp, nil
|
return certAuthLoginFailureResponse(config, resp, req), nil
|
||||||
} else {
|
} else {
|
||||||
matched = verifyResp
|
matched = verifyResp
|
||||||
}
|
}
|
||||||
@@ -118,7 +126,7 @@ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, data *fra
|
|||||||
if verifyResp, resp, err := b.verifyCredentials(ctx, req, data); err != nil {
|
if verifyResp, resp, err := b.verifyCredentials(ctx, req, data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if resp != nil {
|
} else if resp != nil {
|
||||||
return resp, nil
|
return certAuthLoginFailureResponse(config, resp, req), nil
|
||||||
} else {
|
} else {
|
||||||
matched = verifyResp
|
matched = verifyResp
|
||||||
}
|
}
|
||||||
@@ -181,6 +189,56 @@ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, data *fra
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func certAuthLoginFailureResponse(config *config, resp *logical.Response, req *logical.Request) *logical.Response {
|
||||||
|
if !config.EnableMetadataOnFailures || !resp.IsError() {
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
var initialErrMsg string
|
||||||
|
if err := resp.Error(); err != nil {
|
||||||
|
initialErrMsg = err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
clientCert, exists := getClientCert(req)
|
||||||
|
if !exists {
|
||||||
|
return logical.ErrorResponse("no client certificate found\n" + initialErrMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim these values as they can be anything from any sort of failed certificate
|
||||||
|
// and we don't want to expose audit entries to randomly large strings.
|
||||||
|
const maxChars = 100
|
||||||
|
metadata := map[string]string{
|
||||||
|
"common_name": trimToMaxChars(clientCert.Subject.CommonName, maxChars),
|
||||||
|
"serial_number": trimToMaxChars(clientCert.SerialNumber.String(), maxChars),
|
||||||
|
"subject_key_id": trimToMaxChars(certutil.GetHexFormatted(clientCert.SubjectKeyId, ":"), maxChars),
|
||||||
|
"authority_key_id": trimToMaxChars(certutil.GetHexFormatted(clientCert.AuthorityKeyId, ":"), maxChars),
|
||||||
|
}
|
||||||
|
|
||||||
|
return logical.ErrorResponseWithData(metadata, initialErrMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getClientCert(req *logical.Request) (*x509.Certificate, bool) {
|
||||||
|
if req == nil || req.Connection == nil || req.Connection.ConnState == nil || req.Connection.ConnState.PeerCertificates == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
clientCerts := req.Connection.ConnState.PeerCertificates
|
||||||
|
if len(clientCerts) == 0 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
clientCert := clientCerts[0]
|
||||||
|
if clientCert == nil || clientCert.IsCA {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return clientCert, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func trimToMaxChars(formatted string, maxSize int) string {
|
||||||
|
if len(formatted) > maxSize {
|
||||||
|
return formatted[:maxSize-3] + "..."
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatted
|
||||||
|
}
|
||||||
|
|
||||||
func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
|
||||||
config, err := b.Config(ctx, req.Storage)
|
config, err := b.Config(ctx, req.Storage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -195,7 +253,7 @@ func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *f
|
|||||||
if verifyResp, resp, err := b.verifyCredentials(ctx, req, d); err != nil {
|
if verifyResp, resp, err := b.verifyCredentials(ctx, req, d); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if resp != nil {
|
} else if resp != nil {
|
||||||
return resp, nil
|
return certAuthLoginFailureResponse(config, resp, req), nil
|
||||||
} else {
|
} else {
|
||||||
matched = verifyResp
|
matched = verifyResp
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import (
|
|||||||
logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
|
logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
|
||||||
"github.com/hashicorp/vault/sdk/helper/certutil"
|
"github.com/hashicorp/vault/sdk/helper/certutil"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/crypto/ocsp"
|
"golang.org/x/crypto/ocsp"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -203,6 +204,51 @@ func testAccStepResolveRoleExpectRoleResolutionToFail(t *testing.T, connState tl
|
|||||||
t.Fatal("Error not part of response.")
|
t.Fatal("Error not part of response.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, dataKeyExists := resp.Data["data"]; dataKeyExists {
|
||||||
|
t.Fatal("metadata key 'data' existed in response without feature enabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(errString, certAuthFailMsg) {
|
||||||
|
t.Fatalf("Error was not due to invalid role name. Error: %s", errString)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"name": certName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccStepResolveRoleExpectRoleResolutionToFailWithData(t *testing.T, connState tls.ConnectionState, certName string) logicaltest.TestStep {
|
||||||
|
return logicaltest.TestStep{
|
||||||
|
Operation: logical.ResolveRoleOperation,
|
||||||
|
Path: "login",
|
||||||
|
Unauthenticated: true,
|
||||||
|
ConnState: &connState,
|
||||||
|
ErrorOk: true,
|
||||||
|
Check: func(resp *logical.Response) error {
|
||||||
|
if resp == nil && !resp.IsError() {
|
||||||
|
t.Fatalf("Response was not an error: resp:%#v", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
errString, ok := resp.Data["error"].(string)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Error not part of response.")
|
||||||
|
}
|
||||||
|
|
||||||
|
dataKeysRaw, dataKeyExists := resp.Data["data"]
|
||||||
|
if !dataKeyExists {
|
||||||
|
t.Fatal("metadata key 'data' did not exist in response feature enabled")
|
||||||
|
}
|
||||||
|
dataKeys, ok := dataKeysRaw.(map[string]string)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("the 'data' field was not a map: %T", dataKeysRaw)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range []string{"common_name", "serial_number", "authority_key_id", "subject_key_id"} {
|
||||||
|
require.Contains(t, dataKeys, key, "response metadata key %s was missing in response: %v", key, resp)
|
||||||
|
}
|
||||||
|
|
||||||
if !strings.Contains(errString, certAuthFailMsg) {
|
if !strings.Contains(errString, certAuthFailMsg) {
|
||||||
t.Fatalf("Error was not due to invalid role name. Error: %s", errString)
|
t.Fatalf("Error was not due to invalid role name. Error: %s", errString)
|
||||||
}
|
}
|
||||||
@@ -420,6 +466,44 @@ func TestCert_RoleResolveOCSP(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func serialFromBigInt(serial *big.Int) string {
|
// TestCert_MetadataOnFailure verifies that we return the cert metadata
|
||||||
return strings.TrimSpace(certutil.GetHexFormatted(serial.Bytes(), ":"))
|
// in the response on failures if the configuration option is enabled.
|
||||||
|
func TestCert_MetadataOnFailure(t *testing.T) {
|
||||||
|
certTemplate := &x509.Certificate{
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "example.com",
|
||||||
|
},
|
||||||
|
DNSNames: []string{"example.com"},
|
||||||
|
IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||||
|
x509.ExtKeyUsageServerAuth,
|
||||||
|
x509.ExtKeyUsageClientAuth,
|
||||||
|
},
|
||||||
|
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement,
|
||||||
|
SerialNumber: big.NewInt(mathrand.Int63()),
|
||||||
|
NotBefore: time.Now().Add(-30 * time.Second),
|
||||||
|
NotAfter: time.Now().Add(262980 * time.Hour),
|
||||||
|
}
|
||||||
|
|
||||||
|
tempDir, connState, err := generateTestCertAndConnState(t, certTemplate)
|
||||||
|
if tempDir != "" {
|
||||||
|
defer os.RemoveAll(tempDir)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error testing connection state: %v", err)
|
||||||
|
}
|
||||||
|
ca, err := ioutil.ReadFile(filepath.Join(tempDir, "ca_cert.pem"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logicaltest.Test(t, logicaltest.TestCase{
|
||||||
|
CredentialBackend: testFactory(t),
|
||||||
|
Steps: []logicaltest.TestStep{
|
||||||
|
testStepEnableMetadataFailures(),
|
||||||
|
testAccStepCert(t, "web", ca, "foo", allowed{dns: "example.com"}, false),
|
||||||
|
testAccStepLoginWithName(t, connState, "web"),
|
||||||
|
testAccStepResolveRoleExpectRoleResolutionToFailWithData(t, connState, "notweb"),
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
3
changelog/29044.txt
Normal file
3
changelog/29044.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
```release-note:improvement
|
||||||
|
auth/cert: Add new configuration option `enable_metadata_on_failures` to add client cert metadata on login failures to audit log and response
|
||||||
|
```
|
||||||
@@ -141,6 +141,15 @@ func ErrorResponse(text string, vargs ...interface{}) *Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrorResponseWithData is used to format an error response with additional data returned
|
||||||
|
// within the "data" sub-field of the Data field. Useful to return additional information to the client
|
||||||
|
// and or appear within audited responses.
|
||||||
|
func ErrorResponseWithData(data interface{}, text string, vargs ...interface{}) *Response {
|
||||||
|
resp := ErrorResponse(text, vargs...)
|
||||||
|
resp.Data["data"] = data
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
// ListResponse is used to format a response to a list operation.
|
// ListResponse is used to format a response to a list operation.
|
||||||
func ListResponse(keys []string) *Response {
|
func ListResponse(keys []string) *Response {
|
||||||
resp := &Response{
|
resp := &Response{
|
||||||
|
|||||||
24
sdk/logical/response_test.go
Normal file
24
sdk/logical/response_test.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package logical
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestResponse_ErrorResponse validates that our helper functions produce responses
|
||||||
|
// that we consider errors.
|
||||||
|
func TestResponse_ErrorResponse(t *testing.T) {
|
||||||
|
simpleResp := ErrorResponse("a test %s", "error")
|
||||||
|
assert.True(t, simpleResp.IsError())
|
||||||
|
|
||||||
|
dataMap := map[string]string{
|
||||||
|
"test1": "testing",
|
||||||
|
}
|
||||||
|
|
||||||
|
withDataResp := ErrorResponseWithData(dataMap, "a test %s", "error")
|
||||||
|
assert.True(t, withDataResp.IsError())
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/golang/protobuf/ptypes"
|
"github.com/golang/protobuf/ptypes"
|
||||||
memdb "github.com/hashicorp/go-memdb"
|
"github.com/hashicorp/go-memdb"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/go-secure-stdlib/strutil"
|
"github.com/hashicorp/go-secure-stdlib/strutil"
|
||||||
"github.com/hashicorp/vault/helper/identity"
|
"github.com/hashicorp/vault/helper/identity"
|
||||||
@@ -286,14 +286,7 @@ func (i *IdentityStore) pathEntityMergeID() framework.OperationFunc {
|
|||||||
return logical.ErrorResponse(userErr.Error()), nil
|
return logical.ErrorResponse(userErr.Error()), nil
|
||||||
}
|
}
|
||||||
// Alias clash error, so include additional details
|
// Alias clash error, so include additional details
|
||||||
resp := &logical.Response{
|
return logical.ErrorResponseWithData(aliases, userErr.Error()), nil
|
||||||
Data: map[string]interface{}{
|
|
||||||
"error": userErr.Error(),
|
|
||||||
"data": aliases,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp, nil
|
|
||||||
}
|
}
|
||||||
if intErr != nil {
|
if intErr != nil {
|
||||||
return nil, intErr
|
return nil, intErr
|
||||||
|
|||||||
@@ -367,6 +367,9 @@ Configuration options for the method.
|
|||||||
that this cache is used for all configured certificates.
|
that this cache is used for all configured certificates.
|
||||||
- `role_cache_size` `(int: 200)` - The size of the role cache. Use `-1` to disable
|
- `role_cache_size` `(int: 200)` - The size of the role cache. Use `-1` to disable
|
||||||
role caching.
|
role caching.
|
||||||
|
- `enable_metadata_on_failures` `(boolean: false)` - If set, metadata of the client
|
||||||
|
certificate such as common name, serial, subject key id and authority key id will
|
||||||
|
be returned on authentication failures and appear in auditing records.
|
||||||
|
|
||||||
### Sample payload
|
### Sample payload
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user