Ubuntu服务器安全加固:配置密码复杂度与fail2ban防暴力破解
1. 项目概述为什么你的Ubuntu服务器需要这两道“门禁”最近在帮几个朋友的公司做服务器安全巡检发现一个挺普遍的现象很多刚接触Linux运维的朋友把Ubuntu服务器搭起来装上应用就跑业务了对系统最基本的安全加固却常常忽略。这就好比你家装了扇很贵的防盗门却忘了给门上锁或者锁的密码是“123456”。今天要聊的这两个功能——密码复杂度策略和连续登录失败处理——就是给Ubuntu服务器这扇“门”加上两道最基础、也最关键的“门禁”。简单来说密码复杂度策略决定了用户设置的密码必须有多“强壮”比如必须包含大小写字母、数字和特殊字符防止被轻易猜解或暴力破解。而连续登录失败处理则是在有人或自动化脚本反复尝试错误密码时系统会自动采取防御措施比如锁定账户一段时间直接阻断这种“撞门”行为。在当前的网络环境下针对SSH22端口的暴力破解尝试几乎是7x24小时不间断的如果你的服务器公网IP暴露且没有这两项配置被攻陷可能只是时间问题。这篇文章我会以一个运维老兵的视角带你从零开始手把手完成这两项功能的配置。我不会只给你干巴巴的命令而是会解释清楚每条命令背后的原理、不同配置方案的取舍以及我在实际生产环境中踩过的坑和总结的技巧。无论你是刚接触Ubuntu的开发者还是需要维护一两台服务器的运维新手跟着做一遍都能让你服务器的安全基线提升一个明显的等级。2. 核心安全思路拆解从“被动防御”到“主动预警”在动手敲命令之前我们得先理清思路。安全配置不是命令的堆砌而是基于对攻击链的理解构建层层防御。密码复杂度和登录失败处理分别作用于攻击的不同阶段。密码复杂度是第一道静态防线。它的目标是大幅增加攻击者“猜中”密码的难度。假设密码是6位纯数字那么暴力破解的尝试空间只有10^6一百万种可能这对于现代计算机来说微不足道。而如果强制要求8位且包含大小写字母、数字和特殊字符尝试空间将急剧膨胀到26261030^8 ≈ 7.2×10^15种这几乎是一个不可能完成的任务。复杂度策略的核心就是通过规则强制用户设置出这样一个高熵值的密码。连续登录失败处理则是第二道动态防线。它基于一个简单的逻辑正常用户很少会连续输错密码超过5次。如果一个IP地址或账户在短时间内出现大量失败登录极有可能是自动化工具在尝试暴力破解。此时系统不应再被动地接受尝试而应主动介入通过暂时锁定账户或封禁IP来延缓甚至阻止攻击。这不仅能直接保护被攻击的账户还能为管理员发现异常、介入处理争取宝贵时间。这两者结合构成了“强密码防猜解失败锁定防爆破”的纵深防御体系。在Ubuntu上它们通常通过两个核心组件实现PAM和fail2ban。PAM全称“可插拔认证模块”。你可以把它想象成系统认证的“总调度中心”。无论是本地登录、SSH远程登录还是sudo提权只要涉及密码验证请求都会经过PAM。我们配置密码复杂度就是通过修改PAM的规则来实现的。fail2ban这是一个更“智能”的防御程序。它不直接参与认证而是作为一个“监控者”实时扫描系统日志如/var/log/auth.log。当它发现符合“连续登录失败”模式的日志条目时就会自动调用系统防火墙如iptables或ufw临时封禁这个可疑的IP地址。它实现了从“检测”到“响应”的自动化。理解了这些我们的配置路径就清晰了用PAM管密码用fail2ban管登录行为。注意在开始任何系统级配置前务必确保你有一个活跃的、非root的sudo用户会话并且已通过SSH密钥或另一个终端成功登录。因为错误的PAM配置可能导致所有用户包括root都无法登录。保持一个有效的会话作为“逃生通道”是运维操作的第一铁律。3. 密码复杂度策略配置详解打造你的“密码生成器”Ubuntu默认的密码策略非常宽松几乎没有任何限制。我们需要通过配置PAM模块pam_pwquality在旧版本中可能是pam_cracklib来强化它。3.1 定位与编辑PAM密码策略文件首先找到负责密码修改的PAM配置文件。对于大多数使用passwd命令修改密码的场景核心文件是/etc/pam.d/common-password。sudo vim /etc/pam.d/common-password打开文件后找到包含pam_pwquality.so或pam_cracklib.so的那一行。在Ubuntu 20.04及更新版本中通常是下面这样一行password requisite pam_pwquality.so retry3如果这一行被注释了以#开头或者根本不存在你可能需要先安装libpam-pwquality包sudo apt install libpam-pwquality。3.2 核心参数解析与配置方案我们需要在这行配置中添加参数。pam_pwquality提供了丰富的选项下面我结合常用配置和实战经验给出一个兼顾安全与可用性的方案将上面那行修改或补充为password requisite pam_pwquality.so retry3 minlen12 difok6 ucredit-1 lcredit-1 dcredit-1 ocredit-1 enforce_for_root现在我们来拆解每一个参数的含义和我的配置理由retry3用户设置新密码时如果不符合复杂度要求系统会提示并给予最多3次重试机会。这个数字不宜过少1次太苛刻也不宜过多5次以上显得冗长3次是比较合理的平衡点。minlen12这是最重要的参数之一规定密码的最小长度。为什么是12而不是8根据目前的计算能力和主流安全建议8位密码在GPU暴力破解面前已显脆弱。12位长度能极大增加破解所需的时间成本。这是提升安全性的最有效手段。difok6设置新密码时必须与旧密码至少有多少个字符不同。防止用户只是简单地在旧密码后加个“1”了事。设为6意味着新密码需要有实质性的改变。ucredit-1lcredit-1dcredit-1ocredit-1这四个参数分别控制密码中必须包含的大写字母、小写字母、数字和特殊字符其他字符的最低要求。-1表示“至少1个”。你也可以设置为-2要求至少2个。我强烈建议至少启用其中三类。全设为-1意味着密码必须同时包含大小写字母、数字和特殊字符这是最高强度但可能对某些遗留系统或特定用户造成困扰。一个折中的强力方案是minlen14 ucredit-1 lcredit-1 dcredit-1即14位且必须包含大小写字母和数字不强制特殊字符。enforce_for_root关键参数这个选项强制root用户在修改密码时也必须遵守同样的复杂度规则。很多时候root账户因为权限最高而被忽视安全这个选项堵上了这个漏洞。3.3 配置生效与测试保存并退出编辑器。无需重启任何服务PAM的配置是即时生效的。现在我们来测试一下配置是否起作用。打开一个新的终端尝试为一个测试用户或者直接用自己的非root用户修改密码# 尝试设置一个简单的密码 passwd系统会提示你输入当前密码然后输入新密码。如果你输入一个不符合规则的密码比如短于12位、没有数字等你会立刻看到类似这样的错误提示BAD PASSWORD: The password is shorter than 12 characters BAD PASSWORD: The password does not contain enough uppercase letters ...其他错误信息只有当你输入的密码满足了所有条件系统才会提示你再次输入以确认并最终成功修改。实操心得与避坑指南历史密码检查pam_pwquality默认可能不检查历史密码。如果你希望禁止用户使用最近用过的密码需要配合pam_unix模块的remember参数。这通常在/etc/pam.d/common-password文件中另一行配置形如password [success1 defaultignore] pam_unix.so ... remember5。remember5表示会记住最近5次使用的密码哈希禁止复用。对已有用户的影响此配置只对后续通过passwd命令修改密码生效。系统不会强制所有已有用户立即修改密码。为了提升整体安全管理员应通过策略或通知要求所有用户在一定期限内更新密码。服务账户密码对于像nginx、mysql这类服务账户它们通常使用随机生成的、非常长的密码或密钥对且不通过交互式passwd修改。这些账户不受此策略影响。我们的策略主要针对人类用户。配置备份在修改任何PAM配置文件前习惯性地做一个备份sudo cp /etc/pam.d/common-password /etc/pam.d/common-password.bak。一旦配置出错导致无法登录你可以通过之前保留的“逃生通道”另一个有效会话用备份文件覆盖回来。4. 连续登录失败处理配置部署自动化的“安全哨兵”密码策略防住了弱密码但挡不住黑客拿着字典没完没了地试。接下来我们配置fail2ban来对付这种暴力破解。4.1 安装与基础配置fail2ban首先安装fail2ban包sudo apt update sudo apt install fail2ban -y安装完成后fail2ban服务会自动启动。它的配置文件主要位于/etc/fail2ban/目录。我们不直接修改主配置文件jail.conf而是创建一个本地覆盖文件jail.local这样在软件升级时我们的配置不会被覆盖。sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local sudo vim /etc/fail2ban/jail.local4.2 核心监狱配置以SSH为例在jail.local文件中我们需要关注[DEFAULT]区块和具体的“监狱”区块。一个“监狱”定义了监控哪种服务的日志、匹配什么错误模式、以及采取什么行动。我们先配置一些全局默认参数在[DEFAULT]部分找到或添加[DEFAULT] # 忽略的IP地址范围本地网络和本机通常需要忽略防止自己把自己锁了 ignoreip 127.0.0.1/8 ::1 192.168.1.0/24 # 封禁时间秒。这里设置为1小时3600秒。对于SSH爆破这个时间足够让自动化脚本放弃。 bantime 3600 # 查找时间窗口秒。在600秒10分钟内发生的失败登录会被累计。 findtime 600 # 最大失败次数。在findtime时间窗口内达到5次失败就触发封禁。 maxretry 5 # 使用的封禁动作。iptables-multiport是常用的它通过iptables规则封禁IP。 banaction iptables-multiport # 邮件通知相关可选需要配置系统邮件发送。这里先禁用。 # destemail your-emailexample.com # sender fail2banyour-hostname # mta sendmail # action %(action_mwl)s接下来配置SSH的监狱。在文件中找到[sshd]部分如果不存在就手动添加[sshd] # 启用此监狱 enabled true # 监控的日志文件路径。Ubuntu上SSH日志通常在这里。 port ssh logpath %(sshd_log)s # 通常使用默认的过滤器即可它能识别常见的SSH登录失败消息。 filter sshd # 覆盖全局的maxretry如果你希望对SSH更严格或更宽松的话 # maxretry 3配置参数深度解读ignoreip这是安全配置的重中之重。务必把你自己办公室或家庭的公网IP段、服务器所在的内网网段如192.168.1.0/24加进去。否则一旦你自己输错几次密码或者内网有设备异常尝试你就会被自己设置的规则封禁在外只能通过控制台如果云服务器提供或联系机房人员解封非常麻烦。bantimefindtimemaxretry这三个参数共同定义了触发机制。maxretry5和findtime600意味着“10分钟内输错5次密码就封你1小时”。这个强度对于SSH来说比较适中。你可以根据服务器暴露程度调整如果服务器直接暴露在公网且非常敏感可以设置为maxretry3, bantime86400错3次封1天。对于内网测试服务器可以放宽到maxretry10。banactioniptables-multiport是经典且有效的动作。fail2ban会在iptables的INPUT链头部插入一条规则丢弃来自被封禁IP的所有流量。你也可以使用ufw如果系统用ufw管理防火墙对应的动作是ufw。4.3 生效、测试与状态查看保存jail.local文件后需要重启fail2ban服务以使配置生效sudo systemctl restart fail2ban sudo systemctl enable fail2ban # 设置开机自启现在fail2ban已经开始监控/var/log/auth.log了。我们可以通过以下命令查看它的状态# 查看fail2ban服务状态 sudo systemctl status fail2ban # 查看所有启用的监狱及其状态非常有用 sudo fail2ban-client status # 查看sshd监狱的详细状态包括当前被封禁的IP列表 sudo fail2ban-client status sshd如何进行安全测试警告请务必在测试环境中进行或使用明确加入ignoreip的IP进行测试。你可以从另一台不在忽略列表中的机器尝试用错误密码SSH登录你的服务器连续错5次。然后立刻回到服务器上执行sudo fail2ban-client status sshd你应该能在“Banned IP list”里看到测试机的IP。同时在测试机上再次尝试SSH会收到Connection refused或超时的错误持续时长就是你设置的bantime。4.4 扩展防护为其他服务配置监狱fail2ban的强大之处在于它的可扩展性。除了SSH它还可以保护很多其他服务。配置方法类似只需在jail.local中启用并配置对应的监狱。例如保护Web服务器如Nginx against brute-force login如果网站有登录页面如WordPress的wp-login.php可以防止针对它的暴力破解。你需要使用或编写匹配Nginx访问日志中登录失败模式的过滤器。保护MySQL/MariaDB启用[mysqld-auth]监狱防止针对数据库端口的密码爆破。保护Vsftpd启用[vsftpd]监狱。大多数常见服务的监狱定义已经包含在/etc/fail2ban/jail.d/或默认配置中通常只需将enabled false改为enabled true。更复杂的防护可能需要自定义filter过滤器规则。5. 高级调优与生产环境实践基础配置能挡住90%的自动化脚本攻击但在真正的生产环境我们还需要考虑更多。5.1 密码策略的进阶考量密码过期策略强制用户定期更换密码。这可以通过chage命令或修改/etc/login.defs文件实现。例如在/etc/login.defs中设置PASS_MAX_DAYS 90表示密码最长使用90天。但请注意最新的安全指南如NIST不再推荐强制频繁更换密码因为这可能导致用户采用有规律的弱密码如Password2024!-Password2025!。更推荐的做法是结合高强度密码策略和实时凭证泄露监控。账户锁定策略除了fail2ban的IP封禁PAM本身也提供账户锁定功能pam_tally2或pam_faillock。这与fail2ban的区别在于fail2ban封IP而PAM账户锁定是锁用户账号。如果一个账号在本地控制台非网络连续输错密码PAM锁定会生效。你可以根据是否需要本地登录防护来决定是否启用它。密码哈希算法确保系统使用强密码哈希算法如yescryptUbuntu 22.04默认、sha512。这可以在/etc/login.defs中通过ENCRYPT_METHOD设置。强哈希能增加攻击者在获取密码哈希文件/etc/shadow后的破解难度。5.2 fail2ban的高可用与监控日志与监控fail2ban自己的日志在/var/log/fail2ban.log。建议使用logwatch、Logcheck或接入ELK、Graylog等日志系统对封禁事件进行监控和告警。突然激增的封禁IP可能意味着正在遭受大规模攻击。防止误封与解封即使设置了ignoreip误封仍可能发生例如动态IP用户恰好IP变了。除了定期检查封禁列表可以编写脚本将封禁时间超过一定时长如24小时的IP自动解封或者建立一个简单的Web界面供授权用户自助申请解封。应对分布式暴力破解高级攻击者会使用僵尸网络从成千上万个不同的IP进行低速、长期的破解每个IP只试几次以此绕过基于单个IP频率的检测。对付这种攻击fail2ban的单机防御就显得不足了。需要考虑网络层防御使用云服务商提供的WAFWeb应用防火墙或安全组策略限制访问源IP范围。应用层改造对SSH服务最有效的方法是禁用密码登录全面改用SSH密钥对认证。这是从根本上杜绝密码爆破的方法。在/etc/ssh/sshd_config中设置PasswordAuthentication no和PubkeyAuthentication yes。更改默认端口将SSH端口从22改为一个非标准的高位端口如2222可以过滤掉绝大部分漫无目的的自动化扫描。但这只是“安全通过隐匿”不能替代真正的认证加固。使用crowdsec等现代方案crowdsec可以看作是fail2ban的“升级版”它自带社区共享的威胁情报能更有效地识别和拦截分布式攻击。5.3 配置备份与恢复流程将/etc/pam.d/目录下修改过的文件和/etc/fail2ban/目录下的配置纳入你的服务器配置管理如Ansible、SaltStack或至少进行定期备份。在系统镜像或新服务器部署时这些安全配置应该作为基线的一部分被自动应用。一套完整的恢复流程应该是1) 备份现有配置2) 在测试环境验证新配置3) 在生产环境分批次或低峰期应用4) 应用后立即通过监控验证关键服务如SSH、Web的可用性。6. 常见问题排查与实战技巧实录即使按照指南操作你也可能会遇到一些问题。下面是我在多年运维中总结的一些典型场景和解决方法。6.1 密码策略相关问题1配置了密码复杂度但passwd命令修改时似乎没生效排查首先确认你编辑的是正确的PAM文件/etc/pam.d/common-password。然后检查该行是否被正确启用没有#注释。最后使用sudo pam-auth-update命令确保Unix authentication和Password strength checking等模块被选中。有时图形界面修改密码的工具可能使用不同的PAM栈。技巧使用sudo grep -E “password.*pam_pwquality” /etc/pam.d/*命令查看所有PAM配置文件中关于密码质量的配置确保没有在其他地方被覆盖。问题2用户抱怨密码规则太复杂记不住。解决这是一个安全与用户体验的平衡。可以考虑使用密码管理器推广使用Bitwarden、1Password等密码管理器生成和保存高强度密码这是最佳实践。采用口令短语向用户解释一个由多个随机单词组成的“口令短语”如Correct-Horse-Battery-Staple-42!长度足够且比乱码好记。这符合minlen要求也容易满足字符类型要求。适当放宽字符类型要求如果业务压力大可以暂时将ucredit-1 lcredit-1 dcredit-1 ocredit-1改为只要求其中两类但务必保持甚至增加minlen如14或16。长度是密码强度的基石。6.2 fail2ban相关问题1fail2ban服务启动失败提示“Failed to start Fail2Ban Service”。排查首先查看详细日志sudo journalctl -xe -u fail2ban。最常见的原因是jail.local配置文件有语法错误比如括号不匹配、参数名拼写错误。仔细检查你修改过的部分。另一个常见原因是定义的logpath不存在或fail2ban用户没有读取权限。问题2fail2ban状态显示sshd监狱是active但Banned IP list总是空的即使我故意输错密码。排查这是最典型的问题。按以下步骤检查确认日志路径执行sudo fail2ban-client get sshd logpath查看监控的日志文件是否正确。Ubuntu上通常是/var/log/auth.log。测试过滤器执行sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf。这个命令会模拟fail2ban的日志分析过程。查看输出看它是否能成功匹配到你故意登录失败产生的日志行。如果匹配数为0说明日志格式可能不匹配。可能是你SSH的日志级别或格式被修改过。检查SSH日志直接查看日志文件sudo tail -f /var/log/auth.log然后尝试一次失败的SSH登录观察生成的日志是否包含类似 “Failed password for invalid user” 或 “Failed password for” 的字样。fail2ban的默认sshd过滤器就是匹配这些关键词。检查ignoreip确认测试用的IP地址没有包含在ignoreip列表中。问题3IP被错误封禁如何手动解封解决使用以下命令# 解封特定IP sudo fail2ban-client set sshd unbanip 192.168.1.100 # 如果你想暂时停止整个监狱的封禁动作比如在调试期间 sudo fail2ban-client set sshd banip 192.168.1.100 # 这是封禁注意 # 更安全的方法是将你的IP临时加入 ignoreip然后重启fail2ban服务。问题4fail2ban封禁了IP但该IP依然能访问我的网站80/443端口。原因fail2ban默认的iptables-multiport动作其封禁规则可能只针对监狱中定义的端口如[sshd]的port ssh即22端口。它不会封禁其他端口。解决如果你希望封禁IP时阻断其所有访问可以修改监狱的banaction为iptables-allports或者自定义一个动作。但请注意这可能导致更严重的误伤比如封禁了一个重要的API调用者。6.3 综合安全加固检查清单配置完成后建议运行一个快速的安全检查检查SSH配置执行sudo sshd -T | grep -E “(passwordauthentication|permitrootlogin|port)”。确保PasswordAuthentication是no如果已用密钥PermitRootLogin最好是prohibit-password或noPort是否已更改。检查用户和权限sudo less /etc/passwd查看是否有不必要的用户sudo less /etc/sudoers和/etc/sudoers.d/下的文件确保sudo权限只授予必要的人。检查监听端口sudo ss -tulpn或sudo netstat -tulpn关闭所有不必要的网络服务。更新系统sudo apt update sudo apt upgrade -y定期更新是弥补已知漏洞最有效的方法。安全是一个持续的过程而不是一次性的配置。密码复杂度和fail2ban是坚固的基石但请记住没有百分之百的安全。结合定期审计、最小权限原则、及时更新和有效的监控告警才能构建起真正有韧性的系统防御体系。从我个人的经验来看花一下午时间把这些基础配置做好后续能省去无数处理入侵事件的深夜加班这笔时间投资绝对划算。