121 lines
3.5 KiB
Bash
121 lines
3.5 KiB
Bash
#!/bin/bash
|
|
set -e
|
|
|
|
echo "=== Environment ==="
|
|
echo "Hostname: $(hostname)"
|
|
echo "IPs: $(hostname -I)"
|
|
echo "Python: $(python3 --version)"
|
|
|
|
# 创建目录
|
|
sudo mkdir -p /opt/xiaoxia-cmd-agent
|
|
|
|
# 写入server.py
|
|
sudo tee /opt/xiaoxia-cmd-agent/server.py > /dev/null << 'PYEND'
|
|
import http.server
|
|
import subprocess
|
|
import json
|
|
import os
|
|
import secrets
|
|
|
|
TOKEN_FILE = "/etc/xiaoxia-cmd-agent.token"
|
|
LOG_FILE = "/var/log/xiaoxia-cmd-agent.log"
|
|
LISTEN_HOST = "0.0.0.0"
|
|
LISTEN_PORT = 18888
|
|
|
|
def ensure_token():
|
|
if not os.path.exists(TOKEN_FILE):
|
|
os.makedirs(os.path.dirname(TOKEN_FILE), exist_ok=True)
|
|
token = secrets.token_hex(16)
|
|
with open(TOKEN_FILE, "w") as f:
|
|
f.write(token)
|
|
os.chmod(TOKEN_FILE, 0o600)
|
|
with open(TOKEN_FILE) as f:
|
|
return f.read().strip()
|
|
|
|
class CmdHandler(http.server.BaseHTTPRequestHandler):
|
|
def log_message(self, fmt, *args):
|
|
try:
|
|
with open(LOG_FILE, "a") as f:
|
|
f.write("%s - %s\n" % (self.address_string(), fmt % args))
|
|
except Exception:
|
|
pass
|
|
|
|
def do_POST(self):
|
|
if self.path == "/exec":
|
|
auth = self.headers.get("Authorization", "")
|
|
token = ensure_token()
|
|
if auth != "xsa-" + token:
|
|
self.send_response(401)
|
|
self.end_headers()
|
|
self.wfile.write(b"Unauthorized")
|
|
return
|
|
length = int(self.headers.get("Content-Length", 0))
|
|
body = json.loads(self.rfile.read(length))
|
|
cmd = body.get("command", "")
|
|
timeout = body.get("timeout", 30)
|
|
try:
|
|
result = subprocess.run(
|
|
cmd, shell=True, capture_output=True, text=True,
|
|
timeout=timeout
|
|
)
|
|
resp = {
|
|
"exit_code": result.returncode,
|
|
"stdout": result.stdout,
|
|
"stderr": result.stderr
|
|
}
|
|
except subprocess.TimeoutExpired:
|
|
resp = {"exit_code": -1, "stdout": "", "stderr": "timeout"}
|
|
self.send_response(200)
|
|
self.send_header("Content-Type", "application/json")
|
|
self.end_headers()
|
|
self.wfile.write(json.dumps(resp).encode())
|
|
elif self.path == "/status":
|
|
self.send_response(200)
|
|
self.send_header("Content-Type", "application/json")
|
|
self.end_headers()
|
|
self.wfile.write(json.dumps({"status": "ok", "version": "1.0"}).encode())
|
|
else:
|
|
self.send_response(404)
|
|
self.end_headers()
|
|
|
|
if __name__ == "__main__":
|
|
ensure_token()
|
|
server = http.server.HTTPServer((LISTEN_HOST, LISTEN_PORT), CmdHandler)
|
|
print("CMD Agent listening on %s:%d" % (LISTEN_HOST, LISTEN_PORT))
|
|
server.serve_forever()
|
|
PYEND
|
|
|
|
# 写入service文件
|
|
sudo tee /etc/systemd/system/xiaoxia-cmd-agent.service > /dev/null << 'SVCEND'
|
|
[Unit]
|
|
Description=Xiaoxia CMD Agent
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart=/usr/bin/python3 /opt/xiaoxia-cmd-agent/server.py
|
|
Restart=always
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
SVCEND
|
|
|
|
# 启动服务
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable xiaoxia-cmd-agent
|
|
sudo systemctl restart xiaoxia-cmd-agent
|
|
|
|
sleep 2
|
|
|
|
echo ""
|
|
echo "=== Service Status ==="
|
|
sudo systemctl is-active xiaoxia-cmd-agent
|
|
echo ""
|
|
echo "=== TOKEN ==="
|
|
sudo cat /etc/xiaoxia-cmd-agent.token
|
|
echo ""
|
|
echo "=== Test /status ==="
|
|
curl -s http://127.0.0.1:18888/status
|
|
echo ""
|