frps 防 ssh 暴力破解

最近发现老集群上的 fail2ban 把我的办公电脑IP给禁了,无奈只能设置忽略该ip,然后在集群上运行 lastb 可以发现大量来自我电脑的尝试 ssh 登录,各种用户名。真是奇怪,难道我的办公电脑被入侵了?查找了很久也没有发现问题所在。今天重启了这台电脑,在集群上可以发现重启的那几分钟没有了尝试登录,可是当电脑重启后又出现了尝试登录,看来问题确实在这台电脑上。由于是刚重启,我重复执行命令 netstat -nap | grep 集群ip进行搜索,时不时出现的只有一个 frpc 进程。最初我一直以为这个进程是正常的,因为它就是为登录集群而设的,所以与集群有连接最初并没有引起我的注意。但是我突然想到,如果外部没有人在通过 frp 连接我的集群的话,是不会出现这个连接的。于是恍然大悟,这些尝试登录的连接都是通过暴露在外网的 frps 端口连进来的!但是对集群来说,它并不知道 frp 的存在,它只知道是从我的电脑上过来的连接。于是去查看 frps 的日志,果然有来自各处的许多 ip 频繁的在尝试登录!

问题找到了,接下来如何解决该问题呢?通过搜索发现了这个帖子,这位仁兄写了一个 frptables 程序,https://github.com/zngw/frptables,其功能是分析 frps 的日志文件(0.36版本之后才有日志文件),然后统计连接 frps 的 ip 数据,设定规则当给定时间内连接数超过设定值时则设置防火墙禁止该 ip,防火墙支持 iptables 和 firewalld。Github 上下载下来的zip压缩包里就是一个单独的 linux 可执行文件frptables,只需要自己设好相应 .yml 配置文件即可使用了。

一、安装 iptables

我的 frps 是在 nas 上的 docker 容器里运行的(snowdreamtech/frps),进入容器终端运行 iptables -L 发现根本没有该命令,cat /etc/issue 发现容器是 alpine linux,其包管理器是 apk,于是通过如下命令方便的安装 iptables

apk update
apk add iptables

装完后发现运行 iptables -L 依然不行,提示如下

iptables v1.8.7 (legacy): can't initialize iptables table `filter': Permission denied (you must be root)                                                                                                    
Perhaps iptables or your kernel needs to be upgraded.  

通过搜索发现是容器的权限问题,只需要给容器 NET_RAW 和 NET_ADMIN 权限即可,而默认情况下前者是有的,只需要再加上后者即可。当然如果图省事打开所有权限也行。

二、手动运行 frptables

把 frptables 拷到 nas 上的 /mypath/to/frps 并把该路径挂载为容器的 /etc/frp,设置 frptables-config.yml 如下

# frps日志文件
frps_log: /etc/frp/frps.log

# frptables 的输出日志目录
logs: /etc/frp/banlog/

# frps 名字端口对应配置
name_port:
  "myssh1": 12345
  "myssh2": 12346
  "ssh00": 12347

# 启用防火墙类型 iptables / firewall / md (Microsoft Defender)
tables_type: iptables

# ip白名单:
allow_ip:
  - 127.0.0.1

# 端口白名单
allow_port:
  - 7500
#  - 443

# 规则访问
rules:

  # 按数组顺序来,匹配到了就按匹配的规则执行,跳过此规则。
  # 地区 country-国家, regionName-省名,名字中不带省字, city-市名,名字中也不带市字
  # 端口: -1 所有端口
  # time: 时间区间
  # count: 访问次数,-1不限,0限制。其他为 time时间内访问count次,超出频率就限制

  - # 20分钟内访问15次则加入防火墙
    port: -1
    country:
    regionName:
    city:
    time: 1200
    count: 15

#  - # 中国上海IP允许
#    port: -1
#    country: 中国
#    regionName: 上海
#    city: 上海
#    time: 1
#    count: -1
#
#  - # 中国地区IP 10分钟3次,超出这频率添加防火墙
#    port: -1
#    country: 中国
#    regionName: 浙江
#    city:
#    time: 600
#    count: 3
#
#  - # 其他地区IP 直接加入防火墙
#    port: -1
#    country:
#    regionName:
#    city:
#    time: 1
#    count: 0

然后手动运行 ./frptables -c ./frptables-config.yml,一开始并不成功。原来是程序内部是通过 bash 来调用命令行的,然而容器默认没有装 bash,只有 sh,所以需要先安装 bash(只需命令 apk add bash 即可)。安装完 bash,一开始运行时还是出错,发现问题竟然是 "ssh00": 12347 前多了一个空格。最初的时候为了让它与上一行的冒号对其,"ssh00": 12347 前有三个空格,结果出错,改成两个空格就正常了。然后查看日志 banlog/frptables.info.2022-08-15,会有类似如下输出

