深色模式
ChatOps 入门:在群里执行命令
摘要:ChatOps 的价值不是方便,而是透明——命令和结果都在群里可见,天然形成协作与审计。本文用 Python 搭一个最小机器人:接收 webhook → 白名单校验 → 执行命令 → 结果回传。
适用环境
- 一台能被 IM 平台回调的服务器(需公网或内网可达的 HTTP 服务)
- Python 3.9+,Flask 或 FastAPI
- 一个支持自定义机器人 / webhook 的群聊工具
操作步骤
1. 先定规矩:哪些命令能上群
允许(只读 + 低风险):status、看日志尾部、看资源使用、列出实例。禁止(一律不允许):任意 shell 执行、删除类操作、直接改生产的写操作。有条件(需二次确认):重启服务、扩容、发布。
2. 最小可用服务
python
#!/usr/bin/env python3
"""只执行白名单命令,结果回传群聊"""
import subprocess
from flask import Flask, request, jsonify
app = Flask(__name__)
ALLOWED = { # 指令 -> 实际命令,不含任何用户输入拼接
"/status": ["uptime"],
"/disk": ["df", "-h", "/"],
"/pod": ["kubectl", "get", "pod", "-A", "--no-headers"],
}
OPERATORS = {"alice", "bob"}
def run(cmd):
p = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
return (p.stdout + p.stderr).strip()[:2000] or "(no output)"
@app.post("/hook")
def hook():
data = request.get_json(force=True, silent=True) or {}
user, text = data.get("user", ""), (data.get("text") or "").strip()
cmd = ALLOWED.get(text)
if not cmd:
return jsonify({"text": f"不支持的指令: {text}\n可用: {', '.join(ALLOWED)}"})
if user not in OPERATORS:
return jsonify({"text": f"{user} 无权限执行 {text}"})
print(f"[audit] user={user} cmd={text}", flush=True)
return jsonify({"text": f"```\n{run(cmd)}\n```"})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
3. 启动与二次确认
bash
pip install flask gunicorn && gunicorn -w 2 -b 0.0.0.0:5000 bot:app1
python
PENDING = {} # user -> 待确认命令
# text == "/confirm" 且 user in PENDING 时才执行,否则先回复"请 60 秒内发送 /confirm 确认"1
2
2
4. 审计留痕
python
import logging
logging.basicConfig(filename="/var/log/chatops.log", format="%(asctime)s %(message)s")
logging.warning("user=%s cmd=%s", user, text)1
2
3
2
3
群聊本身也是审计:谁下的命令、结果是什么,全员可见。
危险
绝不允许把用户输入拼接进 shell(如 os.system("bash -c " + text))。这会立刻变成任意命令执行漏洞。必须用白名单映射,用户输入只能用于选择,不能参与拼接。
验证
bash
curl -s -X POST http://127.0.0.1:5000/hook -H 'Content-Type: application/json' -d '{"user":"alice","text":"/disk"}'
curl -s -X POST http://127.0.0.1:5000/hook -H 'Content-Type: application/json' -d '{"user":"alice","text":"/disk; rm -rf /"}'1
2
2
- [ ] 白名单内指令能返回结果
- [ ] 非 OPERATORS 用户被拒绝
- [ ] 带
;拼接的恶意输入被当作未知指令拒绝 - [ ]
/var/log/chatops.log中有完整审计记录
常见坑
没有校验请求来源
只开裸 HTTP 接口,任何人 curl 都能触发。必须校验平台签名/Token,并限制来源 IP。
超时与阻塞
命令跑 5 分钟会把群聊卡住。设 timeout=30,长任务改为异步执行 + 结果回调。
输出刷屏
kubectl get pod -A 几百行会淹没群聊。必须做输出截断与关键信息提取。