mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 19:17:58 +00:00
Add a -dev-three-node option for devs. (#3081)
This commit is contained in:
@@ -3,17 +3,20 @@ package command
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
@@ -35,6 +38,7 @@ import (
|
||||
"github.com/hashicorp/vault/helper/logformat"
|
||||
"github.com/hashicorp/vault/helper/mlock"
|
||||
"github.com/hashicorp/vault/helper/parseutil"
|
||||
"github.com/hashicorp/vault/helper/reload"
|
||||
vaulthttp "github.com/hashicorp/vault/http"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/meta"
|
||||
@@ -56,16 +60,17 @@ type ServerCommand struct {
|
||||
|
||||
meta.Meta
|
||||
|
||||
logger log.Logger
|
||||
logGate *gatedwriter.Writer
|
||||
logger log.Logger
|
||||
|
||||
cleanupGuard sync.Once
|
||||
|
||||
reloadFuncsLock *sync.RWMutex
|
||||
reloadFuncs *map[string][]vault.ReloadFunc
|
||||
reloadFuncs *map[string][]reload.ReloadFunc
|
||||
}
|
||||
|
||||
func (c *ServerCommand) Run(args []string) int {
|
||||
var dev, verifyOnly, devHA, devTransactional, devLeasedGeneric bool
|
||||
var dev, verifyOnly, devHA, devTransactional, devLeasedGeneric, devThreeNode bool
|
||||
var configPath []string
|
||||
var logLevel, devRootTokenID, devListenAddress string
|
||||
flags := c.Meta.FlagSet("server", meta.FlagSetDefault)
|
||||
@@ -77,6 +82,7 @@ func (c *ServerCommand) Run(args []string) int {
|
||||
flags.BoolVar(&devHA, "dev-ha", false, "")
|
||||
flags.BoolVar(&devTransactional, "dev-transactional", false, "")
|
||||
flags.BoolVar(&devLeasedGeneric, "dev-leased-generic", false, "")
|
||||
flags.BoolVar(&devThreeNode, "dev-three-node", false, "")
|
||||
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
||||
flags.Var((*sliceflag.StringFlag)(&configPath), "config", "config")
|
||||
if err := flags.Parse(args); err != nil {
|
||||
@@ -85,7 +91,7 @@ func (c *ServerCommand) Run(args []string) int {
|
||||
|
||||
// Create a logger. We wrap it in a gated writer so that it doesn't
|
||||
// start logging too early.
|
||||
logGate := &gatedwriter.Writer{Writer: colorable.NewColorable(os.Stderr)}
|
||||
c.logGate = &gatedwriter.Writer{Writer: colorable.NewColorable(os.Stderr)}
|
||||
var level int
|
||||
logLevel = strings.ToLower(strings.TrimSpace(logLevel))
|
||||
switch logLevel {
|
||||
@@ -112,9 +118,9 @@ func (c *ServerCommand) Run(args []string) int {
|
||||
}
|
||||
switch strings.ToLower(logFormat) {
|
||||
case "vault", "vault_json", "vault-json", "vaultjson", "json", "":
|
||||
c.logger = logformat.NewVaultLoggerWithWriter(logGate, level)
|
||||
c.logger = logformat.NewVaultLoggerWithWriter(c.logGate, level)
|
||||
default:
|
||||
c.logger = log.NewLogger(logGate, "vault")
|
||||
c.logger = log.NewLogger(c.logGate, "vault")
|
||||
c.logger.SetLevel(level)
|
||||
}
|
||||
grpclog.SetLogger(&grpclogFaker{
|
||||
@@ -129,7 +135,7 @@ func (c *ServerCommand) Run(args []string) int {
|
||||
devListenAddress = os.Getenv("VAULT_DEV_LISTEN_ADDRESS")
|
||||
}
|
||||
|
||||
if devHA || devTransactional || devLeasedGeneric {
|
||||
if devHA || devTransactional || devLeasedGeneric || devThreeNode {
|
||||
dev = true
|
||||
}
|
||||
|
||||
@@ -250,6 +256,10 @@ func (c *ServerCommand) Run(args []string) int {
|
||||
}
|
||||
}
|
||||
|
||||
if devThreeNode {
|
||||
return c.enableThreeNodeDevCluster(coreConfig, info, infoKeys, devListenAddress)
|
||||
}
|
||||
|
||||
var disableClustering bool
|
||||
|
||||
// Initialize the separate HA storage backend, if it exists
|
||||
@@ -422,7 +432,7 @@ CLUSTER_SYNTHESIS_COMPLETE:
|
||||
c.reloadFuncsLock.Lock()
|
||||
lns := make([]net.Listener, 0, len(config.Listeners))
|
||||
for i, lnConfig := range config.Listeners {
|
||||
ln, props, reloadFunc, err := server.NewListener(lnConfig.Type, lnConfig.Config, logGate)
|
||||
ln, props, reloadFunc, err := server.NewListener(lnConfig.Type, lnConfig.Config, c.logGate)
|
||||
if err != nil {
|
||||
c.Ui.Output(fmt.Sprintf(
|
||||
"Error initializing listener of type %s: %s",
|
||||
@@ -567,7 +577,7 @@ CLUSTER_SYNTHESIS_COMPLETE:
|
||||
|
||||
// If we're in Dev mode, then initialize the core
|
||||
if dev {
|
||||
init, err := c.enableDev(core, devRootTokenID)
|
||||
init, err := c.enableDev(core, coreConfig)
|
||||
if err != nil {
|
||||
c.Ui.Output(fmt.Sprintf(
|
||||
"Error initializing Dev mode: %s", err))
|
||||
@@ -618,7 +628,7 @@ CLUSTER_SYNTHESIS_COMPLETE:
|
||||
c.Ui.Output("==> Vault server started! Log data will stream in below:\n")
|
||||
|
||||
// Release the log gate.
|
||||
logGate.Flush()
|
||||
c.logGate.Flush()
|
||||
|
||||
// Wait for shutdown
|
||||
shutdownTriggered := false
|
||||
@@ -642,7 +652,7 @@ CLUSTER_SYNTHESIS_COMPLETE:
|
||||
|
||||
case <-c.SighupCh:
|
||||
c.Ui.Output("==> Vault reload triggered")
|
||||
if err := c.Reload(configPath); err != nil {
|
||||
if err := c.Reload(c.reloadFuncsLock, c.reloadFuncs, configPath); err != nil {
|
||||
c.Ui.Output(fmt.Sprintf("Error(s) were encountered during reload: %s", err))
|
||||
}
|
||||
}
|
||||
@@ -653,7 +663,7 @@ CLUSTER_SYNTHESIS_COMPLETE:
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *ServerCommand) enableDev(core *vault.Core, rootTokenID string) (*vault.InitResult, error) {
|
||||
func (c *ServerCommand) enableDev(core *vault.Core, coreConfig *vault.CoreConfig) (*vault.InitResult, error) {
|
||||
// Initialize it with a basic single key
|
||||
init, err := core.Initialize(&vault.InitParams{
|
||||
BarrierConfig: &vault.SealConfig{
|
||||
@@ -700,14 +710,14 @@ func (c *ServerCommand) enableDev(core *vault.Core, rootTokenID string) (*vault.
|
||||
}
|
||||
}
|
||||
|
||||
if rootTokenID != "" {
|
||||
if coreConfig.DevToken != "" {
|
||||
req := &logical.Request{
|
||||
ID: "dev-gen-root",
|
||||
Operation: logical.UpdateOperation,
|
||||
ClientToken: init.RootToken,
|
||||
Path: "auth/token/create",
|
||||
Data: map[string]interface{}{
|
||||
"id": rootTokenID,
|
||||
"id": coreConfig.DevToken,
|
||||
"policies": []string{"root"},
|
||||
"no_parent": true,
|
||||
"no_default_policy": true,
|
||||
@@ -715,13 +725,13 @@ func (c *ServerCommand) enableDev(core *vault.Core, rootTokenID string) (*vault.
|
||||
}
|
||||
resp, err := core.HandleRequest(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create root token with ID %s: %s", rootTokenID, err)
|
||||
return nil, fmt.Errorf("failed to create root token with ID %s: %s", coreConfig.DevToken, err)
|
||||
}
|
||||
if resp == nil {
|
||||
return nil, fmt.Errorf("nil response when creating root token with ID %s", rootTokenID)
|
||||
return nil, fmt.Errorf("nil response when creating root token with ID %s", coreConfig.DevToken)
|
||||
}
|
||||
if resp.Auth == nil {
|
||||
return nil, fmt.Errorf("nil auth when creating root token with ID %s", rootTokenID)
|
||||
return nil, fmt.Errorf("nil auth when creating root token with ID %s", coreConfig.DevToken)
|
||||
}
|
||||
|
||||
init.RootToken = resp.Auth.ClientToken
|
||||
@@ -747,6 +757,168 @@ func (c *ServerCommand) enableDev(core *vault.Core, rootTokenID string) (*vault.
|
||||
return init, nil
|
||||
}
|
||||
|
||||
func (c *ServerCommand) enableThreeNodeDevCluster(base *vault.CoreConfig, info map[string]string, infoKeys []string, devListenAddress string) int {
|
||||
testCluster := vault.NewTestCluster(&testing.T{}, base, &vault.TestClusterOptions{
|
||||
HandlerFunc: vaulthttp.Handler,
|
||||
BaseListenAddress: devListenAddress,
|
||||
})
|
||||
defer c.cleanupGuard.Do(testCluster.Cleanup)
|
||||
|
||||
info["cluster parameters path"] = testCluster.TempDir
|
||||
info["log level"] = "trace"
|
||||
infoKeys = append(infoKeys, "cluster parameters path", "log level")
|
||||
|
||||
for i, core := range testCluster.Cores {
|
||||
info[fmt.Sprintf("node %d redirect address", i)] = fmt.Sprintf("https://%s", core.Listeners[0].Address.String())
|
||||
infoKeys = append(infoKeys, fmt.Sprintf("node %d redirect address", i))
|
||||
}
|
||||
|
||||
infoKeys = append(infoKeys, "version")
|
||||
verInfo := version.GetVersion()
|
||||
info["version"] = verInfo.FullVersionNumber(false)
|
||||
if verInfo.Revision != "" {
|
||||
info["version sha"] = strings.Trim(verInfo.Revision, "'")
|
||||
infoKeys = append(infoKeys, "version sha")
|
||||
}
|
||||
infoKeys = append(infoKeys, "cgo")
|
||||
info["cgo"] = "disabled"
|
||||
if version.CgoEnabled {
|
||||
info["cgo"] = "enabled"
|
||||
}
|
||||
|
||||
// Server configuration output
|
||||
padding := 24
|
||||
sort.Strings(infoKeys)
|
||||
c.Ui.Output("==> Vault server configuration:\n")
|
||||
for _, k := range infoKeys {
|
||||
c.Ui.Output(fmt.Sprintf(
|
||||
"%s%s: %s",
|
||||
strings.Repeat(" ", padding-len(k)),
|
||||
strings.Title(k),
|
||||
info[k]))
|
||||
}
|
||||
c.Ui.Output("")
|
||||
|
||||
for _, core := range testCluster.Cores {
|
||||
core.Server.Handler = vaulthttp.Handler(core.Core)
|
||||
core.SetClusterHandler(core.Server.Handler)
|
||||
}
|
||||
|
||||
testCluster.Start()
|
||||
|
||||
if base.DevToken != "" {
|
||||
req := &logical.Request{
|
||||
ID: "dev-gen-root",
|
||||
Operation: logical.UpdateOperation,
|
||||
ClientToken: testCluster.RootToken,
|
||||
Path: "auth/token/create",
|
||||
Data: map[string]interface{}{
|
||||
"id": base.DevToken,
|
||||
"policies": []string{"root"},
|
||||
"no_parent": true,
|
||||
"no_default_policy": true,
|
||||
},
|
||||
}
|
||||
resp, err := testCluster.Cores[0].HandleRequest(req)
|
||||
if err != nil {
|
||||
c.Ui.Output(fmt.Sprintf("failed to create root token with ID %s: %s", base.DevToken, err))
|
||||
return 1
|
||||
}
|
||||
if resp == nil {
|
||||
c.Ui.Output(fmt.Sprintf("nil response when creating root token with ID %s", base.DevToken))
|
||||
return 1
|
||||
}
|
||||
if resp.Auth == nil {
|
||||
c.Ui.Output(fmt.Sprintf("nil auth when creating root token with ID %s", base.DevToken))
|
||||
return 1
|
||||
}
|
||||
|
||||
testCluster.RootToken = resp.Auth.ClientToken
|
||||
|
||||
req.ID = "dev-revoke-init-root"
|
||||
req.Path = "auth/token/revoke-self"
|
||||
req.Data = nil
|
||||
resp, err = testCluster.Cores[0].HandleRequest(req)
|
||||
if err != nil {
|
||||
c.Ui.Output(fmt.Sprintf("failed to revoke initial root token: %s", err))
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// Set the token
|
||||
tokenHelper, err := c.TokenHelper()
|
||||
if err != nil {
|
||||
c.Ui.Output(fmt.Sprintf("%v", err))
|
||||
return 1
|
||||
}
|
||||
if err := tokenHelper.Store(testCluster.RootToken); err != nil {
|
||||
c.Ui.Output(fmt.Sprintf("%v", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(testCluster.TempDir, "root_token"), []byte(testCluster.RootToken), 0755); err != nil {
|
||||
c.Ui.Output(fmt.Sprintf("%v", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
c.Ui.Output(fmt.Sprintf(
|
||||
"==> Three node dev mode is enabled\n\n" +
|
||||
"The unseal key and root token are reproduced below in case you\n" +
|
||||
"want to seal/unseal the Vault or play with authentication.\n",
|
||||
))
|
||||
|
||||
for i, key := range testCluster.BarrierKeys {
|
||||
c.Ui.Output(fmt.Sprintf(
|
||||
"Unseal Key %d: %s",
|
||||
i, base64.StdEncoding.EncodeToString(key),
|
||||
))
|
||||
}
|
||||
|
||||
c.Ui.Output(fmt.Sprintf(
|
||||
"\nRoot Token: %s\n", testCluster.RootToken,
|
||||
))
|
||||
|
||||
// Output the header that the server has started
|
||||
c.Ui.Output("==> Vault server started! Log data will stream in below:\n")
|
||||
|
||||
// Release the log gate.
|
||||
c.logGate.Flush()
|
||||
|
||||
// Wait for shutdown
|
||||
shutdownTriggered := false
|
||||
|
||||
for !shutdownTriggered {
|
||||
select {
|
||||
case <-c.ShutdownCh:
|
||||
c.Ui.Output("==> Vault shutdown triggered")
|
||||
|
||||
// Stop the listners so that we don't process further client requests.
|
||||
c.cleanupGuard.Do(testCluster.Cleanup)
|
||||
|
||||
// Shutdown will wait until after Vault is sealed, which means the
|
||||
// request forwarding listeners will also be closed (and also
|
||||
// waited for).
|
||||
for _, core := range testCluster.Cores {
|
||||
if err := core.Shutdown(); err != nil {
|
||||
c.Ui.Output(fmt.Sprintf("Error with core shutdown: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
shutdownTriggered = true
|
||||
|
||||
case <-c.SighupCh:
|
||||
c.Ui.Output("==> Vault reload triggered")
|
||||
for _, core := range testCluster.Cores {
|
||||
if err := c.Reload(core.ReloadFuncsLock, core.ReloadFuncs, nil); err != nil {
|
||||
c.Ui.Output(fmt.Sprintf("Error(s) were encountered during reload: %s", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// detectRedirect is used to attempt redirect address detection
|
||||
func (c *ServerCommand) detectRedirect(detect physical.RedirectDetect,
|
||||
config *server.Config) (string, error) {
|
||||
@@ -921,55 +1093,29 @@ func (c *ServerCommand) setupTelemetry(config *server.Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ServerCommand) Reload(configPath []string) error {
|
||||
c.reloadFuncsLock.RLock()
|
||||
defer c.reloadFuncsLock.RUnlock()
|
||||
func (c *ServerCommand) Reload(lock *sync.RWMutex, reloadFuncs *map[string][]reload.ReloadFunc, configPath []string) error {
|
||||
lock.RLock()
|
||||
defer lock.RUnlock()
|
||||
|
||||
var reloadErrors *multierror.Error
|
||||
|
||||
// Read the new config
|
||||
var config *server.Config
|
||||
for _, path := range configPath {
|
||||
current, err := server.LoadConfig(path, c.logger)
|
||||
if err != nil {
|
||||
reloadErrors = multierror.Append(reloadErrors, fmt.Errorf("Error loading configuration from %s: %s", path, err))
|
||||
goto audit
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
config = current
|
||||
} else {
|
||||
config = config.Merge(current)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure at least one config was found.
|
||||
if config == nil {
|
||||
reloadErrors = multierror.Append(reloadErrors, fmt.Errorf("No configuration files found"))
|
||||
goto audit
|
||||
}
|
||||
|
||||
// Call reload on the listeners. This will call each listener with each
|
||||
// config block, but they verify the address.
|
||||
for _, lnConfig := range config.Listeners {
|
||||
for _, relFunc := range (*c.reloadFuncs)["listener|"+lnConfig.Type] {
|
||||
if err := relFunc(lnConfig.Config); err != nil {
|
||||
reloadErrors = multierror.Append(reloadErrors, fmt.Errorf("Error encountered reloading configuration: %s", err))
|
||||
goto audit
|
||||
for k, relFuncs := range *reloadFuncs {
|
||||
switch {
|
||||
case strings.HasPrefix(k, "listener|"):
|
||||
for _, relFunc := range relFuncs {
|
||||
if relFunc != nil {
|
||||
if err := relFunc(nil); err != nil {
|
||||
reloadErrors = multierror.Append(reloadErrors, fmt.Errorf("Error encountered reloading listener: %v", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
audit:
|
||||
// file audit reload funcs
|
||||
for k, relFuncs := range *c.reloadFuncs {
|
||||
if !strings.HasPrefix(k, "audit_file|") {
|
||||
continue
|
||||
}
|
||||
for _, relFunc := range relFuncs {
|
||||
if relFunc != nil {
|
||||
if err := relFunc(nil); err != nil {
|
||||
reloadErrors = multierror.Append(reloadErrors, fmt.Errorf("Error encountered reloading file audit backend at path %s: %v", strings.TrimPrefix(k, "audit_file|"), err))
|
||||
case strings.HasPrefix(k, "audit_file|"):
|
||||
for _, relFunc := range relFuncs {
|
||||
if relFunc != nil {
|
||||
if err := relFunc(nil); err != nil {
|
||||
reloadErrors = multierror.Append(reloadErrors, fmt.Errorf("Error encountered reloading file audit backend at path %s: %v", strings.TrimPrefix(k, "audit_file|"), err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user