Add support for client certificates to -output-curl-string (#13660)

* Add support for client certificates to -output-curl-string

I did not write tests for this feature as -output-curl-string was not
already tested and this is a simple change. Because the name of the
certificates would be lost once loaded I added fields to Config to keep
track of them. I did not add a public method for the user to set them
explicitely as I don't think anyone would need this functionnality
outside of the Vault CLI.

Closes https://github.com/hashicorp/vault/issues/13376

* Add changelog

* Add lock in ConfigureTLS
This commit is contained in:
Rémi Lapeyre 2022-01-20 19:25:26 +01:00 committed by GitHub
parent 974dbf6082
commit fb4b85d921
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 52 additions and 7 deletions

View File

@ -132,6 +132,12 @@ type Config struct {
// with the same client. Cloning a client will not clone this value.
OutputCurlString bool
// curlCACert, curlCAPath, curlClientCert and curlClientKey are used to keep
// track of the name of the TLS certs and keys when OutputCurlString is set.
// Cloning a client will also not clone those values.
curlCACert, curlCAPath string
curlClientCert, curlClientKey string
// SRVLookup enables the client to lookup the host through DNS SRV lookup
SRVLookup bool
@ -225,9 +231,9 @@ func DefaultConfig() *Config {
return config
}
// ConfigureTLS takes a set of TLS configurations and applies those to the
// HTTP client.
func (c *Config) ConfigureTLS(t *TLSConfig) error {
// configureTLS is a lock free version of ConfigureTLS that can be used in
// ReadEnvironment where the lock is already hold
func (c *Config) configureTLS(t *TLSConfig) error {
if c.HttpClient == nil {
c.HttpClient = DefaultConfig().HttpClient
}
@ -244,11 +250,15 @@ func (c *Config) ConfigureTLS(t *TLSConfig) error {
return err
}
foundClientCert = true
c.curlClientCert = t.ClientCert
c.curlClientKey = t.ClientKey
case t.ClientCert != "" || t.ClientKey != "":
return fmt.Errorf("both client cert and client key must be provided")
}
if t.CACert != "" || t.CAPath != "" {
c.curlCACert = t.CACert
c.curlCAPath = t.CAPath
rootConfig := &rootcerts.Config{
CAFile: t.CACert,
CAPath: t.CAPath,
@ -278,6 +288,15 @@ func (c *Config) ConfigureTLS(t *TLSConfig) error {
return nil
}
// ConfigureTLS takes a set of TLS configurations and applies those to the
// HTTP client.
func (c *Config) ConfigureTLS(t *TLSConfig) error {
c.modifyLock.Lock()
defer c.modifyLock.Unlock()
return c.configureTLS(t)
}
// ReadEnvironment reads configuration information from the environment. If
// there is an error, no configuration value is updated.
func (c *Config) ReadEnvironment() error {
@ -382,7 +401,7 @@ func (c *Config) ReadEnvironment() error {
c.SRVLookup = envSRVLookup
c.Limiter = limit
if err := c.ConfigureTLS(t); err != nil {
if err := c.configureTLS(t); err != nil {
return err
}
@ -1122,6 +1141,10 @@ START:
LastOutputStringError = &OutputStringError{
Request: req,
TLSSkipVerify: c.config.HttpClient.Transport.(*http.Transport).TLSClientConfig.InsecureSkipVerify,
ClientCert: c.config.curlClientCert,
ClientKey: c.config.curlClientKey,
ClientCACert: c.config.curlCACert,
ClientCAPath: c.config.curlCAPath,
}
return nil, LastOutputStringError
}

View File

@ -15,9 +15,11 @@ var LastOutputStringError *OutputStringError
type OutputStringError struct {
*retryablehttp.Request
TLSSkipVerify bool
parsingError error
parsedCurlString string
TLSSkipVerify bool
ClientCACert, ClientCAPath string
ClientCert, ClientKey string
parsingError error
parsedCurlString string
}
func (d *OutputStringError) Error() string {
@ -46,6 +48,22 @@ func (d *OutputStringError) parseRequest() {
if d.Request.Method != "GET" {
d.parsedCurlString = fmt.Sprintf("%s-X %s ", d.parsedCurlString, d.Request.Method)
}
if d.ClientCACert != "" {
clientCACert := strings.Replace(d.ClientCACert, "'", "'\"'\"'", -1)
d.parsedCurlString = fmt.Sprintf("%s--cacert '%s' ", d.parsedCurlString, clientCACert)
}
if d.ClientCAPath != "" {
clientCAPath := strings.Replace(d.ClientCAPath, "'", "'\"'\"'", -1)
d.parsedCurlString = fmt.Sprintf("%s--capath '%s' ", d.parsedCurlString, clientCAPath)
}
if d.ClientCert != "" {
clientCert := strings.Replace(d.ClientCert, "'", "'\"'\"'", -1)
d.parsedCurlString = fmt.Sprintf("%s--cert '%s' ", d.parsedCurlString, clientCert)
}
if d.ClientKey != "" {
clientKey := strings.Replace(d.ClientKey, "'", "'\"'\"'", -1)
d.parsedCurlString = fmt.Sprintf("%s--key '%s' ", d.parsedCurlString, clientKey)
}
for k, v := range d.Request.Header {
for _, h := range v {
if strings.ToLower(k) == "x-vault-token" {

4
changelog/13660.txt Normal file
View File

@ -0,0 +1,4 @@
```release-note:bug
core: `-output-curl-string` now properly sets cURL options for client and CA
certificates.
```