主题
Jumpserver 安全加固检查与执行记录
检查对象:
jumpserver虚拟机 (192.168.122.250/szc122250)
执行时间:2026-07-18
关联文档:2026-07-18-jumpserver-36000.md
一、最终执行状态
| 加固项 | 状态 | 备注 |
|---|---|---|
| 1. 禁用 SSH 反向隧道 | ✅ 完成 | ssh-reverse-tunnel.service 已停止并禁用 |
2. 修复 jms_chen 容器 | ✅ 完成 | 添加 i18n.endpoint=http://jms_web 到 config.txt |
| 3. SSH 改为密钥登录 | ✅ 完成 | root 密码登录已禁用,已部署宿主机公钥 |
| 4. 启用防火墙并配置规则 | ⚠️ 部分完成 | firewalld 启用后阻断 Docker 容器间通信,已回退;改用 iptables 手工规则并持久化 |
| 5. 启用 SELinux permissive | ✅ 完成 | 配置文件已改,通过虚拟机重启已生效 |
| 6. Jumpserver MFA 和登录限制 | ✅ 完成 | 已通过数据库设置 SECURITY_MFA_AUTH=1 等 |
| 7. HTTPS 域名访问 | ✅ 完成 | 已配置 https://ai-ear.cn:36000 使用有效 SSL 证书 |
| 8. 7500/9000-9009 HTTPS 域名访问 | ✅ 完成 | nginx 监听 7500/9000-9009 SSL,反代到 frps 17500/19000-19009 |
| 9. 9000/9001 Basic Auth | ✅ 完成 | nginx 层已启用 \1xxx\2 |
二、HTTPS 域名访问配置
2.1 需求
用户希望 Jumpserver 可通过域名 ai-ear.cn 访问,且 ai-ear.cn 已解析到公网服务器 8.153.84.140。
最终选择:https://ai-ear.cn:36000
2.2 实施步骤
从公网服务器
8.153.84.140复制 SSL 证书到jumpserver虚拟机:- 公网服务器源文件:
/etc/pki/nginx/ai-ear.cn.crt/etc/pki/nginx/private/ai-ear.cn.key
- 目标文件(覆盖 Jumpserver 默认测试证书):
/opt/jumpserver/config/nginx/cert/server.crt/opt/jumpserver/config/nginx/cert/server.key
- 公网服务器源文件:
修改
/opt/jumpserver/config/nginx/lb_http_server.conf:- 取消注释
server_name并设置为ai-ear.cn
- 取消注释
修改
/opt/jumpserver/config/config.txt:
ini
HTTP_PORT=80
HTTPS_PORT=36000
SERVER_NAME=ai-ear.cn
SSL_CERTIFICATE=/opt/jumpserver/config/nginx/cert/server.crt
SSL_CERTIFICATE_KEY=/opt/jumpserver/config/nginx/cert/server.key
DOMAINS=ai-ear.cn:36000- 重启 Jumpserver:
bash
cd /opt/jumpserver-installer-v4.9.0
./jmsctl.sh restart2.3 验证结果
bash
# 公网 HTTPS 访问
curl -k https://ai-ear.cn:36000
# 状态码: 200
# 证书信息
openssl s_client -connect ai-ear.cn:36000 -servername ai-ear.cn
# subject=CN = ai-ear.cn
# issuer=DigiCert DV TLS CA - G2
# 有效期: 2026-07-16 至 2026-10-15注意:
http://ai-ear.cn:36000会返回 400,因为 36000 端口现在只提供 HTTPS。所有访问请使用https://。
2.4 扩展:7500/9000/9001 也改为 HTTPS 域名访问
用户需求:将 frp dashboard (7500) 和 frp 代理端口 9000、9001 也改为通过 https://ai-ear.cn:PORT 访问。
实施步骤
调整 frps 端口,释放
7500、9000-9009给 nginx:- 修改
/etc/frp/frps.toml:tomlwebServer.port = 17500 - 重启 frps
- 修改
调整 frpc 端口映射:
- 修改
/etc/frp/frpc.toml,将9000-9009的 remotePort 全部改为19000-19009 - 重启 frpc
- 修改
在
8.153.84.140上配置 nginx 监听 7500/9000-9009 SSL:每个端口一个
server块,均使用ai-ear.cn证书,反代到 frps 新端口。以9000为例:nginxserver { listen 9000 ssl; server_name ai-ear.cn; ssl_certificate "/etc/pki/nginx/ai-ear.cn.crt"; ssl_certificate_key "/etc/pki/nginx/private/ai-ear.cn.key"; location / { proxy_pass http://127.0.0.1:19000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }对
7500(反代到17500)和9001-9009(反代到19001-19009)做同样配置,然后重启 nginx。
验证结果
bash
curl -k https://ai-ear.cn:7500 # 401(frp dashboard 需要认证)
curl -k https://ai-ear.cn:9000 # 401(nginx Basic Auth)
curl -k https://ai-ear.cn:9001 # 401(nginx Basic Auth)
curl -k https://ai-ear.cn:9002 # 502(当前无后端服务)
...注意:
9001-9009在宿主机yu上当前没有服务监听,因此会返回502。当这些端口启动服务后,HTTPS 访问会自动可用。
2.5 9000/9001 增加 nginx Basic Auth
由于 9000 对应的服务是 python serve.py /home/xxx/docs/docs --host 0.0.0.0 --port 9000,本身无内置认证;9001 likewise,因此需要在 nginx 入口层增加 HTTP Basic Authentication。
实施步骤
创建密码文件
xxx:bashhtpasswd -cb /etc/nginx/.htpasswd admin 'xxx' chmod 640 /etc/nginx/.htpasswd chown root:nginx /etc/nginx/.htpasswd修改
/etc/nginx/nginx.conf,在9000和9001的 server 块中添加:nginxauth_basic "Restricted"; auth_basic_user_file /etc/nginx/.htpasswd;测试并重载 nginx:
bashnginx -t systemctl reload nginx
验证结果
bash
# 不带认证 → 401
curl -sk -o /dev/null -w "%{http_code}\n" https://ai-ear.cn:9000/
# 401
# 错误密码 → 401
curl -sk -o /dev/null -w "%{http_code}\n" -u admin:xxx https://ai-ear.cn:9000/
# 401
# 正确密码 → 200
curl -sk -o /dev/null -w "%{http_code}\n" -u admin:xxx https://ai-ear.cn:9000/
# 200
# 9001 同样生效
curl -sk -o /dev/null -w "%{http_code}\n" https://ai-ear.cn:9001/
# 401
curl -sk -o /dev/null -w "%{http_code}\n" -u admin:xxx https://ai-ear.cn:9001/
# 200账号:xxx
三、执行详情(加固部分)
3.1 准备:部署 SSH 公钥
由于后续要禁用 root 密码登录,先在宿主机生成/确认密钥,并将公钥写入 jumpserver 虚拟机:
bash
# 在宿主机 yu 上
mkdir -p /home/xxx/.ssh && chmod 700 /home/xxx/.ssh
# 本机已有 /home/xxx/.ssh/id_rsa.pub
# 部署公钥到 jumpserver root
sshpass -p 'ai-ear.cn' ssh -o StrictHostKeyChecking=no root@192.168.122.250 \
'mkdir -p /root/.ssh && chmod 700 /root/.ssh'
sshpass -p 'ai-ear.cn' scp -o StrictHostKeyChecking=no \
/home/xxx/.ssh/id_rsa.pub root@192.168.122.250:/tmp/id_rsa.pub.local
sshpass -p 'ai-ear.cn' ssh -o StrictHostKeyChecking=no root@192.168.122.250 \
'cat /tmp/id_rsa.pub.local >> /root/.ssh/authorized_keys && chmod 600 /root/.ssh/authorized_keys'
# 验证无密码登录
ssh -o StrictHostKeyChecking=no -o PasswordAuthentication=no root@192.168.122.250 'echo ok'3.2 禁用 SSH 反向隧道
bash
systemctl disable --now ssh-reverse-tunnel.service- 原隧道使用 root 身份连接公网服务器,且
-o StrictHostKeyChecking=no,存在中间人风险。 - 禁用后,公网
36000仍通过宿主机frpc映射保持可用。
3.3 修复 jms_chen 容器
故障现象:容器反复重启,日志报错:
text
Could not resolve placeholder 'i18n.endpoint' in value "${i18n.endpoint}"修复:
bash
cp /opt/jumpserver/config/config.txt \
/opt/jumpserver/config/config.txt.bak.202607181131
# 在 /opt/jumpserver/config/config.txt 末尾添加
# i18n endpoint for jms_chen
i18n.endpoint=http://jms_web
cd /opt/jumpserver-installer-v4.9.0
./jmsctl.sh restart chen修复后 jms_chen 状态恢复 healthy。
3.4 SSH 改为密钥登录
bash
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak.202607181135
# 修改 /etc/ssh/sshd_config
PermitRootLogin prohibit-password
PasswordAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
MaxAuthTries 3
ClientAliveInterval 300
ClientAliveCountMax 2
Protocol 2
sshd -t
systemctl restart sshd验证:
bash
# 密钥登录成功
ssh -o PasswordAuthentication=no root@192.168.122.250 'echo ok'
# 密码登录被拒绝
sshpass -p 'ai-ear.cn' ssh -o PreferredAuthentications=password root@192.168.122.250 'echo ok'
# Permission denied (publickey,gssapi-keyex,gssapi-with-mic)3.5 防火墙配置(firewalld 回退,改用手工 iptables)
尝试启用 firewalld(已回退)
bash
systemctl enable --now firewalld
firewall-cmd --permanent --zone=internal --add-source=192.168.122.0/24
firewall-cmd --permanent --zone=internal --add-service=ssh
firewall-cmd --permanent --zone=internal --add-port=36000/tcp
firewall-cmd --permanent --zone=internal --add-port=2222/tcp
firewall-cmd --reload问题:firewalld(nftables backend)与 Docker 的 iptables-nft 规则冲突。启用后,firewalld 的 filter_FORWARD 链将 Docker 网桥流量导向 public zone 并默认 reject,导致:
jms_core无法连接redis:6379(No route to host)- 虚拟机入站 SSH 中断
- 最终通过
virsh console进入虚拟机,停止并禁用firewalld
当前使用的 iptables 规则
bash
# 安装持久化服务
dnf install -y iptables-services
# INPUT 链
iptables -P INPUT DROP
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables -F INPUT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 5/second -j ACCEPT
iptables -A INPUT -p tcp -s 192.168.122.0/24 --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
# Docker 暴露端口访问控制(DOCKER-USER 链)
iptables -F DOCKER-USER
iptables -A DOCKER-USER -i enp1s0 -p tcp -s 8.153.84.140 --dport 36000 -j ACCEPT
iptables -A DOCKER-USER -i enp1s0 -p tcp -s 192.168.122.0/24 --dport 36000 -j ACCEPT
iptables -A DOCKER-USER -i enp1s0 -p tcp --dport 36000 -j DROP
iptables -A DOCKER-USER -i enp1s0 -p tcp -s 8.153.84.140 --dport 2222 -j ACCEPT
iptables -A DOCKER-USER -i enp1s0 -p tcp -s 192.168.122.0/24 --dport 2222 -j ACCEPT
iptables -A DOCKER-USER -i enp1s0 -p tcp --dport 2222 -j DROP
iptables -A DOCKER-USER -j RETURN
# 保存并启用开机恢复
service iptables save
systemctl enable iptables注意:当前规则通过
iptables-services持久化到/etc/sysconfig/iptables。
3.6 SELinux
bash
cp /etc/selinux/config /etc/selinux/config.bak.202607181200
sed -i "s/^SELINUX=.*/SELINUX=permissive/" /etc/selinux/config由于之前已从 disabled 改为 permissive,需要通过虚拟机重启生效。在执行 virsh shutdown/start 后,当前运行模式已为 Permissive。
3.7 Jumpserver 安全设置
通过 manage.py shell 修改数据库设置:
python
from settings.models import Setting
import json
settings_to_update = {
"SECURITY_MFA_AUTH": 1,
"SECURITY_LOGIN_LIMIT_COUNT": 5,
"SECURITY_LOGIN_IP_LIMIT_COUNT": 5,
"SECURITY_PASSWORD_MIN_LENGTH": 12,
"SECURITY_PASSWORD_EXPIRATION_TIME": 90,
}
for name, value in settings_to_update.items():
Setting.objects.update_or_create(
name=name,
defaults={
"value": json.dumps(value),
"category": "security",
"encrypted": False,
"enabled": True,
}
)重启 jms_core 后验证:
text
SECURITY_MFA_AUTH: 1
SECURITY_LOGIN_LIMIT_COUNT: 5
SECURITY_LOGIN_IP_LIMIT_COUNT: 5
SECURITY_PASSWORD_MIN_LENGTH: 12
SECURITY_PASSWORD_EXPIRATION_TIME: 90同时 config.txt 中已设置:
text
SESSION_EXPIRE_AT_BROWSER_CLOSE=true3.8 系统密码策略
bash
cp /etc/login.defs /etc/login.defs.bak.202607181200
# /etc/login.defs
PASS_MAX_DAYS 90
PASS_MIN_DAYS 1
PASS_MIN_LEN 12
PASS_WARN_AGE 143.9 系统安全更新
尝试执行 dnf update --security -y,但虚拟机无法访问外网(ping 8.8.8.8 不通),下载失败。
待处理:检查宿主机是否为虚拟机 virbr0 提供 NAT/转发。
四、当前网络与访问控制
4.1 监听端口
text
0.0.0.0:36000 docker-proxy -> jms_web:36000 (HTTPS Web)
0.0.0.0:80 docker-proxy -> jms_web:80 (HTTP,内部重定向到 HTTPS)
0.0.0.0:2222 docker-proxy -> jms_koko:2222 (SSH 堡垒机)
0.0.0.0:22 sshd (系统管理,仅密钥)4.2 访问方式
| 访问地址 | 协议 | 说明 |
|---|---|---|
https://ai-ear.cn:36000 | HTTPS | Jumpserver,SSL 证书有效 |
https://ai-ear.cn:7500 | HTTPS | frp dashboard(需要 admin/xxx) |
https://ai-ear.cn:9000 | HTTPS | 文档目录服务,需要 Basic Auth(admin/xxx) |
https://ai-ear.cn:9001 | HTTPS | 需要 Basic Auth(admin/xxx) |
https://ai-ear.cn:9002-9009 | HTTPS | frp 代理端口,当前无后端服务,返回 502 |
http://ai-ear.cn:36000 | HTTP | 会返回 400,因为 36000 只提供 HTTPS |
http://8.153.84.140:36000 | HTTP | 已不可用 |
4.3 访问来源限制
| 端口 | 允许源 | 说明 |
|---|---|---|
| 22 | 192.168.122.0/24 | 仅宿主机网段,密钥登录 |
| 36000 | 8.153.84.140, 192.168.122.0/24 | 公网服务器和宿主机网段 |
| 2222 | 8.153.84.140, 192.168.122.0/24 | 公网服务器和宿主机网段 |
五、遗留问题与建议
防火墙长期方案:当前使用
iptables-services手工管理。如果组织强制要求firewalld,需要研究firewalld与 Docker 的兼容配置(如将 Docker 网桥加入trustedzone,或配置firewalld的 Docker 集成),并在测试环境验证后再上线。虚拟机外网访问:当前
jumpserver虚拟机无法访问外网,导致dnf update失败。需检查宿主机virbr0的 NAT/转发配置。系统重启后验证:已启用
iptables.service开机恢复规则,但建议下次计划维护时重启虚拟机,确认:- iptables 规则自动加载
- SELinux 仍为 Permissive
- Jumpserver 容器正常启动
https://ai-ear.cn:36000可访问
管理员 MFA:数据库中已全局启用
SECURITY_MFA_AUTH=1,但建议登录 Jumpserver Web UI,为现有管理员账号绑定 OTP/邮件 MFA。证书续期:当前证书有效期至 2026-10-15,请在到期前更新证书并替换
/opt/jumpserver/config/nginx/cert/server.crt和server.key。定期审计:建议每月检查
lastb、Jumpserver 登录日志、容器健康状态,并定期轮转SECRET_KEY/BOOTSTRAP_TOKEN。