- internal/native/ — systemd units, installer download, node install/remove/status (ported from wallarm-native.sh) - internal/docker/ — Docker engine install (apt/yum/apk), image load, container deploy/remove/status (ported from wallarm-ct-deploy.sh) - Makefile — cross-compile targets (linux-amd64, linux-arm64)
202 lines
5.9 KiB
Go
202 lines
5.9 KiB
Go
// 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, ""
|
|
}
|