Skip to content

Commit

Permalink
command/server: add dev-tls flag (#16421)
Browse files Browse the repository at this point in the history
* command/server: add dev-tls flag

* Add website documentation

* changelog

* Lower file permissions

* Update cert gen per review

* Add dev-tls-cert-dir flag and cert clean up

* fmt

* Update cert generation per review

* Remove unused function

* Add better error messages

* Log errors in cleanup, fix directory not existing bug

* Remove hidden flag from -dev-tls-cert-dir

* Add usage

* Update 16421.txt

* Update variable names for files

* Remove directory on cleanup
  • Loading branch information
jasonodonnell committed Jul 22, 2022
1 parent 8581f33 commit 62cc652
Show file tree
Hide file tree
Showing 5 changed files with 329 additions and 7 deletions.
3 changes: 3 additions & 0 deletions changelog/16421.txt
@@ -0,0 +1,3 @@
```release-note:improvement
command/server: add `-dev-tls` and `-dev-tls-cert-dir` subcommands to create a Vault dev server with generated certificates and private key.
```
103 changes: 96 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: "",
Usage: "Directory where generated TLS files are created if `-dev-tls` is " +
"specified. If left unset, files are generated in a temporary directory.",
})

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,59 @@ 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 {
c.UI.Error(err.Error())
return 1
}

certDir = c.flagDevTLSCertDir
} else {
certDir, err = os.MkdirTemp("", "vault-tls")
if err != nil {
c.UI.Error(err.Error())
return 1
}
}
config, err = server.DevTLSConfig(devStorageType, certDir)

defer func() {
err := os.Remove(fmt.Sprintf("%s/%s", certDir, server.VaultDevCAFilename))
if err != nil {
c.UI.Error(err.Error())
}

err = os.Remove(fmt.Sprintf("%s/%s", certDir, server.VaultDevCertFilename))
if err != nil {
c.UI.Error(err.Error())
}

err = os.Remove(fmt.Sprintf("%s/%s", certDir, server.VaultDevKeyFilename))
if err != nil {
c.UI.Error(err.Error())
}

// Only delete temp directories we made.
if c.flagDevTLSCertDir == "" {
err = os.Remove(certDir)
if err != nil {
c.UI.Error(err.Error())
}
}
}()

} 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 +1563,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 +2510,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 +2676,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 +2727,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 +2745,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
62 changes: 62 additions & 0 deletions command/server/config.go
Expand Up @@ -22,6 +22,12 @@ import (
"github.com/hashicorp/vault/sdk/helper/consts"
)

const (
VaultDevCAFilename = "vault-ca.pem"
VaultDevCertFilename = "vault-cert.pem"
VaultDevKeyFilename = "vault-key.pem"
)

var entConfigValidate = func(_ *Config, _ string) []configutil.ConfigError {
return nil
}
Expand Down Expand Up @@ -151,6 +157,62 @@ 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, VaultDevCAFilename), []byte(ca.PEM), 0o444); err != nil {
return nil, err
}

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

if err := os.WriteFile(fmt.Sprintf("%s/%s", certDir, VaultDevKeyFilename), []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
}

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

0 comments on commit 62cc652

Please sign in to comment.