EKS Ingress+ALB如何突破白名单数量限制?

一、前言

继上篇文章所言,我们在AWS EKS中使用 Ingress+ALB 对外暴露服务,在上篇文章中我测试了Ingress+NLB方案并取得了成功,但对当前环境改动较大(修改DNS解析、域名和服务的绑定全部都要切到新Ingress上)最终还是作为备选方案。

二、方案思考

虽然 Ingress+NLB 方案只能作为备选,但我们团队也提出了其它方案的思考:

  • ALB是否支持插件,这样我们只需要在插件中配置白名单,如果是允许的来源IP就返回给ALB放行;
  • AWS WAF是一项Web应用程序防火墙服务,可以调研下看看是否可以用在当前环境;

带着这样两个问题,又重新翻阅了AWS的文档,然而并没有找到ALB是否支持插件的相关说明;于是将目标转向了AWS WAF服务,在对WAF服务进行测试中发现WAF只能绑定ALB并不支持ALB后端的虚拟主机(规则),在来看看AWS WAF服务功能说明:AWS WAF是一项Web应用程序防火墙服务,可让您监视转发到Amazon API Gateway API,Amazon CloudFront发行版或应用程序负载均衡器的Web请求。您可以根据指定的条件(例如,请求源的IP地址)来保护那些资源。 WAF这个服务可以看作是Web应用程序防火墙,它位于ALB等资源的前端,可以用它来保护我们的API Gateway、ALB、AWS AppSync等资源可以用它来防范DDoS攻击、SQL注入等。

既然以上两个方案都行不通,那有没有其它可行的方案呢?

三、灵机一动

这时,我从之前的 Ingress+NLB 方案中有了启发,那需要怎么进行调整呢,通过下图我们一起来看看:

这是调整前服务暴露方式

图片1

这是调整后的服务暴露方式

图片2

首先:有服务A.service.com和B.service.com 两个服务,我们需要对B.service.com配置白名单访问,由于ALB只支持5个白名单配置,图1 中当白名单超过5个时便达到了瓶颈;

同样:在图2 中我们只需要将配置白名单的服务B.service.com从ALB转发给自建Ingress 由该Ingress服务进行白名单审核之后将流量再转发给后端的service-B,我们只需要在自建Ingress中配置白名单,而且自建Ingress内部是nginx服务理论上支持的白名单数量无限。

这样一来,就可以完美解决ALB白名单不够用的问题了。

那么,具体要怎么实现呢

四、实施过程

4.1 部署Ingress+ALB

这里已经部署好了 EKS Ingress+ALB 就不在进行阐述了,可以参考AWS官方文档进行部署

4.2 部署自建Ingress

自建Ingress我选择的是nginx-ingress,部署和上篇文章差不多但,但在部署Service时略有差异,具体我们一起来看下:

4.2.1 部署deployment

首先从GitHub克隆下来代码,切换到最新的tag

1
2
3
4
5
6
$ git clone https://github.com/kubernetes/ingress-nginx.git
$ cd ingress-nginx/
$ git checkout nginx-0.30.0
$ git branch
* (HEAD detached at nginx-0.30.0)
master

接下来部署deploy/static目录下的mandatory.yaml ,生产环境建把pod副本数设置为2以上

1
2
3
4
$ cd deploy/static/
$ ls
configmap.yaml namespace.yaml rbac.yaml
mandatory.yaml provider/ with-rbac.yaml

修改时区

1
2
3
4
$ vim mandatory.yaml
env:
- name: TZ
value: Asia/Shanghai

如果有需要特殊调整(例如:修改Ingress健康检测路径与ALB一致),可以参考https://kubernetes.github.io/ingress-nginx/user-guide/cli-arguments/

我这里是测试环境,将mandatory.yaml文件的namespace都改成了qa

1
$ kubectl apply -f mandatory.yaml

部署完后查看deployment部署状态

1
2
3
4
5
$ kubectl get pod -n qa | grep ingress
nginx-ingress-controller-7cccb55579-4rqvv 1/1 Running 0 25h

$ kubectl get deployment -n qa | grep ingress
nginx-ingress-controller 1/1 1 1 25h

如果有多namespace部署Ingress的需求,需要把ClusterRoleBindingname 修改为不同名称(否则容器会启动失败),我这里将原来的nginx-ingress-clusterrole-nisa-binding改为了nginx-ingress-clusterrole-nisa-binding-qa 最好是ClusterRolename 也一起修改。

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: nginx-ingress-clusterrole-qa

…………忽略部分内容…………

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: nginx-ingress-clusterrole-nisa-binding-qa
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: nginx-ingress-clusterrole-qa
subjects:

  • kind: ServiceAccount
    name: nginx-ingress-serviceaccount
    namespace: qa

4.2.2 部署service

在上篇文章中我们创建Service是为了让它自动去创建相应的NLB服务然后自动和Service关联上。

但在这里,我调整了架构(将ALB流量转发到自建Ingress),所以要把Service创建NLB部分的配置去掉,让我们自建的Ingress对接ALB的转发规则。后面我们只需要ALB把客户端的真实IP传过来就可以做白名单限制了。

