From 57590457a82d283dfdbea49fed8a1587246343a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Pa=C3=9F?= <22845248+mpass99@users.noreply.github.com> Date: Wed, 24 Jan 2024 12:43:27 +0100 Subject: [PATCH] Add logging filter token The token is used to filter out request logs when the user agent matches a randomly generated string. --- cmd/poseidon/main.go | 18 ++++++++++++++++-- configuration.example.yaml | 5 +++++ internal/config/config.go | 15 +++++++++++++++ pkg/dto/dto.go | 11 +++++++++++ pkg/logging/logging.go | 6 +++++- 5 files changed, 52 insertions(+), 3 deletions(-) diff --git a/cmd/poseidon/main.go b/cmd/poseidon/main.go index 659a192..faa51f4 100644 --- a/cmd/poseidon/main.go +++ b/cmd/poseidon/main.go @@ -16,6 +16,7 @@ import ( "github.com/openHPI/poseidon/internal/environment" "github.com/openHPI/poseidon/internal/nomad" "github.com/openHPI/poseidon/internal/runner" + "github.com/openHPI/poseidon/pkg/dto" "github.com/openHPI/poseidon/pkg/logging" "github.com/openHPI/poseidon/pkg/monitoring" "golang.org/x/sys/unix" @@ -40,7 +41,7 @@ var ( pgoEnabled = "false" ) -func getVcsRevision() string { +func getVcsRevision(short bool) string { vcsRevision := "unknown" vcsModified := false @@ -59,6 +60,10 @@ func getVcsRevision() string { } } + if short { + vcsRevision = vcsRevision[:7] + } + if vcsModified { return vcsRevision + "-modified" } else { @@ -66,9 +71,15 @@ func getVcsRevision() string { } } +func initializeUserAgent() { + dto.UserAgentOut = strings.ReplaceAll(dto.UserAgentOut, dto.UserAgentVCSPlaceholder, getVcsRevision(true)) + dto.UserAgentFiltered = strings.ReplaceAll(dto.UserAgentFiltered, dto.UserAgentVCSPlaceholder, getVcsRevision(true)) + dto.UserAgentFiltered = strings.ReplaceAll(dto.UserAgentFiltered, dto.UserAgentFilterTokenPlaceholder, config.Config.Server.LoggingFilterToken) +} + func initSentry(options *sentry.ClientOptions, profilingEnabled bool) { if options.Release == "" { - commit := getVcsRevision() + commit := getVcsRevision(false) options.Release = commit } @@ -278,6 +289,8 @@ func notifySystemdWatchdog(ctx context.Context, healthURL string, client *http.C if err != nil { return } + + req.Header.Set("User-Agent", dto.UserAgentFiltered) resp, err := client.Do(req) if err != nil { log.WithError(err).Debug("Failed watchdog health check") @@ -425,6 +438,7 @@ func main() { if err := config.InitConfig(); err != nil { log.WithError(err).Warn("Could not initialize configuration") } + initializeUserAgent() logging.InitializeLogging(config.Config.Logger.Level, config.Config.Logger.Formatter) initSentry(&config.Config.Sentry, config.Config.Profiling.CPUEnabled) diff --git a/configuration.example.yaml b/configuration.example.yaml index ff6ea43..36144fc 100644 --- a/configuration.example.yaml +++ b/configuration.example.yaml @@ -25,6 +25,11 @@ server: interactivestderr: true # If set, the file at the given path overwrites the default Nomad job file in internal/environment/template-environment-job.hcl # templatejobfile: ./poseidon.hcl + # The LoggingFilterToken filters out Systemd Watchdog requests from the logs and is preconfigured with a random value. + # It can also be manually configured to hide additional requests from the logs, such as those from monitoring systems. + # To use this feature, the respective user agent must be set according to `dto.UserAgentFiltered`. + # However, it is important to consider the security implications of using this expert-level setting for manual values. + # loggingfiltertoken: secret # alert defines how poseidon should handle specific risks. alert: # The prewarming pool threshold [0, 1) defines which part of the prewarming pool should always be filled. diff --git a/internal/config/config.go b/internal/config/config.go index 9359708..32a45ac 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,7 +1,9 @@ package config import ( + "crypto/rand" "crypto/tls" + "encoding/base64" "errors" "flag" "fmt" @@ -37,6 +39,7 @@ var ( PrewarmingPoolThreshold: 0, PrewarmingPoolReloadTimeout: 0, }, + LoggingFilterToken: randomFilterToken(), }, Nomad: Nomad{ Enabled: true, @@ -100,6 +103,7 @@ type server struct { InteractiveStderr bool TemplateJobFile string Alert alert + LoggingFilterToken string } // URL returns the URL of the Poseidon webserver. @@ -280,3 +284,14 @@ func loadValue(prefix string, value reflect.Value, logEntry *logrus.Entry) { Warn("Setting configuration option via environment variables is not supported") } } + +func randomFilterToken() string { + const tokenLength = 32 + randomBytes := make([]byte, tokenLength) //nolint:all // length required to be filled by rand.Read. + n, err := rand.Read(randomBytes) + if n != tokenLength || err != nil { + log.WithError(err).WithField("byteCount", n).Fatal("Failed to generate random token") + } + + return base64.URLEncoding.EncodeToString(randomBytes) +} diff --git a/pkg/dto/dto.go b/pkg/dto/dto.go index 6d674fe..70afb66 100644 --- a/pkg/dto/dto.go +++ b/pkg/dto/dto.go @@ -9,6 +9,17 @@ import ( "strings" ) +var ( + // UserAgentOut for outgoing requests (without libraries). The Git Hash will be replaced by main.go. + UserAgentOut = "Poseidon/" + UserAgentVCSPlaceholder + " Go-http-client/1.1" + UserAgentFiltered = "Poseidon/" + UserAgentVCSPlaceholder + " (" + UserAgentFilterTokenPlaceholder + ") Go-http-client/1.1" +) + +const ( + UserAgentVCSPlaceholder = "<7 Git Hash>" + UserAgentFilterTokenPlaceholder = "FilterToken" +) + // RunnerRequest is the expected json structure of the request body for the ProvideRunner function. type RunnerRequest struct { ExecutionEnvironmentID int `json:"executionEnvironmentId"` diff --git a/pkg/logging/logging.go b/pkg/logging/logging.go index ed2ff06..821b664 100644 --- a/pkg/logging/logging.go +++ b/pkg/logging/logging.go @@ -93,7 +93,11 @@ func HTTPLoggingMiddleware(next http.Handler) http.Handler { "duration": latency, "user_agent": RemoveNewlineSymbol(r.UserAgent()), }) - logEntry.Debug() + if r.UserAgent() == dto.UserAgentFiltered { + logEntry.Trace() + } else { + logEntry.Debug() + } }) }