aboutsummaryrefslogtreecommitdiffstats
path: root/client/go/internal
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2024-06-12 11:55:11 +0200
committerMartin Polden <mpolden@mpolden.no>2024-06-12 12:01:57 +0200
commit885635cb82a648f04c96aad63e10c86ba3fe67f6 (patch)
treedc6d83f59da4e12b1d45a00f15c92fc90456000d /client/go/internal
parent17f9406eb10982c8201ace9de673c0d53af98ed0 (diff)
Detect missing or mismatching certificate
Diffstat (limited to 'client/go/internal')
-rw-r--r--client/go/internal/cli/cmd/cert.go36
-rw-r--r--client/go/internal/cli/cmd/deploy.go2
-rw-r--r--client/go/internal/cli/cmd/deploy_test.go15
-rw-r--r--client/go/internal/cli/cmd/prod.go2
-rw-r--r--client/go/internal/vespa/application.go9
5 files changed, 49 insertions, 15 deletions
diff --git a/client/go/internal/cli/cmd/cert.go b/client/go/internal/cli/cmd/cert.go
index 14e4861cec3..24b414443a0 100644
--- a/client/go/internal/cli/cmd/cert.go
+++ b/client/go/internal/cli/cmd/cert.go
@@ -160,10 +160,10 @@ func doCertAdd(cli *CLI, overwriteCertificate bool, args []string) error {
if pkg.HasCertificate() && !overwriteCertificate {
return errHint(fmt.Errorf("application package '%s' already contains a certificate", pkg.Path), "Use -f flag to force overwriting")
}
- return maybeCopyCertificate(true, false, cli, target, pkg)
+ return requireCertificate(true, false, cli, target, pkg)
}
-func maybeCopyCertificate(force, ignoreZip bool, cli *CLI, target vespa.Target, pkg vespa.ApplicationPackage) error {
+func requireCertificate(force, ignoreZip bool, cli *CLI, target vespa.Target, pkg vespa.ApplicationPackage) error {
if pkg.IsZip() {
if ignoreZip {
cli.printWarning("Cannot verify existence of "+color.CyanString("security/clients.pem")+" since '"+pkg.Path+"' is compressed",
@@ -175,10 +175,28 @@ func maybeCopyCertificate(force, ignoreZip bool, cli *CLI, target vespa.Target,
return errHint(fmt.Errorf("cannot add certificate to compressed application package: '%s'", pkg.Path), hint)
}
}
+ tlsOptions, err := cli.config.readTLSOptions(target.Deployment().Application, target.Type())
+ if err != nil {
+ return err
+ }
+ if len(tlsOptions.CertificatePEM) == 0 {
+ return errHint(fmt.Errorf("no certificate exists for %s", target.Deployment().Application.String()), "Try (re)creating the certificate with 'vespa auth cert'")
+ }
if force {
- return copyCertificate(cli, target, pkg)
+ return copyCertificate(tlsOptions, cli, pkg)
}
if pkg.HasCertificate() {
+ matches, err := pkg.HasMatchingCertificate(tlsOptions.CertificatePEM)
+ if err != nil {
+ return err
+ }
+ if !matches {
+ return errHint(fmt.Errorf("certificate in %s does not match the stored key pair for %s",
+ filepath.Join("security", "clients.pem"),
+ target.Deployment().Application.String()),
+ "If this application was deployed using a different application ID in the past, the matching key pair may be stored under a different ID in "+cli.config.homeDir,
+ "Specify the matching application with --application, or add the current certificate to the package using --add-cert")
+ }
return nil
}
if cli.isTerminal() {
@@ -188,7 +206,7 @@ func maybeCopyCertificate(force, ignoreZip bool, cli *CLI, target vespa.Target,
return err
}
if ok {
- return copyCertificate(cli, target, pkg)
+ return copyCertificate(tlsOptions, cli, pkg)
}
}
return errHint(fmt.Errorf("deployment to Vespa Cloud requires certificate in application package"),
@@ -196,15 +214,7 @@ func maybeCopyCertificate(force, ignoreZip bool, cli *CLI, target vespa.Target,
"Pass --add-cert to use the certificate of the current application")
}
-func copyCertificate(cli *CLI, target vespa.Target, pkg vespa.ApplicationPackage) error {
- tlsOptions, err := cli.config.readTLSOptions(target.Deployment().Application, target.Type())
- if err != nil {
- return err
- }
- hint := "Try generating the certificate with 'vespa auth cert'"
- if tlsOptions.CertificateFile == "" {
- return errHint(fmt.Errorf("no certificate exists for "+target.Deployment().Application.String()), hint)
- }
+func copyCertificate(tlsOptions vespa.TLSOptions, cli *CLI, pkg vespa.ApplicationPackage) error {
data, err := os.ReadFile(tlsOptions.CertificateFile)
if err != nil {
return errHint(fmt.Errorf("could not read certificate file: %w", err))
diff --git a/client/go/internal/cli/cmd/deploy.go b/client/go/internal/cli/cmd/deploy.go
index 718e2b27b2b..c9f08655759 100644
--- a/client/go/internal/cli/cmd/deploy.go
+++ b/client/go/internal/cli/cmd/deploy.go
@@ -68,7 +68,7 @@ $ vespa deploy -t cloud -z perf.aws-us-east-1c`,
opts.Version = version
}
if target.Type() == vespa.TargetCloud {
- if err := maybeCopyCertificate(copyCert, true, cli, target, pkg); err != nil {
+ if err := requireCertificate(copyCert, true, cli, target, pkg); err != nil {
return err
}
}
diff --git a/client/go/internal/cli/cmd/deploy_test.go b/client/go/internal/cli/cmd/deploy_test.go
index 8d1c202e554..b8a684cf1cf 100644
--- a/client/go/internal/cli/cmd/deploy_test.go
+++ b/client/go/internal/cli/cmd/deploy_test.go
@@ -62,6 +62,21 @@ Hint: Pass --add-cert to use the certificate of the current application
buf.WriteString("y\n")
require.Nil(t, cli.Run("deploy", "--add-cert=false", "--wait=0", pkgDir2))
assert.Contains(t, stdout.String(), "Success: Triggered deployment")
+
+ // Missing application certificate is detected
+ stderr.Reset()
+ require.NotNil(t, cli.Run("deploy", "--application=t1.a2.i2", pkgDir2))
+ assert.Equal(t, "Error: no certificate exists for t1.a2.i2\nHint: Try (re)creating the certificate with 'vespa auth cert'\n", stderr.String())
+
+ // Mismatching certificate is detected
+ stdout.Reset()
+ stderr.Reset()
+ assert.Nil(t, cli.Run("auth", "cert", "--application=t1.a1.i1", "-f", "--no-add"))
+ require.NotNil(t, cli.Run("deploy", "--application=t1.a1.i1", pkgDir2))
+ assert.Equal(t, `Error: certificate in security/clients.pem does not match the stored key pair for t1.a1.i1
+Hint: If this application was deployed using a different application ID in the past, the matching key pair may be stored under a different ID in `+
+ cli.config.homeDir+"\nHint: Specify the matching application with --application, or add the current certificate to the package using --add-cert\n",
+ stderr.String())
}
func TestDeployCloudFastWait(t *testing.T) {
diff --git a/client/go/internal/cli/cmd/prod.go b/client/go/internal/cli/cmd/prod.go
index 08d4086719d..139e4690ed2 100644
--- a/client/go/internal/cli/cmd/prod.go
+++ b/client/go/internal/cli/cmd/prod.go
@@ -154,7 +154,7 @@ $ vespa prod deploy`,
if err := verifyTests(cli, pkg); err != nil {
return err
}
- if err := maybeCopyCertificate(options.copyCert, true, cli, target, pkg); err != nil {
+ if err := requireCertificate(options.copyCert, true, cli, target, pkg); err != nil {
return err
}
deployment := vespa.DeploymentOptions{ApplicationPackage: pkg, Target: target}
diff --git a/client/go/internal/vespa/application.go b/client/go/internal/vespa/application.go
index 618d23b6bab..d499006c982 100644
--- a/client/go/internal/vespa/application.go
+++ b/client/go/internal/vespa/application.go
@@ -3,6 +3,7 @@ package vespa
import (
"archive/zip"
+ "bytes"
"errors"
"fmt"
"io"
@@ -21,6 +22,14 @@ type ApplicationPackage struct {
func (ap *ApplicationPackage) HasCertificate() bool { return ap.hasFile("security", "clients.pem") }
+func (ap *ApplicationPackage) HasMatchingCertificate(certificatePEM []byte) (bool, error) {
+ clientsPEM, err := os.ReadFile(filepath.Join(ap.Path, "security", "clients.pem"))
+ if err != nil {
+ return false, err
+ }
+ return bytes.Equal(clientsPEM, certificatePEM), nil
+}
+
func (ap *ApplicationPackage) HasDeploymentSpec() bool { return ap.hasFile("deployment.xml", "") }
func (ap *ApplicationPackage) hasFile(pathSegment ...string) bool {