From e2dc347e34bb49b1e6b288677910e84413d1e9f0 Mon Sep 17 00:00:00 2001 From: Anna Simon Date: Thu, 20 Jul 2023 14:27:26 +0900 Subject: [PATCH 1/2] feat(config/server): Add option to deploy a HTTP server for the metrics endpoint Signed-off-by: Anna Simon --- README.md | 4 ++++ config.go | 2 ++ config_example.yaml | 2 ++ main.go | 39 +++++++++++++++++++++++++++++++++++++-- types/types.go | 12 +++++++----- 5 files changed, 52 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index aaaf171d3..15bd44d91 100644 --- a/README.md +++ b/README.md @@ -208,6 +208,8 @@ tlsserver: keyfile: "/etc/certs/server/server.key" # server key mutualtls: false # if true, mTLS server will be deployed instead of TLS, deploy also has to be true cacertfile: "/etc/certs/server/ca.crt" # for client certification if mutualtls is true + metricshttp: false # if true, a separate http server will be deployed for the Prometheus metrics endpoint + metricsport: 2802 # port to serve metrics http server if deployed (default: 2802) slack: webhookurl: "" # Slack WebhookURL (ex: https://hooks.slack.com/services/XXXX/YYYY/ZZZZ), if not empty, Slack output is enabled @@ -688,6 +690,8 @@ care of lower/uppercases**) : `yaml: a.b --> envvar: A_B` : - **TLSSERVER_KEYFILE**: server key file for TLS Server (default: "/etc/certs/server/server.key") - **TLSSERVER_MUTUALTLS**: if _true_ mutual TLS server will be deployed instead of TLS, deploy also has to be true - **TLSSERVER_CACERTFILE**: CA certification file for client certification if TLSSERVER_MUTUALTLS is _true_ (default: "/etc/certs/server/ca.crt") +- **TLSSERVER_METRICSHTTP**: if true, a separate http server will be deployed for the Prometheus metrics endpoint +- **TLSSERVER_METRICSPORT**: port to serve metrics http server if deployed (default: 2802) - **SLACK_WEBHOOKURL** : Slack Webhook URL (ex: https://hooks.slack.com/services/XXXX/YYYY/ZZZZ) - **SLACK_CHANNEL** : Slack Channel (optionnal) - **SLACK_FOOTER** : Slack footer diff --git a/config.go b/config.go index de47185ea..01696811a 100644 --- a/config.go +++ b/config.go @@ -58,6 +58,8 @@ func getConfig() *types.Configuration { v.SetDefault("TLSServer.KeyFile", "/etc/certs/server/server.key") v.SetDefault("TLSServer.MutualTLS", false) v.SetDefault("TLSServer.CaCertFile", "/etc/certs/server/ca.crt") + v.SetDefault("TLSServer.MetricsHTTP", false) + v.SetDefault("TLSServer.MetricsPort", 2802) v.SetDefault("Slack.WebhookURL", "") v.SetDefault("Slack.Footer", "https://github.com/falcosecurity/falcosidekick") diff --git a/config_example.yaml b/config_example.yaml index ed4e3bfa5..b4f9f5469 100644 --- a/config_example.yaml +++ b/config_example.yaml @@ -19,6 +19,8 @@ tlsserver: keyfile: "/etc/certs/server/server.key" # server key mutualtls: false # if true, mTLS server will be deployed instead of TLS, deploy also has to be true cacertfile: "/etc/certs/server/ca.crt" # for client certification if mutualtls is true + metricshttp: false # if true, a separate http server will be deployed for the Prometheus metrics endpoint + metricsport: 2802 # port to serve metrics http server if deployed (default: 2802) slack: diff --git a/main.go b/main.go index ccb4b937a..1e0bfe037 100644 --- a/main.go +++ b/main.go @@ -767,8 +767,31 @@ func main() { log.Printf("[DEBUG] : running TLS server") } - if err := server.ListenAndServeTLS(config.TLSServer.CertFile, config.TLSServer.KeyFile); err != nil { - log.Fatalf("[ERROR] : %v", err.Error()) + if config.TLSServer.MetricsHTTP { + if config.Debug { + log.Printf("[DEBUG] : running HTTP server for /metrics endpoint") + } + + metricsServeMux := http.NewServeMux() + metricsServeMux.Handle("/metrics", promhttp.Handler()) + + metricsServer := &http.Server{ + Addr: fmt.Sprintf("%s:%d", config.ListenAddress, config.TLSServer.MetricsPort), + Handler: metricsServeMux, + // Timeouts + ReadTimeout: 60 * time.Second, + ReadHeaderTimeout: 60 * time.Second, + WriteTimeout: 60 * time.Second, + IdleTimeout: 60 * time.Second, + } + errs := make(chan error, 1) + go serveTLS(server, errs) + go serveHTTP(metricsServer, errs) + log.Fatal(<-errs) + } else { + if err := server.ListenAndServeTLS(config.TLSServer.CertFile, config.TLSServer.KeyFile); err != nil { + log.Fatalf("[ERROR] : %v", err.Error()) + } } } else { if config.Debug { @@ -779,8 +802,20 @@ func main() { log.Printf("[WARN] : tlsserver.deploy is false but tlsserver.mutualtls is true, change tlsserver.deploy to true to use mTLS") } + if config.TLSServer.MetricsHTTP { + log.Printf("[WARN] : tlsserver.deploy is false but tlsserver.metricshttp is true, change tlsserver.deploy to true to use TLS") + } + if err := server.ListenAndServe(); err != nil { log.Fatalf("[ERROR] : %v", err.Error()) } } } + +func serveTLS(server *http.Server, errs chan<- error) { + errs <- server.ListenAndServeTLS(config.TLSServer.CertFile, config.TLSServer.KeyFile) +} + +func serveHTTP(server *http.Server, errs chan<- error) { + errs <- server.ListenAndServe() +} diff --git a/types/types.go b/types/types.go index 08011668a..580156ffb 100644 --- a/types/types.go +++ b/types/types.go @@ -116,11 +116,13 @@ type MutualTLSClient struct { // TLSServer represents parameters for TLS Server type TLSServer struct { - Deploy bool - CertFile string - KeyFile string - MutualTLS bool - CaCertFile string + Deploy bool + CertFile string + KeyFile string + MutualTLS bool + CaCertFile string + MetricsHTTP bool + MetricsPort int } // SlackOutputConfig represents parameters for Slack From 26126da9da66b752093cd8c952c2409290d80078 Mon Sep 17 00:00:00 2001 From: Anna Simon Date: Mon, 24 Jul 2023 17:49:04 +0900 Subject: [PATCH 2/2] feat(config/server): Add option to deploy a HTTP server for specified endpoints Signed-off-by: Anna Simon --- README.md | 11 +++++--- config.go | 16 ++++++++--- config_example.yaml | 6 +++-- main.go | 66 ++++++++++++++++++++++++++++++++------------- types/types.go | 14 +++++----- 5 files changed, 78 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 15bd44d91..27f287c35 100644 --- a/README.md +++ b/README.md @@ -208,8 +208,11 @@ tlsserver: keyfile: "/etc/certs/server/server.key" # server key mutualtls: false # if true, mTLS server will be deployed instead of TLS, deploy also has to be true cacertfile: "/etc/certs/server/ca.crt" # for client certification if mutualtls is true - metricshttp: false # if true, a separate http server will be deployed for the Prometheus metrics endpoint - metricsport: 2802 # port to serve metrics http server if deployed (default: 2802) + notlsport: 2810 # port to serve http server serving selected endpoints (default: 2810) + # notlspaths: # if not empty, a separate http server will be deployed for the specified endpoints + # - "/metrics" + # - "/healthz" + slack: webhookurl: "" # Slack WebhookURL (ex: https://hooks.slack.com/services/XXXX/YYYY/ZZZZ), if not empty, Slack output is enabled @@ -690,8 +693,8 @@ care of lower/uppercases**) : `yaml: a.b --> envvar: A_B` : - **TLSSERVER_KEYFILE**: server key file for TLS Server (default: "/etc/certs/server/server.key") - **TLSSERVER_MUTUALTLS**: if _true_ mutual TLS server will be deployed instead of TLS, deploy also has to be true - **TLSSERVER_CACERTFILE**: CA certification file for client certification if TLSSERVER_MUTUALTLS is _true_ (default: "/etc/certs/server/ca.crt") -- **TLSSERVER_METRICSHTTP**: if true, a separate http server will be deployed for the Prometheus metrics endpoint -- **TLSSERVER_METRICSPORT**: port to serve metrics http server if deployed (default: 2802) +- **TLSSERVER_NOTLSPORT**: port to serve http server serving selected endpoints (default: 2810) +- **TLSSERVER_NOTLSPATHS**: a comma separated list of endpoints, if not empty, a separate http server will be deployed for the specified endpoints (e.g.: "/metrics,/healtz") - **SLACK_WEBHOOKURL** : Slack Webhook URL (ex: https://hooks.slack.com/services/XXXX/YYYY/ZZZZ) - **SLACK_CHANNEL** : Slack Channel (optionnal) - **SLACK_FOOTER** : Slack footer diff --git a/config.go b/config.go index 01696811a..9028fe66c 100644 --- a/config.go +++ b/config.go @@ -23,6 +23,7 @@ func getConfig() *types.Configuration { c := &types.Configuration{ Customfields: make(map[string]string), Templatedfields: make(map[string]string), + TLSServer: types.TLSServer{NoTLSPaths: make([]string, 0)}, Grafana: types.GrafanaOutputConfig{CustomHeaders: make(map[string]string)}, Loki: types.LokiOutputConfig{CustomHeaders: make(map[string]string)}, Elasticsearch: types.ElasticsearchOutputConfig{CustomHeaders: make(map[string]string)}, @@ -58,8 +59,7 @@ func getConfig() *types.Configuration { v.SetDefault("TLSServer.KeyFile", "/etc/certs/server/server.key") v.SetDefault("TLSServer.MutualTLS", false) v.SetDefault("TLSServer.CaCertFile", "/etc/certs/server/ca.crt") - v.SetDefault("TLSServer.MetricsHTTP", false) - v.SetDefault("TLSServer.MetricsPort", 2802) + v.SetDefault("TLSServer.NoTLSPort", 2810) v.SetDefault("Slack.WebhookURL", "") v.SetDefault("Slack.Footer", "https://github.com/falcosecurity/falcosidekick") @@ -484,6 +484,8 @@ func getConfig() *types.Configuration { } } + v.GetStringSlice("TLSServer.NoTLSPaths") + v.GetStringMapString("Customfields") v.GetStringMapString("Templatedfields") v.GetStringMapString("Webhook.CustomHeaders") @@ -496,6 +498,10 @@ func getConfig() *types.Configuration { log.Printf("[ERROR] : Error unmarshalling config : %s", err) } + if value, present := os.LookupEnv("TLSSERVER_NOTLSPATHS"); present { + c.TLSServer.NoTLSPaths = strings.Split(value, ",") + } + if value, present := os.LookupEnv("CUSTOMFIELDS"); present { customfields := strings.Split(value, ",") for _, label := range customfields { @@ -616,7 +622,11 @@ func getConfig() *types.Configuration { } if c.ListenPort == 0 || c.ListenPort > 65536 { - log.Fatalf("[ERROR] : Bad port number\n") + log.Fatalf("[ERROR] : Bad listening port number\n") + } + + if c.TLSServer.NoTLSPort == 0 || c.TLSServer.NoTLSPort > 65536 { + log.Fatalf("[ERROR] : Bad noTLS server port number\n") } if ip := net.ParseIP(c.ListenAddress); c.ListenAddress != "" && ip == nil { diff --git a/config_example.yaml b/config_example.yaml index b4f9f5469..587d9dd50 100644 --- a/config_example.yaml +++ b/config_example.yaml @@ -19,8 +19,10 @@ tlsserver: keyfile: "/etc/certs/server/server.key" # server key mutualtls: false # if true, mTLS server will be deployed instead of TLS, deploy also has to be true cacertfile: "/etc/certs/server/ca.crt" # for client certification if mutualtls is true - metricshttp: false # if true, a separate http server will be deployed for the Prometheus metrics endpoint - metricsport: 2802 # port to serve metrics http server if deployed (default: 2802) + notlsport: 2810 # port to serve http server serving selected endpoints (default: 2810) + # notlspaths: # if not empty, a separate http server will be deployed for the specified endpoints + # - "/metrics" + # - "/healthz" slack: diff --git a/main.go b/main.go index 1e0bfe037..53ccd7a57 100644 --- a/main.go +++ b/main.go @@ -723,19 +723,46 @@ func init() { } func main() { - http.HandleFunc("/", mainHandler) - http.HandleFunc("/ping", pingHandler) - http.HandleFunc("/healthz", healthHandler) - http.HandleFunc("/test", testHandler) - http.Handle("/metrics", promhttp.Handler()) - - log.Printf("[INFO] : Falco Sidekick is up and listening on %s:%d", config.ListenAddress, config.ListenPort) if config.Debug { log.Printf("[INFO] : Debug mode : %v", config.Debug) } + routes := map[string]http.Handler{ + "/": http.HandlerFunc(mainHandler), + "/ping": http.HandlerFunc(pingHandler), + "/healthz": http.HandlerFunc(healthHandler), + "/test": http.HandlerFunc(testHandler), + "/metrics": promhttp.Handler(), + } + + mainServeMux := http.NewServeMux() + var HTTPServeMux *http.ServeMux + + // configure HTTP routes requested by NoTLSPath config + if config.TLSServer.Deploy { + HTTPServeMux = http.NewServeMux() + for _, r := range config.TLSServer.NoTLSPaths { + handler, ok := routes[r] + if ok { + delete(routes, r) + if config.Debug { + log.Printf("[DEBUG] : %s is served on http", r) + } + HTTPServeMux.Handle(r, handler) + } else { + log.Printf("[WARN] : tlsserver.notlspaths has unknown path '%s'", r) + } + } + } + + // configure main server routes + for r, handler := range routes { + mainServeMux.Handle(r, handler) + } + server := &http.Server{ - Addr: fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort), + Addr: fmt.Sprintf("%s:%d", config.ListenAddress, config.ListenPort), + Handler: mainServeMux, // Timeouts ReadTimeout: 60 * time.Second, ReadHeaderTimeout: 60 * time.Second, @@ -767,28 +794,28 @@ func main() { log.Printf("[DEBUG] : running TLS server") } - if config.TLSServer.MetricsHTTP { + if len(config.TLSServer.NoTLSPaths) != 0 { if config.Debug { - log.Printf("[DEBUG] : running HTTP server for /metrics endpoint") + log.Printf("[DEBUG] : running HTTP server for endpoints defined in tlsserver.notlspaths") } - metricsServeMux := http.NewServeMux() - metricsServeMux.Handle("/metrics", promhttp.Handler()) - - metricsServer := &http.Server{ - Addr: fmt.Sprintf("%s:%d", config.ListenAddress, config.TLSServer.MetricsPort), - Handler: metricsServeMux, + httpServer := &http.Server{ + Addr: fmt.Sprintf("%s:%d", config.ListenAddress, config.TLSServer.NoTLSPort), + Handler: HTTPServeMux, // Timeouts ReadTimeout: 60 * time.Second, ReadHeaderTimeout: 60 * time.Second, WriteTimeout: 60 * time.Second, IdleTimeout: 60 * time.Second, } + log.Printf("[INFO] : Falco Sidekick is up and listening on %s:%d and %s:%d", config.ListenAddress, config.ListenPort, config.ListenAddress, config.TLSServer.NoTLSPort) + errs := make(chan error, 1) go serveTLS(server, errs) - go serveHTTP(metricsServer, errs) + go serveHTTP(httpServer, errs) log.Fatal(<-errs) } else { + log.Printf("[INFO] : Falco Sidekick is up and listening on %s:%d", config.ListenAddress, config.ListenPort) if err := server.ListenAndServeTLS(config.TLSServer.CertFile, config.TLSServer.KeyFile); err != nil { log.Fatalf("[ERROR] : %v", err.Error()) } @@ -802,10 +829,11 @@ func main() { log.Printf("[WARN] : tlsserver.deploy is false but tlsserver.mutualtls is true, change tlsserver.deploy to true to use mTLS") } - if config.TLSServer.MetricsHTTP { - log.Printf("[WARN] : tlsserver.deploy is false but tlsserver.metricshttp is true, change tlsserver.deploy to true to use TLS") + if len(config.TLSServer.NoTLSPaths) != 0 { + log.Printf("[WARN] : tlsserver.deploy is false but tlsserver.notlspaths is not empty, change tlsserver.deploy to true to deploy two servers") } + log.Printf("[INFO] : Falco Sidekick is up and listening on %s:%d", config.ListenAddress, config.ListenPort) if err := server.ListenAndServe(); err != nil { log.Fatalf("[ERROR] : %v", err.Error()) } diff --git a/types/types.go b/types/types.go index 580156ffb..7fa84a73e 100644 --- a/types/types.go +++ b/types/types.go @@ -116,13 +116,13 @@ type MutualTLSClient struct { // TLSServer represents parameters for TLS Server type TLSServer struct { - Deploy bool - CertFile string - KeyFile string - MutualTLS bool - CaCertFile string - MetricsHTTP bool - MetricsPort int + Deploy bool + CertFile string + KeyFile string + MutualTLS bool + CaCertFile string + NoTLSPort int + NoTLSPaths []string } // SlackOutputConfig represents parameters for Slack