156 lines
6.3 KiB
Python
156 lines
6.3 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
挖矿病毒守护 - 安装脚本
|
||
将 miner_guard.sh 上传到服务器,并配置每 30 分钟执行一次。
|
||
使用: python miner_guard_install.py [--yes]
|
||
"""
|
||
import os
|
||
import sys
|
||
import io
|
||
|
||
try:
|
||
import paramiko
|
||
except ImportError:
|
||
print("错误: pip install paramiko")
|
||
sys.exit(1)
|
||
|
||
DEFAULT_SSH_PORT = int(os.environ.get("DEPLOY_SSH_PORT", "22022"))
|
||
|
||
|
||
def get_cfg():
|
||
host = os.environ.get("DEPLOY_HOST")
|
||
if not host:
|
||
try:
|
||
import importlib.util
|
||
spec = importlib.util.spec_from_file_location(
|
||
"master", os.path.join(os.path.dirname(__file__), "master.py")
|
||
)
|
||
mod = importlib.util.module_from_spec(spec)
|
||
spec.loader.exec_module(mod)
|
||
c = mod.get_cfg()
|
||
return {
|
||
"host": c["host"], "user": c.get("user", "root"),
|
||
"password": c.get("password", ""), "ssh_key": c.get("ssh_key", ""),
|
||
"port": int(os.environ.get("DEPLOY_SSH_PORT", str(DEFAULT_SSH_PORT))),
|
||
}
|
||
except Exception:
|
||
pass
|
||
return {
|
||
"host": host or "", "user": os.environ.get("DEPLOY_USER", "root"),
|
||
"password": os.environ.get("DEPLOY_PASSWORD", ""),
|
||
"ssh_key": os.environ.get("DEPLOY_SSH_KEY", ""),
|
||
"port": int(os.environ.get("DEPLOY_SSH_PORT", str(DEFAULT_SSH_PORT))),
|
||
}
|
||
|
||
|
||
def main():
|
||
import argparse
|
||
p = argparse.ArgumentParser(description="安装挖矿守护到服务器")
|
||
p.add_argument("--yes", "-y", action="store_true", help="跳过确认")
|
||
args = p.parse_args()
|
||
|
||
cfg = get_cfg()
|
||
if not cfg.get("host") or (not cfg.get("password") and not cfg.get("ssh_key")):
|
||
print("[错误] 需配置 DEPLOY_HOST 和 DEPLOY_PASSWORD")
|
||
sys.exit(1)
|
||
|
||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||
local_sh = os.path.join(script_dir, "miner_guard.sh")
|
||
if not os.path.isfile(local_sh):
|
||
print("[错误] 未找到 miner_guard.sh")
|
||
sys.exit(1)
|
||
|
||
remote_path = "/root/miner_guard.sh"
|
||
cron_line = "*/30 * * * * /bin/bash %s >> /var/log/miner_guard.log 2>&1" % remote_path
|
||
|
||
print("=" * 60)
|
||
print(" 挖矿病毒守护 - 安装")
|
||
print("=" * 60)
|
||
print(" 服务器: %s" % cfg["host"])
|
||
print(" 脚本路径: %s" % remote_path)
|
||
print(" Cron: 每 30 分钟执行")
|
||
print("=" * 60)
|
||
if not args.yes:
|
||
print("\n确认安装? 输入 yes 继续: ", end="")
|
||
if input().strip().lower() != "yes":
|
||
print("已取消")
|
||
return
|
||
|
||
client = paramiko.SSHClient()
|
||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||
try:
|
||
if cfg.get("ssh_key") and os.path.isfile(cfg["ssh_key"]):
|
||
client.connect(cfg["host"], port=cfg["port"], username=cfg["user"],
|
||
key_filename=cfg["ssh_key"], timeout=15)
|
||
else:
|
||
client.connect(cfg["host"], port=cfg["port"], username=cfg["user"],
|
||
password=cfg["password"], timeout=15)
|
||
except Exception as e:
|
||
print("[连接失败]", str(e))
|
||
sys.exit(1)
|
||
|
||
sftp = client.open_sftp()
|
||
with open(local_sh, "rb") as f:
|
||
sftp.putfo(f, remote_path)
|
||
sftp.chmod(remote_path, 0o755)
|
||
sftp.close()
|
||
|
||
# 通过 SSH 执行写入 /etc/cron.d/(root 有权限)
|
||
cron_line = "*/30 * * * * root /bin/bash %s >> /var/log/miner_guard.log 2>&1" % remote_path
|
||
cron_body = "SHELL=/bin/bash\nPATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin\n%s\n" % cron_line
|
||
cron_d_file = "/etc/cron.d/miner_guard"
|
||
# 用 base64 避免 shell 转义问题
|
||
import base64
|
||
b64 = base64.b64encode(cron_body.encode()).decode()
|
||
stdin, stdout, stderr = client.exec_command(
|
||
"echo %s | base64 -d | tee %s > /dev/null && chmod 644 %s" % (b64, cron_d_file, cron_d_file),
|
||
timeout=10
|
||
)
|
||
err = stderr.read().decode("utf-8", errors="replace").strip()
|
||
if err:
|
||
print("\n[备选] /etc/cron.d 失败")
|
||
# 尝试 crontab
|
||
crontab_line = "*/30 * * * * /bin/bash %s >> /var/log/miner_guard.log 2>&1\n" % remote_path
|
||
tmp_cron = "/tmp/miner_guard_cron_%s" % os.getpid()
|
||
sftp = client.open_sftp()
|
||
try:
|
||
with sftp.file(tmp_cron, "w") as f:
|
||
f.write(crontab_line)
|
||
finally:
|
||
sftp.close()
|
||
sin, sout, serr = client.exec_command("crontab %s 2>&1; rm -f %s; crontab -l 2>/dev/null" % (tmp_cron, tmp_cron), timeout=10)
|
||
out = sout.read().decode("utf-8", errors="replace")
|
||
err_msg = serr.read().decode("utf-8", errors="replace").strip()
|
||
if "miner_guard" in out:
|
||
print("[成功] crontab 已添加")
|
||
else:
|
||
# 尝试 systemd timer
|
||
print(" crontab 不可用,尝试 systemd timer...")
|
||
svc = "[Unit]\nDescription=Miner Guard\nAfter=network.target\n[Service]\nType=oneshot\nExecStart=/bin/bash %s\n[Install]\nWantedBy=multi-user.target\n" % remote_path
|
||
tmr = "[Unit]\nDescription=Miner Guard 30min\n[Timer]\nOnBootSec=1min\nOnUnitActiveSec=30min\nPersistent=true\n[Install]\nWantedBy=timers.target\n"
|
||
try:
|
||
sftp = client.open_sftp()
|
||
sftp.putfo(io.BytesIO(svc.encode()), "/etc/systemd/system/miner-guard.service")
|
||
sftp.putfo(io.BytesIO((tmr.encode())), "/etc/systemd/system/miner-guard.timer")
|
||
sftp.close()
|
||
sin, sout, serr = client.exec_command("systemctl daemon-reload && systemctl enable miner-guard.timer && systemctl start miner-guard.timer 2>&1", timeout=15)
|
||
r = sout.read().decode("utf-8", errors="replace") + serr.read().decode("utf-8", errors="replace")
|
||
if "Failed" not in r and "denied" not in r:
|
||
print("[成功] systemd timer 已启用,每 30 分钟执行")
|
||
else:
|
||
raise IOError(r)
|
||
except Exception as ex:
|
||
print(" systemd 失败: %s" % ex)
|
||
print(" 请 SSH 登录后手动执行: (crontab -l 2>/dev/null; echo '%s') | crontab -" % cron_line.strip())
|
||
else:
|
||
print("\n[成功] 已写入 %s,每 30 分钟执行" % cron_d_file)
|
||
|
||
client.close()
|
||
print("\n日志: /var/log/miner_guard.log")
|
||
print("=" * 60)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|