consul: first pass at keyring integration

pull/336/head
Ryan Uber 2014-09-05 17:22:33 -07:00
parent 6c83ef119a
commit e20a724999
4 changed files with 103 additions and 8 deletions

View File

@ -1,16 +1,21 @@
package agent package agent
import ( import (
"encoding/base64"
"encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log" "log"
"net" "net"
"os" "os"
"path/filepath"
"strconv" "strconv"
"sync" "sync"
"github.com/hashicorp/consul/consul" "github.com/hashicorp/consul/consul"
"github.com/hashicorp/consul/consul/structs" "github.com/hashicorp/consul/consul/structs"
"github.com/hashicorp/memberlist"
"github.com/hashicorp/serf/serf" "github.com/hashicorp/serf/serf"
) )
@ -109,6 +114,33 @@ func Create(config *Config, logOutput io.Writer) (*Agent, error) {
// Initialize the local state // Initialize the local state
agent.state.Init(config, agent.logger) agent.state.Init(config, agent.logger)
// Setup encryption keyring files
if !config.DisableKeyring && config.EncryptKey != "" {
keyringBytes, err := json.MarshalIndent([]string{config.EncryptKey})
if err != nil {
return nil, err
}
paths := []string{
filepath.Join(config.DataDir, "serf", "keyring_lan"),
filepath.Join(config.DataDir, "serf", "keyring_wan"),
}
for _, path := range paths {
if _, err := os.Stat(path); err == nil {
continue
}
fh, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
if err != nil {
return nil, err
}
defer fh.Close()
if _, err := fh.Write(keyringBytes); err != nil {
os.Remove(path)
return nil, err
}
}
}
// Setup either the client or the server // Setup either the client or the server
var err error var err error
if config.Server { if config.Server {
@ -160,11 +192,21 @@ func (a *Agent) consulConfig() *consul.Config {
if a.config.DataDir != "" { if a.config.DataDir != "" {
base.DataDir = a.config.DataDir base.DataDir = a.config.DataDir
} }
if a.config.EncryptKey != "" { if a.config.EncryptKey != "" && a.config.DisableKeyring {
key, _ := a.config.EncryptBytes() key, _ := a.config.EncryptBytes()
base.SerfLANConfig.MemberlistConfig.SecretKey = key base.SerfLANConfig.MemberlistConfig.SecretKey = key
base.SerfWANConfig.MemberlistConfig.SecretKey = key base.SerfWANConfig.MemberlistConfig.SecretKey = key
} }
if !a.config.DisableKeyring {
lanKeyring := filepath.Join(base.DataDir, "serf", "keyring_lan")
wanKeyring := filepath.Join(base.DataDir, "serf", "keyring_wan")
base.SerfLANConfig.KeyringFile = lanKeyring
base.SerfWANConfig.KeyringFile = wanKeyring
base.SerfLANConfig.MemberlistConfig.Keyring = loadKeyringFile(lanKeyring)
base.SerfWANConfig.MemberlistConfig.Keyring = loadKeyringFile(wanKeyring)
}
if a.config.NodeName != "" { if a.config.NodeName != "" {
base.NodeName = a.config.NodeName base.NodeName = a.config.NodeName
} }
@ -253,6 +295,9 @@ func (a *Agent) consulConfig() *consul.Config {
} }
} }
// Setup gossip keyring configuration
base.DisableKeyring = a.config.DisableKeyring
// Setup the loggers // Setup the loggers
base.LogOutput = a.logOutput base.LogOutput = a.logOutput
return base return base
@ -648,3 +693,41 @@ func (a *Agent) deletePid() error {
} }
return nil return nil
} }
// loadKeyringFile will load a keyring out of a file
func loadKeyringFile(keyringFile string) *memberlist.Keyring {
if _, err := os.Stat(keyringFile); err != nil {
return nil
}
// Read in the keyring file data
keyringData, err := ioutil.ReadFile(keyringFile)
if err != nil {
return nil
}
// Decode keyring JSON
keys := make([]string, 0)
if err := json.Unmarshal(keyringData, &keys); err != nil {
return nil
}
// Decode base64 values
keysDecoded := make([][]byte, len(keys))
for i, key := range keys {
keyBytes, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return nil
}
keysDecoded[i] = keyBytes
}
// Create the keyring
keyring, err := memberlist.NewKeyring(keysDecoded, keysDecoded[0])
if err != nil {
return nil
}
// Success!
return keyring
}