2022-08-15 23:05:25.192 [TRACE] - [Tag:link] [allow: [username.myssh1]206.189.226.38:-1 ->1, 美国,XX,XX]                                                                                                     
2022-08-15 23:10:06.952 [TRACE] - [Tag:link] [allow: [username.myssh1]206.189.226.38:-1 ->2, 美国,XX,XX]                                                                                                     
2022-08-15 23:11:10.475 [TRACE] - [Tag:link] [allow: [username.myssh1]180.69.254.177:-1 ->1, 韩国,XX,XX]                                                                                                     
2022-08-15 23:11:21.461 [TRACE] - [Tag:link] [allow: [username.myssh1]206.189.226.38:-1 ->3, 美国,XX,XX]                                                                                                     
2022-08-15 23:12:38.525 [TRACE] - [Tag:link] [allow: [username.myssh1]206.189.226.38:-1 ->4, 美国,XX,XX]                                                                                                     

-1 -> 4 这类的计数超过设定的 15 后,allow 会变为 refuse,再查看 iptables -L 则会发现该 ip 被禁止了。

三、网络设为 macvlan

最初的 frps 容器的网络设的是 host,当在容器中运行 iptables -L 时发现输出的结果和在宿主机(nas)里运行 iptables -L 的结果一样,可见容器在 host 模式下与宿主机共享网络设置,所以如果容器里的 frptables 修改了 iptables 那宿主机的应该也同样改变了,当然也可以直接在宿主机里运行 frptables。但是我不想修改宿主机的 iptables 设置,那就不能对该容器是 host 网络。

换成 bridge 网络试试,发现映射 7000 端口时 tcp 和 udp 都得打开才行,否则连不上,此外还要映射 7500 端口以及需要穿透的端口(比如上面例子中的 12345、12346、12347)。映射好端口并能够正常连接后运行 frptables,为了测试,最初的设置是所有连接直接禁掉。结果发现 frps.log 里的 ip 都成了 172.17.0.1,也就是和容器桥接的宿主机ip,这时由于进行了一次封装,容器里已无法区分与 frps 建立连接的真实 ip 了,看到的只有 172.17.0.1。可见该方案也不行。

最后只能使用 macvlan 网络模式,因为这样会分配给容器一个与宿主机同网段 192.168.0.xxx 的 ip,而此时容器的的网络和宿主机的网络在逻辑上以及完全无关,相当于两台独立的电脑,这时便可以在容器里设置 iptables 而不影响宿主机的 iptables 配置,而容器也能识别真实的连接 ip。

四、设置 frptables 自启动

1、尝试 openrc 失败

最初尝试在 /etc/rc.local 中设置运行 frptables 的命令,然而容器启动时该文件并不生效,与是否设成可执行属性无关。由于容器用的是 alpine linux,网上说 alpine linux 不是通过 /etc/rc.local 来设置开机启动,而是通过 local 服务运行 /etc/local.d/ 下的脚本来实现,而服务则是通过 openrc 来控制,于是尝试如下操作

apk add openrc  # 安装 openrc,容器里默认是没有安装的
rc-update add local default
# 把运行 frptables 的命令写成脚本,命名为 xxx.start 并放到 /etc/local.d/ 目录下
rc-service local start

在执行完 rc-service local start之后,xxx.start 脚本确实执行了,但问题是我尝试重启容器后却并不执行 xxx.start,而且重启后查看 rc-service local status 依然是 started。尝试了很多次都不行,容器启动时 xxx.start 不会自动运行,只有手动执行 rc-service local startrestart 时才会执行,我猜或许是 docker 对其进行了限制,比较这是容器里的 alpine 系统而不是实体机上的。

2、尝试修改 config.v2.json 也失败

在 nas 里运行 docker ps 找到 frps 容器所对应的 id,进而进到其目录 /volume1/@docker/containers/56b415b0a5xxxxx,其目录字符串 56b415b0a5xxxxx 的前面部分即该容器的 id。编辑 config.v2.json,修改如下两部分

"Path":"/bin/sh", "Args":["-c","/usr/bin/frps -c /etc/frp/frps.ini"]
"Entrypoint":["/bin/sh","-c","/usr/bin/frps -c /etc/frp/frps.ini"]

