Add controller-led worker auth flow (#2413)

This adds an action to the API that allows pulling out an activation
token that can be given to a worker in its config (directly or via env
or file). The worker can pass this along with its normal request in
place of its normal nonce and the server will automatically accept it.

Co-authored-by: Irena Rindos <irenarindos@users.noreply.github.com>
This commit is contained in:
Jeff MitchellandIrena Rindos authored and GitHub committed 2022-09-02 14:32:41 -04:00
1 parent a1be125f5b
commit 01fb949d0b
55 files changed
+2728 -625

No files matched your search

@@ -0,0 +1,40 @@
package workerscmd
import (
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/workers"
"github.com/hashicorp/boundary/internal/cmd/base"
)
func init() {
executeExtraControllerLedActions = executeExtraControllerLedActionsImpl
}
func (c *ControllerLedCommand) extraControllerLedHelpFunc(helpMap map[string]func() string) string {
var helpStr string
switch c.Func {
case "create":
helpStr = base.WrapForHelpText([]string{
"Usage: boundary workers create controller-led [options] [args]",
"",
" Create a worker using the controller-led approach, receiving an activation token for a worker. Example:",
"",
` $ boundary workers create controller-led -name us-east-1-1 "`,
"",
"",
})
}
return helpStr + c.Flags().Help()
}
func executeExtraControllerLedActionsImpl(c *ControllerLedCommand, origResp *api.Response, origItem *workers.Worker, origError error, workerClient *workers.Client, version uint32, opts []workers.Option) (*api.Response, *workers.Worker, error) {
switch c.Func {
case "create":
result, err := workerClient.CreateControllerLed(c.Context, c.FlagScopeId, opts...)
if err != nil {
return nil, nil, err
}
return result.GetResponse(), result.GetItem(), nil
}
return origResp, origItem, origError
}
@@ -0,0 +1,247 @@
// Code generated by "make cli"; DO NOT EDIT.
package workerscmd
import (
"errors"
"fmt"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/workers"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/common"
"github.com/hashicorp/go-secure-stdlib/strutil"
"github.com/mitchellh/cli"
"github.com/posener/complete"
)
func initControllerLedFlags() {
flagsOnce.Do(func() {
extraFlags := extraControllerLedActionsFlagsMapFunc()
for k, v := range extraFlags {
flagsControllerLedMap[k] = append(flagsControllerLedMap[k], v...)
}
})
}
var (
_ cli.Command = (*ControllerLedCommand)(nil)
_ cli.CommandAutocomplete = (*ControllerLedCommand)(nil)
)
type ControllerLedCommand struct {
*base.Command
Func string
plural string
}
func (c *ControllerLedCommand) AutocompleteArgs() complete.Predictor {
initControllerLedFlags()
return complete.PredictAnything
}
func (c *ControllerLedCommand) AutocompleteFlags() complete.Flags {
initControllerLedFlags()
return c.Flags().Completions()
}
func (c *ControllerLedCommand) Synopsis() string {
if extra := extraControllerLedSynopsisFunc(c); extra != "" {
return extra
}
synopsisStr := "worker"
synopsisStr = fmt.Sprintf("%s %s", "controller-led-type", synopsisStr)
return common.SynopsisFunc(c.Func, synopsisStr)
}
func (c *ControllerLedCommand) Help() string {
initControllerLedFlags()
var helpStr string
helpMap := common.HelpMap("worker")
switch c.Func {
default:
helpStr = c.extraControllerLedHelpFunc(helpMap)
}
// Keep linter from complaining if we don't actually generate code using it
_ = helpMap
return helpStr
}
var flagsControllerLedMap = map[string][]string{
"create": {"scope-id", "name", "description"},
}
func (c *ControllerLedCommand) Flags() *base.FlagSets {
if len(flagsControllerLedMap[c.Func]) == 0 {
return c.FlagSet(base.FlagSetNone)
}
set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat)
f := set.NewFlagSet("Command Options")
common.PopulateCommonFlags(c.Command, f, "controller-led-type worker", flagsControllerLedMap, c.Func)
extraControllerLedFlagsFunc(c, set, f)
return set
}
func (c *ControllerLedCommand) Run(args []string) int {
initControllerLedFlags()
switch c.Func {
case "":
return cli.RunResultHelp
case "update":
return cli.RunResultHelp
}
c.plural = "controller-led-type worker"
switch c.Func {
case "list":
c.plural = "controller-led-type workers"
}
f := c.Flags()
if err := f.Parse(args); err != nil {
c.PrintCliError(err)
return base.CommandUserError
}
if strutil.StrListContains(flagsControllerLedMap[c.Func], "id") && c.FlagId == "" {
c.PrintCliError(errors.New("ID is required but not passed in via -id"))
return base.CommandUserError
}
var opts []workers.Option
if strutil.StrListContains(flagsControllerLedMap[c.Func], "scope-id") {
switch c.Func {
case "create":
if c.FlagScopeId == "" {
c.PrintCliError(errors.New("Scope ID must be passed in via -scope-id or BOUNDARY_SCOPE_ID"))
return base.CommandUserError
}
}
}
client, err := c.Client()
if c.WrapperCleanupFunc != nil {
defer func() {
if err := c.WrapperCleanupFunc(); err != nil {
c.PrintCliError(fmt.Errorf("Error cleaning kms wrapper: %w", err))
}
}()
}
if err != nil {
c.PrintCliError(fmt.Errorf("Error creating API client: %w", err))
return base.CommandCliError
}
workersClient := workers.NewClient(client)
switch c.FlagName {
case "":
case "null":
opts = append(opts, workers.DefaultName())
default:
opts = append(opts, workers.WithName(c.FlagName))
}
switch c.FlagDescription {
case "":
case "null":
opts = append(opts, workers.DefaultDescription())
default:
opts = append(opts, workers.WithDescription(c.FlagDescription))
}
switch c.FlagRecursive {
case true:
opts = append(opts, workers.WithRecursive(true))
}
if c.FlagFilter != "" {
opts = append(opts, workers.WithFilter(c.FlagFilter))
}
var version uint32
if ok := extraControllerLedFlagsHandlingFunc(c, f, &opts); !ok {
return base.CommandUserError
}
var resp *api.Response
var item *workers.Worker
switch c.Func {
}
resp, item, err = executeExtraControllerLedActions(c, resp, item, err, workersClient, version, opts)
if exitCode := c.checkFuncError(err); exitCode > 0 {
return exitCode
}
output, err := printCustomControllerLedActionOutput(c)
if err != nil {
c.PrintCliError(err)
return base.CommandUserError
}
if output {
return base.CommandSuccess
}
switch c.Func {
}
switch base.Format(c.UI) {
case "table":
c.UI.Output(printItemTable(item, resp))
case "json":
if ok := c.PrintJsonItem(resp); !ok {
return base.CommandCliError
}
}
return base.CommandSuccess
}
func (c *ControllerLedCommand) checkFuncError(err error) int {
if err == nil {
return 0
}
if apiErr := api.AsServerError(err); apiErr != nil {
c.PrintApiError(apiErr, fmt.Sprintf("Error from controller when performing %s on %s", c.Func, c.plural))
return base.CommandApiError
}
c.PrintCliError(fmt.Errorf("Error trying to %s %s: %s", c.Func, c.plural, err.Error()))
return base.CommandCliError
}
var (
extraControllerLedActionsFlagsMapFunc = func() map[string][]string { return nil }
extraControllerLedSynopsisFunc = func(*ControllerLedCommand) string { return "" }
extraControllerLedFlagsFunc = func(*ControllerLedCommand, *base.FlagSets, *base.FlagSet) {}
extraControllerLedFlagsHandlingFunc = func(*ControllerLedCommand, *base.FlagSets, *[]workers.Option) bool { return true }
executeExtraControllerLedActions = func(_ *ControllerLedCommand, inResp *api.Response, inItem *workers.Worker, inErr error, _ *workers.Client, _ uint32, _ []workers.Option) (*api.Response, *workers.Worker, error) {
return inResp, inItem, inErr
}
printCustomControllerLedActionOutput = func(*ControllerLedCommand) (bool, error) { return false, nil }
)
@@ -261,6 +261,9 @@ func printItemTable(item *workers.Worker, resp *api.Response) string {
if !item.LastStatusTime.IsZero() {
nonAttributeMap["Last Status Time"] = item.LastStatusTime
}
if item.ControllerGeneratedActivationToken != "" {
nonAttributeMap["Controller-Generated Activation Token"] = item.ControllerGeneratedActivationToken
}
resultMap := resp.Map
if count, ok := resultMap[globals.ActiveConnectionCountField]; ok {