mirror of
https://github.com/fosrl/pangolin.git
synced 2025-05-12 21:30:35 +01:00
Crowdsec installer works?
This commit is contained in:
parent
fd11fb81d6
commit
5f95500b6f
4 changed files with 165 additions and 57 deletions
|
@ -333,3 +333,61 @@ func replaceInFile(filepath, oldStr, newStr string) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckAndAddTraefikLogVolume(composePath string) error {
|
||||
// Read the docker-compose.yml file
|
||||
data, err := os.ReadFile(composePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading compose file: %w", err)
|
||||
}
|
||||
|
||||
// Parse YAML into a generic map
|
||||
var compose map[string]interface{}
|
||||
if err := yaml.Unmarshal(data, &compose); err != nil {
|
||||
return fmt.Errorf("error parsing compose file: %w", err)
|
||||
}
|
||||
|
||||
// Get services section
|
||||
services, ok := compose["services"].(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("services section not found or invalid")
|
||||
}
|
||||
|
||||
// Get traefik service
|
||||
traefik, ok := services["traefik"].(map[string]interface{})
|
||||
if !ok {
|
||||
return fmt.Errorf("traefik service not found or invalid")
|
||||
}
|
||||
|
||||
// Check volumes
|
||||
logVolume := "./config/traefik/logs:/var/log/traefik"
|
||||
var volumes []interface{}
|
||||
|
||||
if existingVolumes, ok := traefik["volumes"].([]interface{}); ok {
|
||||
// Check if volume already exists
|
||||
for _, v := range existingVolumes {
|
||||
if v.(string) == logVolume {
|
||||
fmt.Println("Traefik log volume is already configured")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
volumes = existingVolumes
|
||||
}
|
||||
|
||||
// Add new volume
|
||||
volumes = append(volumes, logVolume)
|
||||
traefik["volumes"] = volumes
|
||||
|
||||
// Write updated config back to file
|
||||
newData, err := MarshalYAMLWithIndent(compose, 2)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error marshaling updated compose file: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(composePath, newData, 0644); err != nil {
|
||||
return fmt.Errorf("error writing updated compose file: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("Added traefik log volume and created logs directory")
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ services:
|
|||
volumes:
|
||||
- ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration
|
||||
- ./config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates
|
||||
- ./config/traefik/logs:/var/log/traefik # Volume to store Traefik logs
|
||||
|
||||
networks:
|
||||
default:
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func installCrowdsec(config Config) error {
|
||||
|
@ -59,70 +58,30 @@ func installCrowdsec(config Config) error {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := retrieveBouncerKey(config); err != nil {
|
||||
return fmt.Errorf("bouncer key retrieval failed: %v", err)
|
||||
if err := CheckAndAddTraefikLogVolume("docker-compose.yml"); err != nil {
|
||||
fmt.Printf("Error checking and adding Traefik log volume: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// if err := startContainers(); err != nil {
|
||||
// return fmt.Errorf("failed to start containers: %v", err)
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func retrieveBouncerKey(config Config) error {
|
||||
|
||||
fmt.Println("Retrieving bouncer key. Please be patient...")
|
||||
|
||||
// Start crowdsec container
|
||||
cmd := exec.Command("docker", "compose", "up", "-d", "crowdsec")
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to start crowdsec: %v", err)
|
||||
if err := startContainers(); err != nil {
|
||||
return fmt.Errorf("failed to start containers: %v", err)
|
||||
}
|
||||
|
||||
// verify that the container is running if not keep waiting for 10 more seconds then return an error
|
||||
count := 0
|
||||
for {
|
||||
cmd := exec.Command("docker", "inspect", "-f", "{{.State.Running}}", "crowdsec")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to inspect crowdsec container: %v", err)
|
||||
}
|
||||
if strings.TrimSpace(string(output)) == "true" {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Second)
|
||||
count++
|
||||
|
||||
if count > 4 {
|
||||
return fmt.Errorf("crowdsec container is not running")
|
||||
}
|
||||
}
|
||||
|
||||
// Get bouncer key
|
||||
output, err := exec.Command("docker", "exec", "crowdsec", "cscli", "bouncers", "add", "traefik-bouncer").Output()
|
||||
// get API key
|
||||
apiKey, err := GetCrowdSecAPIKey()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get bouncer key: %v", err)
|
||||
return fmt.Errorf("failed to get API key: %v", err)
|
||||
}
|
||||
config.TraefikBouncerKey = apiKey
|
||||
|
||||
if err := replaceInFile("config/traefik/dynamic_config.yml", "PUT_YOUR_BOUNCER_KEY_HERE_OR_IT_WILL_NOT_WORK", config.TraefikBouncerKey); err != nil {
|
||||
return fmt.Errorf("failed to replace bouncer key: %v", err)
|
||||
}
|
||||
|
||||
// Parse key from output
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "key:") {
|
||||
config.TraefikBouncerKey = strings.TrimSpace(strings.Split(line, ":")[1])
|
||||
fmt.Println("Bouncer key:", config.TraefikBouncerKey)
|
||||
break
|
||||
}
|
||||
if err := restartContainer("traefik"); err != nil {
|
||||
return fmt.Errorf("failed to restart containers: %v", err)
|
||||
}
|
||||
|
||||
// Stop crowdsec container
|
||||
cmd = exec.Command("docker", "compose", "down")
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to stop crowdsec: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println("Bouncer key retrieved successfully.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -136,3 +95,27 @@ func checkIsCrowdsecInstalledInCompose() bool {
|
|||
// Check for crowdsec service
|
||||
return bytes.Contains(content, []byte("crowdsec:"))
|
||||
}
|
||||
|
||||
func GetCrowdSecAPIKey() (string, error) {
|
||||
// First, ensure the container is running
|
||||
if err := waitForContainer("crowdsec"); err != nil {
|
||||
return "", fmt.Errorf("waiting for container: %w", err)
|
||||
}
|
||||
|
||||
// Execute the command to get the API key
|
||||
cmd := exec.Command("docker", "exec", "crowdsec", "cscli", "bouncers", "add", "traefik-bouncer", "-o", "raw")
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", fmt.Errorf("executing command: %w", err)
|
||||
}
|
||||
|
||||
// Trim any whitespace from the output
|
||||
apiKey := strings.TrimSpace(out.String())
|
||||
if apiKey == "" {
|
||||
return "", fmt.Errorf("empty API key returned")
|
||||
}
|
||||
|
||||
return apiKey, nil
|
||||
}
|
||||
|
|
|
@ -4,14 +4,16 @@ import (
|
|||
"bufio"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"io"
|
||||
"time"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"bytes"
|
||||
"text/template"
|
||||
"unicode"
|
||||
|
||||
|
@ -590,6 +592,42 @@ func startContainers() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func restartContainer(container string) error {
|
||||
fmt.Printf("Restarting %s container...\n", container)
|
||||
|
||||
// Check which docker compose command is available
|
||||
var useNewStyle bool
|
||||
checkCmd := exec.Command("docker", "compose", "version")
|
||||
if err := checkCmd.Run(); err == nil {
|
||||
useNewStyle = true
|
||||
} else {
|
||||
// Check if docker-compose (old style) is available
|
||||
checkCmd = exec.Command("docker-compose", "version")
|
||||
if err := checkCmd.Run(); err != nil {
|
||||
return fmt.Errorf("neither 'docker compose' nor 'docker-compose' command is available: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to execute docker compose commands
|
||||
executeCommand := func(args ...string) error {
|
||||
var cmd *exec.Cmd
|
||||
if useNewStyle {
|
||||
cmd = exec.Command("docker", append([]string{"compose"}, args...)...)
|
||||
} else {
|
||||
cmd = exec.Command("docker-compose", args...)
|
||||
}
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
if err := executeCommand("-f", "docker-compose.yml", "restart", container); err != nil {
|
||||
return fmt.Errorf("failed to restart %s container: %v", container, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyFile(src, dst string) error {
|
||||
source, err := os.Open(src)
|
||||
if err != nil {
|
||||
|
@ -613,4 +651,32 @@ func moveFile(src, dst string) error {
|
|||
}
|
||||
|
||||
return os.Remove(src)
|
||||
}
|
||||
|
||||
func waitForContainer(containerName string) error {
|
||||
maxAttempts := 30
|
||||
retryInterval := time.Second * 2
|
||||
|
||||
for attempt := 0; attempt < maxAttempts; attempt++ {
|
||||
// Check if container is running
|
||||
cmd := exec.Command("docker", "container", "inspect", "-f", "{{.State.Running}}", containerName)
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
// If the container doesn't exist or there's another error, wait and retry
|
||||
time.Sleep(retryInterval)
|
||||
continue
|
||||
}
|
||||
|
||||
isRunning := strings.TrimSpace(out.String()) == "true"
|
||||
if isRunning {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Container exists but isn't running yet, wait and retry
|
||||
time.Sleep(retryInterval)
|
||||
}
|
||||
|
||||
return fmt.Errorf("container %s did not start within %v seconds", containerName, maxAttempts*int(retryInterval.Seconds()))
|
||||
}
|
Loading…
Reference in a new issue