ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Fail2Ban 的 systemd journal 后端过滤器:FilterSystemd 模块源码级解析

Fail2Ban 的 systemd journal 后端过滤器:FilterSystemd 模块源码级解析 Fail2Ban 的 systemd journal 后端过滤器FilterSystemd 模块源码级解析【免费下载链接】fail2banDaemon to ban hosts that cause multiple authentication errors项目地址: https://gitcode.com/gh_mirrors/fa/fail2ban本指南以仓库 doc/fail2ban.server.filtersystemd.rst 所定义的fail2ban.server.filtersystemd模块为主题深入解析 Fail2Ban 如何借助 systemd journal 直接读取日志并检测登录失败。你将掌握FilterSystemd类的核心成员与工作流程journal 匹配、游标续读、轮转重开、持久化断点、journalmatch的 journalctl 语法用法以及backend systemd在 config/jail.conf 中的配置前提与限制。一、模块定位从日志文件到 systemd journalFail2Ban 的过滤器后端负责读取日志 → 正则匹配 → 把失败交给 FailManager 计次。传统polling/pyinotify后端监视的是普通文本文件logpath而 systemd 后端直接订阅 journald 的日志流不再需要指定logpath。在 fail2ban/server/jail.py 中后端被注册为_BACKENDS [pyinotify, polling, systemd]auto会按该顺序尝试初始化当显式或自动选中systemd时jail.py 调用_initSystemd实例化FilterSystemd。模块继承自 fail2ban/server/filter.py 中的JournalFilter基类基类只是定义了addJournalMatch/delJournalMatch/getJournalMatch的空实现与clearAllParams的清理逻辑真正的实现全部落在filtersystemd.py中。二、构造与初始化参数FilterSystemd.__init__filtersystemd.py先通过_getJournalArgs(kwargs)从配置参数中提取 systemd 相关的关键字参数再创建journal.Reader连接self.__jrnlargs FilterSystemd._getJournalArgs(kwargs) JournalFilter.__init__(self, jail, **kwargs) self.__journal journal.Reader(**self.__jrnlargs)_getJournalArgsfiltersystemd.py支持的 jail 配置参数如下表配置参数含义底层处理journalpath指定 journal 目录路径传入journal.Reader(path...)journalfiles指定要监视的 journal 文件支持通配符经过glob展开、去重后传入filesjournalflags传给journal.Reader的 flags 整数直接int()转换后传入flagsrotated是否监视已轮转的日志文件布尔值为假时改用排除轮转文件的策略namespace命名空间参数systemd 239传入journal.Reader(namespace...)需要注意的默认行为当未显式给出journalflags时默认 flags 取决于rotated——若rotated为真默认journal.SYSTEM_ONLY也可用环境变量F2B_SYSTEMD_DEFAULT_FLAGS覆盖。同时为避免打开大量用户会话导致 Too many open files对应 gh-2392当rotated为假且未指定files/namespace时会调用_globJournalFiles只选取system.journal与user-uid.journal显式剔除**.journal这类轮转文件并把flags、path置空因为files不能与二者同时指定。_globJournalFilesfiltersystemd.py还依赖_getSystemdPath调用systemd-path system-state-logs/system-runtime-logs获取持久日志与运行时日志目录结果带缓存失败时回退到/var/log/journal与/run/log/journal非 root 运行时还会过滤掉无读权限的文件。三、journal 匹配journalmatch 的增删改查匹配的核心是journalmatch配置项它使用 journalctl 兼容语法。addJournalMatchfiltersystemd.py把形如_SYSTEMD_UNITsshd.service _COMMsshd的列表按拆分成多组再由_addJournalMatches依次调用journal.add_match(...)与journal.add_disjunction()即同一组内是 AND、组与组之间是 ORdef _addJournalMatches(self, matches): if self.__matches: self.__journal.add_disjunction() # Add OR for match in matches: for match_element in match: self.__journal.add_match(match_element) self.__journal.add_disjunction()resetJournalMatchesfiltersystemd.py在清理失败或重开 journal 时先flush_matches()再重建全部匹配delJournalMatchfiltersystemd.py支持按索引删除或一次性清空。若启动时未配置任何journalmatchrun()会打出 notice 日志提示将针对全部 journal 条目做正则匹配出于性能考虑不建议这样做filtersystemd.py。从调用链看这些方法通过 fail2ban/server/server.py 与 fail2ban/server/transmitter.py 暴露为 fail2ban-client 命令set jail addjournalmatch、deljournalmatch、get jail journalmatch并有 fail2ban/tests/servertestcase.py 与 fail2ban/tests/clientbeautifiertestcase.py 的测试用例覆盖。四、时间戳、格式化与断点续读getJrnEntTimefiltersystemd.py优先取_SOURCE_REALTIME_TIMESTAMP缺失时回退到__REALTIME_TIMESTAMP返回(ISO 字符串, POSIX 时间戳)。formatJournalEntryfiltersystemd.py把 journal 条目拼装成 syslog 风格行_HOSTNAME、SYSLOG_IDENTIFIER缺省回退_COMM、SYSLOG_PID缺省回退_PID统一格式化为[pid]kernel 消息追加单调时间戳MESSAGE中的换行被转义为\n最后把时间前缀进日志行保证与普通文件过滤器共用同一套正则处理管线。断点续读依赖 fail2ban/server/database.py 的getJournalPos/updateJournal在run()主循环中若 jail 关联了数据库startTime取max(数据库记录位置, now - findtime)再seekToTime(startTime)回溯处理处理过程中每 100 个 tick 或达到更新间隔时调用_updateDBPending落库afterStop时也会确保待写位置全部更新filtersystemd.py。这正是重启不重扫、不漏报的实现基础。五、主循环等待、轮转感知与重开run()filtersystemd.py是过滤器线程的主干关键流程为定位起点seek_tail()取最后一条记录结合数据库位置与findtime决定回溯起点空 journal 时直接进入运行中模式并seekToTime(now)。等待新条目运行模式下用Utils.wait_for包住journal.wait(...)以非轮询方式等待APPEND/INVALIDATE信号空闲时仍按sleeptime节流避免高负载。处理轮转INVALIDATE收到INVALIDATE先短暂等待轮转结束必要时前后移动游标避免进入死区gh-3396并通过_journalAlive检测连接是否失效如OSError: [Errno 99] Cannot assign requested address失效则调用_reopenJournal重建journal.Reader。逐条处理get_next()取条目 →formatJournalEntry格式化 →processLineAndAdd(line, tm)交给基类正则匹配与 FailManager累计 100 条后继续处理而不进入等待。运行模式切换处理到当前时刻或越过起点游标后切换为inOperationMode此后新到条目直接实时处理。_reopenJournalfiltersystemd.py对应 gh-3929 场景轮转后 journal 描述符失效先尝试原地重新__init__底层 Reader失败则整体重建随后恢复 jail 配置的 journal 匹配并短暂抑制重复的 Invalidate 提示。六、配置实战在 jail 中启用 systemd 后端修改config/jail.conf或jail.d/*.local把目标 jail 的backend设为systemd并通过journalmatch缩小监视范围例如 sshd[sshd] enabled true backend systemd journalmatch _SYSTEMD_UNITsshd.service _COMMsshd maxretry 5 findtime 10m bantime 10mjournalmatch的优先级从高到低为jail 内显式配置 → 过滤器配置文件的journalmatch段如 fail2ban/tests/files/filter.d/testcase01.conf 中的_COMMsshd _SYSTEMD_UNITsshd.service _UID0→ 缺省匹配全部 journal。连接多个匹配组时组间为 OR同一组内多个键值对为 AND。根据 config/jail.conf 的说明使用systemd后端时不能再指定logpath若默认后端为 systemd、但某 jail 的日志只存在于自己的日志文件中应给该 jail 单独指定其他后端如polling并留空journalmatch。启用前需确认系统已安装systemd的 Python 绑定模块顶部from systemd import journal依赖它例如 Debian/Ubuntu 的python3-systemd包Fail2Ban 源码中FilterSystemd及相关方法标注了# pragma: systemd no cover意味着在未安装绑定的环境下该后端不可用。运行验证可执行fail2ban-client status sshd # 查看 Filter 行与 Journal matches fail2ban-client set sshd addjournalmatch _COMMsshd fail2ban-client get sshd journalmatch fail2ban-client set sshd deljournalmatch七、小结FilterSystemd把 Fail2Ban 的正则判定失败能力无缝嫁接到 systemd journal 上journalmatch提供与 journalctl 一致的筛选语法formatJournalEntry保证条目与文件日志走同一条处理管线数据库断点实现重启续读INVALIDATE感知与_reopenJournal应对日志轮转和描述符失效。整套机制在 fail2ban/server/filtersystemd.py 一个文件中即可完整阅读是理解 Fail2Ban 过滤器抽象fail2ban/server/filter.py与后端扩展机制的最佳范例。【免费下载链接】fail2banDaemon to ban hosts that cause multiple authentication errors项目地址: https://gitcode.com/gh_mirrors/fa/fail2ban创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进