问题背景
在Cluster中,我需要将一个service通过nodePort方式暴露给外部使用。但是,又需要限制只允许白名单的ip访问。
不论是 nginx-ingress,ambassador,好像都没找到合适的方式能直接实现。
于是,只能通过外层的proxy中来控制tcp的流量转发及访问控制了。
Nginx TCP 转发
一般情况,我们是使用
1 | stream { |
然后,通过这种方式,接收方是无法获取客户请求的真实ip,需要透明代理的方式
1 | stream { |
但是问题是,直接这么配置,连接是不通的。还需要手动添加路由表才行。这样看来并不方便。
那么,还有什么方法呢?
Nginx proxy_protocol
查询资料,发现可以通过proxy_protocol的方式来获取到客户的真实ip
1 | # proxy server |
server接收到请求,通过proxy_protocol的方式,转发到内部的proxy。
内部proxy通过 map $remote_addr 方式,可以指定ip转发流量,达到需要的目的。
尝试解决实际的问题
需求:Cluster内部有个redis服务。需要对外网指定ip暴露访问。
建立redis服务的代理服务,redis-proxy (通过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# Deployment的yaml就不写了,重点是nginx的配置
# nginx.conf
http {
...
# 定义 default-svc 的应答
server {
listen 80;
location / {
return 200 "ok.";
}
}
}
stream {
upstream redis-svc {
server redis-svc:6379;
}
upstream default-svc {
server 127.0.0.1:80;
}
map $remote_addr $backend_svr {
x.x.x.x "redis-svc"; # xxxx是放开的ip,仅这个ip能通过公网访问redis
default "default-svc"; # 其余的走默认处理
}
server {
listen port proxy_protocol;
set_real_ip_from 0.0.0.0/0;
proxy_pass $backend_srv;
}
}将redis-proxy通过nodePort方式暴露
1
2
3
4
5
6
7
8
9
10
11
12
13
14apiVersion: v1
kind: Service
metadata:
name: redis-proxy
spec:
type: NodePort
externalTrafficPolicy: Local
ports:
- port: 6379
targetPort: 6379
protocol: TCP
nodePort: 30001
selector:
app: redis-proxyserver的nginx代理配置
1
2
3
4
5
6
7
8# nginx.conf
stream {
server {
listen 6379;
proxy_pass 127.0.0.1:30001; # nodePort的端口
proxy_protocol on;
}
}通过redis-cli进行测试 (redis是带密码的)
1
2
3redis-cli -h x.x.x.x -p 6379
x.x.x.x:6379> AUTH 1
(error) ERR invalid password在允许访问的机器上访问,能看到正常应答
1
2
3redis-cli -h x.x.x.x -p 6379
x.x.x.x:6379> AUTH 1
Error: Protocol error, got "H" as reply type byte在其他机器上请求,会看到协议错误,因为设置中是重定向到一个http的server中。
1
2curl http://x.x.x.x:6379
ok.%通过curl直接http访问,有正常的应答,符合预期,问题解决。
结语
实际上这种方式也非常不灵活,然而在 nginx-ingress/ambassador中,好像也看不到能简单实现满足这个需求的方式。http/https 可以轻易实现达到,但如果是tcp的流量,好像并没有方便的方式。有待学习研究。