summaryrefslogtreecommitdiffstats
path: root/client/go/cmd/cert.go
blob: 97147d1eec09eb7879c64cd72215b21a29e84267 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// vespa cert command
// Author: mpolden
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"
)

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)
	deprecatedCertCmd.Flags().BoolVarP(&overwriteCertificate, "force", "f", false, "Force overwrite of existing certificate and private key")
	deprecatedCertCmd.MarkPersistentFlagRequired(applicationFlag)
}

func certExample() string {
	return "$ vespa auth cert -a my-tenant.my-app.my-instance"
}

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,
	SilenceUsage:      true,
	Args:              cobra.MaximumNArgs(1),
	RunE:              doCert,
}

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,
	SilenceUsage:      true,
	Args:              cobra.MaximumNArgs(1),
	Deprecated:        "use 'vespa auth cert' instead",
	Hidden:            true,
	RunE:              doCert,
}

func doCert(_ *cobra.Command, args []string) error {
	app, err := getApplication()
	if err != nil {
		return err
	}
	pkg, err := vespa.FindApplicationPackage(applicationSource(args), false)
	if err != nil {
		return err
	}
	cfg, err := LoadConfig()
	if err != nil {
		return err
	}
	privateKeyFile, err := cfg.PrivateKeyPath(app)
	if err != nil {
		return err
	}
	certificateFile, err := cfg.CertificatePath(app)
	if err != nil {
		return err
	}

	if !overwriteCertificate {
		hint := "Use -f flag to force overwriting"
		if pkg.HasCertificate() {
			return errHint(fmt.Errorf("application package %s already contains a certificate", pkg.Path), hint)
		}
		if util.PathExists(privateKeyFile) {
			return errHint(fmt.Errorf("private key %s already exists", color.Cyan(privateKeyFile)), hint)
		}
		if util.PathExists(certificateFile) {
			return errHint(fmt.Errorf("certificate %s already exists", color.Cyan(certificateFile)), hint)
		}
	}
	if pkg.IsZip() {
		hint := "Try running 'mvn clean' before 'vespa auth cert', and then 'mvn package'"
		return errHint(fmt.Errorf("cannot add certificate to compressed application package %s", pkg.Path), hint)
	}

	keyPair, err := vespa.CreateKeyPair()
	if err != nil {
		return err
	}
	pkgCertificateFile := filepath.Join(pkg.Path, "security", "clients.pem")
	if err := os.MkdirAll(filepath.Dir(pkgCertificateFile), 0755); err != nil {
		return fmt.Errorf("could not create security directory: %w", err)
	}
	if err := keyPair.WriteCertificateFile(pkgCertificateFile, overwriteCertificate); err != nil {
		return fmt.Errorf("could not write certificate to application package: %w", err)
	}
	if err := keyPair.WriteCertificateFile(certificateFile, overwriteCertificate); err != nil {
		return fmt.Errorf("could not write certificate: %w", err)
	}
	if err := keyPair.WritePrivateKeyFile(privateKeyFile, overwriteCertificate); err != nil {
		return fmt.Errorf("could not write private key: %w", err)
	}
	printSuccess("Certificate written to ", color.Cyan(pkgCertificateFile))
	printSuccess("Certificate written to ", color.Cyan(certificateFile))
	printSuccess("Private key written to ", color.Cyan(privateKeyFile))
	return nil
}