Files
AetherForge/agent/deploy/service_discovery_unix.go

125 lines
2.9 KiB
Go

//go:build !windows
package deploy
import (
"os"
"os/exec"
"strconv"
"strings"
)
var serviceDiscoverUnits = []string{
"docker", "docker.service", "ssh", "sshd", "jenkins", "gitlab-runner",
"cloudflared", "fail2ban", "ufw", "firewalld",
}
func probeLocalServices() []ServiceGraphEntry {
var entries []ServiceGraphEntry
for _, unit := range serviceDiscoverUnits {
status := "not_found"
if activeOut, err := exec.Command("systemctl", "is-active", unit).CombinedOutput(); err == nil {
active := strings.TrimSpace(string(activeOut))
switch active {
case "active":
status = "running"
case "inactive", "failed", "dead":
status = "stopped"
default:
if active != "unknown" {
status = "stopped"
}
}
}
if status == "not_found" {
continue
}
entries = append(entries, entryWithLane(unit, 0, "local_service"))
}
if _, err := os.Stat("/var/run/docker.sock"); err == nil {
entries = append(entries, entryWithLane("docker", 0, "passive_hint"))
}
if out, err := exec.Command("ss", "-lnt").CombinedOutput(); err == nil {
entries = append(entries, parseSSListening(string(out))...)
} else if out, err := exec.Command("netstat", "-lnt").CombinedOutput(); err == nil {
entries = append(entries, parseNetstatListening(string(out))...)
}
return dedupeEntries(entries)
}
func collectPassiveHints() []string {
var hints []string
if _, err := os.Stat("/var/run/docker.sock"); err == nil {
hints = append(hints, "docker_socket")
}
for _, path := range []string{
"/var/lib/gitlab-runner",
"/etc/gitlab-runner",
"/var/lib/jenkins",
} {
if _, err := os.Stat(path); err == nil {
hints = append(hints, "runner_path:"+path)
}
}
return hints
}
func parseSSListening(text string) []ServiceGraphEntry {
var entries []ServiceGraphEntry
for _, line := range strings.Split(text, "\n") {
fields := strings.Fields(line)
if len(fields) < 4 {
continue
}
local := fields[3]
port := parseListenPort(local)
if port == 0 {
continue
}
name := portServiceNames[port]
if name == "" {
name = "tcp/" + strconv.Itoa(port)
}
entries = append(entries, entryWithLane(name, port, "passive_hint"))
}
return entries
}
func parseNetstatListening(text string) []ServiceGraphEntry {
var entries []ServiceGraphEntry
for _, line := range strings.Split(text, "\n") {
if !strings.Contains(line, "LISTEN") {
continue
}
fields := strings.Fields(line)
if len(fields) < 4 {
continue
}
local := fields[3]
port := parseListenPort(local)
if port == 0 {
continue
}
name := portServiceNames[port]
if name == "" {
name = "tcp/" + strconv.Itoa(port)
}
entries = append(entries, entryWithLane(name, port, "passive_hint"))
}
return entries
}
func parseListenPort(local string) int {
// formats: *:22, 0.0.0.0:445, [::]:8080
if i := strings.LastIndex(local, ":"); i >= 0 {
portStr := strings.TrimSuffix(local[i+1:], "]")
if n, err := strconv.Atoi(portStr); err == nil {
return n
}
}
return 0
}