View File

@ -67,6 +67,7 @@ func (c *Command) readConfig() *Config {
cmdFlags.StringVar(&cmdConfig.UiDir, "ui-dir", "", "path to the web UI directory") 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.PidFile, "pid-file", "", "path to file to store PID")
cmdFlags.StringVar(&cmdConfig.EncryptKey, "encrypt", "", "gossip encryption key") cmdFlags.StringVar(&cmdConfig.EncryptKey, "encrypt", "", "gossip encryption key")
cmdFlags.BoolVar(&cmdConfig.DisableKeyring, "disable-keyring", false, "disable use of encryption keyring")
cmdFlags.BoolVar(&cmdConfig.Server, "server", false, "run agent as server") cmdFlags.BoolVar(&cmdConfig.Server, "server", false, "run agent as server")
cmdFlags.BoolVar(&cmdConfig.Bootstrap, "bootstrap", false, "enable server bootstrap mode") cmdFlags.BoolVar(&cmdConfig.Bootstrap, "bootstrap", false, "enable server bootstrap mode")
@ -143,13 +144,6 @@ func (c *Command) readConfig() *Config {
config.NodeName = hostname config.NodeName = hostname
} }
if config.EncryptKey != "" {
if _, err := config.EncryptBytes(); err != nil {
c.Ui.Error(fmt.Sprintf("Invalid encryption key: %s", err))
return nil
}
}
// Ensure we have a data directory // Ensure we have a data directory
if config.DataDir == "" { if config.DataDir == "" {
c.Ui.Error("Must specify data directory using -data-dir") c.Ui.Error("Must specify data directory using -data-dir")
@ -180,6 +174,13 @@ func (c *Command) readConfig() *Config {
return nil return nil
} }
if config.EncryptKey != "" {
if _, err := config.EncryptBytes(); err != nil {
c.Ui.Error(fmt.Sprintf("Invalid encryption key: %s", err))
return nil
}
}
// Compile all the watches // Compile all the watches
for _, params := range config.Watches { for _, params := range config.Watches {
// Parse the watches, excluding the handler // Parse the watches, excluding the handler

View File

@ -104,6 +104,9 @@ type Config struct {
// recursors array. // recursors array.
DNSRecursor string `mapstructure:"recursor"` DNSRecursor string `mapstructure:"recursor"`
// Disable use of an encryption keyring.
DisableKeyring bool `mapstructure:"disable_keyring"`
// DNSRecursors can be set to allow the DNS servers to recursively // DNSRecursors can be set to allow the DNS servers to recursively
// resolve non-consul domains // resolve non-consul domains
DNSRecursors []string `mapstructure:"recursors"` DNSRecursors []string `mapstructure:"recursors"`
@ -676,6 +679,9 @@ func MergeConfig(a, b *Config) *Config {
if b.EncryptKey != "" { if b.EncryptKey != "" {
result.EncryptKey = b.EncryptKey result.EncryptKey = b.EncryptKey
} }
if b.DisableKeyring {
result.DisableKeyring = true
}
if b.LogLevel != "" { if b.LogLevel != "" {
result.LogLevel = b.LogLevel result.LogLevel = b.LogLevel
} }

View File

@ -165,6 +165,11 @@ type Config struct {
// UserEventHandler callback can be used to handle incoming // UserEventHandler callback can be used to handle incoming
// user events. This function should not block. // user events. This function should not block.
UserEventHandler func(serf.UserEvent) UserEventHandler func(serf.UserEvent)
// DisableKeyring is used to disable persisting the encryption keyring to
// filesystem. By default, if encryption is enabled, Consul will create a
// file inside of the DataDir to keep track of changes made to the ring.
DisableKeyring bool
} }
// CheckVersion is used to check if the ProtocolVersion is valid // CheckVersion is used to check if the ProtocolVersion is valid