修改service.yaml,或者直接使用没有云服务商配置的service.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ cd provider/baremetal/
$ cat service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: qa
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
- name: https
port: 443
targetPort: 443
protocol: TCP
selector:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx

---

$ kubectl apply -f service-nodeport.yaml

部署完后查看service状态

1
2
$ kubectl get service -n qa| grep ingress
ingress-nginx NodePort 172.20.103.222 <none> 80:30971/TCP,443:32039/TCP 25h

4.3 测试白名单

4.3.1 部署测试服务

部署nginx服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
$ cat nginx-deployment-service.yaml

kind: Deployment
apiVersion: apps/v1
metadata:
name: nginx-deployment
namespace: qa
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- name: nginx
image: 'nginx:1.16.0'
ports:
- containerPort: 80
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: IfNotPresent
restartPolicy: Always
terminationGracePeriodSeconds: 30
dnsPolicy: ClusterFirst
nodeSelector:
running_env: qa
securityContext: {}
schedulerName: default-scheduler
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%
revisionHistoryLimit: 10
progressDeadlineSeconds: 600
---
kind: Service
apiVersion: v1
metadata:
name: nginx-service
namespace: qa
labels:
app: nginx
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: nginx

$ kubectl apply -f nginx-deployment-service.yaml

4.3.2 配置Ingress转发规则

这里需要配置两个Ingress规则(AWS Ingress和自建Ingress)

  • AWS Ingress:将需要配置白名单的服务转发给自建Ingress

  • 自建Ingress:配置白名单,将流量转发给后端服务

配置 AWS Ingress规则

1
2
3
4
5
6
7
8
9
- host: au-nginx.qa.service.com
http:
paths:
- backend:
serviceName: ssl-redirect
servicePort: use-annotation
- backend:
serviceName: ingress-nginx
servicePort: 80

只需要将原先backend转发到服务的Service改为我们部署的ingress-nginx Service即可

配置自建Ingress规则1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
kind: Ingress
apiVersion: extensions/v1beta1
metadata:
name: au-nginx.qa.service.com
namespace: qa
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/server-snippet: |
set_real_ip_from 10.0.0.0/8; #上一级代理的IP地址或者IP段,可以写多行。
real_ip_header X-Forwarded-For; #从哪个header头检索出需要的IP地址。
real_ip_recursive on; #递归的去除所配置中的可信IP。
access_log /var/log/nginx/au-nginx.access.log upstreaminfo;
nginx.ingress.kubernetes.io/whitelist-source-range: 12.96.203.241/32
spec:
rules:
- host: au-nginx.qa.service.com
http:
paths:
- backend:
serviceName: nginx-service
servicePort: 80
  • nginx.ingress.kubernetes.io/server-snippet:在nginx 的 server 配置块中添加自定义配置;
  • nginx.ingress.kubernetes.io/whitelist-source-range:允许的客户端IP源范围,该值是逗号分隔的CIDR列表,例如10.0.0.0/24,172.10.0.1

如果不知道上一级代理的IP地址或者IP段,可以使用下面第二种方式

配置自建Ingress规则2

修改 nginx-igress 的 configmap 配置 nginx-configuration,增加以下配置

1
2
3
4
data:
compute-full-forwarded-for: 'true'
forwarded-for-header: X-Forwarded-For
use-forwarded-headers: 'true'

该配置对所在 namespace 下的所有 ingress 都生效,所以在该 namespace 下的 ingress 只需要配置允许访问的白名单IP即可

然后配置ingress规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
kind: Ingress
apiVersion: extensions/v1beta1
metadata:
name: au-nginx.qa.service.com
namespace: qa
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/server-snippet: |
access_log /var/log/nginx/au-nginx.access.log upstreaminfo;
nginx.ingress.kubernetes.io/whitelist-source-range: 12.96.203.241/32
spec:
rules:
- host: au-nginx.qa.service.com
http:
paths:
- backend:
serviceName: nginx-service
servicePort: 80

以上便是架构调整部分的具体实施步骤(实际就修改了两个Ingress配置),现在我们来验证服务是否可以访问,以及白名单是否生效。

4.3.3 测试白名单配置

以上配置好后,当我们在白名单所在服务器上访问时

1
2
3
4
5
6
7
8
9
10
11
12
[root@host ~]# curl -Ik http://au-nginx.qa.service.com
HTTP/1.1 200 OK
Date: Fri, 28 May 2021 09:02:58 GMT
Content-Type: text/html
Content-Length: 1241
Connection: keep-alive
Server: nginx/1.17.8
Vary: Accept-Encoding
Last-Modified: Wed, 26 May 2021 10:47:38 GMT
ETag: "60ae274a-4d9"
Cache-Control: no-store, no-cache, private
Accept-Ranges: bytes

在其它服务器上访问时

1
2
3
4
5
6
7
$ curl -Ik http://au-nginx.qa.service.com
HTTP/1.1 403 Forbidden
Date: Fri, 28 May 2021 09:17:05 GMT
Content-Type: text/html
Content-Length: 153
Connection: keep-alive
Server: nginx/1.17.8

到这里已经把原先ALB的白名单移到了我们自建的Ingress上,以后再也不用为ALB的白名单配额不够而发愁了。

-------------本文结束感谢您的阅读-------------