2015-03-12 22:21:11 +00:00
|
|
|
package command
|
|
|
|
|
|
|
|
import (
|
2015-03-31 23:44:47 +00:00
|
|
|
"encoding/hex"
|
2015-03-12 22:30:07 +00:00
|
|
|
"fmt"
|
2015-04-04 19:06:41 +00:00
|
|
|
"log"
|
2015-03-13 17:09:38 +00:00
|
|
|
"net"
|
|
|
|
"net/http"
|
2015-04-04 19:06:41 +00:00
|
|
|
"os"
|
|
|
|
"sort"
|
2015-03-12 22:21:11 +00:00
|
|
|
"strings"
|
|
|
|
|
2015-04-04 19:11:10 +00:00
|
|
|
"github.com/hashicorp/logutils"
|
2015-04-05 01:07:53 +00:00
|
|
|
"github.com/hashicorp/vault/audit"
|
2015-03-12 22:30:07 +00:00
|
|
|
"github.com/hashicorp/vault/command/server"
|
2015-03-12 22:21:11 +00:00
|
|
|
"github.com/hashicorp/vault/helper/flag-slice"
|
2015-04-04 19:06:41 +00:00
|
|
|
"github.com/hashicorp/vault/helper/gated-writer"
|
2015-03-13 17:09:38 +00:00
|
|
|
vaulthttp "github.com/hashicorp/vault/http"
|
2015-03-20 18:32:18 +00:00
|
|
|
"github.com/hashicorp/vault/logical"
|
2015-03-13 17:09:38 +00:00
|
|
|
"github.com/hashicorp/vault/physical"
|
|
|
|
"github.com/hashicorp/vault/vault"
|
2015-03-12 22:21:11 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// ServerCommand is a Command that starts the Vault server.
|
|
|
|
type ServerCommand struct {
|
2015-04-05 01:07:53 +00:00
|
|
|
AuditBackends map[string]audit.Factory
|
2015-04-01 22:48:13 +00:00
|
|
|
CredentialBackends map[string]logical.Factory
|
|
|
|
LogicalBackends map[string]logical.Factory
|
2015-03-20 18:32:18 +00:00
|
|
|
|
2015-03-12 22:21:11 +00:00
|
|
|
Meta
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ServerCommand) Run(args []string) int {
|
2015-03-31 23:44:47 +00:00
|
|
|
var dev bool
|
2015-03-12 22:21:11 +00:00
|
|
|
var configPath []string
|
2015-04-04 19:11:10 +00:00
|
|
|
var logLevel string
|
2015-03-12 22:21:11 +00:00
|
|
|
flags := c.Meta.FlagSet("server", FlagSetDefault)
|
2015-03-31 23:44:47 +00:00
|
|
|
flags.BoolVar(&dev, "dev", false, "")
|
2015-04-04 19:11:10 +00:00
|
|
|
flags.StringVar(&logLevel, "log-level", "info", "")
|
2015-03-12 22:21:11 +00:00
|
|
|
flags.Usage = func() { c.Ui.Error(c.Help()) }
|
|
|
|
flags.Var((*sliceflag.StringFlag)(&configPath), "config", "config")
|
|
|
|
if err := flags.Parse(args); err != nil {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validation
|
2015-03-31 23:44:47 +00:00
|
|
|
if !dev && len(configPath) == 0 {
|
2015-03-12 22:21:11 +00:00
|
|
|
c.Ui.Error("At least one config path must be specified with -config")
|
|
|
|
flags.Usage()
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load the configuration
|
2015-03-12 22:30:07 +00:00
|
|
|
var config *server.Config
|
2015-03-31 23:44:47 +00:00
|
|
|
if dev {
|
|
|
|
config = server.DevConfig()
|
|
|
|
}
|
2015-03-12 22:30:07 +00:00
|
|
|
for _, path := range configPath {
|
|
|
|
current, err := server.LoadConfig(path)
|
|
|
|
if err != nil {
|
|
|
|
c.Ui.Error(fmt.Sprintf(
|
|
|
|
"Error loading configuration from %s: %s", path, err))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
if config == nil {
|
|
|
|
config = current
|
|
|
|
} else {
|
|
|
|
config = config.Merge(current)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-04 19:06:41 +00:00
|
|
|
// Create a logger. We wrap it in a gated writer so that it doesn't
|
|
|
|
// start logging too early.
|
|
|
|
logGate := &gatedwriter.Writer{Writer: os.Stderr}
|
2015-04-04 19:11:10 +00:00
|
|
|
logger := log.New(&logutils.LevelFilter{
|
|
|
|
Levels: []logutils.LogLevel{
|
|
|
|
"TRACE", "DEBUG", "INFO", "WARN", "ERR"},
|
|
|
|
MinLevel: logutils.LogLevel(strings.ToUpper(logLevel)),
|
|
|
|
Writer: logGate,
|
|
|
|
}, "", log.LstdFlags)
|
2015-04-04 19:06:41 +00:00
|
|
|
|
2015-03-13 17:09:38 +00:00
|
|
|
// Initialize the backend
|
|
|
|
backend, err := physical.NewBackend(
|
|
|
|
config.Backend.Type, config.Backend.Config)
|
|
|
|
if err != nil {
|
|
|
|
c.Ui.Error(fmt.Sprintf(
|
|
|
|
"Error initializing backend of type %s: %s",
|
|
|
|
config.Backend.Type, err))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the core
|
|
|
|
core, err := vault.NewCore(&vault.CoreConfig{
|
2015-04-01 22:48:13 +00:00
|
|
|
Physical: backend,
|
2015-04-05 01:07:53 +00:00
|
|
|
AuditBackends: c.AuditBackends,
|
2015-04-01 22:48:13 +00:00
|
|
|
CredentialBackends: c.CredentialBackends,
|
|
|
|
LogicalBackends: c.LogicalBackends,
|
2015-04-04 19:06:41 +00:00
|
|
|
Logger: logger,
|
2015-03-13 17:09:38 +00:00
|
|
|
})
|
|
|
|
|
2015-03-31 23:44:47 +00:00
|
|
|
// If we're in dev mode, then initialize the core
|
|
|
|
if dev {
|
|
|
|
init, err := c.enableDev(core)
|
|
|
|
if err != nil {
|
|
|
|
c.Ui.Error(fmt.Sprintf(
|
|
|
|
"Error initializing dev mode: %s", err))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
c.Ui.Output(fmt.Sprintf(
|
|
|
|
"WARNING: Dev mode is enabled!\n\n"+
|
|
|
|
"In this mode, Vault is completely in-memory and unsealed.\n"+
|
|
|
|
"Vault is configured to only have a single unseal key. The root\n"+
|
|
|
|
"token has already been authenticated with the CLI, so you can\n"+
|
|
|
|
"immediately begin using the Vault CLI.\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\n"+
|
2015-04-04 19:06:41 +00:00
|
|
|
"Unseal Key: %s\nRoot Token: %s\n",
|
2015-03-31 23:44:47 +00:00
|
|
|
hex.EncodeToString(init.SecretShares[0]),
|
|
|
|
init.RootToken,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2015-04-04 19:06:41 +00:00
|
|
|
// Compile server information for output later
|
|
|
|
infoKeys := make([]string, 0, 10)
|
|
|
|
info := make(map[string]string)
|
|
|
|
info["backend"] = config.Backend.Type
|
2015-04-04 19:11:10 +00:00
|
|
|
info["log level"] = logLevel
|
|
|
|
infoKeys = append(infoKeys, "log level", "backend")
|
2015-04-04 19:06:41 +00:00
|
|
|
|
2015-03-12 22:30:07 +00:00
|
|
|
// Initialize the listeners
|
2015-03-13 17:09:38 +00:00
|
|
|
lns := make([]net.Listener, 0, len(config.Listeners))
|
2015-04-04 19:06:41 +00:00
|
|
|
for i, lnConfig := range config.Listeners {
|
|
|
|
ln, props, err := server.NewListener(lnConfig.Type, lnConfig.Config)
|
2015-03-13 17:09:38 +00:00
|
|
|
if err != nil {
|
|
|
|
c.Ui.Error(fmt.Sprintf(
|
|
|
|
"Error initializing listener of type %s: %s",
|
|
|
|
lnConfig.Type, err))
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
2015-04-04 19:06:41 +00:00
|
|
|
// Store the listener props for output later
|
|
|
|
key := fmt.Sprintf("listener %d", i+1)
|
|
|
|
propsList := make([]string, 0, len(props))
|
|
|
|
for k, v := range props {
|
|
|
|
propsList = append(propsList, fmt.Sprintf(
|
|
|
|
"%s: %q", k, v))
|
|
|
|
}
|
|
|
|
sort.Strings(propsList)
|
|
|
|
infoKeys = append(infoKeys, key)
|
|
|
|
info[key] = fmt.Sprintf(
|
|
|
|
"%s (%s)", lnConfig.Type, strings.Join(propsList, ", "))
|
|
|
|
|
2015-03-13 17:09:38 +00:00
|
|
|
lns = append(lns, ln)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the HTTP server
|
|
|
|
server := &http.Server{}
|
|
|
|
server.Handler = vaulthttp.Handler(core)
|
|
|
|
for _, ln := range lns {
|
|
|
|
go server.Serve(ln)
|
|
|
|
}
|
2015-03-12 22:21:11 +00:00
|
|
|
|
2015-04-04 19:06:41 +00:00
|
|
|
// Server configuration output
|
|
|
|
padding := 18
|
|
|
|
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("")
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
logGate.Flush()
|
|
|
|
|
2015-03-13 17:09:38 +00:00
|
|
|
<-make(chan struct{})
|
2015-03-12 22:21:11 +00:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2015-03-31 23:44:47 +00:00
|
|
|
func (c *ServerCommand) enableDev(core *vault.Core) (*vault.InitResult, error) {
|
|
|
|
// Initialize it with a basic single key
|
|
|
|
init, err := core.Initialize(&vault.SealConfig{
|
|
|
|
SecretShares: 1,
|
|
|
|
SecretThreshold: 1,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy the key so that it can be zeroed
|
|
|
|
key := make([]byte, len(init.SecretShares[0]))
|
|
|
|
copy(key, init.SecretShares[0])
|
|
|
|
|
|
|
|
// Unseal the core
|
|
|
|
unsealed, err := core.Unseal(key)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if !unsealed {
|
|
|
|
return nil, fmt.Errorf("failed to unseal Vault for dev mode")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the token
|
|
|
|
tokenHelper, err := c.TokenHelper()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := tokenHelper.Store(init.RootToken); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return init, nil
|
|
|
|
}
|
|
|
|
|
2015-03-12 22:21:11 +00:00
|
|
|
func (c *ServerCommand) Synopsis() string {
|
|
|
|
return "Start a Vault server"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ServerCommand) Help() string {
|
|
|
|
helpText := `
|
|
|
|
Usage: vault server [options]
|
|
|
|
|
|
|
|
Start a Vault server.
|
|
|
|
|
|
|
|
This command starts a Vault server that responds to API requests.
|
|
|
|
Vault will start in a "sealed" state. The Vault must be unsealed
|
|
|
|
with "vault unseal" or the API before this server can respond to requests.
|
|
|
|
This must be done for every server.
|
|
|
|
|
|
|
|
If the server is being started against a storage backend that has
|
|
|
|
brand new (no existing Vault data in it), it must be initialized with
|
|
|
|
"vault init" or the API first.
|
|
|
|
|
|
|
|
|
|
|
|
General Options:
|
|
|
|
|
|
|
|
-config=<path> Path to the configuration file or directory. This can be
|
|
|
|
specified multiple times. If it is a directory, all
|
|
|
|
files with a ".hcl" or ".json" suffix will be loaded.
|
|
|
|
|
2015-04-04 19:11:10 +00:00
|
|
|
-log-level=info Log verbosity. Defaults to "info", will be outputted
|
|
|
|
to stderr.
|
|
|
|
|
2015-03-12 22:21:11 +00:00
|
|
|
`
|
|
|
|
return strings.TrimSpace(helpText)
|
|
|
|
}
|