From 2c8b7b5ca5ccc031f60f4c3b627483bade477ecc Mon Sep 17 00:00:00 2001 From: Siddharth Bansal Date: Sun, 19 Apr 2026 12:27:16 +0530 Subject: [PATCH 1/2] (fix): Added a logrotate function to the crowdsec.go installer file --- install/crowdsec.go | 80 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/install/crowdsec.go b/install/crowdsec.go index c75dccf32..d2817a76c 100644 --- a/install/crowdsec.go +++ b/install/crowdsec.go @@ -6,6 +6,7 @@ import ( "log" "os" "os/exec" + "path/filepath" "strings" "gopkg.in/yaml.v3" @@ -40,6 +41,8 @@ func installCrowdsec(config Config) error { os.Exit(1) } + setupTraefikLogRotate() + if err := copyDockerService("config/crowdsec/docker-compose.yml", "docker-compose.yml", "crowdsec"); err != nil { fmt.Printf("Error copying docker service: %v\n", err) os.Exit(1) @@ -208,3 +211,80 @@ func CheckAndAddCrowdsecDependency(composePath string) error { fmt.Println("Added dependency of crowdsec to traefik") return nil } + +// setupTraefikLogRotate writes a logrotate config for the Traefik access log +// that CrowdSec depends on. This is only needed when CrowdSec is installed +// because the default Pangolin install does not enable Traefik access logs. +// +// copytruncate is used so Traefik does not need to be restarted or sent a +// signal after rotation — it keeps writing to the same file descriptor while +// the rotated copy is made and the original is truncated in place. +func setupTraefikLogRotate() { + const logrotateDir = "/etc/logrotate.d" + const logrotateFile = "/etc/logrotate.d/pangolin-traefik" + + // Resolve the absolute path to the install directory so the logrotate + // config references the correct log file regardless of where the user + // installed Pangolin. + installDir, err := filepath.Abs(".") + if err != nil { + fmt.Printf("[logrotate] Warning: could not resolve install directory: %v\n", err) + fmt.Println("[logrotate] Skipping logrotate setup. Set it up manually:") + printLogrotateConfig("/config/traefik/logs/access.log") + return + } + + logPath := filepath.Join(installDir, "config/traefik/logs/access.log") + + if os.Geteuid() != 0 { + fmt.Println("\n[logrotate] Skipping automatic logrotate setup: not running as root.") + fmt.Println("[logrotate] To prevent unbounded growth of the Traefik access log used by CrowdSec,") + fmt.Println("[logrotate] create the file /etc/logrotate.d/pangolin-traefik manually with:") + printLogrotateConfig(logPath) + return + } + + config := fmt.Sprintf(`# Logrotate config for Traefik access logs used by CrowdSec. +# Generated by the Pangolin installer. Safe to edit. +%s { + daily + rotate 7 + compress + delaycompress + missingok + notifempty + copytruncate +} +`, logPath) + + if err := os.MkdirAll(logrotateDir, 0755); err != nil { + fmt.Printf("[logrotate] Warning: could not create %s: %v\n", logrotateDir, err) + return + } + + if err := os.WriteFile(logrotateFile, []byte(config), 0644); err != nil { + fmt.Printf("[logrotate] Warning: could not write %s: %v\n", logrotateFile, err) + fmt.Println("[logrotate] Set it up manually:") + printLogrotateConfig(logPath) + return + } + + fmt.Printf("[logrotate] Wrote logrotate config to %s\n", logrotateFile) + fmt.Println("[logrotate] Traefik access logs will be rotated daily, keeping 7 compressed copies.") +} + +// printLogrotateConfig prints a logrotate config block to stdout so users can +// set it up manually when the installer cannot write to /etc. +func printLogrotateConfig(logPath string) { + fmt.Printf(` + %s { + daily + rotate 7 + compress + delaycompress + missingok + notifempty + copytruncate + } +`, logPath) +} From 473bce856d4d864875caa6786a4185bbe15802d2 Mon Sep 17 00:00:00 2001 From: Siddharth Bansal Date: Mon, 20 Apr 2026 21:36:42 +0530 Subject: [PATCH 2/2] Pass installdir as a parameter --- install/crowdsec.go | 17 +++-------------- install/main.go | 2 +- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/install/crowdsec.go b/install/crowdsec.go index d2817a76c..8dff42d99 100644 --- a/install/crowdsec.go +++ b/install/crowdsec.go @@ -12,7 +12,7 @@ import ( "gopkg.in/yaml.v3" ) -func installCrowdsec(config Config) error { +func installCrowdsec(config Config, installDir string) error { if err := stopContainers(config.InstallationContainerType); err != nil { return fmt.Errorf("failed to stop containers: %v", err) @@ -41,7 +41,7 @@ func installCrowdsec(config Config) error { os.Exit(1) } - setupTraefikLogRotate() + setupTraefikLogRotate(installDir) if err := copyDockerService("config/crowdsec/docker-compose.yml", "docker-compose.yml", "crowdsec"); err != nil { fmt.Printf("Error copying docker service: %v\n", err) @@ -219,21 +219,10 @@ func CheckAndAddCrowdsecDependency(composePath string) error { // copytruncate is used so Traefik does not need to be restarted or sent a // signal after rotation — it keeps writing to the same file descriptor while // the rotated copy is made and the original is truncated in place. -func setupTraefikLogRotate() { +func setupTraefikLogRotate(installDir string) { const logrotateDir = "/etc/logrotate.d" const logrotateFile = "/etc/logrotate.d/pangolin-traefik" - // Resolve the absolute path to the install directory so the logrotate - // config references the correct log file regardless of where the user - // installed Pangolin. - installDir, err := filepath.Abs(".") - if err != nil { - fmt.Printf("[logrotate] Warning: could not resolve install directory: %v\n", err) - fmt.Println("[logrotate] Skipping logrotate setup. Set it up manually:") - printLogrotateConfig("/config/traefik/logs/access.log") - return - } - logPath := filepath.Join(installDir, "config/traefik/logs/access.log") if os.Geteuid() != 0 { diff --git a/install/main.go b/install/main.go index a38d78fc6..13e506d06 100644 --- a/install/main.go +++ b/install/main.go @@ -259,7 +259,7 @@ func main() { } config.DoCrowdsecInstall = true - err := installCrowdsec(config) + err := installCrowdsec(config, installDir) if err != nil { fmt.Printf("Error installing CrowdSec: %v\n", err) return