aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Marius Venstad <jonmv@users.noreply.github.com>2022-08-29 18:27:23 +0200
committerGitHub <noreply@github.com>2022-08-29 18:27:23 +0200
commit382bad5ed1a824b679b9f755d1a0e97f1a7f0977 (patch)
tree6fb2fc7e5b4024f6a5dc3e7df2306b8a7c05842b
parentefa3ed8aac856672ef206f2d7f35ba5bdb32ceba (diff)
parentf27efcd592228aea4711a5bed86999578a5987b0 (diff)
Merge pull request #23831 from vespa-engine/mpolden/athenz-cert-from-env
Support specifying Athenz key pair through environment
-rw-r--r--client/go/cmd/cert.go14
-rw-r--r--client/go/cmd/config.go64
-rw-r--r--client/go/cmd/prod_test.go4
-rw-r--r--client/go/cmd/root.go43
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.