diff options
author | HÃ¥kon Hallingstad <hakon@yahooinc.com> | 2022-02-03 10:53:56 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-03 10:53:56 +0100 |
commit | 756ab78df5287519fa220982685d6335f19a54e1 (patch) | |
tree | 12f1a22756c2d3803f79305a349e2b6358f4be81 /client | |
parent | 62cfcc941317c21aefaf44862c4f63173974059f (diff) | |
parent | 10446fafad7096e6f890c74792f2f343665bc32e (diff) |
Merge pull request #21048 from vespa-engine/mpolden/keys-in-env
Support passing keys through environment
Diffstat (limited to 'client')
-rw-r--r-- | client/go/cmd/api_key.go | 24 | ||||
-rw-r--r-- | client/go/cmd/cert.go | 32 | ||||
-rw-r--r-- | client/go/cmd/config.go | 40 | ||||
-rw-r--r-- | client/go/cmd/helpers.go | 22 |
4 files changed, 97 insertions, 21 deletions
diff --git a/client/go/cmd/api_key.go b/client/go/cmd/api_key.go index 032d98c96fe..0f665007107 100644 --- a/client/go/cmd/api_key.go +++ b/client/go/cmd/api_key.go @@ -15,13 +15,31 @@ import ( var overwriteKey bool +const apiKeyLongDoc = `Create a new user API key for authentication with Vespa Cloud. + +The API key will be stored in the Vespa CLI home directory +(see 'vespa help config'). Other commands will then automatically load the API +key as necessary. + +It's possible to override the API key used through environment variables. This +can be useful in continuous integration systems. + +* VESPA_CLI_API_KEY containing the key directly: + + export VESPA_CLI_API_KEY="my api key" + +* VESPA_CLI_API_KEY_FILE containing path to the key: + + export VESPA_CLI_API_KEY_FILE=/path/to/api-key + +Note that when overriding API key through environment variables, that key will +always be used. It's not possible to specify a tenant-specific key.` + func init() { apiKeyCmd.Flags().BoolVarP(&overwriteKey, "force", "f", false, "Force overwrite of existing API key") apiKeyCmd.MarkPersistentFlagRequired(applicationFlag) } -var example string - func apiKeyExample() string { if vespa.Auth0AccessTokenEnabled() { return "$ vespa auth api-key -a my-tenant.my-app.my-instance" @@ -33,6 +51,7 @@ func apiKeyExample() string { var apiKeyCmd = &cobra.Command{ Use: "api-key", Short: "Create a new user API key for authentication with Vespa Cloud", + Long: apiKeyLongDoc, Example: apiKeyExample(), DisableAutoGenTag: true, Args: cobra.ExactArgs(0), @@ -42,6 +61,7 @@ var apiKeyCmd = &cobra.Command{ var deprecatedApiKeyCmd = &cobra.Command{ Use: "api-key", Short: "Create a new user API key for authentication with Vespa Cloud", + Long: apiKeyLongDoc, Example: apiKeyExample(), DisableAutoGenTag: true, Args: cobra.ExactArgs(0), diff --git a/client/go/cmd/cert.go b/client/go/cmd/cert.go index e79a45d3af8..9319c799ae5 100644 --- a/client/go/cmd/cert.go +++ b/client/go/cmd/cert.go @@ -5,15 +5,41 @@ package cmd import ( "fmt" + "os" + "path/filepath" + "github.com/spf13/cobra" "github.com/vespa-engine/vespa/client/go/util" "github.com/vespa-engine/vespa/client/go/vespa" - "os" - "path/filepath" ) var overwriteCertificate bool +const longDoc = `Create a new private key and self-signed certificate for Vespa Cloud deployment. + +The private key and certificate will be stored in the Vespa CLI home directory +(see 'vespa help config'). Other commands will then automatically load the +certificate as necessary. + +It's possible to override the private key and certificate used through +environment variables. This can be useful in continuous integration systems. + +* VESPA_CLI_DATA_PLANE_CERT and VESPA_CLI_DATA_PLANE_KEY containing the + certificate and private key directly: + + export VESPA_CLI_DATA_PLANE_CERT="my cert" + export VESPA_CLI_DATA_PLANE_KEY="my private key" + +* VESPA_CLI_DATA_PLANE_CERT_FILE and VESPA_CLI_DATA_PLANE_KEY_FILE containing + paths to the certificate and private key: + + export VESPA_CLI_DATA_PLANE_CERT_FILE=/path/to/cert + export VESPA_CLI_DATA_PLANE_KEY_FILE=/path/to/key + +Note that when overriding key pair through environment variables, that key pair +will always be used for all applications. It's not possible to specify an +application-specific key.` + func init() { certCmd.Flags().BoolVarP(&overwriteCertificate, "force", "f", false, "Force overwrite of existing certificate and private key") certCmd.MarkPersistentFlagRequired(applicationFlag) @@ -30,6 +56,7 @@ func certExample() string { var certCmd = &cobra.Command{ Use: "cert", Short: "Create a new private key and self-signed certificate for Vespa Cloud deployment", + Long: longDoc, Example: certExample(), DisableAutoGenTag: true, Args: cobra.MaximumNArgs(1), @@ -39,6 +66,7 @@ var certCmd = &cobra.Command{ var deprecatedCertCmd = &cobra.Command{ Use: "cert", Short: "Create a new private key and self-signed certificate for Vespa Cloud deployment", + Long: longDoc, Example: "$ vespa cert -a my-tenant.my-app.my-instance", DisableAutoGenTag: true, Args: cobra.MaximumNArgs(1), diff --git a/client/go/cmd/config.go b/client/go/cmd/config.go index 3a6e43e7ffe..e375e2c66b9 100644 --- a/client/go/cmd/config.go +++ b/client/go/cmd/config.go @@ -5,6 +5,7 @@ package cmd import ( + "crypto/tls" "fmt" "io/ioutil" "log" @@ -107,6 +108,12 @@ type Config struct { createDirs bool } +type KeyPair struct { + KeyPair tls.Certificate + CertificateFile string + PrivateKeyFile string +} + func LoadConfig() (*Config, error) { home, err := vespaCliHome() if err != nil { @@ -146,11 +153,44 @@ func (c *Config) PrivateKeyPath(app vespa.ApplicationID) (string, error) { return c.applicationFilePath(app, "data-plane-private-key.pem") } +func (c *Config) X509KeyPair(app vespa.ApplicationID) (KeyPair, error) { + cert, certOk := os.LookupEnv("VESPA_CLI_DATA_PLANE_CERT") + key, keyOk := os.LookupEnv("VESPA_CLI_DATA_PLANE_KEY") + if certOk && keyOk { + // Use key pair from environment + kp, err := tls.X509KeyPair([]byte(cert), []byte(key)) + return KeyPair{KeyPair: kp}, err + } + privateKeyFile, err := c.PrivateKeyPath(app) + if err != nil { + return KeyPair{}, err + } + certificateFile, err := c.CertificatePath(app) + if err != nil { + return KeyPair{}, err + } + kp, err := tls.LoadX509KeyPair(certificateFile, privateKeyFile) + if err != nil { + return KeyPair{}, err + } + return KeyPair{ + KeyPair: kp, + CertificateFile: certificateFile, + PrivateKeyFile: privateKeyFile, + }, nil +} + func (c *Config) APIKeyPath(tenantName string) string { + if override, ok := os.LookupEnv("VESPA_CLI_API_KEY_FILE"); ok { + return override + } return filepath.Join(c.Home, tenantName+".api-key.pem") } func (c *Config) ReadAPIKey(tenantName string) ([]byte, error) { + if override, ok := os.LookupEnv("VESPA_CLI_API_KEY"); ok { + return []byte(override), nil + } return ioutil.ReadFile(c.APIKeyPath(tenantName)) } diff --git a/client/go/cmd/helpers.go b/client/go/cmd/helpers.go index add81334d35..b3a25c577e8 100644 --- a/client/go/cmd/helpers.go +++ b/client/go/cmd/helpers.go @@ -5,10 +5,8 @@ package cmd import ( - "crypto/tls" "encoding/json" "fmt" - "io/ioutil" "log" "os" "path/filepath" @@ -187,23 +185,13 @@ func getTarget() vespa.Target { endpoints := getEndpointsFromEnv() var apiKey []byte = nil - apiKey, err = ioutil.ReadFile(cfg.APIKeyPath(deployment.Application.Tenant)) + apiKey, err = cfg.ReadAPIKey(deployment.Application.Tenant) if !vespa.Auth0AccessTokenEnabled() && endpoints == nil { if err != nil { fatalErrHint(err, "Deployment to cloud requires an API key. Try 'vespa api-key'") } } - privateKeyFile, err := cfg.PrivateKeyPath(deployment.Application) - if err != nil { - fatalErr(err) - return nil - } - certificateFile, err := cfg.CertificatePath(deployment.Application) - if err != nil { - fatalErr(err) - return nil - } - kp, err := tls.LoadX509KeyPair(certificateFile, privateKeyFile) + kp, err := cfg.X509KeyPair(deployment.Application) if err != nil { var msg string if vespa.Auth0AccessTokenEnabled() { @@ -229,9 +217,9 @@ func getTarget() vespa.Target { return vespa.CloudTarget(getApiURL(), deployment, apiKey, vespa.TLSOptions{ - KeyPair: kp, - CertificateFile: certificateFile, - PrivateKeyFile: privateKeyFile, + KeyPair: kp.KeyPair, + CertificateFile: kp.CertificateFile, + PrivateKeyFile: kp.PrivateKeyFile, }, vespa.LogOptions{ Writer: stdout, |