github.com/outbrain/consul@v1.4.5/agent/keyring.go (about)

     1  package agent
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  
    11  	"github.com/hashicorp/consul/agent/consul"
    12  	"github.com/hashicorp/consul/agent/structs"
    13  	"github.com/hashicorp/memberlist"
    14  	"github.com/hashicorp/serf/serf"
    15  )
    16  
    17  const (
    18  	SerfLANKeyring = "serf/local.keyring"
    19  	SerfWANKeyring = "serf/remote.keyring"
    20  )
    21  
    22  // initKeyring will create a keyring file at a given path.
    23  func initKeyring(path, key string) error {
    24  	var keys []string
    25  
    26  	if keyBytes, err := base64.StdEncoding.DecodeString(key); err != nil {
    27  		return fmt.Errorf("Invalid key: %s", err)
    28  	} else if err := memberlist.ValidateKey(keyBytes); err != nil {
    29  		return fmt.Errorf("Invalid key: %s", err)
    30  	}
    31  
    32  	// Just exit if the file already exists.
    33  	if _, err := os.Stat(path); err == nil {
    34  		return nil
    35  	}
    36  
    37  	keys = append(keys, key)
    38  	keyringBytes, err := json.Marshal(keys)
    39  	if err != nil {
    40  		return err
    41  	}
    42  
    43  	if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
    44  		return err
    45  	}
    46  
    47  	fh, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
    48  	if err != nil {
    49  		return err
    50  	}
    51  	defer fh.Close()
    52  
    53  	if _, err := fh.Write(keyringBytes); err != nil {
    54  		os.Remove(path)
    55  		return err
    56  	}
    57  
    58  	return nil
    59  }
    60  
    61  // loadKeyringFile will load a gossip encryption keyring out of a file. The file
    62  // must be in JSON format and contain a list of encryption key strings.
    63  func loadKeyringFile(c *serf.Config) error {
    64  	if c.KeyringFile == "" {
    65  		return nil
    66  	}
    67  
    68  	if _, err := os.Stat(c.KeyringFile); err != nil {
    69  		return err
    70  	}
    71  
    72  	keyringData, err := ioutil.ReadFile(c.KeyringFile)
    73  	if err != nil {
    74  		return err
    75  	}
    76  
    77  	keys := make([]string, 0)
    78  	if err := json.Unmarshal(keyringData, &keys); err != nil {
    79  		return err
    80  	}
    81  
    82  	return loadKeyring(c, keys)
    83  }
    84  
    85  // loadKeyring takes a list of base64-encoded strings and installs them in the
    86  // given Serf's keyring.
    87  func loadKeyring(c *serf.Config, keys []string) error {
    88  	keysDecoded := make([][]byte, len(keys))
    89  	for i, key := range keys {
    90  		keyBytes, err := base64.StdEncoding.DecodeString(key)
    91  		if err != nil {
    92  			return err
    93  		}
    94  		keysDecoded[i] = keyBytes
    95  	}
    96  
    97  	if len(keysDecoded) == 0 {
    98  		return fmt.Errorf("no keys present in keyring: %s", c.KeyringFile)
    99  	}
   100  
   101  	keyring, err := memberlist.NewKeyring(keysDecoded, keysDecoded[0])
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	c.MemberlistConfig.Keyring = keyring
   107  	return nil
   108  }
   109  
   110  // keyringProcess is used to abstract away the semantic similarities in
   111  // performing various operations on the encryption keyring.
   112  func (a *Agent) keyringProcess(args *structs.KeyringRequest) (*structs.KeyringResponses, error) {
   113  	var reply structs.KeyringResponses
   114  
   115  	if _, ok := a.delegate.(*consul.Server); !ok {
   116  		return nil, fmt.Errorf("keyring operations must run against a server node")
   117  	}
   118  	if err := a.RPC("Internal.KeyringOperation", args, &reply); err != nil {
   119  		return &reply, err
   120  	}
   121  
   122  	return &reply, nil
   123  }
   124  
   125  // ParseRelayFactor validates and converts the given relay factor to uint8
   126  func ParseRelayFactor(n int) (uint8, error) {
   127  	if n < 0 || n > 5 {
   128  		return 0, fmt.Errorf("Relay factor must be in range: [0, 5]")
   129  	}
   130  	return uint8(n), nil
   131  }
   132  
   133  // ListKeys lists out all keys installed on the collective Consul cluster. This
   134  // includes both servers and clients in all DC's.
   135  func (a *Agent) ListKeys(token string, relayFactor uint8) (*structs.KeyringResponses, error) {
   136  	args := structs.KeyringRequest{Operation: structs.KeyringList}
   137  	parseKeyringRequest(&args, token, relayFactor)
   138  	return a.keyringProcess(&args)
   139  }
   140  
   141  // InstallKey installs a new gossip encryption key
   142  func (a *Agent) InstallKey(key, token string, relayFactor uint8) (*structs.KeyringResponses, error) {
   143  	args := structs.KeyringRequest{Key: key, Operation: structs.KeyringInstall}
   144  	parseKeyringRequest(&args, token, relayFactor)
   145  	return a.keyringProcess(&args)
   146  }
   147  
   148  // UseKey changes the primary encryption key used to encrypt messages
   149  func (a *Agent) UseKey(key, token string, relayFactor uint8) (*structs.KeyringResponses, error) {
   150  	args := structs.KeyringRequest{Key: key, Operation: structs.KeyringUse}
   151  	parseKeyringRequest(&args, token, relayFactor)
   152  	return a.keyringProcess(&args)
   153  }
   154  
   155  // RemoveKey will remove a gossip encryption key from the keyring
   156  func (a *Agent) RemoveKey(key, token string, relayFactor uint8) (*structs.KeyringResponses, error) {
   157  	args := structs.KeyringRequest{Key: key, Operation: structs.KeyringRemove}
   158  	parseKeyringRequest(&args, token, relayFactor)
   159  	return a.keyringProcess(&args)
   160  }
   161  
   162  func parseKeyringRequest(req *structs.KeyringRequest, token string, relayFactor uint8) {
   163  	req.Token = token
   164  	req.RelayFactor = relayFactor
   165  }