Update config encrypt/decrypt CLI command (#309)
This updates the command to our emergent standards. It also enables using a separate config file for a config kms, which allows non-string values to be encrypted. This allowed some nice cleanup as well.
This commit is contained in:
1 parent
274afa6b02
commit
28df6eb7b0
18 files changed
+307
-197
No files matched your search
+1
-1
@@ -122,6 +122,6 @@ internal/ui/assets.go
|
||||
update-ui-assets*
|
||||
|
||||
# Test config file
|
||||
test.hcl
|
||||
test*.hcl
|
||||
|
||||
# vim: set filetype=conf :
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
|
||||
"github.com/hashicorp/boundary/api"
|
||||
"github.com/hashicorp/boundary/api/authtokens"
|
||||
"github.com/hashicorp/boundary/internal/wrapper"
|
||||
"github.com/hashicorp/boundary/recovery"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/pkg/errors"
|
||||
@@ -183,10 +184,13 @@ func (c *Command) Client(opt ...Option) (*api.Client, error) {
|
||||
tokenName := "default"
|
||||
switch {
|
||||
case c.FlagRecoveryConfig != "":
|
||||
wrapper, err := recovery.GetWrapper(c.Context, c.FlagRecoveryConfig)
|
||||
wrapper, err := wrapper.GetWrapper(c.FlagRecoveryConfig, "recovery")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if wrapper == nil {
|
||||
return nil, errors.New(`No "kms" block with purpose "recovery" found`)
|
||||
}
|
||||
if err := wrapper.Init(c.Context); err != nil {
|
||||
return nil, fmt.Errorf("Error initializing kms: %w", err)
|
||||
}
|
||||
@@ -284,11 +288,6 @@ func (c *Command) FlagSet(bit FlagSetBit) *FlagSets {
|
||||
c.flagsOnce.Do(func() {
|
||||
set := NewFlagSets(c.UI)
|
||||
|
||||
// These flag sets will apply to all leaf subcommands.
|
||||
// TODO: Optional, but FlagSetHTTP can be safely removed from the individual
|
||||
// Flags() subcommands.
|
||||
bit = bit | FlagSetHTTP
|
||||
|
||||
if bit&FlagSetHTTP != 0 {
|
||||
f := set.NewFlagSet("Connection Options")
|
||||
|
||||
|
||||
@@ -213,7 +213,13 @@ func (b *Server) PrintInfo(ui cli.Ui, mode string) {
|
||||
}
|
||||
|
||||
// Server configuration output
|
||||
padding := 36
|
||||
padding := 0
|
||||
for _, k := range b.InfoKeys {
|
||||
currPadding := padding - len(k)
|
||||
if currPadding < 2 {
|
||||
padding = len(k) + 2
|
||||
}
|
||||
}
|
||||
sort.Strings(b.InfoKeys)
|
||||
ui.Output(fmt.Sprintf("==> Boundary %s configuration:\n", mode))
|
||||
for _, k := range b.InfoKeys {
|
||||
|
||||
@@ -144,12 +144,13 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
|
||||
"config encrypt": func() (cli.Command, error) {
|
||||
return &config.EncryptDecryptCommand{
|
||||
Command: base.NewCommand(ui),
|
||||
Encrypt: true,
|
||||
Func: "encrypt",
|
||||
}, nil
|
||||
},
|
||||
"config decrypt": func() (cli.Command, error) {
|
||||
return &config.EncryptDecryptCommand{
|
||||
Command: base.NewCommand(ui),
|
||||
Func: "decrypt",
|
||||
}, nil
|
||||
},
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
@@ -18,23 +16,21 @@ func (c *Command) Synopsis() string {
|
||||
}
|
||||
|
||||
func (c *Command) Help() string {
|
||||
helpText := `
|
||||
Usage: boundary config <subcommand> [options] [args]
|
||||
|
||||
This command groups subcommands for operators interacting with Boundary's
|
||||
config files. Here are a few examples of config commands:
|
||||
|
||||
Encrypt sensitive values in a config file:
|
||||
|
||||
$ boundary config encrypt config.hcl
|
||||
|
||||
Decrypt sensitive values in a config file:
|
||||
|
||||
$ boundary config decrypt config.hcl
|
||||
|
||||
Please see the individual subcommand help for detailed usage information.`
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
return base.WrapForHelpText([]string{
|
||||
"Usage: boundary config <subcommand> [options] [args]",
|
||||
"",
|
||||
" This command groups subcommands for operators interacting with Boundary's config files. Here are a few examples of config commands:",
|
||||
"",
|
||||
" Encrypt sensitive values in a config file:",
|
||||
"",
|
||||
" $ boundary config encrypt config.hcl",
|
||||
"",
|
||||
" Decrypt sensitive values in a config file:",
|
||||
"",
|
||||
" $ boundary config decrypt config.hcl",
|
||||
"",
|
||||
" Please see the individual subcommand help for detailed usage information.",
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Command) Run(args []string) int {
|
||||
|
||||
@@ -3,12 +3,13 @@ package config
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/textproto"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/boundary/internal/wrapper"
|
||||
"github.com/hashicorp/shared-secure-libs/configutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/strutil"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
@@ -18,49 +19,82 @@ var _ cli.CommandAutocomplete = (*EncryptDecryptCommand)(nil)
|
||||
|
||||
type EncryptDecryptCommand struct {
|
||||
*base.Command
|
||||
Encrypt bool
|
||||
Func string
|
||||
|
||||
flagConfig string
|
||||
flagConfigKms string
|
||||
flagOverwrite bool
|
||||
flagStrip bool
|
||||
}
|
||||
|
||||
func (c *EncryptDecryptCommand) Synopsis() string {
|
||||
dir := "Decrypts"
|
||||
if c.Encrypt {
|
||||
dir = "Encrypts"
|
||||
}
|
||||
return fmt.Sprintf("%s sensitive values in Vault's configuration file", dir)
|
||||
return fmt.Sprintf("%s sensitive values in Boundary's configuration file", textproto.CanonicalMIMEHeaderKey(c.Func))
|
||||
}
|
||||
|
||||
func (c *EncryptDecryptCommand) Help() string {
|
||||
subCmd := "Decrypt"
|
||||
if c.Encrypt {
|
||||
subCmd = "Encrypt"
|
||||
}
|
||||
helpText := `
|
||||
Usage: boundary config %s [options] [args]
|
||||
|
||||
%s sensitive values in a Boundary's configuration file. These values must be marked
|
||||
with {{%s()}} as appropriate. This can only be used with string parameters, and
|
||||
the markers must be inside the quote marks delimiting the string; as an example:
|
||||
|
||||
foo = "{{encrypt(bar)}}"
|
||||
|
||||
By default this will print the new configuration out. To overwrite into the same
|
||||
file use the -overwrite flag.
|
||||
|
||||
$ boundary config %s -overwrite config.hcl
|
||||
`
|
||||
helpText = fmt.Sprintf(helpText, strings.ToLower(subCmd), subCmd, strings.ToLower(subCmd), strings.ToLower(subCmd))
|
||||
var args []string
|
||||
args = append(args,
|
||||
"Usage: boundary config {{func}} [options] [args]",
|
||||
"",
|
||||
" {{upperfunc}} sensitive values in a Boundary's configuration file. These values must be marked with {{{{func}}()}} as appropriate. Example:",
|
||||
"",
|
||||
` foo = "{{encrypt(bar)}}"`,
|
||||
"",
|
||||
" By default this will print out the new configuration. To overwrite into the same file use the -overwrite flag.",
|
||||
"",
|
||||
" $ boundary config {{func}} -overwrite config.hcl",
|
||||
"",
|
||||
` In order for this command to perform its task, a "kms" block must be defined within a configuration file. Example:`,
|
||||
"",
|
||||
` kms "aead" {`,
|
||||
` purpose = "config"`,
|
||||
` aead_type = "aes-gcm"`,
|
||||
` key = "7xtkEoS5EXPbgynwd+dDLHopaCqK8cq0Rpep4eooaTs="`,
|
||||
` }`,
|
||||
"",
|
||||
` The "kms" block can be defined in the configuration file or via the -config flag. If defined in the configuration file, only string parameters are supported, and the markers must be inside the quote marks delimiting the string. Additionally, if the block is defined inline, do NOT use an an "aead" block with the key defined in the configuration file as it provides no protection.`,
|
||||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
for i, line := range args {
|
||||
args[i] =
|
||||
strings.Replace(
|
||||
strings.Replace(
|
||||
line, "{{func}}", c.Func, -1,
|
||||
),
|
||||
"{{upperfunc}}", textproto.CanonicalMIMEHeaderKey(c.Func), -1,
|
||||
)
|
||||
}
|
||||
|
||||
return base.WrapForHelpText(args) + c.Flags().Help()
|
||||
}
|
||||
|
||||
func (c *EncryptDecryptCommand) Flags() *base.FlagSets {
|
||||
set := c.FlagSet(0)
|
||||
set := c.FlagSet(base.FlagSetNone)
|
||||
|
||||
f := set.NewFlagSet("Command Options")
|
||||
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: "config-kms",
|
||||
Target: &c.flagConfigKms,
|
||||
Completion: complete.PredictOr(
|
||||
complete.PredictFiles("*.hcl"),
|
||||
complete.PredictFiles("*.json"),
|
||||
),
|
||||
Usage: `If specified, the given file will be parsed for a "kms" block with purpose "config" and will use it to perform the command. If not set, the command will expect a block inline with the configuration file, and will only be able to support quoted string parameters.`,
|
||||
})
|
||||
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: "config",
|
||||
Target: &c.flagConfig,
|
||||
Completion: complete.PredictOr(
|
||||
complete.PredictFiles("*.hcl"),
|
||||
complete.PredictFiles("*.json"),
|
||||
),
|
||||
Usage: `The configuration file upon which to perform encryption or decryption`,
|
||||
})
|
||||
|
||||
f.BoolVar(&base.BoolVar{
|
||||
Name: "overwrite",
|
||||
Target: &c.flagOverwrite,
|
||||
@@ -85,54 +119,51 @@ func (c *EncryptDecryptCommand) AutocompleteFlags() complete.Flags {
|
||||
}
|
||||
|
||||
func (c *EncryptDecryptCommand) Run(args []string) (ret int) {
|
||||
op := "decrypt"
|
||||
if c.Encrypt {
|
||||
op = "encrypt"
|
||||
}
|
||||
|
||||
f := c.Flags()
|
||||
if err := f.Parse(args); err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
path := ""
|
||||
args = f.Args()
|
||||
switch len(args) {
|
||||
case 1:
|
||||
path = strings.TrimSpace(args[0])
|
||||
|
||||
switch c.flagConfig {
|
||||
case "":
|
||||
c.UI.Error(`Missing required parameter -config`)
|
||||
return 1
|
||||
default:
|
||||
c.UI.Error(fmt.Sprintf("Incorrect arguments (expected 1, got %d)", len(args)))
|
||||
return 1
|
||||
c.flagConfig = strings.TrimSpace(c.flagConfig)
|
||||
}
|
||||
|
||||
if path == "" {
|
||||
c.UI.Error("A configuration file must be specified")
|
||||
return 1
|
||||
kmsDefFile := c.flagConfig
|
||||
|
||||
switch c.flagConfigKms {
|
||||
case "":
|
||||
default:
|
||||
kmsDefFile = strings.TrimSpace(c.flagConfigKms)
|
||||
}
|
||||
|
||||
kmses, err := configutil.LoadConfigKMSes(path)
|
||||
wrapper, err := wrapper.GetWrapper(kmsDefFile, "config")
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Errorf("Error loading configuration from %s: %w", path, err).Error())
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
if wrapper == nil {
|
||||
c.UI.Error(`No wrapper with "config" purpose found"`)
|
||||
return 1
|
||||
}
|
||||
|
||||
var kms *configutil.KMS
|
||||
for _, v := range kmses {
|
||||
if strutil.StrListContains(v.Purpose, "config") {
|
||||
if kms != nil {
|
||||
c.UI.Error("Only one kms block marked for \"config\" purpose is allowed")
|
||||
return 1
|
||||
}
|
||||
kms = v
|
||||
if err := wrapper.Init(c.Context); err != nil {
|
||||
c.UI.Error(fmt.Errorf("Error initializing KMS: %w", err).Error())
|
||||
return 1
|
||||
}
|
||||
defer func() {
|
||||
if err := wrapper.Finalize(c.Context); err != nil {
|
||||
c.UI.Warn(fmt.Errorf("Error encountered when finalizing KMS: %w", err).Error())
|
||||
}
|
||||
}
|
||||
if kms == nil {
|
||||
c.UI.Error("No kms block with \"config\" purpose defined in the configuration file")
|
||||
return 1
|
||||
}
|
||||
}()
|
||||
|
||||
d, err := ioutil.ReadFile(path)
|
||||
d, err := ioutil.ReadFile(c.flagConfig)
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Errorf("Error reading config file: %w", err).Error())
|
||||
return 1
|
||||
@@ -140,18 +171,9 @@ func (c *EncryptDecryptCommand) Run(args []string) (ret int) {
|
||||
|
||||
raw := string(d)
|
||||
|
||||
wrapper, err := configutil.ConfigureWrapper(kms, nil, nil, nil)
|
||||
raw, err = configutil.EncryptDecrypt(raw, c.Func == "decrypt", c.flagStrip, wrapper)
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Errorf("Error creating kms: %w", err).Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
wrapper.Init(c.Context)
|
||||
defer wrapper.Finalize(c.Context)
|
||||
|
||||
raw, err = configutil.EncryptDecrypt(raw, !c.Encrypt, c.flagStrip, wrapper)
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Errorf("Error %sing via kms: %w", op, err).Error())
|
||||
c.UI.Error(fmt.Errorf("Error %sing via kms: %w", c.Func, err).Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -160,7 +182,7 @@ func (c *EncryptDecryptCommand) Run(args []string) (ret int) {
|
||||
return 0
|
||||
}
|
||||
|
||||
file, err := os.Create(path)
|
||||
file, err := os.Create(c.flagConfig)
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Errorf("Error opening file for writing: %w", err).Error())
|
||||
return 1
|
||||
@@ -182,5 +204,5 @@ func (c *EncryptDecryptCommand) Run(args []string) (ret int) {
|
||||
c.UI.Error(fmt.Sprintf("Wrong number of bytes written to file, expected %d, wrote %d", len(raw), n))
|
||||
}
|
||||
|
||||
return 0
|
||||
return
|
||||
}
|
||||
@@ -15,31 +15,52 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
configEncryptPath = "./fixtures/configEncrypt.hcl"
|
||||
configDecryptPath = "./fixtures/configDecrypt.hcl"
|
||||
configEncryptPath = "./fixtures/config_encrypt.hcl"
|
||||
configDecryptPath = "./fixtures/config_decrypt.hcl"
|
||||
configKmsPath = "./fixtures/config_kms.hcl"
|
||||
configExtEncryptPath = "./fixtures/config_ext_encrypt.hcl"
|
||||
configExtEncryptStrippedPath = "./fixtures/config_ext_encrypt_stripped.hcl"
|
||||
configExtDecryptPath = "./fixtures/config_ext_decrypt.hcl"
|
||||
)
|
||||
|
||||
func TestEncryptDecrypt(t *testing.T) {
|
||||
|
||||
cases := []struct {
|
||||
encrypt bool
|
||||
config string
|
||||
exp string
|
||||
f string
|
||||
config string
|
||||
configKms string
|
||||
exp string
|
||||
strip bool
|
||||
}{
|
||||
{
|
||||
encrypt: true,
|
||||
config: configEncryptPath,
|
||||
exp: "",
|
||||
f: "encrypt",
|
||||
config: configEncryptPath,
|
||||
},
|
||||
{
|
||||
encrypt: false,
|
||||
config: configDecryptPath,
|
||||
exp: configEncryptPath,
|
||||
f: "decrypt",
|
||||
config: configDecryptPath,
|
||||
exp: configEncryptPath,
|
||||
},
|
||||
{
|
||||
f: "encrypt-ext",
|
||||
config: configExtEncryptPath,
|
||||
configKms: configKmsPath,
|
||||
},
|
||||
{
|
||||
f: "decrypt-ext",
|
||||
config: configExtDecryptPath,
|
||||
configKms: configKmsPath,
|
||||
exp: configExtEncryptPath,
|
||||
},
|
||||
{
|
||||
f: "decrypt-ext-strip",
|
||||
config: configExtDecryptPath,
|
||||
configKms: configKmsPath,
|
||||
exp: configExtEncryptStrippedPath,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(fmt.Sprintf("Test encrypt %v", c.encrypt), func(t *testing.T) {
|
||||
t.Run(fmt.Sprintf("Test encrypt %v", c.f), func(t *testing.T) {
|
||||
|
||||
var b bytes.Buffer
|
||||
|
||||
@@ -51,9 +72,17 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||
|
||||
cmd := &EncryptDecryptCommand{
|
||||
Command: base.NewCommand(ui),
|
||||
Encrypt: c.encrypt}
|
||||
Func: c.f,
|
||||
}
|
||||
|
||||
if err := cmd.Run([]string{c.config}); err != 0 {
|
||||
args := []string{"-config", c.config}
|
||||
if c.configKms != "" {
|
||||
args = append(args, "-config-kms", c.configKms)
|
||||
}
|
||||
if c.strip {
|
||||
args = append(args, "-strip")
|
||||
}
|
||||
if err := cmd.Run(args); err != 0 {
|
||||
assert.Equal(t, err, 0)
|
||||
}
|
||||
|
||||
@@ -64,7 +93,7 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||
// If it's encrypting, then we can assume that it did the right thing since
|
||||
// there are many tests on the underlying codebase for that. If it's not
|
||||
// encrypting, compare it to the cleartext to verify because we can.
|
||||
if !c.encrypt {
|
||||
if c.f == "decrypt" {
|
||||
expected, err := ioutil.ReadFile(c.exp)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,7 @@
|
||||
int_val = {{decrypt(Ch7a69AX8R5w_cCZJUqLTQkWesuSHMrHxrRMMZnq53QqAA)}}
|
||||
bool_val = {{decrypt(CiDURLrWUXLhEfkOemqqiQlcD_gsGsIx-kxVTIlVncN6-yoA)}}
|
||||
kms "aead" {
|
||||
purpose = "root"
|
||||
aead_type = "aes-gcm"
|
||||
key ="{{decrypt(CkiRTINwX19TnC3AB-zx5E133TXI9KzBWb8TxfVDrPb9m3Yfm9K99OkuJgRTj1rjmeMF-Kpl-0oouEc8_mNk6oPIqD8nUNvH3FYqAA)}}"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
int_val = {{encrypt(20)}}
|
||||
bool_val = {{encrypt(true)}}
|
||||
kms "aead" {
|
||||
purpose = "root"
|
||||
aead_type = "aes-gcm"
|
||||
key ="{{encrypt(aA1hxJo0JUAqcIATx/r0QTjAGD/btCPechEsukI2bt0=)}}"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
int_val = 20
|
||||
bool_val = true
|
||||
kms "aead" {
|
||||
purpose = "root"
|
||||
aead_type = "aes-gcm"
|
||||
key ="aA1hxJo0JUAqcIATx/r0QTjAGD/btCPechEsukI2bt0=)"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
kms "aead" {
|
||||
purpose = "config"
|
||||
aead_type = "aes-gcm"
|
||||
key = "c964AJj8VW8w4hKz/Jd8MvuLt0kkcjVuFqMiMvTvvN8="
|
||||
}
|
||||
@@ -10,9 +10,10 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/boundary/internal/cmd/config"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller"
|
||||
"github.com/hashicorp/boundary/internal/wrapper"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/shared-secure-libs/configutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/mlock"
|
||||
"github.com/hashicorp/vault/sdk/helper/strutil"
|
||||
"github.com/mitchellh/cli"
|
||||
@@ -33,9 +34,10 @@ type Command struct {
|
||||
Config *config.Config
|
||||
controller *controller.Controller
|
||||
|
||||
configKMS *configutil.KMS
|
||||
configWrapper wrapping.Wrapper
|
||||
|
||||
flagConfig string
|
||||
flagConfigKms string
|
||||
flagLogLevel string
|
||||
flagLogFormat string
|
||||
flagCombineLogs bool
|
||||
@@ -78,7 +80,17 @@ func (c *Command) Flags() *base.FlagSets {
|
||||
complete.PredictFiles("*.hcl"),
|
||||
complete.PredictFiles("*.json"),
|
||||
),
|
||||
Usage: "Path to a configuration file.",
|
||||
Usage: "Path to the configuration file.",
|
||||
})
|
||||
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: "config-kms",
|
||||
Target: &c.flagConfigKms,
|
||||
Completion: complete.PredictOr(
|
||||
complete.PredictFiles("*.hcl"),
|
||||
complete.PredictFiles("*.json"),
|
||||
),
|
||||
Usage: `Path to a configuration file containing a "kms" block marked for "config" purpose, to perform decryption of the main configuration file. If not set, will look for such a block in the main configuration file, which has some drawbacks; see the help output for "boundary config encrypt -h" for details.`,
|
||||
})
|
||||
|
||||
f.StringVar(&base.StringVar{
|
||||
@@ -178,6 +190,14 @@ func (c *Command) Run(args []string) int {
|
||||
return result
|
||||
}
|
||||
|
||||
if c.configWrapper != nil {
|
||||
defer func() {
|
||||
if err := c.configWrapper.Finalize(c.Context); err != nil {
|
||||
c.UI.Warn(fmt.Errorf("Error finalizing config kms: %w", err).Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if err := c.SetupLogging(c.flagLogLevel, c.flagLogFormat, c.Config.LogLevel, c.Config.LogFormat); err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
@@ -317,19 +337,20 @@ func (c *Command) ParseFlagsAndConfig(args []string) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// preload the KMS for encrypting/decrypting the config parameters
|
||||
kmss, err := configutil.LoadConfigKMSes(c.flagConfig)
|
||||
wrapperPath := c.flagConfig
|
||||
if c.flagConfigKms != "" {
|
||||
wrapperPath = c.flagConfigKms
|
||||
}
|
||||
wrapper, err := wrapper.GetWrapper(wrapperPath, "config")
|
||||
if err != nil {
|
||||
c.UI.Error("error loading KMS config: " + err.Error())
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
for _, kms := range kmss {
|
||||
for _, purpose := range kms.Purpose {
|
||||
if purpose == "config" {
|
||||
c.configKMS = kms
|
||||
break
|
||||
}
|
||||
if wrapper != nil {
|
||||
c.configWrapper = wrapper
|
||||
if err := wrapper.Init(c.Context); err != nil {
|
||||
c.UI.Error(fmt.Errorf("Could not initialize kms: %w", err).Error())
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,7 +372,7 @@ func (c *Command) ParseFlagsAndConfig(args []string) int {
|
||||
c.flagDevLoginName = ""
|
||||
}
|
||||
|
||||
c.Config, err = config.LoadFile(c.flagConfig, c.configKMS)
|
||||
c.Config, err = config.LoadFile(c.flagConfig, wrapper)
|
||||
if err != nil {
|
||||
c.UI.Error("Error parsing config: " + err.Error())
|
||||
return 1
|
||||
@@ -361,7 +382,7 @@ func (c *Command) ParseFlagsAndConfig(args []string) int {
|
||||
if len(c.flagConfig) == 0 {
|
||||
c.Config, err = config.DevController()
|
||||
} else {
|
||||
c.Config, err = config.LoadFile(c.flagConfig, c.configKMS)
|
||||
c.Config, err = config.LoadFile(c.flagConfig, wrapper)
|
||||
}
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Errorf("Error creating dev config: %w", err).Error())
|
||||
@@ -468,7 +489,7 @@ func (c *Command) WaitForInterrupt() int {
|
||||
goto RUNRELOADFUNCS
|
||||
}
|
||||
|
||||
newConf, err = config.LoadFile(c.flagConfig, c.configKMS)
|
||||
newConf, err = config.LoadFile(c.flagConfig, c.configWrapper)
|
||||
if err != nil {
|
||||
c.Logger.Error("could not reload config", "path", c.flagConfig, "error", err)
|
||||
goto RUNRELOADFUNCS
|
||||
@@ -480,9 +501,6 @@ func (c *Command) WaitForInterrupt() int {
|
||||
goto RUNRELOADFUNCS
|
||||
}
|
||||
|
||||
// Commented out until we need this
|
||||
//controller.SetConfig(config)
|
||||
|
||||
if newConf.LogLevel != "" {
|
||||
configLogLevel := strings.ToLower(strings.TrimSpace(newConf.LogLevel))
|
||||
switch configLogLevel {
|
||||
|
||||
@@ -9,9 +9,10 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/cmd/base"
|
||||
"github.com/hashicorp/boundary/internal/cmd/config"
|
||||
"github.com/hashicorp/boundary/internal/servers/worker"
|
||||
"github.com/hashicorp/boundary/internal/wrapper"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/shared-secure-libs/configutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/mlock"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/posener/complete"
|
||||
@@ -33,9 +34,10 @@ type Command struct {
|
||||
Config *config.Config
|
||||
worker *worker.Worker
|
||||
|
||||
configKMS *configutil.KMS
|
||||
configWrapper wrapping.Wrapper
|
||||
|
||||
flagConfig string
|
||||
flagConfigKms string
|
||||
flagLogLevel string
|
||||
flagLogFormat string
|
||||
flagDev bool
|
||||
@@ -76,6 +78,16 @@ func (c *Command) Flags() *base.FlagSets {
|
||||
Usage: "Path to a configuration file.",
|
||||
})
|
||||
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: "config-kms",
|
||||
Target: &c.flagConfigKms,
|
||||
Completion: complete.PredictOr(
|
||||
complete.PredictFiles("*.hcl"),
|
||||
complete.PredictFiles("*.json"),
|
||||
),
|
||||
Usage: `Path to a configuration file containing a "kms" block marked for "config" purpose, to perform decryption of the main configuration file. If not set, will look for such a block in the main configuration file, which has some drawbacks; see the help output for "boundary config encrypt -h" for details.`,
|
||||
})
|
||||
|
||||
f.StringVar(&base.StringVar{
|
||||
Name: "log-level",
|
||||
Target: &c.flagLogLevel,
|
||||
@@ -135,6 +147,14 @@ func (c *Command) Run(args []string) int {
|
||||
return result
|
||||
}
|
||||
|
||||
if c.configWrapper != nil {
|
||||
defer func() {
|
||||
if err := c.configWrapper.Finalize(c.Context); err != nil {
|
||||
c.UI.Warn(fmt.Errorf("Error finalizing config kms: %w", err).Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if err := c.SetupLogging(c.flagLogLevel, c.flagLogFormat, c.Config.LogLevel, c.Config.LogFormat); err != nil {
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
@@ -206,19 +226,20 @@ func (c *Command) ParseFlagsAndConfig(args []string) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// preload the KMS for encrypting/decrypting the config parameters
|
||||
kmss, err := configutil.LoadConfigKMSes(c.flagConfig)
|
||||
wrapperPath := c.flagConfig
|
||||
if c.flagConfigKms != "" {
|
||||
wrapperPath = c.flagConfigKms
|
||||
}
|
||||
wrapper, err := wrapper.GetWrapper(wrapperPath, "config")
|
||||
if err != nil {
|
||||
c.UI.Error("error loading KMS config: " + err.Error())
|
||||
c.UI.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
for _, kms := range kmss {
|
||||
for _, purpose := range kms.Purpose {
|
||||
if purpose == "config" {
|
||||
c.configKMS = kms
|
||||
break
|
||||
}
|
||||
if wrapper != nil {
|
||||
c.configWrapper = wrapper
|
||||
if err := wrapper.Init(c.Context); err != nil {
|
||||
c.UI.Error(fmt.Errorf("Could not initialize kms: %w", err).Error())
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +249,7 @@ func (c *Command) ParseFlagsAndConfig(args []string) int {
|
||||
c.UI.Error("Must supply a config file with -config")
|
||||
return 1
|
||||
}
|
||||
c.Config, err = config.LoadFile(c.flagConfig, c.configKMS)
|
||||
c.Config, err = config.LoadFile(c.flagConfig, wrapper)
|
||||
if err != nil {
|
||||
c.UI.Error("Error parsing config: " + err.Error())
|
||||
return 1
|
||||
@@ -238,7 +259,7 @@ func (c *Command) ParseFlagsAndConfig(args []string) int {
|
||||
if len(c.flagConfig) == 0 {
|
||||
c.Config, err = config.DevWorker()
|
||||
} else {
|
||||
c.Config, err = config.LoadFile(c.flagConfig, c.configKMS)
|
||||
c.Config, err = config.LoadFile(c.flagConfig, wrapper)
|
||||
}
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Errorf("Error creating dev config: %s", err).Error())
|
||||
@@ -311,7 +332,7 @@ func (c *Command) WaitForInterrupt() int {
|
||||
goto RUNRELOADFUNCS
|
||||
}
|
||||
|
||||
newConf, err = config.LoadFile(c.flagConfig, c.configKMS)
|
||||
newConf, err = config.LoadFile(c.flagConfig, c.configWrapper)
|
||||
if err != nil {
|
||||
c.Logger.Error("could not reload config", "path", c.flagConfig, "error", err)
|
||||
goto RUNRELOADFUNCS
|
||||
@@ -323,9 +344,6 @@ func (c *Command) WaitForInterrupt() int {
|
||||
goto RUNRELOADFUNCS
|
||||
}
|
||||
|
||||
// Commented out until we need this
|
||||
//c.worker.SetConfig(config)
|
||||
|
||||
if newConf.LogLevel != "" {
|
||||
configLogLevel := strings.ToLower(strings.TrimSpace(newConf.LogLevel))
|
||||
switch configLogLevel {
|
||||
|
||||
@@ -2,13 +2,13 @@ package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/hashicorp/hcl"
|
||||
"github.com/hashicorp/shared-secure-libs/configutil"
|
||||
)
|
||||
@@ -175,7 +175,7 @@ func New() *Config {
|
||||
}
|
||||
|
||||
// LoadFile loads the configuration from the given file.
|
||||
func LoadFile(path string, kms *configutil.KMS) (*Config, error) {
|
||||
func LoadFile(path string, wrapper wrapping.Wrapper) (*Config, error) {
|
||||
d, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -183,8 +183,8 @@ func LoadFile(path string, kms *configutil.KMS) (*Config, error) {
|
||||
|
||||
raw := string(d)
|
||||
|
||||
if kms != nil {
|
||||
raw, err = configDecrypt(raw, kms)
|
||||
if wrapper != nil {
|
||||
raw, err = configutil.EncryptDecrypt(raw, true, true, wrapper)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -235,15 +235,3 @@ func (c *Config) Sanitized() map[string]interface{} {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func configDecrypt(raw string, kms *configutil.KMS) (string, error) {
|
||||
wrapper, err := configutil.ConfigureWrapper(kms, nil, nil, nil)
|
||||
if err != nil {
|
||||
return raw, err
|
||||
}
|
||||
|
||||
wrapper.Init(context.Background())
|
||||
defer wrapper.Finalize(context.Background())
|
||||
|
||||
return configutil.EncryptDecrypt(raw, true, true, wrapper)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package wrapper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/hashicorp/shared-secure-libs/configutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/strutil"
|
||||
)
|
||||
|
||||
func GetWrapper(path, purpose string) (wrapping.Wrapper, error) {
|
||||
kmses, err := configutil.LoadConfigKMSes(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error parsing config file: %w", err)
|
||||
}
|
||||
|
||||
var kms *configutil.KMS
|
||||
for _, v := range kmses {
|
||||
if strutil.StrListContains(v.Purpose, purpose) {
|
||||
if kms != nil {
|
||||
return nil, fmt.Errorf("Only one %q block marked for %q purpose is allowed", "kms", purpose)
|
||||
}
|
||||
kms = v
|
||||
}
|
||||
}
|
||||
if kms == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
wrapper, err := configutil.ConfigureWrapper(kms, nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error configuring kms: %w", err)
|
||||
}
|
||||
|
||||
return wrapper, nil
|
||||
}
|
||||
@@ -11,8 +11,6 @@ import (
|
||||
|
||||
wrapping "github.com/hashicorp/go-kms-wrapping"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/shared-secure-libs/configutil"
|
||||
"github.com/hashicorp/vault/sdk/helper/strutil"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
@@ -30,33 +28,6 @@ type Info struct {
|
||||
CreationTime time.Time `json:"creation_time"`
|
||||
}
|
||||
|
||||
func GetWrapper(ctx context.Context, path string) (wrapping.Wrapper, error) {
|
||||
kmses, err := configutil.LoadConfigKMSes(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error parsing config file: %w", err)
|
||||
}
|
||||
|
||||
var kms *configutil.KMS
|
||||
for _, v := range kmses {
|
||||
if strutil.StrListContains(v.Purpose, "recovery") {
|
||||
if kms != nil {
|
||||
return nil, fmt.Errorf("Only one %q block marked for %q purpose is allowed", "kms", "recovery")
|
||||
}
|
||||
kms = v
|
||||
}
|
||||
}
|
||||
if kms == nil {
|
||||
return nil, fmt.Errorf("No %q block marked for %q purpose is allowed", "kms", "recovery")
|
||||
}
|
||||
|
||||
wrapper, err := configutil.ConfigureWrapper(kms, nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error configuring kms: %w", err)
|
||||
}
|
||||
|
||||
return wrapper, nil
|
||||
}
|
||||
|
||||
func GenerateRecoveryToken(ctx context.Context, wrapper wrapping.Wrapper) (string, error) {
|
||||
b, err := uuid.GenerateRandomBytes(nonceLength)
|
||||
if err != nil {
|
||||
|
||||
Reference in new issue
Block a user