"Path":"/bin/sh", "Args":["-c","/etc/frp/mystart.sh"]
"Entrypoint":["/bin/sh","-c","/etc/frp/mystart.sh"]

结果并没用,重启容器后又自动恢复原来的样子了。即使我用了 systemctl restart pkg-Docker-dockerd 重启了 docker 服务(当然所有容器也都重启了)也不行。

3、无奈只能修改 /usr/bin/frps

既然默认的启动命令是 /usr/bin/frps -c /etc/frp/frps.ini,那干脆通过文件映射把自己写的启动脚本 mystart.sh 映射为容器里的 /usr/bin/frps,这样就可以在容器启动时执行 mystart.sh 里的内容了。

五、保存被禁 ip 列表

首先是 iptables 设置的导入导出,标准做法是用 iptables-save > save.txt 进行保存设置,用 iptables-restore < save.txt来恢复设置。那么我想到的是通过 cron 任务定义保存设置,而恢复设置的命令则应写在开机运行脚本里。谁知默认的 crontab -e 里指定的 run-parts /etc/periodic/hourly 等命令并不管用。/etc/periodic/ 目录下有 15min、hourly、daily、weekly、monthly目录,默认都是空的,我把可执行脚本放入其中一个,比如15min里,结果并不管用。直接手动运行 run-parts /etc/periodic/15min 也会报错。干脆不用 run-parts 相关的目录设置,直接在 crontab -e 里设置运行命令,发现依然不运行!原来是 crond 守护进程根本没有启动! 只需运行一下 crond 命令即可启动,且自动后台运行了。于是按上面方案保存和恢复 iptables 设置,为测试,事先将已经有三个被禁ip的设置手动保存,结果重启后发现在容器里运行 iptables -L 时相当慢,三个被禁的ip半天出一个。而且发现 frptables 的日志也似乎不正常,本来 -1 -> 3-> 后面的计数都成了 0,按说 0 是强制封禁,但是也没有对相应 ip 封禁,且日志中相应 ip 后的地区识别也没有,都是空的。

于是尝试不使用 iptables-restore 恢复,因为 iptables-save 的输出中除了含有 DROP 命令的相关行是真正要封禁的 ip 外还有好多其他设置,而这些其他的设置中的有些数值在每次重启容器时还会有所不同。于是想到只提取 iptables-save 里含 DROP 的行(下面 -A 及之后的部分),并在前面加上 iptables 就成了可以运行的导入命令,如

iptables -A INPUT -s 119.167.99.194/32 -j DROP                                                                                                                                                              
iptables -A INPUT -s 149.129.250.12/32 -j DROP                                                                                                                                                              
iptables -A INPUT -s 154.221.19.143/32 -j DROP  

然后在启动脚本中运行上述命令即可导入。实测表明这样导出导入没有出现问题,一切运行正常。

六、最终设置

容器没有设时区,导致默认时间差着八小时。于是把宿主机的 /etc/localtime 拷为 /mypath/to/frps/etc-localtime,并映射为容器的 /etc/localtime,然后时间就正常了。容器里的 /usr/bin/frps 是 0.43 版本,把它拷为宿主机的 /mypath/to/frps/frps-0.43。/mypath/to/frps 目录下 ls -F 有如下内容(其中*为可执行文件):

banlog/  etc-localtime  frps-0.43*  frps.ini  frps.log  frptables*
frptables-config.yml  iprules.txt*  mystart.sh*  save-iprules.cron*

其中启动脚本 mystart.sh 内容为

#!/bin/sh
/bin/sh /etc/frp/iprules.txt
/usr/sbin/crond
/etc/frp/frptables -c /etc/frp/frptables-config.yml &
/etc/frp/frps-0.43 -c /etc/frp/frps.ini 

save-iprules.cron 内容如下

# map this file to /var/spool/cron/crontabs/root 
# min   hour    day     month   weekday command 
*/15    *       *       *       *        /sbin/iptables-save |grep DROP|sort -u|sed 's/^/iptables /' >/etc/frp/iprules.txt 

最后是容器的文件映射,如下:

#    宿主机                            容器装载路径
/mypath/to/frps/                     /etc/frp/
/mypath/to/frps/etc-localtime        /etc/localtime
/mypath/to/frps/mystart.sh           /usr/bin/frps
/mypath/to/frps/save-iprules.cron    /var/spool/cron/crontabs/root

最后提示

如果某个 ip 被误封禁,只能手动修改 iptables,可参考如下命令:

iptables -L --line-numbers  #以编号形式列出所有规则
iptables -D INPUT 3   #删除编号为3的规则
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容