aboutsummaryrefslogtreecommitdiffstats
path: root/client/go/internal/cli/cmd/api_key.go
blob: 7c187aa5da765cd351ff45ae6f922c3034193008 (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
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
// vespa api-key command
// Author: mpolden
package cmd

import (
	"fmt"
	"log"
	"os"

	"github.com/fatih/color"
	"github.com/spf13/cobra"
	"github.com/vespa-engine/vespa/client/go/internal/util"
	"github.com/vespa-engine/vespa/client/go/internal/vespa"
)

func newAPIKeyCmd(cli *CLI) *cobra.Command {
	var overwriteKey bool
	cmd := &cobra.Command{
		Use:   "api-key",
		Short: "Create a new user API key for control-plane authentication with Vespa Cloud",
		Long: `Create a new user API key for control-plane 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.

Example of setting the key in-line:

    export VESPA_CLI_API_KEY="my api key"

Example of loading the key from a custom path:

    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.

Read more in https://cloud.vespa.ai/en/security/guide`,
		Example:           "$ vespa auth api-key -a my-tenant.my-app.my-instance",
		DisableAutoGenTag: true,
		SilenceUsage:      true,
		Args:              cobra.ExactArgs(0),
		RunE: func(cmd *cobra.Command, args []string) error {
			return doApiKey(cli, overwriteKey, args)
		},
	}
	cmd.Flags().BoolVarP(&overwriteKey, "force", "f", false, "Force overwrite of existing API key")
	cmd.MarkPersistentFlagRequired(applicationFlag)
	return cmd
}

func doApiKey(cli *CLI, overwriteKey bool, args []string) error {
	targetType, err := cli.targetType(true)
	if err != nil {
		return err
	}
	app, err := cli.config.application()
	if err != nil {
		return err
	}
	system, err := cli.system(targetType.name)
	if err != nil {
		return err
	}
	apiKeyFile := cli.config.apiKeyPath(app.Tenant)
	if util.PathExists(apiKeyFile) && !overwriteKey {
		err := fmt.Errorf("refusing to overwrite %s", apiKeyFile)
		cli.printErr(err, "Use -f to overwrite it")
		printPublicKey(system, apiKeyFile, app.Tenant)
		return ErrCLI{error: err, quiet: true}
	}
	apiKey, err := vespa.CreateAPIKey()
	if err != nil {
		return fmt.Errorf("could not create api key: %w", err)
	}
	if err := os.WriteFile(apiKeyFile, apiKey, 0600); err == nil {
		cli.printSuccess("API private key written to ", apiKeyFile)
		return printPublicKey(system, apiKeyFile, app.Tenant)
	} else {
		return fmt.Errorf("failed to write: %s: %w", apiKeyFile, err)
	}
}

func printPublicKey(system vespa.System, apiKeyFile, tenant string) error {
	pemKeyData, err := os.ReadFile(apiKeyFile)
	if err != nil {
		return fmt.Errorf("failed to read: %s: %w", apiKeyFile, err)
	}
	key, err := vespa.ECPrivateKeyFrom(pemKeyData)
	if err != nil {
		return fmt.Errorf("failed to load key: %w", err)
	}
	pemPublicKey, err := vespa.PEMPublicKeyFrom(key)
	if err != nil {
		return fmt.Errorf("failed to extract public key: %w", err)
	}
	fingerprint, err := vespa.FingerprintMD5(pemPublicKey)
	if err != nil {
		return fmt.Errorf("failed to extract fingerprint: %w", err)
	}
	log.Printf("\nThis is your public key:\n%s", color.GreenString(string(pemPublicKey)))
	log.Printf("Its fingerprint is:\n%s\n", color.CyanString(fingerprint))
	log.Print("\nTo use this key in Vespa Cloud click 'Add custom key' at")
	log.Printf(color.CyanString("%s/tenant/%s/account/keys"), system.ConsoleURL, tenant)
	log.Print("and paste the entire public key including the BEGIN and END lines.")
	return nil
}