mirror of https://github.com/hashicorp/consul
agent: move keyring initialization out of agent, add -init option to keys command
parent
0952535e33
commit
8a40f3888c
|
@ -20,8 +20,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
serfLANKeyring = "serf/local.keyring"
|
||||
serfWANKeyring = "serf/remote.keyring"
|
||||
SerfLANKeyring = "serf/local.keyring"
|
||||
SerfWANKeyring = "serf/remote.keyring"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -119,18 +119,6 @@ func Create(config *Config, logOutput io.Writer) (*Agent, error) {
|
|||
// Initialize the local state
|
||||
agent.state.Init(config, agent.logger)
|
||||
|
||||
// Setup encryption keyring files
|
||||
if config.PersistKeyring && config.EncryptKey != "" {
|
||||
if config.Server {
|
||||
if err := agent.initKeyringFile(serfWANKeyring); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := agent.initKeyringFile(serfLANKeyring); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Setup either the client or the server
|
||||
var err error
|
||||
if config.Server {
|
||||
|
@ -182,16 +170,16 @@ func (a *Agent) consulConfig() *consul.Config {
|
|||
if a.config.DataDir != "" {
|
||||
base.DataDir = a.config.DataDir
|
||||
}
|
||||
if a.config.EncryptKey != "" && !a.config.PersistKeyring {
|
||||
if a.config.EncryptKey != "" {
|
||||
key, _ := a.config.EncryptBytes()
|
||||
base.SerfLANConfig.MemberlistConfig.SecretKey = key
|
||||
base.SerfWANConfig.MemberlistConfig.SecretKey = key
|
||||
}
|
||||
if a.config.PersistKeyring {
|
||||
lanKeyring := filepath.Join(base.DataDir, serfLANKeyring)
|
||||
wanKeyring := filepath.Join(base.DataDir, serfWANKeyring)
|
||||
base.SerfLANConfig.KeyringFile = lanKeyring
|
||||
base.SerfWANConfig.KeyringFile = wanKeyring
|
||||
if a.config.Server && a.config.keyringFilesExist() {
|
||||
pathWAN := filepath.Join(base.DataDir, SerfWANKeyring)
|
||||
pathLAN := filepath.Join(base.DataDir, SerfLANKeyring)
|
||||
base.SerfWANConfig.KeyringFile = pathWAN
|
||||
base.SerfLANConfig.KeyringFile = pathLAN
|
||||
}
|
||||
if a.config.NodeName != "" {
|
||||
base.NodeName = a.config.NodeName
|
||||
|
@ -813,39 +801,3 @@ func (a *Agent) RemoveKeyLAN(key string) (*serf.KeyResponse, error) {
|
|||
km := a.client.KeyManagerLAN()
|
||||
return km.RemoveKey(key)
|
||||
}
|
||||
|
||||
// initKeyringFile is used to create and initialize a persistent keyring file
|
||||
// for gossip encryption keys. It is used at agent startup to dump the initial
|
||||
// encryption key into a keyfile if persistence is enabled.
|
||||
func (a *Agent) initKeyringFile(path string) error {
|
||||
serfDir := filepath.Join(a.config.DataDir, "serf")
|
||||
if err := os.MkdirAll(serfDir, 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keys := []string{a.config.EncryptKey}
|
||||
keyringBytes, err := json.MarshalIndent(keys, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keyringFile := filepath.Join(a.config.DataDir, path)
|
||||
|
||||
// If the keyring file already exists, don't re-initialize
|
||||
if _, err := os.Stat(keyringFile); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
fh, err := os.OpenFile(keyringFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
if _, err := fh.Write(keyringBytes); err != nil {
|
||||
os.Remove(keyringFile)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -67,8 +67,6 @@ func (c *Command) readConfig() *Config {
|
|||
cmdFlags.StringVar(&cmdConfig.UiDir, "ui-dir", "", "path to the web UI directory")
|
||||
cmdFlags.StringVar(&cmdConfig.PidFile, "pid-file", "", "path to file to store PID")
|
||||
cmdFlags.StringVar(&cmdConfig.EncryptKey, "encrypt", "", "gossip encryption key")
|
||||
cmdFlags.BoolVar(&cmdConfig.PersistKeyring, "persist-keyring", false, "persist keyring changes")
|
||||
|
||||
cmdFlags.BoolVar(&cmdConfig.Server, "server", false, "run agent as server")
|
||||
cmdFlags.BoolVar(&cmdConfig.Bootstrap, "bootstrap", false, "enable server bootstrap mode")
|
||||
cmdFlags.IntVar(&cmdConfig.BootstrapExpect, "bootstrap-expect", 0, "enable automatic bootstrap via expect mode")
|
||||
|
@ -219,11 +217,10 @@ func (c *Command) readConfig() *Config {
|
|||
c.Ui.Error("WARNING: Windows is not recommended as a Consul server. Do not use in production.")
|
||||
}
|
||||
|
||||
// Warn if an encryption key is passed while a keyring already exists
|
||||
if config.EncryptKey != "" && (config.PersistKeyring && config.CheckKeyringFiles()) {
|
||||
c.Ui.Error(fmt.Sprintf(
|
||||
"WARNING: Keyring already exists, ignoring new key %s",
|
||||
config.EncryptKey))
|
||||
// Error if an encryption key is passed while a keyring already exists
|
||||
if config.EncryptKey != "" && config.keyringFilesExist() {
|
||||
c.Ui.Error(fmt.Sprintf("Error: -encrypt specified but keyring files exist"))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set the version info
|
||||
|
@ -595,7 +592,7 @@ func (c *Command) Run(args []string) int {
|
|||
|
||||
// Determine if gossip is encrypted
|
||||
gossipEncrypted := false
|
||||
if config.EncryptKey != "" || (config.PersistKeyring && config.CheckKeyringFiles()) {
|
||||
if config.EncryptKey != "" || config.keyringFilesExist() {
|
||||
gossipEncrypted = true
|
||||
}
|
||||
|
||||
|
|
|
@ -150,11 +150,6 @@ type Config struct {
|
|||
// the TERM signal. Defaults false. This can be changed on reload.
|
||||
LeaveOnTerm bool `mapstructure:"leave_on_terminate"`
|
||||
|
||||
// Enable keyring persistence. There are currently two keyrings; one for
|
||||
// the LAN serf cluster and the other for the WAN. Each will maintain its
|
||||
// own keyring file in the agent's data directory.
|
||||
PersistKeyring bool `mapstructure:"persist_keyring"`
|
||||
|
||||
// SkipLeaveOnInt controls if Serf skips a graceful leave when receiving
|
||||
// the INT signal. Defaults false. This can be changed on reload.
|
||||
SkipLeaveOnInt bool `mapstructure:"skip_leave_on_interrupt"`
|
||||
|
@ -416,13 +411,13 @@ func (c *Config) ClientListenerAddr(override string, port int) (string, error) {
|
|||
return addr.String(), nil
|
||||
}
|
||||
|
||||
// CheckKeyringFiles checks for existence of the keyring files for Serf
|
||||
func (c *Config) CheckKeyringFiles() bool {
|
||||
if _, err := os.Stat(filepath.Join(c.DataDir, serfLANKeyring)); err != nil {
|
||||
// keyringFilesExist checks for existence of the keyring files for Serf
|
||||
func (c *Config) keyringFilesExist() bool {
|
||||
if _, err := os.Stat(filepath.Join(c.DataDir, SerfLANKeyring)); err != nil {
|
||||
return false
|
||||
}
|
||||
if c.Server {
|
||||
if _, err := os.Stat(filepath.Join(c.DataDir, serfWANKeyring)); err != nil {
|
||||
if _, err := os.Stat(filepath.Join(c.DataDir, SerfWANKeyring)); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -697,9 +692,6 @@ func MergeConfig(a, b *Config) *Config {
|
|||
if b.EncryptKey != "" {
|
||||
result.EncryptKey = b.EncryptKey
|
||||
}
|
||||
if b.PersistKeyring {
|
||||
result.PersistKeyring = true
|
||||
}
|
||||
if b.LogLevel != "" {
|
||||
result.LogLevel = b.LogLevel
|
||||
}
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/consul/command/agent"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/ryanuber/columnize"
|
||||
)
|
||||
|
@ -16,7 +21,7 @@ type KeysCommand struct {
|
|||
}
|
||||
|
||||
func (c *KeysCommand) Run(args []string) int {
|
||||
var installKey, useKey, removeKey string
|
||||
var installKey, useKey, removeKey, init, dataDir string
|
||||
var listKeys, wan bool
|
||||
|
||||
cmdFlags := flag.NewFlagSet("keys", flag.ContinueOnError)
|
||||
|
@ -27,6 +32,8 @@ func (c *KeysCommand) Run(args []string) int {
|
|||
cmdFlags.StringVar(&removeKey, "remove", "", "remove key")
|
||||
cmdFlags.BoolVar(&listKeys, "list", false, "list keys")
|
||||
cmdFlags.BoolVar(&wan, "wan", false, "operate on wan keys")
|
||||
cmdFlags.StringVar(&init, "init", "", "initialize keyring")
|
||||
cmdFlags.StringVar(&dataDir, "data-dir", "", "data directory")
|
||||
|
||||
rpcAddr := RPCAddrFlag(cmdFlags)
|
||||
if err := cmdFlags.Parse(args); err != nil {
|
||||
|
@ -40,15 +47,11 @@ func (c *KeysCommand) Run(args []string) int {
|
|||
Ui: c.Ui,
|
||||
}
|
||||
|
||||
var out []string
|
||||
var failures map[string]string
|
||||
var err error
|
||||
|
||||
// Only accept a single argument
|
||||
found := listKeys
|
||||
for _, arg := range []string{installKey, useKey, removeKey} {
|
||||
for _, arg := range []string{installKey, useKey, removeKey, init} {
|
||||
if found && len(arg) > 0 {
|
||||
c.Ui.Error("Only one of -list, -install, -use, or -remove allowed")
|
||||
c.Ui.Error("Only a single action is allowed")
|
||||
return 1
|
||||
}
|
||||
found = found || len(arg) > 0
|
||||
|
@ -60,6 +63,43 @@ func (c *KeysCommand) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
var out, paths []string
|
||||
var failures map[string]string
|
||||
var err error
|
||||
|
||||
if init != "" {
|
||||
if dataDir == "" {
|
||||
c.Ui.Error("Must provide -data-dir")
|
||||
return 1
|
||||
}
|
||||
if _, err := base64.StdEncoding.DecodeString(init); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Invalid key: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
paths = append(paths, filepath.Join(dataDir, agent.SerfLANKeyring))
|
||||
if wan {
|
||||
paths = append(paths, filepath.Join(dataDir, agent.SerfWANKeyring))
|
||||
}
|
||||
|
||||
keys := []string{init}
|
||||
keyringBytes, err := json.MarshalIndent(keys, "", " ")
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error: %s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
for _, path := range paths {
|
||||
if err := initializeKeyring(path, keyringBytes); err != nil {
|
||||
c.Ui.Error("Error: %s", err)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// All other operations will require a client connection
|
||||
client, err := RPCClient(*rpcAddr)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
|
||||
|
@ -181,6 +221,30 @@ func (c *KeysCommand) Run(args []string) int {
|
|||
return 0
|
||||
}
|
||||
|
||||
// initializeKeyring will create a keyring file at a given path.
|
||||
func initializeKeyring(path string, key []byte) error {
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return fmt.Errorf("File already exists: %s", path)
|
||||
}
|
||||
|
||||
fh, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
if _, err := fh.Write(keyringBytes); err != nil {
|
||||
os.Remove(path)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *KeysCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: consul keys [options]
|
||||
|
@ -204,6 +268,12 @@ Options:
|
|||
-list List all keys currently in use within the cluster.
|
||||
-wan If talking with a server node, this flag can be used
|
||||
to operate on the WAN gossip layer.
|
||||
-init=<key> Create an initial keyring file for Consul to use
|
||||
containing the provided key. By default, this option
|
||||
will only initialize the LAN keyring. If the -wan
|
||||
option is also passed, then the wan keyring will be
|
||||
created as well. The -data-dir argument is required
|
||||
with this option.
|
||||
-rpc-addr=127.0.0.1:8400 RPC address of the Consul agent.
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
|
|
Loading…
Reference in New Issue