125 lines
2.9 KiB
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
|
|
}
|