aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHÃ¥kon Hallingstad <hakon@yahooinc.com>2022-02-03 10:53:56 +0100
committerGitHub <noreply@github.com>2022-02-03 10:53:56 +0100
commit756ab78df5287519fa220982685d6335f19a54e1 (patch)
tree12f1a22756c2d3803f79305a349e2b6358f4be81
parent62cfcc941317c21aefaf44862c4f63173974059f (diff)
parent10446fafad7096e6f890c74792f2f343665bc32e (diff)
Merge pull request #21048 from vespa-engine/mpolden/keys-in-env
Support passing keys through environment
-rw-r--r--client/go/cmd/api_key.go24
-rw-r--r--client/go/cmd/cert.go32
-rw-r--r--client/go/cmd/config.go40
-rw-r--r--client/go/cmd/helpers.go22
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,