Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

command/server: add dev-tls flag #16421

Merged
merged 16 commits into from Jul 22, 2022
Merged
3 changes: 3 additions & 0 deletions changelog/16421.txt
@@ -0,0 +1,3 @@
```release-note:improvement
command/server: add `-dev-tls` subcommand to create a Vault dev server with generated certificates and private key.
```
81 changes: 74 additions & 7 deletions command/server.go
Expand Up @@ -117,6 +117,8 @@ type ServerCommand struct {
flagLogFormat string
flagRecovery bool
flagDev bool
flagDevTLS bool
flagDevTLSCertDir string
flagDevRootTokenID string
flagDevListenAddr string
flagDevNoStoreToken bool
Expand Down Expand Up @@ -245,6 +247,23 @@ func (c *ServerCommand) Flags() *FlagSets {
"production.",
})

f.BoolVar(&BoolVar{
Name: "dev-tls",
Target: &c.flagDevTLS,
Usage: "Enable TLS development mode. In this mode, Vault runs in-memory and " +
"starts unsealed, with a generated TLS CA, certificate and key. " +
"As the name implies, do not run \"dev-tls\" mode in " +
"production.",
})

f.StringVar(&StringVar{
Name: "dev-tls-cert-dir",
Target: &c.flagDevTLSCertDir,
Default: "",
Completion: complete.PredictDirs("*"),
Hidden: true,
jasonodonnell marked this conversation as resolved.
Show resolved Hide resolved
})

