wallarm/internal/native/native.go

174 lines
4.6 KiB
Go

package native
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"git.sechpoint.app/customer-engineering/wallarm/internal/state"
)
const (
BaseDir = "/opt/fw"
Symlink = "/opt/wallarm"
NginxDir = BaseDir + "/nginx"
NodesDir = BaseDir
)
func installerURL() string {
if u := os.Getenv("WALLARM_INSTALLER_URL"); u != "" {
return u
}
return "https://storage.googleapis.com/meganode_storage/6.13/wallarm-6.13.0.x86_64-glibc.sh"
}
func InstallNode(node state.Node, apiToken, apiHost, labels string) error {
if apiToken == "" {
return fmt.Errorf("API token required")
}
instanceDir := filepath.Join(BaseDir, node.Name, "wallarm")
installerPath := filepath.Join(BaseDir, "wallarm-aio.sh")
_ = labels
// 1. Install NGINX (shared)
installNginx()
// 2. Download AIO once
if _, err := os.Stat(installerPath); os.IsNotExist(err) {
fmt.Printf("[%s] Downloading installer...\n", node.Name)
cmd := exec.Command("curl", "-fsSL", "-o", installerPath, installerURL())
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("download: %w\n%s", err, string(out))
}
os.Chmod(installerPath, 0755)
}
// 3. Create instance dir + symlink (setup.sh hardcodes /opt/wallarm)
os.MkdirAll(instanceDir, 0755)
os.Remove(Symlink)
os.Symlink(instanceDir, Symlink)
// 4. Extract AIO to instance via symlink
fmt.Printf("[%s] Extracting...\n", node.Name)
cmd := exec.Command("bash", installerPath, "--noexec", "--keep", "--target", instanceDir, "--noprogress", "--accept")
if out, err := cmd.CombinedOutput(); err != nil {
return fmt.Errorf("extract: %w\n%s", err, string(out))
}
// 5. Run setup.sh (sees /opt/wallarm → instance dir)
fmt.Printf("[%s] Running setup...\n", node.Name)
cmd = exec.Command("bash", filepath.Join(instanceDir, "setup.sh"),
"--batch", "--token", apiToken, "--cloud", cloudFromHost(apiHost),
"--custom-ngx-build",
)
logFile, _ := os.Create(filepath.Join(instanceDir, "install.log"))
if logFile != nil {
cmd.Stdout = logFile
cmd.Stderr = logFile
}
runErr := cmd.Run()
if logFile != nil {
logFile.Close()
}
if runErr != nil {
if data, _ := os.ReadFile(filepath.Join(instanceDir, "install.log")); len(data) > 0 {
lines := strings.Split(string(data), "\n")
s := len(lines) - 5
if s < 0 {
s = 0
}
return fmt.Errorf("%s", strings.Join(lines[s:], "\n"))
}
return fmt.Errorf("setup failed: %v", runErr)
}
fmt.Printf("[%s] Done.\n", node.Name)
return nil
}
func RemoveNode(nodeName string) error {
serviceName := "wallarm-node@" + nodeName
exec.Command("systemctl", "stop", serviceName).Run()
exec.Command("systemctl", "disable", serviceName).Run()
fmt.Printf("Node %s removed.\n", nodeName)
return nil
}
func Status(nodeName string) (string, error) {
if nodeName != "" {
out, _ := exec.Command("systemctl", "status", "wallarm-node@"+nodeName, "--no-pager").CombinedOutput()
return string(out), nil
}
var sb strings.Builder
sb.WriteString("Wallarm Nodes:\n")
entries, _ := os.ReadDir(NodesDir)
for _, e := range entries {
if e.IsDir() {
sb.WriteString("--- " + e.Name() + " ---\n")
}
}
return sb.String(), nil
}
func installNginx() {
if _, err := os.Stat(NginxDir + "/sbin/nginx"); err == nil {
return
}
fmt.Println("Installing NGINX...")
// Try system nginx first
if path, err := exec.LookPath("nginx"); err == nil {
os.MkdirAll(NginxDir+"/sbin", 0755)
exec.Command("cp", path, NginxDir+"/sbin/nginx").Run()
fmt.Println(" Copied from system.")
return
}
// Try package managers
for _, pm := range [][]string{
{"apt-get", "update", "-qq"},
{"apt-get", "install", "-y", "-qq", "nginx"},
{"yum", "install", "-y", "-q", "nginx"},
{"dnf", "install", "-y", "-q", "nginx"},
} {
if _, err := exec.LookPath(pm[0]); err == nil {
exec.Command(pm[0], pm[1:]...).Run()
if _, err := os.Stat("/usr/sbin/nginx"); err == nil {
os.MkdirAll(NginxDir+"/sbin", 0755)
exec.Command("cp", "/usr/sbin/nginx", NginxDir+"/sbin/nginx").Run()
return
}
}
}
fmt.Println("Install NGINX manually to", NginxDir)
}
func cloudFromHost(host string) string {
if strings.Contains(host, "us1") {
return "US"
}
if strings.Contains(host, "me1") {
return "ME"
}
return "EU"
}
func GenerateSystemdTemplate() error { return nil }
func CreateNodesDir() error { return os.MkdirAll(NodesDir, 0755) }
func listenPort(addr string) string {
if idx := strings.LastIndex(addr, ":"); idx != -1 {
return addr[idx+1:]
}
return "80"
}
func upstreamAddr(node state.Node) string {
if node.UpstreamIP != "" && node.UpstreamPort != 0 {
return fmt.Sprintf("%s:%d", node.UpstreamIP, node.UpstreamPort)
}
return "127.0.0.1:80"
}