diff options
author | Jon Marius Venstad <jonmv@users.noreply.github.com> | 2022-08-29 18:27:23 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-29 18:27:23 +0200 |
commit | 382bad5ed1a824b679b9f755d1a0e97f1a7f0977 (patch) | |
tree | 6fb2fc7e5b4024f6a5dc3e7df2306b8a7c05842b /client/go | |
parent | efa3ed8aac856672ef206f2d7f35ba5bdb32ceba (diff) | |
parent | f27efcd592228aea4711a5bed86999578a5987b0 (diff) |
Merge pull request #23831 from vespa-engine/mpolden/athenz-cert-from-env
Support specifying Athenz key pair through environment
Diffstat (limited to 'client/go')
-rw-r--r-- | client/go/cmd/cert.go | 14 | ||||
-rw-r--r-- | client/go/cmd/config.go | 64 | ||||
-rw-r--r-- | client/go/cmd/prod_test.go | 4 | ||||
-rw-r--r-- | client/go/cmd/root.go | 43 |
4 files changed, 65 insertions, 60 deletions
diff --git a/client/go/cmd/cert.go b/client/go/cmd/cert.go index ac4d5085782..3b16ec4d342 100644 --- a/client/go/cmd/cert.go +++ b/client/go/cmd/cert.go @@ -100,11 +100,15 @@ func doCert(cli *CLI, overwriteCertificate, noApplicationPackage bool, args []st return err } } - privateKeyFile, err := cli.config.privateKeyPath(app) + targetType, err := cli.config.targetType() if err != nil { return err } - certificateFile, err := cli.config.certificatePath(app) + privateKeyFile, err := cli.config.privateKeyPath(app, targetType) + if err != nil { + return err + } + certificateFile, err := cli.config.certificatePath(app, targetType) if err != nil { return err } @@ -167,7 +171,11 @@ func doCertAdd(cli *CLI, overwriteCertificate bool, args []string) error { if err != nil { return err } - certificateFile, err := cli.config.certificatePath(app) + targetType, err := cli.config.targetType() + if err != nil { + return err + } + certificateFile, err := cli.config.certificatePath(app, targetType) if err != nil { return err } diff --git a/client/go/cmd/config.go b/client/go/cmd/config.go index 8f81f8e359f..acca161727b 100644 --- a/client/go/cmd/config.go +++ b/client/go/cmd/config.go @@ -6,6 +6,7 @@ package cmd import ( "crypto/tls" + "crypto/x509" "fmt" "log" "os" @@ -298,6 +299,14 @@ func loadConfigFrom(dir string, environment map[string]string, flags map[string] return c, nil } +func athenzPath(filename string) (string, error) { + userHome, err := os.UserHomeDir() + if err != nil { + return "", err + } + return filepath.Join(userHome, ".athenz", filename), nil +} + func (c *Config) loadLocalConfigFrom(parent string) error { home := filepath.Join(parent, ".vespa") _, err := os.Stat(home) @@ -383,44 +392,69 @@ func (c *Config) deploymentIn(system vespa.System) (vespa.Deployment, error) { return vespa.Deployment{System: system, Application: app, Zone: zone}, nil } -func (c *Config) certificatePath(app vespa.ApplicationID) (string, error) { +func (c *Config) certificatePath(app vespa.ApplicationID, targetType string) (string, error) { if override, ok := c.environment["VESPA_CLI_DATA_PLANE_CERT_FILE"]; ok { return override, nil } + if targetType == vespa.TargetHosted { + return athenzPath("cert") + } return c.applicationFilePath(app, "data-plane-public-cert.pem") } -func (c *Config) privateKeyPath(app vespa.ApplicationID) (string, error) { +func (c *Config) privateKeyPath(app vespa.ApplicationID, targetType string) (string, error) { if override, ok := c.environment["VESPA_CLI_DATA_PLANE_KEY_FILE"]; ok { return override, nil } + if targetType == vespa.TargetHosted { + return athenzPath("key") + } return c.applicationFilePath(app, "data-plane-private-key.pem") } -func (c *Config) x509KeyPair(app vespa.ApplicationID) (KeyPair, error) { +func (c *Config) x509KeyPair(app vespa.ApplicationID, targetType string) (KeyPair, error) { cert, certOk := c.environment["VESPA_CLI_DATA_PLANE_CERT"] key, keyOk := c.environment["VESPA_CLI_DATA_PLANE_KEY"] + var ( + kp tls.Certificate + err error + certFile string + keyFile string + ) 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 + kp, err = tls.X509KeyPair([]byte(cert), []byte(key)) + } else { + keyFile, err = c.privateKeyPath(app, targetType) + if err != nil { + return KeyPair{}, err + } + certFile, err = c.certificatePath(app, targetType) + if err != nil { + return KeyPair{}, err + } + kp, err = tls.LoadX509KeyPair(certFile, keyFile) } - certificateFile, err := c.certificatePath(app) if err != nil { return KeyPair{}, err } - kp, err := tls.LoadX509KeyPair(certificateFile, privateKeyFile) - if err != nil { - return KeyPair{}, err + if targetType == vespa.TargetHosted { + cert, err := x509.ParseCertificate(kp.Certificate[0]) + if err != nil { + return KeyPair{}, err + } + now := time.Now() + expiredAt := cert.NotAfter + if expiredAt.Before(now) { + delta := now.Sub(expiredAt).Truncate(time.Second) + return KeyPair{}, fmt.Errorf("certificate %s expired at %s (%s ago)", certFile, cert.NotAfter, delta) + } + return KeyPair{KeyPair: kp, CertificateFile: certFile, PrivateKeyFile: keyFile}, nil } return KeyPair{ KeyPair: kp, - CertificateFile: certificateFile, - PrivateKeyFile: privateKeyFile, + CertificateFile: certFile, + PrivateKeyFile: keyFile, }, nil } diff --git a/client/go/cmd/prod_test.go b/client/go/cmd/prod_test.go index 9ccc39e02a1..3c1799701a3 100644 --- a/client/go/cmd/prod_test.go +++ b/client/go/cmd/prod_test.go @@ -162,12 +162,12 @@ func TestProdSubmit(t *testing.T) { assert.Nil(t, cli.Run("auth", "cert", pkgDir)) // Remove certificate as it's not required for submission (but it must be part of the application package) - if path, err := cli.config.privateKeyPath(app); err == nil { + if path, err := cli.config.privateKeyPath(app, vespa.TargetCloud); err == nil { os.RemoveAll(path) } else { require.Nil(t, err) } - if path, err := cli.config.certificatePath(app); err == nil { + if path, err := cli.config.certificatePath(app, vespa.TargetCloud); err == nil { os.RemoveAll(path) } else { require.Nil(t, err) diff --git a/client/go/cmd/root.go b/client/go/cmd/root.go index e2f03cbc7ce..c8c49efbe07 100644 --- a/client/go/cmd/root.go +++ b/client/go/cmd/root.go @@ -2,15 +2,12 @@ package cmd import ( - "crypto/tls" - "crypto/x509" "encoding/json" "fmt" "io" "log" "os" "os/exec" - "path/filepath" "strings" "time" @@ -332,7 +329,7 @@ func (c *CLI) createCloudTarget(targetType string, opts targetOptions) (vespa.Ta authConfigPath = c.config.authConfigPath() deploymentTLSOptions = vespa.TLSOptions{} if !opts.noCertificate { - kp, err := c.config.x509KeyPair(deployment.Application) + kp, err := c.config.x509KeyPair(deployment.Application, targetType) if err != nil { return nil, errHint(err, "Deployment to cloud requires a certificate. Try 'vespa auth cert'") } @@ -343,9 +340,9 @@ func (c *CLI) createCloudTarget(targetType string, opts targetOptions) (vespa.Ta } } case vespa.TargetHosted: - kp, err := athenzKeyPair() + kp, err := c.config.x509KeyPair(deployment.Application, targetType) if err != nil { - return nil, err + return nil, errHint(err, "Deployment to hosted requires an Athenz certificate", "Try renewing certificate with 'athenz-user-cert'") } apiTLSOptions = vespa.TLSOptions{ KeyPair: kp.KeyPair, @@ -487,40 +484,6 @@ func isTerminal(w io.Writer) bool { return false } -func athenzPath(filename string) (string, error) { - userHome, err := os.UserHomeDir() - if err != nil { - return "", err - } - return filepath.Join(userHome, ".athenz", filename), nil -} - -func athenzKeyPair() (KeyPair, error) { - certFile, err := athenzPath("cert") - if err != nil { - return KeyPair{}, err - } - keyFile, err := athenzPath("key") - if err != nil { - return KeyPair{}, err - } - kp, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return KeyPair{}, err - } - cert, err := x509.ParseCertificate(kp.Certificate[0]) - if err != nil { - return KeyPair{}, err - } - now := time.Now() - expiredAt := cert.NotAfter - if expiredAt.Before(now) { - delta := now.Sub(expiredAt).Truncate(time.Second) - return KeyPair{}, errHint(fmt.Errorf("certificate %s expired at %s (%s ago)", certFile, cert.NotAfter, delta), "Try renewing certificate with 'athenz-user-cert'") - } - return KeyPair{KeyPair: kp, CertificateFile: certFile, PrivateKeyFile: keyFile}, nil -} - // applicationPackageFrom returns an application loaded from args. If args is empty, the application package is loaded // from the working directory. If requirePackaging is true, the application package is required to be packaged with mvn // package. |