我给架构师建议安装两个traefik
“ 写给自己看,说给别人听。你好,这是think123的第82篇原创文章”
之前有说过,我用traefik做网关,无论是内外网请求都会经过网关。
但是我们有一部分API是只有内网会用,为了安全,我们要保证这些内网的API只有内网可以访问到。
但是由于之前的设置,这些API是匿名访问的,如果修改为需要权限,那么需要其他依赖于我们服务的team来做对应的修改,是由于一些原因,这条路走不通。
摆在我面前的就只有一条路,那就是安装两个traefik,一个对内,一个对外。安装两个traefik很容易,由于traefik安装采用的是DaemonSet的方式,所以两个traefik的访问端口必须不同
--entryPoints.traefik.address=:8080
比如internet(外网)的traefik的端口是8080,那么internal的traefik就可以是9090。
traefik安装请看我之前的文章--在 kubernetes 中安装 traefik2。
然后就可以啦?当然没有那么容易,为了实现下面的效果,
对应的IngressRoute也同样需要修改
以前的IngressRoute如下
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: my-service-traefik-ingress
spec:
entryPoints:
- websecure
routes:
- match: Host(`think123.com`) && PathPrefix(`/api/anon/health`, `/api/anon/article`)
kind: Rule
services:
- name: my-service
port: 9000
我想要/api/anon/article
这个request path只能内网访问,那么需要将配置修改如下
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: my-service-traefik-ingress
spec:
entryPoints:
- websecure
routes:
- match: Host(`think123.com`) && PathPrefix(`/api/anon/health`)
kind: Rule
services:
- name: my-service
port: 9000
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: my-service-internal-traefik-ingress
spec:
entryPoints:
- websecure
routes:
- match: Host(`think123-internal.com`) && PathPrefix(`/api/anon/article`)
kind: Rule
services:
- name: my-service
port: 9000
看上去没问题了,配置好LoadBalance之后,访问对应的域名就可以了。但是这样还存在一个问题
curl -H 'Host:www.think123-internal.com' -X GET https://www.think123.com/api/anon/article
通过指定header的方式,我们发现它匹配了 my-service-internal-traefik-ingress
这个ingressRoute,最终返回了只有内网用户才能访问的API。
为啥我做了一大堆,安全性问题仍然存在?
实际上是因为无论是内网的traefik还是只监控外网的traefik,它们两个都会加载所有的IngressRoute,所以就有了上面的那个问题。
必须想个办法让它们只加载属于自己的IngressRoute,我去翻阅了下Traefik的文档,发现有3个参数可以用。
第一个是可以指定要监视哪些namespaces,则traefik就只处理它监控下的namespaces请求
--providers.kubernetescrd.namespaces=default,production
第二个是给资源打上label,但是只对Traefik Custom Resources起作用,对Kubernetes的Service,Secrets这些不起作用
--providers.kubernetescrd.labelselector="app=traefik"
第三个是指定ingressClass
--providers.kubernetescrd.ingressclass=traefik-internal
我们需要在对应的资源上加上kubernetes.io/ingress.class
注解,用于标识要处理的资源对象。
由于我们处理的是同一个namespace下的资源,所以namespace的方式不合适。
而对于第二种或者第三种而言,由于我们对IngressRoute进行处理,所以都是合适的。但是考虑到其他环境存在nginx和traefik共存的情况,我们决定使用ingressClass的方式,实际上nginx也是建议我们这么做的。
最开始我的修改是这样的,首先在DaemonSet模板中添加ingressClass设定
--providers.kubernetescrd.ingressclass=$(ingress_class)
接下来只需要在不同的traefik中配置不同的环境变量(configMap)即可。比如internet traefik设置ingress_class=traefik-internet
,而intranet traefik该值为空,它被当成默认的ingress controller。
然后在相应的ingressRoute中指定 kubernetes.io/ingress.class: traefik-internet
就可以了。其他IngressRoute不指定该注解。
看上去很棒,给自己点个赞。
可是想象是美好的,现实却给了我无情一击。这样修改完成之后,我发现当我访问 https://www.think123-internal.com/api/anon/article
这个api的时候,没有traefik来处理我的请求。
internal traefik不是我的默认traefik吗?我看官方文档还这样写的呀
If the parameter is non-empty, only resources containing an annotation with the same value are processed.
Otherwise, resources missing the annotation, having an empty value, or the value traefik are processed
难道这个空不是空串,而是不指定的意思?真是不讲武德!
所以,我只能换一种修改方式,在DaemonSet的模板中不指定ingressClass,而是在通过kustomize来动态添加。kustomize的使用请参考我的这篇文章
注意,两个traefik只有些许配置不一样,所以,我们安装的时候一般指定一个模板,动态的值则由不同的traefik来指定
patches:
- target:
version: v1beta2
kind: DaemonSet
name: traefik
patch: |-
- op: add
path: /spec/template/spec/containers/0/args/-
value: --providers.kubernetescrd.ingressclass=traefik-internet
在我的配置中,internet traefik名字叫做traefik,internal traefik名字叫做traefik-internal,区分内外网是逻辑区分,而不仅仅是通过名字。
我们只在internet traefik中指定ingressClass,intranet traefik则不指定。
同样的,IngressRoute也需要动态修改的。
# kustomization.yaml
patchesJson6902:
- target:
group: traefik.containo.us
version: v1alpha1
kind: IngressRoute
name: my-service-traefik-ingress
path: patches/patch-internet-ingressroute.yaml
# patch-internet-ingressroute.yaml
- op: add
path: /metadata/annotations
value:
kubernetes.io/ingress.class: traefik-internet
这样,internet traefik就只会处理 kubernetes.io/ingress.class: traefik-internet
的ingressRoute了。之前的安全性问题也就不存在了。
参考文档
https://kubernetes.github.io/ingress-nginx/user-guide/multiple-ingress/ https://doc.traefik.io/traefik/providers/kubernetes-crd/#ingressclass