Fix: Optionally reload x509 key-pair from disk on agent auto-auth (#19002)

* Optionally reload x509 key-pair from disk

* Document 'reload' config value

* Added changelog release note
This commit is contained in:
Karel 2023-03-22 16:01:58 +01:00 committed by GitHub
parent 2fcaec4b21
commit 7469b0828a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 72 additions and 1 deletions

3
changelog/19002.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
agent: Added `reload` option to cert auth configuration in case of external renewals of local x509 key-pairs.
```

View File

@ -23,6 +23,7 @@ type certMethod struct {
caCert string caCert string
clientCert string clientCert string
clientKey string clientKey string
reload bool
// Client is the cached client to use if cert info was provided. // Client is the cached client to use if cert info was provided.
client *api.Client client *api.Client
@ -76,6 +77,14 @@ func NewCertAuthMethod(conf *auth.AuthConfig) (auth.AuthMethod, error) {
return nil, errors.New("could not convert 'cert_key' config value to string") return nil, errors.New("could not convert 'cert_key' config value to string")
} }
} }
reload, ok := conf.Config["reload"]
if ok {
c.reload, ok = reload.(bool)
if !ok {
return nil, errors.New("could not convert 'reload' config value to bool")
}
}
} }
return c, nil return c, nil
@ -111,7 +120,7 @@ func (c *certMethod) AuthClient(client *api.Client) (*api.Client, error) {
if c.caCert != "" || (c.clientKey != "" && c.clientCert != "") { if c.caCert != "" || (c.clientKey != "" && c.clientCert != "") {
// Return cached client if present // Return cached client if present
if c.client != nil { if c.client != nil && !c.reload {
return c.client, nil return c.client, nil
} }

View File

@ -133,3 +133,59 @@ func TestCertAuthMethod_AuthClient_withCerts(t *testing.T) {
t.Fatal("expected client from AuthClient to return back a cached client") t.Fatal("expected client from AuthClient to return back a cached client")
} }
} }
func TestCertAuthMethod_AuthClient_withCertsReload(t *testing.T) {
clientCert, err := os.Open("./test-fixtures/keys/cert.pem")
if err != nil {
t.Fatal(err)
}
defer clientCert.Close()
clientKey, err := os.Open("./test-fixtures/keys/key.pem")
if err != nil {
t.Fatal(err)
}
defer clientKey.Close()
config := &auth.AuthConfig{
Logger: hclog.NewNullLogger(),
MountPath: "cert-test",
Config: map[string]interface{}{
"name": "with-certs-reloaded",
"client_cert": clientCert.Name(),
"client_key": clientKey.Name(),
"reload": true,
},
}
method, err := NewCertAuthMethod(config)
if err != nil {
t.Fatal(err)
}
client, err := api.NewClient(nil)
if err != nil {
t.Fatal(err)
}
clientToUse, err := method.(auth.AuthMethodWithClient).AuthClient(client)
if err != nil {
t.Fatal(err)
}
if client == clientToUse {
t.Fatal("expected client from AuthClient to be different from original client")
}
// Call AuthClient again to get back a new client with reloaded certificates
reloadedClient, err := method.(auth.AuthMethodWithClient).AuthClient(client)
if err != nil {
t.Fatal(err)
}
if reloadedClient == clientToUse {
t.Fatal("expected client from AuthClient to return back a new client")
}
}

View File

@ -30,3 +30,6 @@ Stanza](/vault/docs/agent#vault-stanza).
- `client_key` `(string: optional)` - Path on the local disk to a single - `client_key` `(string: optional)` - Path on the local disk to a single
PEM-encoded private key matching the client certificate from client_cert. PEM-encoded private key matching the client certificate from client_cert.
- `reload` `(bool: optional, default: false)` - If true, causes the local x509 key-pair to be reloaded from disk on each authentication attempt.
This is useful in situations where client certificates are short-lived and automatically renewed.