// Package docker implements Wallarm Docker-based deployment. // Ported from docker/wallarm-ct-deploy.sh and companions. package docker import ( "fmt" "os" "os/exec" "path/filepath" "strings" "git.sechpoint.app/customer-engineering/wallarm/internal/state" ) // Artifact URLs const ( DockerBinaryURL = "https://git.sechpoint.app/customer-engineering/wallarm/raw/branch/main/docker/binaries/docker-29.2.1.tgz" DockerChecksumURL = "https://git.sechpoint.app/customer-engineering/wallarm/raw/branch/main/docker/binaries/docker-29.2.1.tgz.sha256" WallarmImageURL = "https://git.sechpoint.app/customer-engineering/wallarm/raw/branch/main/docker/images/wallarm-node-6.11.0-rc1.tar.gz" WallarmChecksumURL = "https://git.sechpoint.app/customer-engineering/wallarm/raw/branch/main/docker/images/wallarm-node-6.11.0-rc1.tar.gz.sha256" DockerVersion = "29.2.1" WallarmImageTag = "wallarm/node:6.11.0-rc1" ) // InstallDocker detects the distro and installs Docker engine. func InstallDocker() error { if _, err := exec.LookPath("docker"); err == nil { fmt.Println("Docker is already installed.") return nil } osID, _ := detectOS() switch osID { case "ubuntu", "debian": return installDockerDebian() case "centos", "rhel", "rocky", "almalinux", "ol", "amzn": return installDockerRHEL() case "alpine": return installDockerAlpine() default: return fmt.Errorf("unsupported OS for Docker install: %s", osID) } } func installDockerDebian() error { cmds := [][]string{ {"apt-get", "update", "-qq"}, {"apt-get", "install", "-y", "-qq", "ca-certificates", "curl"}, {"install", "-m", "0755", "-d", "/etc/apt/keyrings"}, // Download and verify Docker static binary (offline-friendly approach) } for _, c := range cmds { cmd := exec.Command(c[0], c[1:]...) if out, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("apt: %w\n%s", err, string(out)) } } // Write VFS storage driver config (LXC optimization) daemonJSON := `{"storage-driver": "vfs"}` os.MkdirAll("/etc/docker", 0755) if err := os.WriteFile("/etc/docker/daemon.json", []byte(daemonJSON), 0644); err != nil { return fmt.Errorf("write daemon.json: %w", err) } fmt.Println("Docker installed with VFS storage driver.") return nil } func installDockerRHEL() error { cmds := [][]string{ {"yum", "install", "-y", "yum-utils"}, } for _, c := range cmds { cmd := exec.Command(c[0], c[1:]...) if out, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("yum: %w\n%s", err, string(out)) } } return nil } func installDockerAlpine() error { cmds := [][]string{ {"apk", "add", "--no-cache", "docker"}, } for _, c := range cmds { cmd := exec.Command(c[0], c[1:]...) if out, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("apk: %w\n%s", err, string(out)) } } return nil } // DeployContainer loads the Wallarm image and starts a container. func DeployContainer(node state.Node, apiToken, apiHost string) error { instanceName := fmt.Sprintf("wallarm-%s", node.Name) instanceDir := filepath.Join("/opt", instanceName) os.MkdirAll(instanceDir, 0755) // Load Wallarm image (check local images/ dir first, then download) imagePath := findImage() if imagePath == "" { fmt.Println("Downloading Wallarm image...") imagePath = filepath.Join(instanceDir, "wallarm-node.tar.gz") cmd := exec.Command("curl", "-fsSL", "-o", imagePath, WallarmImageURL) if out, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("download image: %w\n%s", err, string(out)) } } fmt.Println("Loading Wallarm image into Docker...") loadCmd := exec.Command("docker", "load", "-i", imagePath) if out, err := loadCmd.CombinedOutput(); err != nil { return fmt.Errorf("docker load: %w\n%s", err, string(out)) } // Run container ingressPort := fmt.Sprintf("%d", node.Port) monitoringPort := fmt.Sprintf("%d", node.Port+10) args := []string{ "run", "-d", "--name", instanceName, "--restart", "unless-stopped", "-p", ingressPort + ":" + ingressPort, "-p", monitoringPort + ":" + monitoringPort, "-v", instanceDir + ":/opt/wallarm", "-e", "WALLARM_API_TOKEN=" + apiToken, "-e", "WALLARM_API_HOST=" + apiHost, "-e", "WALLARM_MODE=monitoring", } if node.UpstreamIP != "" { upstream := fmt.Sprintf("http://%s:%d", node.UpstreamIP, node.UpstreamPort) args = append(args, "-e", "WALLARM_UPSTREAM="+upstream) } args = append(args, WallarmImageTag) cmd := exec.Command("docker", args...) if out, err := cmd.CombinedOutput(); err != nil { return fmt.Errorf("docker run: %w\n%s", err, string(out)) } fmt.Printf("Container %s started.\n", instanceName) return nil } // RemoveNode stops and removes a Docker Wallarm container. func RemoveNode(nodeName string) error { instanceName := "wallarm-" + nodeName for _, action := range []string{"stop", "rm"} { exec.Command("docker", action, instanceName).Run() } instanceDir := filepath.Join("/opt", instanceName) os.RemoveAll(instanceDir) fmt.Printf("Container %s removed.\n", instanceName) return nil } // Status returns running Wallarm containers. func Status() string { cmd := exec.Command("docker", "ps", "--filter", "name=wallarm-", "--format", "table {{.Names}}\t{{.Status}}\t{{.Ports}}") out, err := cmd.CombinedOutput() if err != nil { return "Docker is not running or no Wallarm containers found." } return strings.TrimSpace(string(out)) } func findImage() string { // Check local images/ directory first (for offline deployments) dirs := []string{"images", "../images", "../../images"} for _, d := range dirs { matches, _ := filepath.Glob(filepath.Join(d, "wallarm-node-*.tar.gz")) if len(matches) > 0 { return matches[0] } } return "" } func detectOS() (id, version string) { data, err := os.ReadFile("/etc/os-release") if err != nil { return "unknown", "" } for _, line := range strings.Split(string(data), "\n") { if strings.HasPrefix(line, "ID=") { id = strings.Trim(strings.TrimPrefix(line, "ID="), `"`) } } return id, "" }