- 网络安全
- 运维
- 应用安全
【免费下载链接】ansible-collection-hardening
This Ansible collection provides battle tested hardening for Linux, SSH, nginx, MySQL
本篇技术文章以ansible-collection-hardening集合中 ssh_hardening 角色的 CHANGELOG 为主线,梳理该角色从 1.0.0 到 9.8.0 共九个版本的功能演进脉络,并结合当前仓库的 defaults/main.yml、hardening.yml、opensshd.conf.j2 等源码,还原每一项变更背后的加固动机、变量设计与实现方式。读完本文,你将理解一个"久经战场"的 SSH 加固 Ansible 角色是如何围绕加密套件、认证策略、SFTP、Match 条件块、平台兼容与幂等性等维度逐步打磨成型的,并可直接对照当前代码落地同类加固配置。
一、角色定位:为什么需要专门为 SSH 写一个加固角色
ssh_hardening是 ansible-collection-hardening 集合中用于生成安全加固的 SSH 客户端(/etc/ssh/ssh_config)与 SSH 服务端(/etc/ssh/sshd_config)配置的角色。它的目标是对齐业界 SSH 基线要求,默认策略偏保守:默认禁用 root 密码登录、默认仅开放公钥认证、默认关闭各种转发。使用该角色前必须确保已存在一个可登录的普通用户并具备 su/sudo 权限,否则角色执行后可能把自己锁在门外——这是 README 明确给出的警告。
从 CHANGELOG 看,这个角色并非一蹴而就。1.0.0(2015-04-30)时它只是 dev-sec 系列中一个简单的加固脚本,随后五年内经历了 9 个大版本、数十次小版本迭代,才形成今天我们看到的结构。因此,阅读 CHANGELOG 是理解该角色设计决策的最快路径:每一行"enhancement"背后,都对应一个变量、一段任务或一个模板分支的诞生。
二、执行模型:角色如何"装配"加固配置
在展开演进细节前,先看当前角色在运行时做了什么,这有助于把 CHANGELOG 中的历史变更映射到具体代码。角色的入口 tasks/main.yml 非常简单——仅当ssh_hardening_enabled为 true 时引入 hardening.yml。后者是真正的执行主线:
- 加载 OS 依赖变量:通过
with_first_found依次尝试Distribution_MajorVersion.yml、Distribution.yml、os_family_MajorVersion.yml、os_family.yml(如 Debian.yml、Fedora.yml),并用varnames查找避免覆盖用户显式定义的变量——这正是 CHANGELOG 3.1.0 中"把变量从 vars 移到 defaults"(#60)与 8.0.0 中"重命名系统发现变量"(#268)的遗产。 - 安装 openssh 包并配置服务:对应 install.yml,涉及 9.7.0 在 Fedora 上安装 systemd(#321)。
- 探测并解析 OpenSSH 版本:执行
ssh -V,用正则解析出sshd_version(如8.9)。8.0.0 中"不再依赖 bash 获取 SSH 版本"(#266)和 4.1.2 中"给该任务加 check_mode: no"(#117)都落在这一步。 - 按版本设置加密默认值:通过 crypto_hostkeys.yml、crypto_ciphers.yml、crypto_macs.yml、crypto_kex.yml 四组任务,依据
sshd_version选择对应版本的安全算法清单。 - 渲染配置模板:用 opensshd.conf.j2 生成服务端配置(
0600权限,并用sshd -T -C ... -f %s做语法校验后触发重启 handler),用 openssh.conf.j2 生成客户端配置(0644权限)。 - 清理弱 DH 素数:用
awk '$5 < 2048'检查 moduli 文件中的小素数并剔除,缓解 Logjam 攻击。 - 按条件引入 CA 密钥、SELinux、CRYPTO_POLICY 等补充任务。
理解了这条流水线,下面按主题回顾 CHANGELOG 中每一类变更。
三、平台支持演进:从 RHEL/Oracle 起步,覆盖六大 OS 家族
平台兼容是 CHANGELOG 中占比最大的一类变更,直接决定了角色的适用范围:
- 1.0.0:引入 Oracle 支持(#5),并添加 Travis 测试矩阵;
- 3.1.0:新增 Ubuntu 16.04 LTS(Xenial)与 Docker 测试支持(#63、#71);
- 4.0.0:新增 FreeBSD 支持(#95);
- 5.0.0:新增 Amazon Linux(#145)与 OpenBSD 支持(#171);
- 6.0.0:新增 Ubuntu 18.04 支持(#186);
- 8.0.0:新增 Debian Buster 支持(#249),并完成 RHEL/OL/CentOS 8 支持(#242);
- 9.2.0:新增 Arch Linux(#291)与 RHEL 8(#261);
- 9.4.0:正式支持 CentOS 8(#309);
- 9.6.0:新增 SmartOS(#294);
- 9.8.0:新增 SuSE(#328)。
这些平台差异如何落地?看 vars/Debian.yml 与 vars/Fedora.yml 的对比就能明白:Debian 系包名为openssh-server/openssh-client,服务名为ssh;Fedora/RHEL 系包名为openssh,服务名为sshd;Fedora 还额外设置sshd_disable_crypto_policy: true——这是 9.5.0 重新设计的 CRYPTO_POLICY 处理(#314)的产物,它会在系统安装了crypto-policies时覆盖/etc/sysconfig/sshd,让 sshd 配置中的算法设置真正生效。另外 OpenBSD 分支在模板中会跳过 GSSAPI 相关行(opensshd.conf.j2 中os_family != 'OpenBSD'判断),FreeBSD 分支则不输出PrintLastLog——这些正是 CHANGELOG 各版本"add support"背后模板条件分支的体现。
四、加密与密钥体系演进:拒绝弱算法,拥抱现代密码学
加密套件是 SSH 加固的核心,CHANGELOG 记录了三个关键方向:按 OpenSSH 版本动态选算法、默认禁用弱算法、允许用户覆盖。
4.1 按版本分层的算法清单
当前 crypto_kex.yml 依据 OpenSSH 版本(5.9 / 6.6 / 8.0 / 8.5)分四档设置 KexAlgorithms;crypto_macs.yml 按 5.3 / 5.9 / 6.6 / 7.6 分四档设置 MACs;crypto_ciphers.yml 按 5.3 / 6.6 分两档设置 Ciphers。这套"版本感知"机制最早源自 3.1.0 的"为 RHEL 7 及以上使用新 ciphers/kex/macs 与沙箱权限分离"(#72、#73),并在 5.0.0 得到全面重构(#179)。
之所以要按版本分档,是因为 OpenSSH 7.6 才移除 SSHv1、8.0 才引入量子抗性 KEX(9.0 前的 8.0.0 版本即支持 OpenSSH 8.0+ 与量子抗性 KEX)。模板 opensshd.conf.j2 中也有大量sshd_version is version(...)分支:< 7.6时输出Protocol 2、< 7.4时输出UseLogin no、< 7.5时输出UsePrivilegeSeparation、>= 6.2时才输出AuthenticationMethods和增强版AllowTcpForwarding取值。
4.2 默认禁用弱算法
- 9.4.0:默认禁用 CBC 密码套件(#308),只在兼容旧客户端(如 Ruby Net::SSH)需要时通过变量放开;
- 5.0.0:修复
Bad SSH2 mac spec(#135),MACs 默认全部采用 SHA-2 系; - 8.0.0:RHEL 6 / CentOS 6 上改用 SHA-2 HMAC(#270);
- 4.0.0:从默认主机密钥中移除 DSA(#92),并对齐 ssh-baseline 基线。
4.3 主机密钥与 DH 参数管理
hardening.yml 中sshd_moduli_minimum: 2048(defaults/main.yml)配合 awk 检查/剔除小素数任务,源自 4.0.0 的"避免小素数 DH、允许重建 DH 素数"(#89、#97)。
主机密钥方面,crypto_hostkeys.yml 会:
- 用
community.crypto.openssh_keypair将默认 2048 位 RSA 主机密钥替换为ssh_host_rsa_key_size(默认 4096)位的密钥对; - 按版本(5.3 / 6.0 / 6.3)决定启用 RSA、ECDSA、Ed25519 中的哪几个主机密钥——Ed25519 密钥正是 4.0.0 引入的(#96);
- 统一设置主机私钥权限为
0600。
如果不想让角色生成密钥,可以像 4.3.0 那样通过设置ssh_host_key_files来保留手工密钥(#125)。从 8.1.0 起,还能通过ssh_host_key_algorithms自定义服务端算法列表(#278),对应模板中的HostKeyAlgorithms行;客户端侧则通过ssh_client_host_key_algorithms控制偏好顺序。CHANGELOG 中 9.4.0 关闭的"自定义主机密钥算法"需求(#243)由此得到闭环。
五、认证与访问控制演进:从"全关"到"精细化"
5.1 根登录与密码登录
- 6.1.0:将
PermitRootLogin参数化(#195、#190),默认"no",允许设"without-password"或"yes"(README 特别提示引号必填); - 4.1.0:新增
ssh_server_password_login,允许按需放开服务端密码登录(#107、#106); - 8.0.0:将
PermitUserEnvironment与AcceptEnv解耦(#251、#232),并重构AuthenticationMethods允许用户自定义认证方法组合(#245)。
当前默认值为sshd_authenticationmethods: publickey(defaults/main.yml),即只允许公钥认证;若要开放密码登录,需同时设ssh_server_password_login: true并把password加入sshd_authenticationmethods,README 对此有明确提示。
5.2 UsePAM 与 GSSAPI
- 7.0.0:
UsePAM默认改为yes(#233),让系统级 PAM 策略(如 faillock 账户锁定)得以生效; - 6.0.0:修复 GSSAPI 无法启用的问题(#194、#192),通过
ssh_gssapi_support/ssh_gssapi_delegation控制客户端与服务端的 GSSAPI 行为; - 4.0.0:
ChallengeResponseAuthentication可配置化(#85)。
5.3 用户/组白名单与撤销密钥
模板中AllowUsers/AllowGroups/DenyUsers/DenyGroups对应ssh_allow_users、ssh_allow_groups、ssh_deny_users、ssh_deny_groups四个变量(defaults/main.yml),这部分可追溯到 1.2.1 的"SSH 白名单组支持"(#40)。此外:
- 4.1.3:支持指定被撤销公钥列表(#120),当前由
ssh_server_revoked_keys变量配合 revoked_keys.j2 模板生成/etc/ssh/revoked_keys(0600权限),模板固定输出RevokedKeys /etc/ssh/revoked_keys; - 5.0.0:新增
TrustedUserCAKeys与AuthorizedPrincipalsFile支持(#157),当前由 ca_keys_and_principals.yml 实现,配合ssh_trusted_user_ca_keys_file、ssh_trusted_user_ca_keys、ssh_authorized_principals_file、ssh_authorized_principals四个变量使用,模板中TrustedUserCAKeys与AuthorizedPrincipalsFile仅在对应文件变量非空时输出。
5.4 爆破防护参数
9.1.0 将登录宽限时间与最大会话数参数化(#287),当前默认ssh_login_grace_time: 30s、ssh_max_sessions: 10;ssh_max_auth_retries: 2、ssh_max_startups: 10:30:60分别在 1.1.0(#31)与 5.0.0(#149)引入。这些最终都会写入 opensshd.conf.j2 的LoginGraceTime、MaxAuthTries、MaxSessions、MaxStartups行。
六、SFTP 与 Match 条件块:精细化规则的两次关键进化
6.1 SFTP 子系统
- 3.0.0:引入
sftp_enabled、sftp_chroot_dir等变量(#57); - 5.0.0:支持禁用 SFTP chroot(#166);
- 8.0.0:SFTP 默认 umask 设为 0027(#252);
- 9.6.0:修复
sftp_umask被当作八进制数存储的问题,改为字符串字面量(#317)。
当前 opensshd.conf.j2 中,sftp_enabled时输出Subsystem sftp internal-sftp -l INFO -f LOCAL6 -u {{ sftp_umask }},并在文件末尾追加Match Group sftponly块:ForceCommand internal-sftp、可选ChrootDirectory {{ sftp_chroot_dir }}(默认/home/%u)、并强制关闭 TCP/Agent/X11 转发与 root 登录。README 提醒:若sftp_enabled: false,Ansible 默认基于 SFTP 的文件传输会失败,需在 ansible.cfg 设置scp_if_ssh = True,且 OpenSSH 9.0+ 还需scp_extra_args = "-O"。
6.2 Match 条件块
Match 块经历了两次重要修复:
- 6.1.1:修复
Match Group sftponly中ChrootDirectory缩进错误(#222、#221); - 6.1.0:修复多个 Match 规则无法同时生效的问题(#208、#207);
- 6.2.0 / 7.0.0:新增
ssh_server_match_address(#231、#230); - 9.2.0:新增
ssh_server_match_local_port(#295)。
当前模板支持Match Address、Match Group、Match User、Match LocalPort四种条件块(对应ssh_server_match_address、ssh_server_match_group、ssh_server_match_user、ssh_server_match_local_port四个变量),每个块由item.address/group/user/port与item.rules(规则行列表)组成,规则统一缩进 4 空格。注意 README 变量表中这些变量的类型标记为str,但 defaults/main.yml 中默认为false,实际使用时需按"hash 列表"提供。
七、网络与转发控制演进:转发、端口、IPv6 与 MOTD
7.1 转发控制的三段演进
- 8.0.0:
AllowTcpForwarding支持yes/no/all/local/remote五档取值(#257、#255),模板中通过in ('yes','no','local','all','remote')判断决定原样输出还是布尔转换; - 9.1.1:修复
ssh_allow_tcp_forwarding: yes仍输出no的 bug,改用带引号的字符串取值(#288、#286); - 9.0.0:客户端压缩可配置化(#284),即
ssh_client_compression。
其余转发相关变量(defaults/main.yml):ssh_allow_agent_forwarding: false、ssh_x11_forwarding: false、ssh_gateway_ports: false、ssh_permit_tunnel: "no"——分别源自 6.0.0 之前对 GatewayPorts 的引入(#136)、4.1.0 对 PermitTunnel 的支持(#112)等。
7.2 端口与幂等性
9.4.0 专门在 README 增加了"端口与幂等性"章节(#307):若通过ssh_server_ports修改默认端口,sshd 重启后 Ansible 仍会按旧端口连接,需同步更新 inventory;8.0.0 修复了 CentOS 7.6 自定义端口后 SSH 无法启动/连接的问题(#212)。9.4.0 还修复了"变更 sshd 端口时的幂等性"(#299)。另外 6.0.0 关闭的StreamLocalBindUnlink支持(#197)至今仍可通过sshd_custom_options自定义行补齐。
7.3 IPv6
9.5.0 修复了network_ipv6_enable: true不生效的问题(#311),9.4.0 补充了 IPv6 专属处理(#312)。当前模板中network_ipv6_enable控制AddressFamily(any/inet);README 强调:设 false 时必须同时把ssh_listen_to配为 IPv4 地址,若需要 IPv6 监听则设[::]之类地址。
7.4 MOTD 与登录信息
MOTD 是历史上 bug 最多的区域之一:
- 9.0.0:禁用 Ubuntu 动态登录 MOTD(#271);
- 9.7.0:新增
ssh_print_pam_motd独立控制 PAM 侧 MOTD(#320),修复"Ubuntu 上 MOTD 打印两次"(#319); - 1.2:恢复
PrintLastLog可用性(#39)。
当前实现:默认ssh_print_motd: false、ssh_print_pam_motd: false、ssh_print_last_log: false。当 PAM 支持且ssh_print_pam_motd为 false 时,hardening.yml 会用community.general.pamd从 sshd 的 session 配置中移除pam_motd.so(带 backup);模板中同时输出PrintMotd与 Debian 系的DebianBanner(对应ssh_print_debian_banner,控制协议握手时的发行版信息泄露)。
八、平台差异与运维细节:systemd、SELinux 与 check 模式
- systemd:9.7.0 在 Fedora 安装 systemd(#321);README 说明 Debian 12 / Ubuntu 22.04 起 sshd 默认由 systemd socket 激活,角色会回退为传统常驻行为;
- SELinux:3.2.0 增加 SELinux 依赖安装与
semodule检查(#79、#76),6.1.0 改用ansible_facts.selinux事实判断是否启用(#220),4.0.0 修复了"SELinux 关闭时仍执行 SELinux 任务"(#74)。当前 selinux.yml 仅在selinux.status == "enabled"时引入,支持非 22 端口监听(#214),并使用 ssh_password 策略文件; - check 模式:4.1.3 修复
--check模式失败(#111),4.1.2 为版本探测任务加check_mode: no(#117); - 幂等性:4.3.1 修复"重复运行产生重复参数"(#124)、4.1.0 修复"ssh_config 重复生成"(#104)、5.0.0 修复"
ssh_server_weak_kex变量未被使用"(#167); - 8.0.0:彻底移除 bash 依赖(#265),并移除了已废弃的 2FA 功能(#269)。
九、变量体系与自定义扩展:从"黑盒"到"全开放"
早期版本(1.x~3.x)很多配置是写死的,CHANGELOG 记录了变量化过程:1.1.0 将 HMAC 变量按客户端/服务端拆分(#37)、分离客户端与服务器端口(#34)、区分系统变量与可编辑变量(#24);3.1.0 将 cipher/kex/mac 变量全部移入 defaults(#60、#53);3.2.0 将 Banner/DebianBanner 参数化(#77);6.1.0 归档并文档化自定义变量(#217)。
最终形态体现在 README 的完整变量表(约 90 个变量)与 defaults/main.yml 中。对于表中未列出的选项,README 提供了两个逃生舱:
ssh_custom_options:写入客户端配置/etc/ssh/ssh_config的文件开头;sshd_custom_options:写入服务端配置/etc/ssh/sshd_config的文件开头;
示例(README):
- hosts: localhost roles: - devsec.hardening.ssh_hardening vars: ssh_custom_options: - "Include /etc/ssh/ssh_config.d/*" sshd_custom_options: - "AcceptEnv LANG"因为自定义行位于文件开头,而 sshd/ssh 配置遵循"后出现的同名指令覆盖前者"的规则,所以用户可以在文件后部的模板默认配置之上完成覆盖。这一机制回应了 CHANGELOG 中反复出现的"部分选项不可配置"诉求(#239、#175、#199)。顺带一提,6.1.2 曾修复sshd_custom_options被误用于 ssh_config 生成的问题(#225、#224),可见两个变量从诞生起就严格分工。
十、质量保障:测试与 CI 的持续强化
CHANGELOG 的另一条主线是测试体系:1.0.0 引入 Travis 与 test-kitchen(#9、#2);3.0.0 改用 InSpec 作为测试框架(#48);5.0.0 加入新基线测试(#161);6.0.0 提出引入 molecule 测试(#183);9.5.0 优化 kitchen/travis 测试(#313)。
在当前仓库中,这一体系演变为 molecule/ssh_hardening 场景(另有ssh_hardening_vm、ssh_hardening_custom_tests两个派生场景)。其 verify.yml 通过cincproject/auditor容器执行 DevSec ssh-baseline 的 InSpec 基线测试,rc != 0即判定失败——这与 README"角色对齐 DevSec SSH Baseline"的定位完全一致。测试覆盖正是保证 9.x 系列大量平台与算法变更不回归的关键。
十一、总结:从 CHANGELOG 读懂 SSH 加固的完整设计图谱
纵观 1.0.0 至 9.8.0 的演进,ssh_hardening的设计哲学清晰可见:
- 默认安全、显式放开:所有高风险能力(root 登录、密码认证、各类转发)默认关闭,需要时通过带引号的字符串变量显式开启;
- 版本感知的算法治理:Ciphers/MACs/KexAlgorithms 按 OpenSSH 版本动态选择,既保证新系统用最强算法,又兼容旧系统;
- 平台差异收敛到 vars:每个 OS 家族的包名、服务名、路径、SELinux 策略通过
vars/<OS>.yml隔离,模板只依赖统一变量; - 可组合的 Match 块与自定义行:既提供结构化的 Match Address/Group/User/LocalPort 变量,又保留
sshd_custom_options兜底通道; - 运维友好:语法校验、check 模式兼容、幂等处理、MOTD 去重、systemd 行为回退、SFTP 与 Ansible 传输方式的适配,均为生产可重复执行设计。
如果你正在规划自己的 SSH 加固策略,这份 CHANGELOG 结合 defaults/main.yml、opensshd.conf.j2 与 README 变量表 就是一份可逐条对照的最完整需求清单:每一个变量、每一条默认值、每一处版本分支,都对应着一个真实的生产安全事故或兼容性痛点。
- 网络安全
- 运维
- 应用安全
【免费下载链接】ansible-collection-hardening
This Ansible collection provides battle tested hardening for Linux, SSH, nginx, MySQL
相关推荐
Ansible SSH 加固实战:深入解析 ansible-collection-hardening 的 ssh_hardening 角色
Ansible SSH 加固实战:深入解析 ansible collection hardening 的 ssh_hardening 角色 导读 本文围绕开源
网络安全运维应用安全ansible-collection-hardening 之 mysql_hardening 角色演进实录:从 1.0.0 到 2.2.2 的 MySQL 加固能力迭代剖析
ansible collection hardening 之 mysql_hardening 角色演进实录:从 1.0.0 到 2.2.2 的 MySQL 加固
网络安全运维应用安全SymPy 量子张量积:TensorProduct 类的原理、矩阵 Kronecker 积与实用操作指南
SymPy 量子张量积:TensorProduct 类的原理、矩阵 Kronecker 积与实用操作指南 导读 本文以 SymPy 量子力学模块( sympy/
网络安全运维应用安全
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考