fail2ban 监视 docker 内的 wordpress
更新记录:
2020.07.27 适配 wp-fail2ban 4.3.0 版本
2023.11.20 适配 wp-fail2ban 5.2.1 版本
环境说明
- 宿主机运行 fail2ban
- docker 内运行 wordpress fpm 和 nginx,都是官方镜像
- wordpress 开启 wp-fail2ban 插件
遇到的问题
- wp-fail2ban 插件只会输出日志到 syslog,无法修改输出方式
- 由于懒得手写 Dockerfile,直接使用了官方镜像,但官方镜像不包含 syslog 服务,于是就无法获取到日志
可能的解决方案
- 在 docker 容器内跑一个 syslog 服务
- 修改 wp-fail2ban 源码,把日志输出到其他位置
纠结之后,选择了后者,因为实在懒得写 Dockerfile,写完还要调试,各种麻烦。修改 wp-fail2ban 源码的话,把日志直接输出给 docker 引擎,fail2ban 监视 docker 日志文件就可以了。
修改 wp-fail2ban
还好代码没有混淆,结构清晰,非常容易改。打开 wp-fail2ban/lib/syslog.php
文件,找到 write()
函数,修改为以下内容:
public static function write(int $level, string $msg, string $remote_addr = null): bool { $rv = false; if (null === ($rv = apply_filters(__METHOD__, null, $level, $msg, $remote_addr))) { $msg .= ' from '; $msg .= (is_null($remote_addr)) ? remote_addr() : $remote_addr; // 写出日志 error_log($msg); if (false === ($rv = \syslog($level, $msg))) { error_log("WPf2b: Cannot write to syslog: '{$msg}'", 0); // @codeCoverageIgnore } elseif (defined('WP_FAIL2BAN_TRACE')) { error_log("WPf2b: Wrote to syslog: '{$msg}'", 0); // @codeCoverageIgnore } } // 省略之后内容 ...
由于 docker 官方的 wordpress fpm 镜像默认开启了错误输出,所以这是可行的,添加后将能够在 docker logs
命令中查看。
编写 fail2ban 规则
分为两部分,filter 和 jail,先说 filter。
进入 /etc/fail2ban/filter.d/
目录,创建文件 wordpress.conf
,写入以下内容。
[Definition] failregex = ^{.*PHP message: Empty username from <HOST>\\n.*$ ^{.*PHP message: Authentication failure for .* from <HOST>\\n.*$ ^{.*PHP message: REST authentication failure for .* from <HOST>\\n.*$ ^{.*PHP message: XML-RPC authentication failure for .* from <HOST>\\n.*$ ^{.*PHP message: Authentication attempt for unknown user .* from <HOST>\\n.*$ ^{.*PHP message: Blocked username authentication attempt for .* from <HOST>\\n.*$ ^{.*PHP message: Pingback requested from <HOST>\\n.*$ ^{.*PHP message: Comment attempt on .* post \d+ from <HOST>\\n.*$ ^{.*PHP message: Untrusted X-Forwarded-For header from <HOST>\\n.*$ ^{.*PHP message: REST authentication attempt for unknown user .* from <HOST>\\n.*$ ^{.*PHP message: XML-RPC authentication attempt for unknown user .* from <HOST>\\n.*$ ^{.*PHP message: Immediately block connections from <HOST>\\n.*$ ^{.*PHP message: Blocked access from country '..' from <HOST>\\n.*$ ^{.*PHP message: XML-RPC request blocked from <HOST>\\n.*$ ^{.*PHP message: .*; Bogus Pingback from <HOST>\\n.*$ ^{.*PHP message: Akismet discarded spam comment from <HOST>\\n.*$ ^{.*PHP message: Spam comment \d+ from <HOST>\\n.*$ ^{.*PHP message: Blocked authentication attempt for .* from <HOST>\\n.*$ ^{.*PHP message: XML-RPC multicall authentication failure from <HOST>\\n.*$ ^{.*PHP message: Pingback error .* generated from <HOST>\\n.*$ ^{.*PHP message: Blocked user enumeration attempt from <HOST>\\n.*$ ignoreregex =
为啥要这样写呢,参考 wp-fail2ban/filters.d/
目录下的 3 个 conf 文件,我也不需要太强的过滤,忽略掉 extra 规则,只包含 soft 和 hard 就好。
wp-fail2ban
提供的默认 filter 是适用于 syslog 日志文件的,不能直接用于 docker 的日志文件,所以需要修改成我上面提供的这样的格式。
有了 filter,再写 jail。
进入 /etc/fail2ban/jail.d/
目录,创建文件 wordpress.local
。
[wordpress] enabled = true port = http,https filter = wordpress logpath = /var/lib/docker/containers/*/*-json.log logtimezone = UTC bantime = 86400 findtime = 3600 maxretry = 3
其中日志路径包含了所有容器的日志,因为目录名和日志文件名是以容器 ID 命名的,ID 并不固定,所以无法固定写死一个文件,索性就监视所有日志。还有一种方式是修改容器的 logging driver,使用 journald
方式输出,这样可以兼顾 docker 工具和 fail2ban;如果不需要在 docker 管理工具中查看日志,甚至可以直接指定一个单独的日志文件名。
特别注意 timezone,docker 镜像默认都是使用 UTC 时区的,这不受宿主机影响,所以 docker 输出的日志中的时间也是 UTC,而 fail2ban 可能存在 bug,并不能正确处理 ISO 8601 时间末尾的“Z”字符(UTC),把这样的时间当成了宿主机默认时区处理,这就导致 fail2ban 的 findtime 出现问题。
其他注意事项
由于我在 wordpress 的前面还跑了一个 nginx 反向代理,以便于在一个统一的位置给所有服务添加 https 支持,所以需要对这层反向代理做额外处理。
打开 wordpress 根目录下的 wp-config.php
文件,加入以下内容。
// 加入这些才能正确支持 https,前提是反代 nginx 需要正确添加 HTTP_X_FORWARDED_PROTO 头 if (strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false) $_SERVER['HTTPS']='on'; // 从 HTTP_X_FORWARDED_FOR 头中提取远程地址,避免某些兼容性差的插件运行异常,比如 login-security-solution 插件 $_SERVER['REMOTE_ADDR'] = preg_replace('/^([^,]+).*$/', '\1', $_SERVER['HTTP_X_FORWARDED_FOR']);
近期评论