diff --git a/command/agent/command.go b/command/agent/command.go index c8bf12b90..2320168af 100644 --- a/command/agent/command.go +++ b/command/agent/command.go @@ -61,7 +61,6 @@ type Command struct { } func (c *Command) readConfig() *Config { - var dev *devModeConfig var configPath []string var servers string var meta []string @@ -86,8 +85,12 @@ func (c *Command) readConfig() *Config { // Role options var devMode bool var devConnectMode bool + var devConsulMode bool + var devVaultMode bool flags.BoolVar(&devMode, "dev", false, "") flags.BoolVar(&devConnectMode, "dev-connect", false, "") + flags.BoolVar(&devConsulMode, "dev-consul", false, "") + flags.BoolVar(&devVaultMode, "dev-vault", false, "") flags.BoolVar(&cmdConfig.Server.Enabled, "server", false, "") flags.BoolVar(&cmdConfig.Client.Enabled, "client", false, "") @@ -221,14 +224,26 @@ func (c *Command) readConfig() *Config { } // Load the configuration - dev, err := newDevModeConfig(devMode, devConnectMode) - if err != nil { - c.Ui.Error(err.Error()) - return nil - } var config *Config - if dev != nil { - config = DevConfig(dev) + + devConfig := &devModeConfig{ + defaultMode: devMode, + connectMode: devConnectMode, + consulMode: devConsulMode, + vaultMode: devVaultMode, + } + if devConfig.enabled() { + err := devConfig.validate() + if err != nil { + c.Ui.Error(err.Error()) + return nil + } + err = devConfig.networkConfig() + if err != nil { + c.Ui.Error(err.Error()) + return nil + } + config = DevConfig(devConfig) } else { config = DefaultConfig() } @@ -1402,9 +1417,19 @@ General Options (clients and servers): -dev-connect Start the agent in development mode, but bind to a public network - interface rather than localhost for using Consul Connect. This + interface rather than localhost for using Consul Connect. It may be used + with -dev-consul to configure default workload identities for Consul. This mode is supported only on Linux as root. + -dev-consul + Starts the agent in development mode with a default Consul configuration + for Nomad workload identity. It may be used with -dev-connect to configure + the agent for Consul Service Mesh. + + -dev-vault + Starts the agent in development mode with a default Vault configuration + for Nomad workload identity. + Server Options: -server diff --git a/command/agent/config.go b/command/agent/config.go index 8fdd7a669..34e58b4ce 100644 --- a/command/agent/config.go +++ b/command/agent/config.go @@ -1177,47 +1177,43 @@ type devModeConfig struct { // mode flags are set at the command line via -dev and -dev-connect defaultMode bool connectMode bool + consulMode bool + vaultMode bool bindAddr string iface string } -// newDevModeConfig parses the optional string value of the -dev flag -func newDevModeConfig(devMode, connectMode bool) (*devModeConfig, error) { - if !devMode && !connectMode { - return nil, nil - } - mode := &devModeConfig{} - mode.defaultMode = devMode - if connectMode { +func (mode *devModeConfig) enabled() bool { + return mode.defaultMode || mode.connectMode || + mode.consulMode || mode.vaultMode +} + +func (mode *devModeConfig) validate() error { + if mode.connectMode { if runtime.GOOS != "linux" { // strictly speaking -dev-connect only binds to the // non-localhost interface, but given its purpose // is to support a feature with network namespaces // we'll return an error here rather than let the agent // come up and fail unexpectedly to run jobs - return nil, fmt.Errorf("-dev-connect is only supported on linux.") + return fmt.Errorf("-dev-connect is only supported on linux.") } u, err := users.Current() if err != nil { - return nil, fmt.Errorf( + return fmt.Errorf( "-dev-connect uses network namespaces and is only supported for root: %v", err) } if u.Uid != "0" { - return nil, fmt.Errorf( + return fmt.Errorf( "-dev-connect uses network namespaces and is only supported for root.") } // Ensure Consul is on PATH if _, err := exec.LookPath("consul"); err != nil { - return nil, fmt.Errorf("-dev-connect requires a 'consul' binary in Nomad's $PATH") + return fmt.Errorf("-dev-connect requires a 'consul' binary in Nomad's $PATH") } - mode.connectMode = true } - err := mode.networkConfig() - if err != nil { - return nil, err - } - return mode, nil + return nil } func (mode *devModeConfig) networkConfig() error { @@ -1289,6 +1285,26 @@ func DevConfig(mode *devModeConfig) *Config { conf.Telemetry.PrometheusMetrics = true conf.Telemetry.PublishAllocationMetrics = true conf.Telemetry.PublishNodeMetrics = true + + if mode.consulMode { + conf.Consuls[0].ServiceIdentity = &config.WorkloadIdentityConfig{ + Audience: []string{"consul.io"}, + TTL: pointer.Of(time.Hour), + } + conf.Consuls[0].TaskIdentity = &config.WorkloadIdentityConfig{ + Audience: []string{"consul.io"}, + TTL: pointer.Of(time.Hour), + } + } + + if mode.vaultMode { + conf.Vaults[0].Enabled = pointer.Of(true) + conf.Vaults[0].Addr = "http://localhost:8200" + conf.Vaults[0].DefaultIdentity = &config.WorkloadIdentityConfig{ + Audience: []string{"vault.io"}, + TTL: pointer.Of(time.Hour), + } + } return conf } diff --git a/command/agent/config_test.go b/command/agent/config_test.go index eea131598..43c3189aa 100644 --- a/command/agent/config_test.go +++ b/command/agent/config_test.go @@ -10,7 +10,6 @@ import ( "path/filepath" "reflect" "runtime" - "strings" "testing" "time" @@ -725,59 +724,61 @@ func TestConfig_Listener(t *testing.T) { } } -func TestConfig_DevModeFlag(t *testing.T) { +func TestConfig_DevMode_validate(t *testing.T) { ci.Parallel(t) cases := []struct { - dev bool - connect bool - expected *devModeConfig + devConfig *devModeConfig expectedErr string }{} if runtime.GOOS != "linux" { cases = []struct { - dev bool - connect bool - expected *devModeConfig + devConfig *devModeConfig expectedErr string }{ - {false, false, nil, ""}, - {true, false, &devModeConfig{defaultMode: true, connectMode: false}, ""}, - {true, true, nil, "-dev-connect is only supported on linux"}, - {false, true, nil, "-dev-connect is only supported on linux"}, + { + devConfig: &devModeConfig{ + connectMode: true, + }, + expectedErr: "-dev-connect is only supported on linux", + }, + { + devConfig: &devModeConfig{ + defaultMode: true, + connectMode: true, + }, + expectedErr: "-dev-connect is only supported on linux", + }, } } if runtime.GOOS == "linux" { testutil.RequireRoot(t) cases = []struct { - dev bool - connect bool - expected *devModeConfig + devConfig *devModeConfig expectedErr string }{ - {false, false, nil, ""}, - {true, false, &devModeConfig{defaultMode: true, connectMode: false}, ""}, - {true, true, &devModeConfig{defaultMode: true, connectMode: true}, ""}, - {false, true, &devModeConfig{defaultMode: false, connectMode: true}, ""}, + { + devConfig: &devModeConfig{ + connectMode: true, + }, + expectedErr: "", + }, + { + devConfig: &devModeConfig{ + defaultMode: true, + connectMode: true, + }, + expectedErr: "", + }, } } for _, c := range cases { t.Run("", func(t *testing.T) { - mode, err := newDevModeConfig(c.dev, c.connect) - if err != nil && c.expectedErr == "" { - t.Fatalf("unexpected error: %v", err) - } - if err != nil && !strings.Contains(err.Error(), c.expectedErr) { - t.Fatalf("expected %s; got %v", c.expectedErr, err) - } - if mode == nil && c.expected != nil { - t.Fatalf("expected %+v but got nil", c.expected) - } - if mode != nil { - if c.expected.defaultMode != mode.defaultMode || - c.expected.connectMode != mode.connectMode { - t.Fatalf("expected %+v, got %+v", c.expected, mode) - } + err := c.devConfig.validate() + if c.expectedErr != "" { + must.Error(t, err) + } else { + must.NoError(t, err) } }) } diff --git a/website/content/docs/commands/agent.mdx b/website/content/docs/commands/agent.mdx index 02c114042..09d054bee 100644 --- a/website/content/docs/commands/agent.mdx +++ b/website/content/docs/commands/agent.mdx @@ -98,8 +98,16 @@ via CLI arguments. The `agent` command accepts the following arguments: but you may pass an optional comma-separated list of mode configurations: - `-dev-connect`: Start the agent in development mode, but bind to a public - network interface rather than localhost for using Consul Connect. This mode - is supported only on Linux as root. + network interface rather than localhost for using Consul Connect. It may be + used with `-dev-consul` to configure default workload identities for Consul. + This mode is supported only on Linux as root. + +- `-dev-consul`: Starts the agent in development mode with a default Consul + configuration for Nomad workload identity. It may be used with `-dev-connect` + to configure the agent for Consul Service Mesh. + +- `-dev-vault`: Starts the agent in development mode with a default Vault + configuration for Nomad workload identity. - `-encrypt`: Set the Serf encryption key. See the [Encryption Overview] for more details.