From 07032f6908806a560daf121d67b9becff42757f5 Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Fri, 20 Mar 2020 12:51:39 -0400 Subject: [PATCH] Refactoring and the worker is more distinct now (#3) --- internal/cmd/base/profiling_on.go | 4 - internal/cmd/base/servers.go | 9 +- .../cmd/commands/controller/controller.go | 50 +++-- internal/cmd/commands/dev/dev.go | 50 ++--- internal/cmd/commands/worker/config/config.go | 122 ------------- internal/cmd/commands/worker/worker.go | 66 ++----- .../controller => }/config/config.go | 79 +++++--- internal/servers/controller/config.go | 2 +- internal/servers/worker/config.go | 2 +- internal/servers/worker/listeners.go | 172 +++++++++--------- 10 files changed, 210 insertions(+), 346 deletions(-) delete mode 100644 internal/cmd/commands/worker/config/config.go rename internal/cmd/{commands/controller => }/config/config.go (81%) diff --git a/internal/cmd/base/profiling_on.go b/internal/cmd/base/profiling_on.go index b10936345..3a00fc84d 100644 --- a/internal/cmd/base/profiling_on.go +++ b/internal/cmd/base/profiling_on.go @@ -12,10 +12,6 @@ import ( "github.com/hashicorp/go-hclog" ) -func init() { - memProfilerEnabled = true -} - func StartMemProfiler(logger hclog.Logger) { profileDir := filepath.Join(os.TempDir(), "watchtowerprof") if err := os.MkdirAll(profileDir, 0700); err != nil { diff --git a/internal/cmd/base/servers.go b/internal/cmd/base/servers.go index 3256e32c2..43a37eafd 100644 --- a/internal/cmd/base/servers.go +++ b/internal/cmd/base/servers.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "io" + "net" "net/http" "os" "sort" @@ -38,9 +39,10 @@ import ( ) type ServerListener struct { - Mux *alpnmux.ALPNMux - Config *configutil.Listener - HTTPServer *http.Server + Mux *alpnmux.ALPNMux + Config *configutil.Listener + HTTPServer *http.Server + ALPNListener net.Listener } type Server struct { @@ -299,7 +301,6 @@ func (b *Server) SetupListeners(ui cli.Ui, config *configutil.SharedConfig) erro b.InfoKeys = append(b.InfoKeys, key) b.Info[key] = fmt.Sprintf( "%s (%s)", lnConfig.Type, strings.Join(propsList, ", ")) - } return nil diff --git a/internal/cmd/commands/controller/controller.go b/internal/cmd/commands/controller/controller.go index dcd742d70..85953ed03 100644 --- a/internal/cmd/commands/controller/controller.go +++ b/internal/cmd/commands/controller/controller.go @@ -11,7 +11,7 @@ import ( "github.com/hashicorp/vault/sdk/helper/mlock" "github.com/hashicorp/watchtower/globals" "github.com/hashicorp/watchtower/internal/cmd/base" - "github.com/hashicorp/watchtower/internal/cmd/commands/controller/config" + "github.com/hashicorp/watchtower/internal/cmd/config" "github.com/hashicorp/watchtower/internal/servers/controller" "github.com/mitchellh/cli" "github.com/posener/complete" @@ -20,8 +20,6 @@ import ( var _ cli.Command = (*Command)(nil) var _ cli.CommandAutocomplete = (*Command)(nil) -var memProfilerEnabled = false - type Command struct { *base.Command *base.Server @@ -39,7 +37,7 @@ type Command struct { flagLogLevel string flagLogFormat string flagDev bool - flagDevAdminToken string + flagDevAdminPassword string flagDevControllerListenAddr string flagCombineLogs bool } @@ -105,11 +103,11 @@ func (c *Command) Flags() *base.FlagSets { }) f.StringVar(&base.StringVar{ - Name: "dev-admin-token", - Target: &c.flagDevAdminToken, + Name: "dev-admin-password", + Target: &c.flagDevAdminPassword, Default: "", - EnvVar: "WATCHTWER_DEV_ADMIN_TOKEN", - Usage: "Initial admin token. This only applies when running in \"dev\" " + + EnvVar: "WATCHTWER_DEV_ADMIN_PASSWORD", + Usage: "Initial admin password. This only applies when running in \"dev\" " + "mode.", }) @@ -152,9 +150,7 @@ func (c *Command) Run(args []string) int { return 1 } - if memProfilerEnabled { - base.StartMemProfiler(c.Logger) - } + base.StartMemProfiler(c.Logger) if err := c.SetupMetrics(c.UI, c.Config.Telemetry); err != nil { c.UI.Error(err.Error()) @@ -171,14 +167,14 @@ func (c *Command) Run(args []string) int { } // If mlockall(2) isn't supported, show a warning. We disable this in dev - // because it is quite scary to see when first using Vault. We also disable + // because it is quite scary to see when first using Watchtower. We also disable // this if the user has explicitly disabled mlock in configuration. if !c.flagDev && !c.Config.DisableMlock && !mlock.Supported() { c.UI.Warn(base.WrapAtLength( "WARNING! mlock is not supported on this system! An mlockall(2)-like " + "syscall to prevent memory from being swapped to disk is not " + - "supported on this system. For better security, only run Vault on " + - "systems where this call is supported. If you are running Vault " + + "supported on this system. For better security, only run Watchtower on " + + "systems where this call is supported. If you are running Watchtower" + "in a Docker container, provide the IPC_LOCK cap to the container.")) } @@ -189,13 +185,13 @@ func (c *Command) Run(args []string) int { // Write out the PID to the file now that server has successfully started if err := c.StorePidFile(c.Config.PidFile); err != nil { - c.UI.Error(fmt.Sprintf("Error storing PID: %w", err)) + c.UI.Error(fmt.Errorf("Error storing PID: %w", err).Error()) return 1 } if c.flagDev { if err := c.CreateDevDatabase(); err != nil { - c.UI.Error(fmt.Sprintf("Error creating dev database container: %s", err.Error())) + c.UI.Error(fmt.Errorf("Error creating dev database container: %s", err.Error()).Error()) return 1 } c.ShutdownFuncs = append(c.ShutdownFuncs, c.DestroyDevDatabase) @@ -225,17 +221,13 @@ func (c *Command) ParseFlagsAndConfig(args []string) int { case len(c.flagConfig) == 0: c.UI.Error("Must specify a config file using -config") return 1 - case c.flagDevAdminToken != "": + case c.flagDevAdminPassword != "": c.UI.Warn(base.WrapAtLength( - "You cannot specify a custom admin token ID outside of \"dev\" mode. " + + "You cannot specify a custom admin password outside of \"dev\" mode. " + "Your request has been ignored.")) - c.flagDevAdminToken = "" + c.flagDevAdminPassword = "" } - if len(c.flagConfig) == 0 { - c.UI.Error("Must supply a config file with -config") - return 1 - } c.Config, err = config.LoadFile(c.flagConfig) if err != nil { c.UI.Error("Error parsing config: " + err.Error()) @@ -243,9 +235,9 @@ func (c *Command) ParseFlagsAndConfig(args []string) int { } } else { - c.Config, err = config.Dev() + c.Config, err = config.DevController() if err != nil { - c.UI.Error(fmt.Sprintf("Error creating dev config: %s", err)) + c.UI.Error(fmt.Errorf("Error creating dev config: %s", err).Error()) return 1 } @@ -267,14 +259,14 @@ func (c *Command) Start() int { // Initialize the core ctlr, err := controller.New(conf) if err != nil { - c.UI.Error(fmt.Sprintf("Error initializing controller: %w", err)) + c.UI.Error(fmt.Errorf("Error initializing controller: %w", err).Error()) return 1 } if err := ctlr.Start(); err != nil { c.UI.Error(fmt.Sprint("Error starting controller: %w", err)) if err := ctlr.Shutdown(); err != nil { - c.UI.Error(fmt.Sprintf("Error with controller shutdown: %w", err)) + c.UI.Error(fmt.Errorf("Error with controller shutdown: %w", err).Error()) } return 1 } @@ -288,7 +280,7 @@ func (c *Command) Start() int { c.UI.Output("==> Watchtower controller shutdown triggered") if err := ctlr.Shutdown(); err != nil { - c.UI.Error(fmt.Sprintf("Error with controller shutdown: %w", err)) + c.UI.Error(fmt.Errorf("Error with controller shutdown: %w", err).Error()) } shutdownTriggered = true @@ -342,7 +334,7 @@ func (c *Command) Start() int { RUNRELOADFUNCS: if err := c.Reload(); err != nil { - c.UI.Error(fmt.Sprintf("Error(s) were encountered during controller reload: %w", err)) + c.UI.Error(fmt.Errorf("Error(s) were encountered during controller reload: %w", err).Error()) } case <-c.SigUSR2Ch: diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index 335d5b135..4ecc8a113 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -8,7 +8,8 @@ import ( "github.com/hashicorp/watchtower/internal/cmd/base" controllercmd "github.com/hashicorp/watchtower/internal/cmd/commands/controller" - controllerconfig "github.com/hashicorp/watchtower/internal/cmd/commands/controller/config" + workercmd "github.com/hashicorp/watchtower/internal/cmd/commands/worker" + "github.com/hashicorp/watchtower/internal/cmd/config" "github.com/mitchellh/cli" "github.com/posener/complete" ) @@ -16,8 +17,6 @@ import ( var _ cli.Command = (*Command)(nil) var _ cli.CommandAutocomplete = (*Command)(nil) -var memProfilerEnabled = false - type Command struct { *base.Command *base.Server @@ -34,7 +33,7 @@ type Command struct { flagLogLevel string flagLogFormat string flagDev bool - flagDevAdminToken string + flagDevAdminPassword string flagDevControllerListenAddr string flagCombineLogs bool } @@ -83,11 +82,11 @@ func (c *Command) Flags() *base.FlagSets { f = set.NewFlagSet("Dev Options") f.StringVar(&base.StringVar{ - Name: "dev-admin-token", - Target: &c.flagDevAdminToken, + Name: "dev-admin-password", + Target: &c.flagDevAdminPassword, Default: "", - EnvVar: "WATCHTWER_DEV_ADMIN_TOKEN", - Usage: "Initial admin token. This only applies when running in \"dev\" " + + EnvVar: "WATCHTWER_DEV_ADMIN_PASSWORD", + Usage: "Initial admin password. This only applies when running in \"dev\" " + "mode.", }) @@ -132,9 +131,9 @@ func (c *Command) Run(args []string) int { childShutdownCh := make(chan struct{}) - devControllerConfig, err := controllerconfig.Dev() + devConfig, err := config.DevController() if err != nil { - c.UI.Error(fmt.Sprintf("Error creating controller dev config: %s", err)) + c.UI.Error(fmt.Errorf("Error creating controller dev config: %s", err).Error()) return 1 } @@ -143,29 +142,27 @@ func (c *Command) Run(args []string) int { return 1 } - if memProfilerEnabled { - base.StartMemProfiler(c.Logger) - } + base.StartMemProfiler(c.Logger) - if err := c.SetupMetrics(c.UI, devControllerConfig.Telemetry); err != nil { + if err := c.SetupMetrics(c.UI, devConfig.Telemetry); err != nil { c.UI.Error(err.Error()) return 1 } - if err := c.SetupKMSes(c.UI, devControllerConfig.SharedConfig, 2); err != nil { + if err := c.SetupKMSes(c.UI, devConfig.SharedConfig, 2); err != nil { c.UI.Error(err.Error()) return 1 } // Initialize the listeners - if err := c.SetupListeners(c.UI, devControllerConfig.SharedConfig); err != nil { + if err := c.SetupListeners(c.UI, devConfig.SharedConfig); err != nil { c.UI.Error(err.Error()) return 1 } // Write out the PID to the file now that server has successfully started - if err := c.StorePidFile(devControllerConfig.PidFile); err != nil { - c.UI.Error(fmt.Sprintf("Error storing PID: %w", err)) + if err := c.StorePidFile(devConfig.PidFile); err != nil { + c.UI.Error(fmt.Errorf("Error storing PID: %w", err).Error()) return 1 } @@ -208,7 +205,7 @@ func (c *Command) Run(args []string) int { defer c.RunShutdownFuncs(c.UI) if err := c.CreateDevDatabase(); err != nil { - c.UI.Error(fmt.Sprintf("Error creating dev database container: %s", err.Error())) + c.UI.Error(fmt.Errorf("Error creating dev database container: %w", err).Error()) return 1 } c.ShutdownFuncs = append(c.ShutdownFuncs, c.DestroyDevDatabase) @@ -218,7 +215,7 @@ func (c *Command) Run(args []string) int { // Instantiate the wait group shutdownWg := &sync.WaitGroup{} - shutdownWg.Add(1) + shutdownWg.Add(2) controllerSighupCh := make(chan struct{}) c.childSighupCh = append(c.childSighupCh, controllerSighupCh) go func() { @@ -228,10 +225,21 @@ func (c *Command) Run(args []string) int { Server: c.Server, ShutdownCh: childShutdownCh, SighupCh: controllerSighupCh, - Config: devControllerConfig, + Config: devConfig, } devController.Start() }() + go func() { + defer shutdownWg.Done() + devWorker := &workercmd.Command{ + Command: c.Command, + Server: c.Server, + ShutdownCh: childShutdownCh, + SighupCh: controllerSighupCh, + Config: devConfig, + } + devWorker.Start() + }() // Wait for shutdown shutdownTriggered := false diff --git a/internal/cmd/commands/worker/config/config.go b/internal/cmd/commands/worker/config/config.go deleted file mode 100644 index 93e237d8b..000000000 --- a/internal/cmd/commands/worker/config/config.go +++ /dev/null @@ -1,122 +0,0 @@ -package config - -import ( - "bytes" - "crypto/rand" - "encoding/base64" - "fmt" - "io" - "io/ioutil" - - "github.com/hashicorp/hcl" - "github.com/hashicorp/vault/internalshared/configutil" -) - -// Config is the configuration for the watchtower controller -type Config struct { - *configutil.SharedConfig `hcl:"-"` -} - -// Dev is a Config that is used for dev mode of Watchtower -func Dev() (*Config, error) { - randBuf := new(bytes.Buffer) - n, err := randBuf.ReadFrom(&io.LimitedReader{ - R: rand.Reader, - N: 64, - }) - if err != nil { - return nil, err - } - if n != 64 { - return nil, fmt.Errorf("expected to read 64 bytes, read %d", n) - } - controllerKey := base64.StdEncoding.EncodeToString(randBuf.Bytes()[0:32]) - workerAuthKey := base64.StdEncoding.EncodeToString(randBuf.Bytes()[32:64]) - - hclStr := ` -disable_mlock = true - -listener "tcp" { - tls_disable = true - proxy_protocol_behavior = "allow_authorized" - proxy_protocol_authorized_addrs = "127.0.0.1" -} - -telemetry { - prometheus_retention_time = "24h" - disable_hostname = true -} -` - - hclStr = fmt.Sprintf(hclStr, controllerKey, workerAuthKey) - parsed, err := Parse(hclStr) - if err != nil { - return nil, fmt.Errorf("error parsing dev config: %w", err) - } - return parsed, nil -} - -func New() *Config { - return &Config{ - SharedConfig: new(configutil.SharedConfig), - } -} - -// LoadFile loads the configuration from the given file. -func LoadFile(path string) (*Config, error) { - // Read the file - d, err := ioutil.ReadFile(path) - if err != nil { - return nil, err - } - - conf, err := Parse(string(d)) - if err != nil { - return nil, err - } - - return conf, nil -} - -func Parse(d string) (*Config, error) { - obj, err := hcl.Parse(d) - if err != nil { - return nil, err - } - - // Nothing to do here right now - result := New() - if err := hcl.DecodeObject(result, obj); err != nil { - return nil, err - } - - sharedConfig, err := configutil.ParseConfig(d) - if err != nil { - return nil, err - } - result.SharedConfig = sharedConfig - - return result, nil -} - -// Sanitized returns a copy of the config with all values that are considered -// sensitive stripped. It also strips all `*Raw` values that are mainly -// used for parsing. -// -// Specifically, the fields that this method strips are: -// - KMS.Config -// - Telemetry.CirconusAPIToken -func (c *Config) Sanitized() map[string]interface{} { - // Create shared config if it doesn't exist (e.g. in tests) so that map - // keys are actually populated - if c.SharedConfig == nil { - c.SharedConfig = new(configutil.SharedConfig) - } - sharedResult := c.SharedConfig.Sanitized() - result := map[string]interface{}{} - for k, v := range sharedResult { - result[k] = v - } - - return result -} diff --git a/internal/cmd/commands/worker/worker.go b/internal/cmd/commands/worker/worker.go index 824836ac7..4ce5ae038 100644 --- a/internal/cmd/commands/worker/worker.go +++ b/internal/cmd/commands/worker/worker.go @@ -9,9 +9,8 @@ import ( "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-multierror" "github.com/hashicorp/vault/sdk/helper/mlock" - "github.com/hashicorp/watchtower/globals" "github.com/hashicorp/watchtower/internal/cmd/base" - "github.com/hashicorp/watchtower/internal/cmd/commands/worker/config" + "github.com/hashicorp/watchtower/internal/cmd/config" "github.com/hashicorp/watchtower/internal/servers/worker" "github.com/mitchellh/cli" "github.com/posener/complete" @@ -20,8 +19,6 @@ import ( var _ cli.Command = (*Command)(nil) var _ cli.CommandAutocomplete = (*Command)(nil) -var memProfilerEnabled = false - type Command struct { *base.Command *base.Server @@ -39,7 +36,6 @@ type Command struct { flagLogLevel string flagLogFormat string flagDev bool - flagDevAdminToken string flagDevWorkerListenAddr string flagCombineLogs bool } @@ -104,15 +100,6 @@ func (c *Command) Flags() *base.FlagSets { "production.", }) - f.StringVar(&base.StringVar{ - Name: "dev-admin-token", - Target: &c.flagDevAdminToken, - Default: "", - EnvVar: "WATCHTWER_DEV_ADMIN_TOKEN", - Usage: "Initial admin token. This only applies when running in \"dev\" " + - "mode.", - }) - f.StringVar(&base.StringVar{ Name: "dev-listen-address", Target: &c.flagDevWorkerListenAddr, @@ -152,33 +139,27 @@ func (c *Command) Run(args []string) int { return 1 } - if memProfilerEnabled { - base.StartMemProfiler(c.Logger) - } + base.StartMemProfiler(c.Logger) if err := c.SetupMetrics(c.UI, c.Config.Telemetry); err != nil { c.UI.Error(err.Error()) return 1 } - if err := c.SetupKMSes(c.UI, c.Config.SharedConfig, 2); err != nil { + if err := c.SetupKMSes(c.UI, c.Config.SharedConfig, 1); err != nil { c.UI.Error(err.Error()) return 1 } - if c.Config.DefaultMaxRequestDuration != 0 { - globals.DefaultMaxRequestDuration = c.Config.DefaultMaxRequestDuration - } - // If mlockall(2) isn't supported, show a warning. We disable this in dev - // because it is quite scary to see when first using Vault. We also disable + // because it is quite scary to see when first using Watchtower. We also disable // this if the user has explicitly disabled mlock in configuration. if !c.flagDev && !c.Config.DisableMlock && !mlock.Supported() { c.UI.Warn(base.WrapAtLength( "WARNING! mlock is not supported on this system! An mlockall(2)-like " + "syscall to prevent memory from being swapped to disk is not " + - "supported on this system. For better security, only run Vault on " + - "systems where this call is supported. If you are running Vault " + + "supported on this system. For better security, only run Watchtower on " + + "systems where this call is supported. If you are running Watchtower" + "in a Docker container, provide the IPC_LOCK cap to the container.")) } @@ -189,18 +170,10 @@ func (c *Command) Run(args []string) int { // Write out the PID to the file now that server has successfully started if err := c.StorePidFile(c.Config.PidFile); err != nil { - c.UI.Error(fmt.Sprintf("Error storing PID: %w", err)) + c.UI.Error(fmt.Errorf("Error storing PID: %w", err).Error()) return 1 } - if c.flagDev { - if err := c.CreateDevDatabase(); err != nil { - c.UI.Error(fmt.Sprintf("Error creating dev database container: %s", err.Error())) - return 1 - } - c.ShutdownFuncs = append(c.ShutdownFuncs, c.DestroyDevDatabase) - } - defer c.RunShutdownFuncs(c.UI) c.PrintInfo(c.UI, "worker") @@ -221,17 +194,6 @@ func (c *Command) ParseFlagsAndConfig(args []string) int { // Validation if !c.flagDev { - switch { - case len(c.flagConfig) == 0: - c.UI.Error("Must specify a config file using -config") - return 1 - case c.flagDevAdminToken != "": - c.UI.Warn(base.WrapAtLength( - "You cannot specify a custom admin token ID outside of \"dev\" mode. " + - "Your request has been ignored.")) - c.flagDevAdminToken = "" - } - if len(c.flagConfig) == 0 { c.UI.Error("Must supply a config file with -config") return 1 @@ -243,9 +205,9 @@ func (c *Command) ParseFlagsAndConfig(args []string) int { } } else { - c.Config, err = config.Dev() + c.Config, err = config.DevWorker() if err != nil { - c.UI.Error(fmt.Sprintf("Error creating dev config: %s", err)) + c.UI.Error(fmt.Errorf("Error creating dev config: %s", err).Error()) return 1 } @@ -267,14 +229,14 @@ func (c *Command) Start() int { // Initialize the core wrkr, err := worker.New(conf) if err != nil { - c.UI.Error(fmt.Sprintf("Error initializing worker: %w", err)) + c.UI.Error(fmt.Errorf("Error initializing worker: %w", err).Error()) return 1 } if err := wrkr.Start(); err != nil { - c.UI.Error(fmt.Sprint("Error starting worker: %w", err)) + c.UI.Error(fmt.Errorf("Error starting worker: %w", err).Error()) if err := wrkr.Shutdown(); err != nil { - c.UI.Error(fmt.Sprintf("Error with worker shutdown: %w", err)) + c.UI.Error(fmt.Errorf("Error with worker shutdown: %w", err).Error()) } return 1 } @@ -288,7 +250,7 @@ func (c *Command) Start() int { c.UI.Output("==> Watchtower worker shutdown triggered") if err := wrkr.Shutdown(); err != nil { - c.UI.Error(fmt.Sprintf("Error with worker shutdown: %w", err)) + c.UI.Error(fmt.Errorf("Error with worker shutdown: %w", err).Error()) } shutdownTriggered = true @@ -342,7 +304,7 @@ func (c *Command) Start() int { RUNRELOADFUNCS: if err := c.Reload(); err != nil { - c.UI.Error(fmt.Sprintf("Error(s) were encountered during worker reload: %w", err)) + c.UI.Error(fmt.Errorf("Error(s) were encountered during worker reload: %w", err).Error()) } case <-c.SigUSR2Ch: diff --git a/internal/cmd/commands/controller/config/config.go b/internal/cmd/config/config.go similarity index 81% rename from internal/cmd/commands/controller/config/config.go rename to internal/cmd/config/config.go index 16650d204..86fa506b6 100644 --- a/internal/cmd/commands/controller/config/config.go +++ b/internal/cmd/config/config.go @@ -12,13 +12,58 @@ import ( "github.com/hashicorp/vault/internalshared/configutil" ) +const ( + devConfig = ` +disable_mlock = true + +listener "tcp" { + tls_disable = true + proxy_protocol_behavior = "allow_authorized" + proxy_protocol_authorized_addrs = "127.0.0.1" +} + +telemetry { + prometheus_retention_time = "24h" + disable_hostname = true +} +` + + devControllerExtraConfig = ` + +kms "aead" { + purpose = "controller" + aead_type = "aes-gcm" + key = "%s" +} + +kms "aead" { + purpose = "worker-auth" + aead_type = "aes-gcm" + key = "%s" +} +` +) + // Config is the configuration for the watchtower controller type Config struct { *configutil.SharedConfig `hcl:"-"` + + DevController bool `hcl:"-"` } -// Dev is a Config that is used for dev mode of Watchtower -func Dev() (*Config, error) { +// DevWorker is a Config that is used for dev mode of Watchtower +// workers +func DevWorker() (*Config, error) { + parsed, err := Parse(devConfig) + if err != nil { + return nil, fmt.Errorf("error parsing dev config: %w", err) + } + return parsed, nil +} + +// DevController is a Config that is used for dev mode of Watchtower +// controllers +func DevController() (*Config, error) { randBuf := new(bytes.Buffer) n, err := randBuf.ReadFrom(&io.LimitedReader{ R: rand.Reader, @@ -33,38 +78,12 @@ func Dev() (*Config, error) { controllerKey := base64.StdEncoding.EncodeToString(randBuf.Bytes()[0:32]) workerAuthKey := base64.StdEncoding.EncodeToString(randBuf.Bytes()[32:64]) - hclStr := ` -disable_mlock = true - -listener "tcp" { - tls_disable = true - proxy_protocol_behavior = "allow_authorized" - proxy_protocol_authorized_addrs = "127.0.0.1" -} - -telemetry { - prometheus_retention_time = "24h" - disable_hostname = true -} - -kms "aead" { - purpose = "controller" - aead_type = "aes-gcm" - key = "%s" -} - -kms "aead" { - purpose = "worker-auth" - aead_type = "aes-gcm" - key = "%s" -} -` - - hclStr = fmt.Sprintf(hclStr, controllerKey, workerAuthKey) + hclStr := fmt.Sprintf(devConfig+devControllerExtraConfig, controllerKey, workerAuthKey) parsed, err := Parse(hclStr) if err != nil { return nil, fmt.Errorf("error parsing dev config: %w", err) } + parsed.DevController = true return parsed, nil } diff --git a/internal/servers/controller/config.go b/internal/servers/controller/config.go index f2fc3b0cb..1a7f4823a 100644 --- a/internal/servers/controller/config.go +++ b/internal/servers/controller/config.go @@ -4,7 +4,7 @@ import ( "context" "github.com/hashicorp/watchtower/internal/cmd/base" - "github.com/hashicorp/watchtower/internal/cmd/commands/controller/config" + "github.com/hashicorp/watchtower/internal/cmd/config" ) type Config struct { diff --git a/internal/servers/worker/config.go b/internal/servers/worker/config.go index a747b8744..bef8b7e2c 100644 --- a/internal/servers/worker/config.go +++ b/internal/servers/worker/config.go @@ -4,7 +4,7 @@ import ( "context" "github.com/hashicorp/watchtower/internal/cmd/base" - "github.com/hashicorp/watchtower/internal/cmd/commands/worker/config" + "github.com/hashicorp/watchtower/internal/cmd/config" ) type Config struct { diff --git a/internal/servers/worker/listeners.go b/internal/servers/worker/listeners.go index dfcd16de8..b2c234df5 100644 --- a/internal/servers/worker/listeners.go +++ b/internal/servers/worker/listeners.go @@ -17,97 +17,92 @@ func (c *Worker) startListeners() error { var retErr *multierror.Error servers := make([]func(), 0, len(c.conf.Listeners)) for _, ln := range c.conf.Listeners { - handler := c.Handler(HandlerProperties{ - ListenerConfig: ln.Config, - }) - - /* - // TODO: As I write this Vault's having this code audited, make sure to - // port over any recommendations - // - // We perform validation on the config earlier, we can just cast here - if _, ok := ln.config["x_forwarded_for_authorized_addrs"]; ok { - hopSkips := ln.config["x_forwarded_for_hop_skips"].(int) - authzdAddrs := ln.config["x_forwarded_for_authorized_addrs"].([]*sockaddr.SockAddrMarshaler) - rejectNotPresent := ln.config["x_forwarded_for_reject_not_present"].(bool) - rejectNonAuthz := ln.config["x_forwarded_for_reject_not_authorized"].(bool) - if len(authzdAddrs) > 0 { - handler = vaulthttp.WrapForwardedForHandler(handler, authzdAddrs, rejectNotPresent, rejectNonAuthz, hopSkips) - } - } - */ - - server := &http.Server{ - Handler: handler, - ReadHeaderTimeout: 10 * time.Second, - ReadTimeout: 30 * time.Second, - IdleTimeout: 5 * time.Minute, - ErrorLog: c.conf.Logger.StandardLogger(nil), - BaseContext: func(net.Listener) context.Context { - return c.baseContext - }, - } - ln.HTTPServer = server - - if ln.Config.HTTPReadHeaderTimeout > 0 { - server.ReadHeaderTimeout = ln.Config.HTTPReadHeaderTimeout - } - if ln.Config.HTTPReadTimeout > 0 { - server.ReadTimeout = ln.Config.HTTPReadTimeout - } - if ln.Config.HTTPWriteTimeout > 0 { - server.WriteTimeout = ln.Config.HTTPWriteTimeout - } - if ln.Config.HTTPIdleTimeout > 0 { - server.IdleTimeout = ln.Config.HTTPIdleTimeout - } - - switch ln.Config.TLSDisable { - case true: - l := ln.Mux.GetListener(alpnmux.NoProto) - if l == nil { - retErr = multierror.Append(retErr, errors.New("could not get non-tls listener")) - continue - } - servers = append(servers, func() { - go server.Serve(l) + switch c.conf.RawConfig.DevController { + case false: + handler := c.Handler(HandlerProperties{ + ListenerConfig: ln.Config, }) - default: - protos := []string{"", "http/1.1", "h2"} - for _, v := range protos { - l := ln.Mux.GetListener(v) + /* + // TODO: As I write this Vault's having this code audited, make sure to + // port over any recommendations + // + // We perform validation on the config earlier, we can just cast here + if _, ok := ln.config["x_forwarded_for_authorized_addrs"]; ok { + hopSkips := ln.config["x_forwarded_for_hop_skips"].(int) + authzdAddrs := ln.config["x_forwarded_for_authorized_addrs"].([]*sockaddr.SockAddrMarshaler) + rejectNotPresent := ln.config["x_forwarded_for_reject_not_present"].(bool) + rejectNonAuthz := ln.config["x_forwarded_for_reject_not_authorized"].(bool) + if len(authzdAddrs) > 0 { + handler = vaulthttp.WrapForwardedForHandler(handler, authzdAddrs, rejectNotPresent, rejectNonAuthz, hopSkips) + } + } + */ + + server := &http.Server{ + Handler: handler, + ReadHeaderTimeout: 10 * time.Second, + ReadTimeout: 30 * time.Second, + IdleTimeout: 5 * time.Minute, + ErrorLog: c.conf.Logger.StandardLogger(nil), + BaseContext: func(net.Listener) context.Context { + return c.baseContext + }, + } + ln.HTTPServer = server + + if ln.Config.HTTPReadHeaderTimeout > 0 { + server.ReadHeaderTimeout = ln.Config.HTTPReadHeaderTimeout + } + if ln.Config.HTTPReadTimeout > 0 { + server.ReadTimeout = ln.Config.HTTPReadTimeout + } + if ln.Config.HTTPWriteTimeout > 0 { + server.WriteTimeout = ln.Config.HTTPWriteTimeout + } + if ln.Config.HTTPIdleTimeout > 0 { + server.IdleTimeout = ln.Config.HTTPIdleTimeout + } + + switch ln.Config.TLSDisable { + case true: + l := ln.Mux.GetListener(alpnmux.NoProto) if l == nil { - retErr = multierror.Append(retErr, fmt.Errorf("could not get tls proto %q listener", v)) + retErr = multierror.Append(retErr, errors.New("could not get non-tls listener")) continue } servers = append(servers, func() { go server.Serve(l) }) + + default: + protos := []string{"", "http/1.1", "h2"} + for _, v := range protos { + l := ln.Mux.GetListener(v) + if l == nil { + retErr = multierror.Append(retErr, fmt.Errorf("could not get tls proto %q listener", v)) + continue + } + servers = append(servers, func() { + go server.Serve(l) + }) + } } - } - workerTLSConfig, peeringInfo, err := c.workerTLS(workerTLSOpts{ - Address: ln.Config.Address, - Protos: []string{"watchtower-worker-v1"}, - }) - if err != nil { - retErr = multierror.Append(retErr, fmt.Errorf("error getting TLS configuration: %w", err)) - continue - } - l, err := ln.Mux.RegisterProto("watchtower-worker-v1", workerTLSConfig) - if err != nil { - retErr = multierror.Append(retErr, fmt.Errorf("error getting sub-listener for worker proto: %w", err)) - continue - } + fallthrough - // TODO: Start listner for real; for now send it to the http server just for testing - servers = append(servers, func() { - go server.Serve(l) - }) - - // TODO: Add peering info into database - _ = peeringInfo + default: + if !ln.Config.TLSDisable { + l := ln.Mux.GetListener(alpnmux.DefaultProto) + if l == nil { + retErr = multierror.Append(retErr, fmt.Errorf("could not get tls proto %q listener", alpnmux.DefaultProto)) + continue + } + ln.ALPNListener = l + // TODO: give this listener to a server to handle ALPN auth + } + // TODO: Register websocket handler against server on this listener + } } err := retErr.ErrorOrNil() @@ -125,6 +120,11 @@ func (c *Worker) startListeners() error { func (c *Worker) stopListeners() error { serverWg := new(sync.WaitGroup) for _, ln := range c.conf.Listeners { + if c.conf.RawConfig.DevController { + // These will get closed by the controller's dev instance + continue + } + if ln.HTTPServer == nil { continue } @@ -140,8 +140,16 @@ func (c *Worker) stopListeners() error { var retErr *multierror.Error for _, ln := range c.conf.Listeners { - if err := ln.Mux.Close(); err != nil { - retErr = multierror.Append(retErr, err) + if ln.ALPNListener != nil { + if err := ln.ALPNListener.Close(); err != nil { + retErr = multierror.Append(retErr, err) + } + } + + if !c.conf.RawConfig.DevController { + if err := ln.Mux.Close(); err != nil { + retErr = multierror.Append(retErr, err) + } } } return retErr.ErrorOrNil()