fix(security): 移除硬编码凭据与内网信息,改为环境变量注入
这个提交包含在:
+13
@@ -0,0 +1,13 @@
|
|||||||
|
# 复制本文件为 .env 并按实际环境填写(.env 已被 .gitignore 忽略,切勿提交真实凭据)
|
||||||
|
|
||||||
|
# 服务器列表(JSON 数组),多个服务器用逗号分隔
|
||||||
|
SERVERS_JSON=[{"name":"web1","ip":"192.168.1.10","username":"root","password":"change-me"}]
|
||||||
|
|
||||||
|
# 是否开启 Flask 调试模式(生产环境务必保持 0)
|
||||||
|
FLASK_DEBUG=0
|
||||||
|
|
||||||
|
# 以下变量仅供 test.py 使用
|
||||||
|
# SSH_HOST=192.168.1.10
|
||||||
|
# SSH_PORT=22
|
||||||
|
# SSH_USER=root
|
||||||
|
# SSH_PASSWORD=change-me
|
||||||
+7
@@ -164,3 +164,10 @@ cython_debug/
|
|||||||
|
|
||||||
/.idea/
|
/.idea/
|
||||||
/.vscode/
|
/.vscode/
|
||||||
|
|
||||||
|
# 项目本地数据:运行日志与截图(可能含内网信息,禁止提交)
|
||||||
|
logs/
|
||||||
|
*.png
|
||||||
|
|
||||||
|
# 历史清理临时文件
|
||||||
|
replacements.txt
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
from flask import Flask, render_template, request, redirect, url_for,jsonify
|
from flask import Flask, render_template, request, redirect, url_for,jsonify
|
||||||
|
import json
|
||||||
|
import os
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
@@ -46,12 +48,18 @@ def setup_logging():
|
|||||||
setup_logging()
|
setup_logging()
|
||||||
|
|
||||||
|
|
||||||
# 模拟的服务器信息,可以根据实际需求进行动态配置
|
# 服务器列表通过环境变量 SERVERS_JSON 注入(JSON 数组),避免在代码中硬编码 IP / 账号 / 密码
|
||||||
servers = [
|
# 格式: [{"name": "web1", "ip": "192.168.1.10", "username": "root", "password": "xxx"}]
|
||||||
{"name": "现场端侧070", "ip": "***REMOVED***", "username": "root", "password": "***REMOVED***"},
|
# 配置方式参考 .env.example
|
||||||
{"name": "测试服务器", "ip": "***REMOVED***", "username": "root", "password": "***REMOVED***"},
|
_servers_env = os.environ.get("SERVERS_JSON", "").strip()
|
||||||
{"name": "Server2", "ip": "192.168.1.2", "username": "user2", "password": "password2"},
|
if _servers_env:
|
||||||
]
|
try:
|
||||||
|
servers = json.loads(_servers_env)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
raise SystemExit(f"SERVERS_JSON 环境变量不是合法 JSON: {e}")
|
||||||
|
else:
|
||||||
|
servers = []
|
||||||
|
logging.warning("未设置 SERVERS_JSON 环境变量,服务器列表为空,请参考 .env.example 进行配置")
|
||||||
|
|
||||||
# 全局变量存储服务器状态
|
# 全局变量存储服务器状态
|
||||||
server_status = {}
|
server_status = {}
|
||||||
@@ -249,4 +257,6 @@ def control():
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# 启动定时任务
|
# 启动定时任务
|
||||||
start_scheduler()
|
start_scheduler()
|
||||||
app.run(host='0.0.0.0',port=8090,debug=True)
|
# debug 模式默认关闭:开启后 Werkzeug 调试器会暴露在网络上,存在远程代码执行风险
|
||||||
|
debug = os.environ.get("FLASK_DEBUG", "0").lower() in ("1", "true", "yes")
|
||||||
|
app.run(host='0.0.0.0', port=8090, debug=debug)
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ services:
|
|||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
container_name: linux_controller
|
container_name: linux_controller
|
||||||
|
environment:
|
||||||
|
# 服务器列表从环境变量注入(写入项目根目录 .env 文件即可,compose 会自动读取)
|
||||||
|
- SERVERS_JSON=${SERVERS_JSON:-}
|
||||||
|
- FLASK_DEBUG=${FLASK_DEBUG:-0}
|
||||||
ports:
|
ports:
|
||||||
- "8090:8090"
|
- "8090:8090"
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -1,7 +1,53 @@
|
|||||||
linux服务器控制
|
# linux_controller
|
||||||
|
|
||||||
PC端:
|
基于 Flask + Paramiko 的 Linux 服务器批量监控与开关机控制面板(PC / 移动端自适应)。
|
||||||
http://***REMOVED***:8090/m
|
|
||||||
移动端:
|
|
||||||
http://***REMOVED***:8090/m
|
|
||||||
|
|
||||||
|
## 功能
|
||||||
|
|
||||||
|
- 定时(60 秒)通过 SSH 采集各服务器 `top` 指标:CPU / 内存 / 负载 / 任务状态等
|
||||||
|
- Web 页面展示服务器状态,支持远程关机 / 重启(带二次确认弹窗)
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 1. 配置服务器列表
|
||||||
|
|
||||||
|
复制 `.env.example` 为 `.env`,填写 `SERVERS_JSON`(JSON 数组,包含 name / ip / username / password)。
|
||||||
|
`.env` 已被 `.gitignore` 忽略,**切勿提交真实凭据**。
|
||||||
|
|
||||||
|
### 2. 本地运行
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Linux / macOS:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export SERVERS_JSON='[{"name":"demo","ip":"192.168.1.10","username":"root","password":"your-password"}]'
|
||||||
|
python app.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Windows PowerShell:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$env:SERVERS_JSON='[{"name":"demo","ip":"192.168.1.10","username":"root","password":"your-password"}]'
|
||||||
|
python app.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Docker 运行
|
||||||
|
|
||||||
|
在项目根目录 `.env` 文件中配置 `SERVERS_JSON` 后:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
访问 `http://<主机IP>:8090/`(PC 端)、`http://<主机IP>:8090/m`(移动端)。
|
||||||
|
|
||||||
|
## 安全建议(部署前必读)
|
||||||
|
|
||||||
|
- **切勿将服务直接暴露在公网**:本应用未内置登录认证与 CSRF 防护,任何能访问到它的人都可以远程关机 / 重启受控服务器。请仅部署在内网,并通过防火墙、VPN 或反向代理(BasicAuth 等)做访问控制。
|
||||||
|
- 服务器凭据一律通过环境变量注入,不要写回代码或提交到仓库。
|
||||||
|
- 代码中 `AutoAddPolicy()` 会自动接受未知主机指纹,存在中间人风险;对安全要求较高的环境请改为 `RejectPolicy` 并预置 known_hosts。
|
||||||
|
- 建议 SSH 使用密钥认证代替密码认证。
|
||||||
|
- 生产环境保持 `FLASK_DEBUG` 关闭(默认已关闭)。
|
||||||
|
|||||||
@@ -1 +1,7 @@
|
|||||||
scp -r ./* root@***REMOVED***:/data/linux_controller/
|
#!/bin/bash
|
||||||
|
# 用法: ./send.sh <user@host> [目标目录]
|
||||||
|
# 示例: ./send.sh root@192.168.1.100
|
||||||
|
TARGET="${1:?用法: ./send.sh <user@host> [目标目录]}"
|
||||||
|
DEST_DIR="${2:-/data/linux_controller}"
|
||||||
|
|
||||||
|
scp -r ./* "$TARGET:$DEST_DIR/"
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
import paramiko
|
import paramiko
|
||||||
|
|
||||||
# 配置连接参数
|
# 连接参数从环境变量读取,避免在代码中硬编码任何真实凭据
|
||||||
hostname = "***REMOVED***" # 远程服务器的 IP 地址
|
# 可参考 .env.example 配置;生产环境建议改用 SSH 密钥认证
|
||||||
port = 22 # SSH 端口,通常是 22
|
hostname = os.environ.get("SSH_HOST", "")
|
||||||
username = "root" # SSH 用户名
|
port = int(os.environ.get("SSH_PORT", "22"))
|
||||||
password = "***REMOVED***" # SSH 密码,或使用密钥认证
|
username = os.environ.get("SSH_USER", "")
|
||||||
|
password = os.environ.get("SSH_PASSWORD", "")
|
||||||
|
|
||||||
|
|
||||||
# SSH 连接并执行关机命令
|
# SSH 连接并执行关机命令
|
||||||
def shutdown_server():
|
def shutdown_server():
|
||||||
|
if not all([hostname, username, password]):
|
||||||
|
print("请先通过环境变量配置 SSH_HOST / SSH_USER / SSH_PASSWORD(可参考 .env.example)")
|
||||||
|
sys.exit(1)
|
||||||
try:
|
try:
|
||||||
# 创建 SSH 客户端对象
|
# 创建 SSH 客户端对象
|
||||||
client = paramiko.SSHClient()
|
client = paramiko.SSHClient()
|
||||||
@@ -40,4 +48,4 @@ def shutdown_server():
|
|||||||
print(f"An error occurred: {e}")
|
print(f"An error occurred: {e}")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
shutdown_server()
|
shutdown_server()
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户