From 07f1409a07470b4e2d9cdc52431bf5f119b015dd Mon Sep 17 00:00:00 2001 From: swayne275 Date: Tue, 26 Oct 2021 10:52:29 -0600 Subject: [PATCH] API Lock CLI OSS port (#12925) * api lock in oss * add namespace lock/unlock help --- command/commands.go | 10 ++++ command/namespace.go | 8 +++ command/namespace_api_lock.go | 87 ++++++++++++++++++++++++++++++ command/namespace_api_unlock.go | 93 +++++++++++++++++++++++++++++++++ 4 files changed, 198 insertions(+) create mode 100644 command/namespace_api_lock.go create mode 100644 command/namespace_api_unlock.go diff --git a/command/commands.go b/command/commands.go index dab9a8e4d4..dc08c4a746 100644 --- a/command/commands.go +++ b/command/commands.go @@ -339,6 +339,16 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { BaseCommand: getBaseCommand(), }, nil }, + "namespace lock": func() (cli.Command, error) { + return &NamespaceAPILockCommand{ + BaseCommand: getBaseCommand(), + }, nil + }, + "namespace unlock": func() (cli.Command, error) { + return &NamespaceAPIUnlockCommand{ + BaseCommand: getBaseCommand(), + }, nil + }, "operator": func() (cli.Command, error) { return &OperatorCommand{ BaseCommand: getBaseCommand(), diff --git a/command/namespace.go b/command/namespace.go index 8adfc66ef2..89cf2e0296 100644 --- a/command/namespace.go +++ b/command/namespace.go @@ -40,6 +40,14 @@ Usage: vault namespace [options] [args] $ vault namespace delete + Lock the API for an existing namespace: + + $ vault namespace lock + + Unlock the API for an existing namespace: + + $ vault namespace unlock + Please see the individual subcommand help for detailed usage information. ` diff --git a/command/namespace_api_lock.go b/command/namespace_api_lock.go new file mode 100644 index 0000000000..48fec344c7 --- /dev/null +++ b/command/namespace_api_lock.go @@ -0,0 +1,87 @@ +package command + +import ( + "fmt" + "strings" + + "github.com/hashicorp/vault/helper/namespace" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +var ( + _ cli.Command = (*NamespaceAPILockCommand)(nil) + _ cli.CommandAutocomplete = (*NamespaceAPILockCommand)(nil) +) + +type NamespaceAPILockCommand struct { + *BaseCommand +} + +func (c *NamespaceAPILockCommand) Synopsis() string { + return "Lock the API for particular namespaces" +} + +func (c *NamespaceAPILockCommand) Help() string { + helpText := ` +Usage: vault namespace lock PATH + + Lock the current namespace, and all descendants: + + $ vault namespace lock + + Lock a child namespace, and all of its descendants (e.g. ns1/ns2/): + + $ vault namespace lock ns1/ns2 + +` + c.Flags().Help() + + return strings.TrimSpace(helpText) +} + +func (c *NamespaceAPILockCommand) Flags() *FlagSets { + return c.flagSet(FlagSetHTTP | FlagSetOutputFormat) +} + +func (c *NamespaceAPILockCommand) AutocompleteArgs() complete.Predictor { + return c.PredictVaultNamespaces() +} + +func (c *NamespaceAPILockCommand) AutocompleteFlags() complete.Flags { + return c.Flags().Completions() +} + +func (c *NamespaceAPILockCommand) Run(args []string) int { + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + args = f.Args() + if len(args) > 1 { + c.UI.Error(fmt.Sprintf("Too many arguments (expected 0 or 1, got %d)", len(args))) + return 1 + } + + // current namespace is already encoded in the :client: + client, err := c.Client() + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + + optionalChildNSPath := "" + if len(args) == 1 { + optionalChildNSPath = fmt.Sprintf("/%s", namespace.Canonicalize(args[0])) + } + + resp, err := client.Logical().Write(fmt.Sprintf("sys/namespaces/api-lock/lock%s", optionalChildNSPath), nil) + if err != nil { + c.UI.Error(fmt.Sprintf("Error locking namespace: %v", err)) + return 2 + } + + return OutputSecret(c.UI, resp) +} diff --git a/command/namespace_api_unlock.go b/command/namespace_api_unlock.go new file mode 100644 index 0000000000..38f4a764d4 --- /dev/null +++ b/command/namespace_api_unlock.go @@ -0,0 +1,93 @@ +package command + +import ( + "fmt" + "strings" + + "github.com/hashicorp/vault/helper/namespace" + "github.com/mitchellh/cli" + "github.com/posener/complete" +) + +var ( + _ cli.Command = (*NamespaceAPIUnlockCommand)(nil) + _ cli.CommandAutocomplete = (*NamespaceAPIUnlockCommand)(nil) +) + +type NamespaceAPIUnlockCommand struct { + *BaseCommand +} + +func (c *NamespaceAPIUnlockCommand) Synopsis() string { + return "Unlock the API for particular namespaces" +} + +func (c *NamespaceAPIUnlockCommand) Help() string { + helpText := ` +Usage: vault namespace unlock [options] PATH + + Unlock the current namespace, and all descendants, with unlock key: + + $ vault namespace unlock -unlock-key= + + Unlock the current namespace, and all descendants (from a root token): + + $ vault namespace unlock + + Unlock a child namespace, and all of its descendants (e.g. ns1/ns2/): + + $ vault namespace lock -unlock-key= ns1/ns2 + +` + c.Flags().Help() + + return strings.TrimSpace(helpText) +} + +func (c *NamespaceAPIUnlockCommand) Flags() *FlagSets { + return c.flagSet(FlagSetHTTP | FlagSetOutputFormat) +} + +func (c *NamespaceAPIUnlockCommand) AutocompleteArgs() complete.Predictor { + return c.PredictVaultNamespaces() +} + +func (c *NamespaceAPIUnlockCommand) AutocompleteFlags() complete.Flags { + return c.Flags().Completions() +} + +func (c *NamespaceAPIUnlockCommand) Run(args []string) int { + f := c.Flags() + + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + args = f.Args() + if len(args) > 1 { + c.UI.Error(fmt.Sprintf("Too many arguments (expected 0 or 1, got %d)", len(args))) + return 1 + } + + // current namespace is already encoded in the :client: + client, err := c.Client() + if err != nil { + c.UI.Error(err.Error()) + return 2 + } + + optionalChildNSPath := "" + if len(args) == 1 { + optionalChildNSPath = fmt.Sprintf("/%s", namespace.Canonicalize(args[0])) + } + + _, err = client.Logical().Write(fmt.Sprintf("sys/namespaces/api-lock/unlock%s", optionalChildNSPath), map[string]interface{}{ + "unlock_key": c.flagUnlockKey, + }) + if err != nil { + c.UI.Error(fmt.Sprintf("Error unlocking namespace: %v", err)) + return 2 + } + + return 0 +}