Skip to content

Commit

Permalink
Add support for client certificates to -output-curl-string (#13660)
Browse files Browse the repository at this point in the history
* 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 #13376

* Add changelog

* Add lock in ConfigureTLS
  • Loading branch information
remilapeyre authored Jan 20, 2022
1 parent 3619a1e commit 4a69e15
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 7 deletions.
31 changes: 27 additions & 4 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
}
Expand All @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}
Expand Down
24 changes: 21 additions & 3 deletions api/output_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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" {
Expand Down
4 changes: 4 additions & 0 deletions changelog/13660.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
```release-note:bug
core: `-output-curl-string` now properly sets cURL options for client and CA
certificates.
```

0 comments on commit 4a69e15

Please sign in to comment.