diff --git a/README.md b/README.md index ba77f0f..9c56aa1 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,11 @@ Usage of ./haproxy-consul-connect: -http-addr string Consul agent address (default "127.0.0.1:8500") -log-level string - Log level (default "INFO") + This app log level (default "INFO") + -log-requests + Enable logging requests by HAproxy + -log-address + Haproxy logs address (default: stderr with this app logs) -sidecar-for string The consul service id to proxy -sidecar-for-tag string 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..d42e24f 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.LogAddress == "" { err := h.startLogger() if err != nil { return err } + } else { + log.Infof("not starting built-in syslog - using %s as syslog address", h.opts.LogAddress) } 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.LogAddress == "", 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 8e69370..615e100 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..43fc5f5 100644 --- a/haproxy/options.go +++ b/haproxy/options.go @@ -9,4 +9,5 @@ type Options struct { StatsListenAddr string StatsRegisterService bool LogRequests bool + LogAddress string } diff --git a/haproxy/state.go b/haproxy/state.go index cc25fcb..a5b24de 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.LogAddress != "" { + logAddress = h.opts.LogAddress + } + newState, err := state.Generate(state.Options{ EnableIntentions: h.opts.EnableIntentions, LogRequests: h.opts.LogRequests, - LogSocket: h.haConfig.LogsSock, + 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 b397232..a46fc41 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 f8ee6bd..1120d54 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..0e09578 100644 --- a/main.go +++ b/main.go @@ -1,14 +1,17 @@ package main import ( + "errors" "flag" "fmt" + "net" "os" "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,9 +62,41 @@ func validateRequirements(dataplaneBin, haproxyBin string) error { return nil } +func validateLogAddress(logAddress string) error { + fi, err := os.Stat(logAddress) + if err != nil { + // maybe it's a syslog host[:port] + if len(strings.Split(logAddress, ":")) == 1 { + // only host - default syslog port is 514 UDP + logAddress += ":514" + } + if govalidator.IsDialString(logAddress) { + conn, err := net.Dial("udp", logAddress) + if err != nil { + return errors.New(fmt.Sprintf("cannot open %s as syslog", logAddress)) + } + _, err = conn.Write([]byte("syslog test\n")) + if err != nil { + return errors.New(fmt.Sprintf("cannot send data to %s as syslog", logAddress)) + } + defer conn.Close() + } else { + return errors.New(fmt.Sprintf("%s should be either syslog host[:port] or a socket", logAddress)) + } + } else { + // maybe it's a socket + 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") + logRequests := flag.Bool("log-requests", false, "Enable logging requests by Haproxy") + logAddress := flag.String("log-address", "", "Syslog address for Haproxy logs (default: log to stderr with this app)") 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") @@ -89,6 +124,13 @@ func main() { } log.SetLevel(ll) + if *logAddress != "" { + err := validateLogAddress(*logAddress) + if err != nil { + log.Fatal(err) + } + } + sd := lib.NewShutdown() consulConfig := &api.Config{ @@ -144,7 +186,8 @@ func main() { EnableIntentions: *enableIntentions, StatsListenAddr: *statsListenAddr, StatsRegisterService: *statsServiceRegister, - LogRequests: ll == log.TraceLevel, + LogRequests: *logRequests, + LogAddress: *logAddress, }) sd.Add(1) go func() {