Skip to content

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_webconfig.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 实施步骤

  1. 从公网服务器 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
  2. 修改 /opt/jumpserver/config/nginx/lb_http_server.conf

    • 取消注释 server_name 并设置为 ai-ear.cn
  3. 修改 /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
  1. 重启 Jumpserver:
bash
cd /opt/jumpserver-installer-v4.9.0
./jmsctl.sh restart

2.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 代理端口 90009001 也改为通过 https://ai-ear.cn:PORT 访问。

实施步骤

  1. 调整 frps 端口,释放 75009000-9009 给 nginx:

    • 修改 /etc/frp/frps.toml
      toml
      webServer.port = 17500
    • 重启 frps
  2. 调整 frpc 端口映射

    • 修改 /etc/frp/frpc.toml,将 9000-9009 的 remotePort 全部改为 19000-19009
    • 重启 frpc
  3. 8.153.84.140 上配置 nginx 监听 7500/9000-9009 SSL

    每个端口一个 server 块,均使用 ai-ear.cn 证书,反代到 frps 新端口。以 9000 为例:

    nginx
    server {
        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。

实施步骤

  1. 创建密码文件 xxx

    bash
    htpasswd -cb /etc/nginx/.htpasswd admin 'xxx'
    chmod 640 /etc/nginx/.htpasswd
    chown root:nginx /etc/nginx/.htpasswd
  2. 修改 /etc/nginx/nginx.conf,在 90009001 的 server 块中添加:

    nginx
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;
  3. 测试并重载 nginx:

    bash
    nginx -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 规则冲突。启用后,firewalldfilter_FORWARD 链将 Docker 网桥流量导向 public zone 并默认 reject,导致:

  • jms_core 无法连接 redis:6379No 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=true

3.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   14

3.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:36000HTTPSJumpserver,SSL 证书有效
https://ai-ear.cn:7500HTTPSfrp dashboard(需要 admin/xxx)
https://ai-ear.cn:9000HTTPS文档目录服务,需要 Basic Auth(admin/xxx)
https://ai-ear.cn:9001HTTPS需要 Basic Auth(admin/xxx)
https://ai-ear.cn:9002-9009HTTPSfrp 代理端口,当前无后端服务,返回 502
http://ai-ear.cn:36000HTTP会返回 400,因为 36000 只提供 HTTPS
http://8.153.84.140:36000HTTP已不可用

4.3 访问来源限制

端口允许源说明
22192.168.122.0/24仅宿主机网段,密钥登录
360008.153.84.140, 192.168.122.0/24公网服务器和宿主机网段
22228.153.84.140, 192.168.122.0/24公网服务器和宿主机网段

五、遗留问题与建议

  1. 防火墙长期方案:当前使用 iptables-services 手工管理。如果组织强制要求 firewalld,需要研究 firewalld 与 Docker 的兼容配置(如将 Docker 网桥加入 trusted zone,或配置 firewalld 的 Docker 集成),并在测试环境验证后再上线。

  2. 虚拟机外网访问:当前 jumpserver 虚拟机无法访问外网,导致 dnf update 失败。需检查宿主机 virbr0 的 NAT/转发配置。

  3. 系统重启后验证:已启用 iptables.service 开机恢复规则,但建议下次计划维护时重启虚拟机,确认:

    • iptables 规则自动加载
    • SELinux 仍为 Permissive
    • Jumpserver 容器正常启动
    • https://ai-ear.cn:36000 可访问
  4. 管理员 MFA:数据库中已全局启用 SECURITY_MFA_AUTH=1,但建议登录 Jumpserver Web UI,为现有管理员账号绑定 OTP/邮件 MFA。

  5. 证书续期:当前证书有效期至 2026-10-15,请在到期前更新证书并替换 /opt/jumpserver/config/nginx/cert/server.crtserver.key

  6. 定期审计:建议每月检查 lastb、Jumpserver 登录日志、容器健康状态,并定期轮转 SECRET_KEY/BOOTSTRAP_TOKEN