使用 fail2ban 保护 ShadowSocks

如果大家在自己的 VPS 上搭 ShadowSocks,总会有贱人来扫描、破解。使用 fail2ban,可以有效地屏蔽这些贱人的 IP,提高安全性。

前提

  • 本文不是 ShadowSocks 或 fail2ban 的科普贴,假定大家已经安装,并对二者有基本的了解
  • 本文使用 ShadowSocks 是多用户版、不是单用户版
  • 本文服务器端为 Ubuntu 16.04 LTS

ShadowSocks 端设置

注:我这里 ShadowSocks 是安装在 /usr/local/shadowsocks 中,下面的文件是和此路径相关的,大家需要根据自己的情况调整路径。

配置 ShadowSocks 的日志

1
sudo vi /usr/local/shadowsocks/config.py

更新日志相关的部分:

1
2
3
4
#LOG CONFIG
LOG_ENABLE = True
LOG_LEVEL = logging.ERROR
LOG_FILE = '/var/log/shadowsocks.log'

注意:需要手动创建 /var/log/shadowsocks.log,并保证运行 ShadowSocks 的账户有写权限。

调整 ShadowSocks 输出的日志

为什么要调整?这和下文介绍的 fail2ban 有关。fail2ban 要求日志以日期开头,但并不能智能识别所有的日期格式、也不支持为其配置日期格式。所以,需要配置 ShadowSocks 输出的日志格式能被 fail2ban 支持。更多 fail2ban 自定义 filter 中正则表达式的介绍,请 参考这里

1
2
sudo cp /usr/local/shadowsocks/servers.py /usr/local/shadowsocks/servers.py.bak
vi /usr/local/shadowsocks/servers.py

注意:

  • 修改自己不熟悉的配置文件前,先备份,始终是好习惯。
  • 这里是 servers.py(即多用户版),不是 server.py

将以下输出日志的代码:

1
logging.basicConfig(format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',datefmt='%Y, %b %d %a %H:%M:%S',filename=config.LOG_FILE,level=config.LOG_LEVEL)

改为:

1
logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',datefmt='%Y-%m-%d %H:%M:%S',filename=config.LOG_FILE,level=config.LOG_LEVEL)

改动并不大:

  • %Y, %b %d %a 改为 %Y-%m-%d,以实际日期示例:将 2016, Jul 17 Sun 改为 2016-07-17
  • 去掉代码行的输出:%(filename)s[line:%(lineno)d]

重启 ShadowSocks

因为这和你启动 ShadowSocks 的方式有关,这里就不提供示例代码了。

创建 ShadowSocks 错误日志

比如,你可以故意在 ShadowSocks 客户端设置错误的密码,然后尝试使用 ShadowSocks 代理访问,应该就会在 VPS 端产生错误日志。

然后,检查 ShadowSocks 日志 /var/log/shadowsocks.log 是否有类似下面的错误信息:

1
2016-07-17 18:05:03 ERROR can not parse header when handling connection from 112.10.137.6:20463

至此,ShadowSocks 端的设置就结束了。

fail2ban 端设置

创建 ShadowSocks 过滤器

fail2ban 默认并没有设置 ShadowSocks 过滤器(当然了,只有天朝才需要梯子;而 fail2ban 并不是国人开发的,自然没有这种多余的东西),需要我们按照 fail2ban 的规则来创建。

1
sudo vi /etc/fail2ban/filter.d/shadowsocks.conf

添加如下内容:

1
2
3
4
5
6
7
[INCLUDES]
before = common.conf
[Definition]
_daemon = shadowsocks
failregex = ^\s+ERROR\s+can not parse header when handling connection from <HOST>:\d+$
ignoreregex =

其中,failregex 就是用来过滤日志中尝试破解 ShadowSocks 的正则表达式。另外,你可以在命令行中测试正式表达式是否正确。

1
fail2ban-regex '2016-07-17 17:27:37 ERROR can not parse header when handling connection from 112.10.137.6:20238' '^\s+ERROR\s+can not parse header when handling connection from <HOST>:\d+$'

输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Running tests
=============
Use failregex line : ^\s+ERROR\s+can not parse header when handling con...
Use single line : 2016-07-17 17:27:37 ERROR can not parse header whe...
Results
=======
Failregex: 1 total
|- #) [# of hits] regular expression
| 1) [1] ^\s+ERROR\s+can not parse header when handling connection from <HOST>:\d+$
`-
Ignoreregex: 0 total
Date template hits:
|- [# of hits] date format
| [1] Year(?P<_sep>[-/.])Month(?P=_sep)Day 24hour:Minute:Second(?:,Microseconds)?
`-
Lines: 1 lines, 0 ignored, 1 matched, 0 missed [processed in 0.00 sec]

其中:

  • Date template hits: 是指该日志中有满足格式的日期,这也是为什么之前要更新 ShadowSocks 输出的日志格式。
  • Failregex: 1 total 是指有一条满足条件的日志被找到。

更新 fail2ban 配置

接下来,需要告诉 fail2ban 来检查 ShadowSocks 日志、并更新 iptables.

1
sudo vi /etc/fail2ban/jail.local

添加如下内容:

1
2
3
4
5
6
7
[shadowsocks]
enabled = true
filter = shadowsocks
port = 10000:20000
logpath = /var/log/shadowsocks.log
maxretry = 5
bantime = 43200

其中:

  • port 为端口范围,设定为 ShadowSocks 所监听的端口范围

重启 fail2ban

1
sudo service fail2ban restart

查看 fail2ban 中 ShadowSocks 监控的状态:

1
sudo fail2ban-client status shadowsocks

会有类似下面的输出:

1
2
3
4
5
6
7
8
9
Status for the jail: shadowsocks
|- Filter
| |- Currently failed: 0
| |- Total failed: 949
| `- File list: /var/log/shadowsocks.log
`- Actions
|- Currently banned: 1
|- Total banned: 1
`- Banned IP list: 112.10.137.6

可以看出,上面日志中的 IP 112.10.137.6 已经被屏蔽。

或者,也可以通过 iptables 来查看:

1
sudo iptables -S

其中,会有类似下面的记录:

1
-A f2b-shadowsocks -s 112.10.137.6/32 -j REJECT --reject-with icmp-port-unreachable

至此,全文结束。