diff --git a/README.md b/README.md index 78b3e1a..76b21c7 100644 --- a/README.md +++ b/README.md @@ -22,31 +22,9 @@ To handle intentions, HAProxy Connect, sets up a SPOE filter on the application ## How to use +Please see this for the current app parameters: ``` ./haproxy-consul-connect --help -Usage of ./haproxy-consul-connect: - -dataplane string - Dataplane binary path (default "dataplane-api") - -enable-intentions - Enable Connect intentions - -haproxy string - Haproxy binary path (default "haproxy") - -haproxy-cfg-base-path string - Haproxy binary path (default "/tmp") - -http-addr string - Consul agent address (default "127.0.0.1:8500") - -log-level string - Log level (default "INFO") - -sidecar-for string - The consul service id to proxy - -sidecar-for-tag string - The consul service id to proxy - -stats-addr string - Listen addr for stats server - -stats-service-register - Register a consul service for connect stats - -token string - Consul ACL token./haproxy-consul-connect --help ``` ## Minimal working example diff --git a/go.mod b/go.mod index 7b5c3cf..e9ce816 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/haproxytech/haproxy-consul-connect go 1.13 require ( - github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect + github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a github.com/criteo/haproxy-spoe-go v0.0.0-20190925130734-97891c13d324 github.com/d4l3k/messagediff v1.2.1 // indirect github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 diff --git a/haproxy/config.go b/haproxy/config.go index 6ce99e0..e4fb48e 100644 --- a/haproxy/config.go +++ b/haproxy/config.go @@ -28,6 +28,7 @@ global tune.ssl.default-dh-param 1024 nbproc 1 nbthread {{.NbThread}} + log-tag haproxy_sidecar userlist controller user {{.DataplaneUser}} insecure-password {{.DataplanePass}} @@ -57,7 +58,6 @@ type baseParams struct { SocketPath string DataplaneUser string DataplanePass string - LogsPath string } type haConfig struct { @@ -113,7 +113,6 @@ func newHaConfig(baseDir string, sd *lib.Shutdown) (*haConfig, error) { err = tmpl.Execute(cfgFile, baseParams{ NbThread: runtime.GOMAXPROCS(0), SocketPath: cfg.StatsSock, - LogsPath: cfg.LogsSock, DataplaneUser: dataplaneUser, DataplanePass: dataplanePass, }) diff --git a/haproxy/haproxy.go b/haproxy/haproxy.go index 0649962..a34d258 100644 --- a/haproxy/haproxy.go +++ b/haproxy/haproxy.go @@ -60,11 +60,13 @@ func (h *HAProxy) start(sd *lib.Shutdown) error { } h.haConfig = hc - if h.opts.LogRequests { + if h.opts.HAProxyLogAddress == "" { err := h.startLogger() if err != nil { return err } + } else { + log.Infof("not starting built-in syslog - using %s as syslog address", h.opts.HAProxyLogAddress) } if h.opts.EnableIntentions { @@ -77,6 +79,7 @@ func (h *HAProxy) start(sd *lib.Shutdown) error { dpc, err := haproxy_cmd.Start(sd, haproxy_cmd.Config{ HAProxyPath: h.opts.HAProxyBin, HAProxyConfigPath: hc.HAProxy, + HAProxyLogWithThisApp: h.opts.HAProxyLogAddress == "", DataplanePath: h.opts.DataplaneBin, DataplaneTransactionDir: hc.DataplaneTransactionDir, DataplaneSock: hc.DataplaneSock, @@ -112,6 +115,8 @@ func (h *HAProxy) startLogger() error { } }(channel) + log.Infof("starting built-in syslog server at %s", h.haConfig.LogsSock) + return nil } diff --git a/haproxy/haproxy_cmd/cmd.go b/haproxy/haproxy_cmd/cmd.go index 6e19473..25c51aa 100644 --- a/haproxy/haproxy_cmd/cmd.go +++ b/haproxy/haproxy_cmd/cmd.go @@ -13,10 +13,12 @@ import ( log "github.com/sirupsen/logrus" ) -func runCommand(sd *lib.Shutdown, cmdPath string, args ...string) (*exec.Cmd, error) { +func runCommand(sd *lib.Shutdown, cmdPath string, logPrefix string, logWithThisApp bool, args ...string) (*exec.Cmd, error) { _, file := path.Split(cmdPath) cmd := exec.Command(cmdPath, args...) - halog.Cmd("haproxy", cmd) + if logWithThisApp { + halog.Cmd(logPrefix, cmd) + } sd.Add(1) err := cmd.Start() diff --git a/haproxy/haproxy_cmd/cmd_test.go b/haproxy/haproxy_cmd/cmd_test.go index cd303c1..711a5ba 100644 --- a/haproxy/haproxy_cmd/cmd_test.go +++ b/haproxy/haproxy_cmd/cmd_test.go @@ -10,7 +10,7 @@ import ( func Test_runCommand_ok(t *testing.T) { t.Parallel() sd := lib.NewShutdown() - cmd, err := runCommand(sd, "ls", ".") + cmd, err := runCommand(sd, "ls", "foobar", true, ".") require.NoError(t, err) cmd.Wait() } @@ -18,7 +18,7 @@ func Test_runCommand_ok(t *testing.T) { func Test_runCommand_nok_wrong_path(t *testing.T) { t.Parallel() sd := lib.NewShutdown() - cmd, err := runCommand(sd, "/path/to/nowhere/that/can/be/found/myExec", "--help") + cmd, err := runCommand(sd, "/path/to/nowhere/that/can/be/found/myExec", "foobar", true, "--help") require.NotNil(t, err) require.Contains(t, err.Error(), "no such file or directory") require.Nil(t, cmd) diff --git a/haproxy/haproxy_cmd/run.go b/haproxy/haproxy_cmd/run.go index 61f16cd..8d878d1 100644 --- a/haproxy/haproxy_cmd/run.go +++ b/haproxy/haproxy_cmd/run.go @@ -25,6 +25,7 @@ const ( type Config struct { HAProxyPath string HAProxyConfigPath string + HAProxyLogWithThisApp bool DataplanePath string DataplaneTransactionDir string DataplaneSock string @@ -35,6 +36,8 @@ type Config struct { func Start(sd *lib.Shutdown, cfg Config) (*dataplane.Dataplane, error) { haCmd, err := runCommand(sd, cfg.HAProxyPath, + "haproxy", + cfg.HAProxyLogWithThisApp, "-f", cfg.HAProxyConfigPath, ) @@ -47,6 +50,8 @@ func Start(sd *lib.Shutdown, cfg Config) (*dataplane.Dataplane, error) { cmd, err := runCommand(sd, cfg.DataplanePath, + "dataplaneapi", + true, "--scheme", "unix", "--socket-path", cfg.DataplaneSock, "--haproxy-bin", cfg.HAProxyPath, diff --git a/haproxy/options.go b/haproxy/options.go index e2a365f..88741bb 100644 --- a/haproxy/options.go +++ b/haproxy/options.go @@ -8,5 +8,6 @@ type Options struct { EnableIntentions bool StatsListenAddr string StatsRegisterService bool - LogRequests bool + HAProxyLogRequests bool + HAProxyLogAddress string } diff --git a/haproxy/state.go b/haproxy/state.go index cc25fcb..3fc476b 100644 --- a/haproxy/state.go +++ b/haproxy/state.go @@ -95,10 +95,15 @@ func (h *HAProxy) watch(sd *lib.Shutdown) error { newConsulCfg := nextState.Load().(consul.Config) + logAddress := h.haConfig.LogsSock + if h.opts.HAProxyLogAddress != "" { + logAddress = h.opts.HAProxyLogAddress + } + newState, err := state.Generate(state.Options{ EnableIntentions: h.opts.EnableIntentions, - LogRequests: h.opts.LogRequests, - LogSocket: h.haConfig.LogsSock, + LogRequests: h.opts.HAProxyLogRequests, + LogAddress: logAddress, SPOEConfigPath: h.haConfig.SPOE, SPOESocket: h.haConfig.SPOESock, }, h.haConfig, currentState, newConsulCfg) diff --git a/haproxy/state/downstream.go b/haproxy/state/downstream.go index be9d41e..f63e987 100644 --- a/haproxy/state/downstream.go +++ b/haproxy/state/downstream.go @@ -44,10 +44,10 @@ func generateDownstream(opts Options, certStore CertificateStore, cfg consul.Dow } // Logging - if opts.LogRequests && opts.LogSocket != "" { + if opts.LogRequests { fe.LogTarget = &models.LogTarget{ ID: int64p(0), - Address: opts.LogSocket, + Address: opts.LogAddress, Facility: models.LogTargetFacilityLocal0, Format: models.LogTargetFormatRfc5424, } @@ -100,10 +100,10 @@ func generateDownstream(opts Options, certStore CertificateStore, cfg consul.Dow } // Logging - if opts.LogRequests && opts.LogSocket != "" { + if opts.LogRequests { be.LogTarget = &models.LogTarget{ ID: int64p(0), - Address: opts.LogSocket, + Address: opts.LogAddress, Facility: models.LogTargetFacilityLocal0, Format: models.LogTargetFormatRfc5424, } diff --git a/haproxy/state/snapshot_test.go b/haproxy/state/snapshot_test.go index 636910a..4a45c8b 100644 --- a/haproxy/state/snapshot_test.go +++ b/haproxy/state/snapshot_test.go @@ -217,7 +217,7 @@ func GetTestHAConfig(baseCfg string) State { var TestOpts = Options{ EnableIntentions: true, LogRequests: true, - LogSocket: "//logs.sock", + LogAddress: "//logs.sock", SPOEConfigPath: "//spoe", } diff --git a/haproxy/state/states.go b/haproxy/state/states.go index b85720d..a9cf072 100644 --- a/haproxy/state/states.go +++ b/haproxy/state/states.go @@ -12,7 +12,7 @@ import ( type Options struct { EnableIntentions bool LogRequests bool - LogSocket string + LogAddress string SPOEConfigPath string SPOESocket string } diff --git a/haproxy/state/upstream.go b/haproxy/state/upstream.go index 3509967..adfa048 100644 --- a/haproxy/state/upstream.go +++ b/haproxy/state/upstream.go @@ -34,10 +34,10 @@ func generateUpstream(opts Options, certStore CertificateStore, cfg consul.Upstr Port: &fePort64, }, } - if opts.LogRequests && opts.LogSocket != "" { + if opts.LogRequests && opts.LogAddress != "" { fe.LogTarget = &models.LogTarget{ ID: int64p(0), - Address: opts.LogSocket, + Address: opts.LogAddress, Facility: models.LogTargetFacilityLocal0, Format: models.LogTargetFormatRfc5424, } @@ -56,10 +56,10 @@ func generateUpstream(opts Options, certStore CertificateStore, cfg consul.Upstr Mode: beMode, }, } - if opts.LogRequests && opts.LogSocket != "" { + if opts.LogRequests && opts.LogAddress != "" { be.LogTarget = &models.LogTarget{ ID: int64p(0), - Address: opts.LogSocket, + Address: opts.LogAddress, Facility: models.LogTargetFacilityLocal0, Format: models.LogTargetFormatRfc5424, } diff --git a/main.go b/main.go index b5b0867..ea4d0b2 100644 --- a/main.go +++ b/main.go @@ -1,14 +1,17 @@ package main import ( + "errors" "flag" "fmt" "os" + "regexp" "strings" + "github.com/asaskevich/govalidator" log "github.com/sirupsen/logrus" - haproxy "github.com/haproxytech/haproxy-consul-connect/haproxy" + "github.com/haproxytech/haproxy-consul-connect/haproxy" "github.com/haproxytech/haproxy-consul-connect/haproxy/haproxy_cmd" "github.com/haproxytech/haproxy-consul-connect/lib" @@ -40,7 +43,7 @@ func (consulLogger) Infof(format string, args ...interface{}) { // Warnf Display warning message func (consulLogger) Warnf(format string, args ...interface{}) { - log.Infof(format, args...) + log.Warnf(format, args...) } // Errorf Display error message @@ -59,15 +62,37 @@ func validateRequirements(dataplaneBin, haproxyBin string) error { return nil } +// HAproxy doesn't exit immediately when you pass incorrect log address, so we try to do it on our own to fail fast +func validateHaproxyLogAddress(logAddress string) error { + // allowed values taken from https://cbonte.github.io/haproxy-dconv/2.0/configuration.html#4.2-log + fi, err := os.Stat(logAddress) + if err != nil { + match, err := regexp.Match(`(fd@<[0-9]+>|stdout|stderr)`, []byte(logAddress)) + if err != nil && match { + return nil + } + if !govalidator.IsHost(logAddress) && !govalidator.IsDialString(logAddress) { + return errors.New(fmt.Sprintf("%s should be either syslog host[:port] or a socket", logAddress)) + } + } else { + if fi.Mode()&os.ModeSocket == 0 { + return errors.New(fmt.Sprintf("%s is a file but not a socket", logAddress)) + } + } + return nil +} + func main() { versionFlag := flag.Bool("version", false, "Show version and exit") - logLevel := flag.String("log-level", "INFO", "Log level") consulAddr := flag.String("http-addr", "127.0.0.1:8500", "Consul agent address") service := flag.String("sidecar-for", "", "The consul service id to proxy") serviceTag := flag.String("sidecar-for-tag", "", "The consul service id to proxy") haproxyBin := flag.String("haproxy", haproxy_cmd.DefaultHAProxyBin, "Haproxy binary path") dataplaneBin := flag.String("dataplane", haproxy_cmd.DefaultDataplaneBin, "Dataplane binary path") - haproxyCfgBasePath := flag.String("haproxy-cfg-base-path", "/tmp", "Haproxy binary path") + haproxyCfgBasePath := flag.String("haproxy-cfg-base-path", "/tmp", "Generated Haproxy configs path") + haproxyLogRequests := flag.Bool("haproxy-log-requests", false, "Enable logging requests by Haproxy") + haproxyLogAddress := flag.String("haproxy-log-address", "", "Address for Haproxy logs (default stderr with this app logs)") + logLevel := flag.String("log-level", "INFO", "This app log level") statsListenAddr := flag.String("stats-addr", "", "Listen addr for stats server") statsServiceRegister := flag.Bool("stats-service-register", false, "Register a consul service for connect stats") enableIntentions := flag.Bool("enable-intentions", false, "Enable Connect intentions") @@ -89,6 +114,12 @@ func main() { } log.SetLevel(ll) + if *haproxyLogAddress != "" { + if err := validateHaproxyLogAddress(*haproxyLogAddress); err != nil { + log.Fatal(err) + } + } + sd := lib.NewShutdown() consulConfig := &api.Config{ @@ -144,7 +175,8 @@ func main() { EnableIntentions: *enableIntentions, StatsListenAddr: *statsListenAddr, StatsRegisterService: *statsServiceRegister, - LogRequests: ll == log.TraceLevel, + HAProxyLogRequests: *haproxyLogRequests, + HAProxyLogAddress: *haproxyLogAddress, }) sd.Add(1) go func() {