背景

在Traefik中,Fail2Ban是一个非常强大的插件,可以轻松实现IP黑名单,IP白名单,IP封禁等操作。我们可以根据不同的需求,选择封禁的时长。

比如:我想针对1分钟内请求100次以上的IP封禁1小时。对于比DDOS小很多的某些攻击,可以有效防止。(如果处于DDOS,大部分都是在服务商那就已经将云服务拉入小黑屋了,
此时服务流量基本进不来了,所以这里只是针对小攻击的处理方法,或者某些暴力爬虫等)

对于Fail2Ban的原理,你可以参考Github,基本原理是时间窗口,go语言写这类工具再对接Kubernetes有天生的优势,所以这里就不再赘述了。

环境

traefik: v2.9.10
kubernetes: v1.24.6+k3s1
Fail2Ban: v0.7.1

安装

在K3S中,我们已经内置了Traefik,所以如果想安装插件,我们需要对内置的Traefik进行改造,添加插件。

由于K3S会周期性的执行 /var/lib/rancher/k3s/server/manifests 目录下的所有yaml,而如果我们修改了默认的traefik.yaml,K3S会自动恢复默认文件,所以我们这里需要新建一个yaml文件,用HelmChartConfig的方式修改traefik安装的配置即可。

创建配置文件 nano /var/lib/rancher/k3s/server/manifests/traefik-config.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
additionalArguments:
- "--experimental.plugins.fail2ban.modulename=github.com/tomMoulard/fail2ban"
- "--experimental.plugins.fail2ban.version=v0.7.1"
- "--providers.kubernetescrd=true"
- "--providers.kubernetesingress=true"
experimental:
plugins:
enabled: true
providers:
kubernetesCRD:
allowCrossNamespace: true
image:
name: "rancher/mirrored-library-traefik"
tag: "2.9.10"
  • additionalArguments用于添加插件的启动参数,在启动Traefik时,他会自动下载,需要注意的是,他是自主访问github下载的,如果网络不通畅,下载插件会失败,但Traefik会正常启动,导致所有使用了fail2ban的Middleware全部失效,所以启动后还要确保有没有fail2ban的成功日志信息
  • experimental用于启动插件(新版本好像不配置也可以)
  • allowCrossNamespace允许跨命名空间调用(比如我在A命名空间创建了有关fail2ban的Middleware,但我想在B命名空间同样使用这个Middleware,所以需要允许跨命名空间调用)
  • 因为自带的Traefik版本比较低,所以指定下新版本的镜像,亲测在2.10版本中无法使用,所以只能升级到2.9版本的,在自带的2.6版本同样无法使用,会报plugin: unknown plugin type: sablier的错误,重启也无效

additionalArguments中插件的CLI配置,可以参考每个插件右上角的Install pLugin,里面写了具体的配置,插件地址是:https://plugins.traefik.io/plugins/628c9ebcffc0cd18356a979f/fail2-ban

在配置好之后,我们等待一会,K3S会自动执行该配置文件。

接下来使用 kubectl get jobs -n kube-system 查看下Helm job是否触发

1
2
3
4
root@m3:/var/lib/rancher/k3s/server/manifests# kubectl get jobs -n kube-system
NAME COMPLETIONS DURATION AGE
helm-install-traefik 1/1 7s 13h
helm-install-traefik-crd 1/1 8s 92d

可以看到,已经执行完了,接下来看下Traefik是否正常重启,使用kubectl describe pods -n kube-system traefik-xxxxx,启动参数是否含有上面配置的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Containers:
traefik:
Container ID: docker://6f058c4329b788f51bbea5f6e8debf15217f8067647
Image: rancher/mirrored-library-traefik:2.9.10
Image ID: docker-pullable://rancher/mirrored-library-traefik@sha256:aaec134463b277ca7aa4f888fe724a
Ports: 9100/TCP, 9000/TCP, 8000/TCP, 8443/TCP
Host Ports: 0/TCP, 0/TCP, 0/TCP, 0/TCP
Args:
...
--experimental.plugins.fail2ban.modulename=github.com/tomMoulard/fail2ban
--experimental.plugins.fail2ban.version=v0.7.1
--providers.kubernetescrd=true
--providers.kubernetesingress=true
...

查看Traefik的日志,看看是否有fail2ban的日志信息

1
2024/04/23 14:05:33 Plugin: FailToBan is up and running

接下来我们只需要创建一个Middleware,然后就可以使用Fail2Ban了。

使用

创建Middleware,规则为1分钟内请求100次以上的IP封禁1小时

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: api-protect
namespace: prod-app
spec:
plugin:
fail2ban:
rules:
bantime: 1h
enabled: 'true'
findtime: 1m
maxretry: '100'

这里参数的配置请参考插件README即可,创建好Middle后,我们需要将流量拦截到这个Middleware上。

在你想要配置的Ingress中,添加如下信息:

1
2
3
4
5
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
traefik.ingress.kubernetes.io/router.middlewares: prod-app-api-protect@kubernetescrd

绑定规则:命名空间-中间件名称@kubernetescrd

如果有多个则用英文逗号分隔(traefik.ingress.kubernetes.io/router.middlewares: prod-app-api-protect1@kubernetescrd,prod-app-api-protect2@kubernetescrd)

比如,我想先从http转https,再拦截,就需要两个middleware。

在Traefik的Dashboard中,能看到对应的Middleware和绑定的pod即可,如下图所示:

测试

我们可以把条件改苛刻些测试下,比如5秒内有两次请求则ban1分钟,请求如果返回403或者5xx则证明ban成功了(想反5xx的看我下一篇文章)

1
2
3
4
5
6
7
8
spec:
plugin:
fail2ban:
rules:
bantime: 1m
enabled: 'true'
findtime: 5s
maxretry: '2'

Fail2Ban基本上满足了我对于某些恶意请求的拦截,在安装的时候经常发现不起作用,后来发现是Traefik版本的问题,2.0~2.6安装插件那会,好像还需要去官网注册token,现在已经摒弃了,取而代之的是Traefik的Hub。

在后续的文章中,我会玩一玩Traefik插件中星星最多的Sablier插件,它能根据流量自动启动pod,没人访问后自动关闭pod。我的测试环境刚好只在我开发的时候才用得到,不用的时候还占用内存(没错,说的就是你这个JVM占好多内存),看我后面的文章吧~