f.StringVar(&StringVar{
Name: "dev-root-token-id",
Target: &c.flagDevRootTokenID,
Expand Down Expand Up @@ -1026,7 +1045,7 @@ func (c *ServerCommand) Run(args []string) int {
}

// Automatically enable dev mode if other dev flags are provided.
if c.flagDevConsul || c.flagDevHA || c.flagDevTransactional || c.flagDevLeasedKV || c.flagDevThreeNode || c.flagDevFourCluster || c.flagDevAutoSeal || c.flagDevKVV1 {
if c.flagDevConsul || c.flagDevHA || c.flagDevTransactional || c.flagDevLeasedKV || c.flagDevThreeNode || c.flagDevFourCluster || c.flagDevAutoSeal || c.flagDevKVV1 || c.flagDevTLS {
c.flagDev = true
}

Expand Down Expand Up @@ -1062,6 +1081,7 @@ func (c *ServerCommand) Run(args []string) int {
// Load the configuration
var config *server.Config
var err error
var certDir string
if c.flagDev {
var devStorageType string
switch {
Expand All @@ -1076,11 +1096,37 @@ func (c *ServerCommand) Run(args []string) int {
default:
devStorageType = "inmem"
}
config, err = server.DevConfig(devStorageType)

if c.flagDevTLS {
if c.flagDevTLSCertDir != "" {
_, err := os.Stat(c.flagDevTLSCertDir)
if err != nil && !os.IsNotExist(err) {
jasonodonnell marked this conversation as resolved.
Show resolved Hide resolved
c.UI.Error(err.Error())
return 1
}

certDir = c.flagDevTLSCertDir
} else {
certDir, err = os.MkdirTemp("", "vault-tls")
jasonodonnell marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
c.UI.Error(err.Error())
return 1
}
}
config, err = server.DevTLSConfig(devStorageType, certDir)

defer os.Remove(fmt.Sprintf("%s/%s", certDir, server.VaultCAFilename))
defer os.Remove(fmt.Sprintf("%s/%s", certDir, server.VaultCertFilename))
defer os.Remove(fmt.Sprintf("%s/%s", certDir, server.VaultKeyFilename))
jasonodonnell marked this conversation as resolved.
Show resolved Hide resolved
} else {
config, err = server.DevConfig(devStorageType)
}

if err != nil {
c.UI.Error(err.Error())
return 1
}

if c.flagDevListenAddr != "" {
config.Listeners[0].Address = c.flagDevListenAddr
}
Expand Down Expand Up @@ -1495,7 +1541,7 @@ func (c *ServerCommand) Run(args []string) int {
}

// If we're in Dev mode, then initialize the core
err = initDevCore(c, &coreConfig, config, core)
err = initDevCore(c, &coreConfig, config, core, certDir)
if err != nil {
c.UI.Error(err.Error())
return 1
Expand Down Expand Up @@ -2442,7 +2488,11 @@ func determineRedirectAddr(c *ServerCommand, coreConfig *vault.CoreConfig, confi
}
}
if coreConfig.RedirectAddr == "" && c.flagDev {
coreConfig.RedirectAddr = fmt.Sprintf("http://%s", config.Listeners[0].Address)
protocol := "http"
if c.flagDevTLS {
protocol = "https"
}
coreConfig.RedirectAddr = fmt.Sprintf("%s://%s", protocol, config.Listeners[0].Address)
}
return retErr
}
Expand Down Expand Up @@ -2604,7 +2654,7 @@ func runListeners(c *ServerCommand, coreConfig *vault.CoreConfig, config *server
return nil
}

func initDevCore(c *ServerCommand, coreConfig *vault.CoreConfig, config *server.Config, core *vault.Core) error {
func initDevCore(c *ServerCommand, coreConfig *vault.CoreConfig, config *server.Config, core *vault.Core, certDir string) error {
if c.flagDev && !c.flagDevSkipInit {

init, err := c.enableDev(core, coreConfig)
Expand Down Expand Up @@ -2655,10 +2705,15 @@ func initDevCore(c *ServerCommand, coreConfig *vault.CoreConfig, config *server.
"token is already authenticated to the CLI, so you can immediately " +
"begin using Vault."))
c.UI.Warn("")
c.UI.Warn("You may need to set the following environment variable:")
c.UI.Warn("You may need to set the following environment variables:")
c.UI.Warn("")

endpointURL := "http://" + config.Listeners[0].Address
protocol := "http://"
if c.flagDevTLS {
protocol = "https://"
}

endpointURL := protocol + config.Listeners[0].Address
if runtime.GOOS == "windows" {
c.UI.Warn("PowerShell:")
c.UI.Warn(fmt.Sprintf(" $env:VAULT_ADDR=\"%s\"", endpointURL))
Expand All @@ -2668,6 +2723,18 @@ func initDevCore(c *ServerCommand, coreConfig *vault.CoreConfig, config *server.
c.UI.Warn(fmt.Sprintf(" $ export VAULT_ADDR='%s'", endpointURL))
}

if c.flagDevTLS {
if runtime.GOOS == "windows" {
c.UI.Warn("PowerShell:")
c.UI.Warn(fmt.Sprintf(" $env:VAULT_CACERT=\"%s/vault-ca.pem\"", certDir))
c.UI.Warn("cmd.exe:")
c.UI.Warn(fmt.Sprintf(" set VAULT_CACERT=%s/vault-ca.pem", certDir))
} else {
c.UI.Warn(fmt.Sprintf(" $ export VAULT_CACERT='%s/vault-ca.pem'", certDir))
}
c.UI.Warn("")
}

// Unseal key is not returned if stored shares is supported
if len(init.SecretShares) > 0 {
c.UI.Warn("")
Expand Down
65 changes: 65 additions & 0 deletions command/server/config.go
Expand Up @@ -22,6 +22,12 @@ import (
"github.com/hashicorp/vault/sdk/helper/consts"
)

const (
VaultCAFilename = "vault-ca.pem"
jasonodonnell marked this conversation as resolved.
Show resolved Hide resolved
VaultCertFilename = "vault-cert.pem"
VaultKeyFilename = "vault-key.pem"
)

var entConfigValidate = func(_ *Config, _ string) []configutil.ConfigError {
return nil
}
Expand Down Expand Up @@ -151,6 +157,65 @@ ui = true
return parsed, nil
}

// DevTLSConfig is a Config that is used for dev tls mode of Vault.
func DevTLSConfig(storageType, certDir string) (*Config, error) {
ca, err := GenerateCA()
if err != nil {
return nil, err
}

cert, key, err := GenerateCert(ca.Template, ca.Signer)
if err != nil {
return nil, err
}

if err := os.WriteFile(fmt.Sprintf("%s/%s", certDir, VaultCAFilename), []byte(ca.PEM), 0o444); err != nil {
return nil, err
}

if err := os.WriteFile(fmt.Sprintf("%s/%s", certDir, VaultCertFilename), []byte(cert), 0o400); err != nil {
return nil, err
}

if err := os.WriteFile(fmt.Sprintf("%s/%s", certDir, VaultKeyFilename), []byte(key), 0o400); err != nil {
return nil, err
}

hclStr := `
disable_mlock = true

listener "tcp" {
address = "[::]:8200"
tls_cert_file = "%s/vault-cert.pem"
tls_key_file = "%s/vault-key.pem"
proxy_protocol_behavior = "allow_authorized"
proxy_protocol_authorized_addrs = "[::]:8200"
}

telemetry {
prometheus_retention_time = "24h"
disable_hostname = true
}
enable_raw_endpoint = true

storage "%s" {
}

ui = true
`

hclStr = fmt.Sprintf(hclStr, certDir, certDir, storageType)
parsed, err := ParseConfig(hclStr, "")
if err != nil {
return nil, err
}

return parsed, nil
}

func CleanupTLS(dir string) {
}
jasonodonnell marked this conversation as resolved.
Show resolved Hide resolved

// Storage is the underlying storage configuration for the server.
type Storage struct {
Type string
Expand Down