如何解决可以使用Traefik通过SSL代理PostgreSQL吗? 动机配置调试 SNI不兼容?
动机
当尝试使用Let's Encrypt通过SSL通过Traefik代理PostgreSQL时,我遇到了一个问题。 我做了一些研究,但没有充分记录,因此我想确认我的观察结果,并把记录留给所有遇到这种情况的人。
配置
我使用最新版本的PostgreSQL v12和Traefik v2。我想使用Let's Encrypt通过TLS在tcp://example.com:5432
-> tcp://postgresql:5432
上构建一个纯TCP流。
Traefik服务的配置如下:
version: "3.6"
services:
traefik:
image: traefik:latest
restart: unless-stopped
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./configuration/traefik.toml:/etc/traefik/traefik.toml:ro"
- "./configuration/dynamic_conf.toml:/etc/traefik/dynamic_conf.toml"
- "./letsencrypt/acme.json:/acme.json"
networks:
- backend
ports:
- "80:80"
- "443:443"
- "5432:5432"
networks:
backend:
external: true
使用静态设置:
[entryPoints]
[entryPoints.web]
address = ":80"
[entryPoints.web.http]
[entryPoints.web.http.redirections.entryPoint]
to = "websecure"
scheme = "https"
[entryPoints.websecure]
address = ":443"
[entryPoints.websecure.http]
[entryPoints.websecure.http.tls]
certresolver = "lets"
[entryPoints.postgres]
address = ":5432"
PostgreSQL服务配置如下:
version: "3.6"
services:
postgresql:
image: postgres:latest
environment:
- POSTGRES_PASSWORD=secret
volumes:
- ./configuration/trial_config.conf:/etc/postgresql/postgresql.conf:ro
- ./configuration/trial_hba.conf:/etc/postgresql/pg_hba.conf:ro
- ./configuration/initdb:/docker-entrypoint-initdb.d
- postgresql-data:/var/lib/postgresql/data
networks:
- backend
#ports:
# - 5432:5432
labels:
- "traefik.enable=true"
- "traefik.docker.network=backend"
- "traefik.tcp.routers.postgres.entrypoints=postgres"
- "traefik.tcp.routers.postgres.rule=HostSNI(`example.com`)"
- "traefic.tcp.routers.postgres.tls=true"
- "traefik.tcp.routers.postgres.tls.certresolver=lets"
- "traefik.tcp.services.postgres.loadBalancer.server.port=5432"
networks:
backend:
external: true
volumes:
postgresql-data:
看来我的Traefik配置正确。日志中一切正常,仪表板中的所有部分都标记为成功(无警告,无错误)。因此,我对上面的Traefik配置充满信心。完整的流程是关于:
EntryPoint(':5432') -> HostSNI(`example.com`) -> TcpRouter(`postgres`) -> Service(`postgres@docker`)
但是,它可能在PostgreSQL方面有局限性。
调试
问题是我无法连接PostgreSQL数据库。我总是收到超时错误。
我已检查PostgreSQL是否在正确监听(超时错误的主要原因):
# - Connection Settings -
listen_addresses = '*'
port = 5432
我检查了是否可以在主机(容器外部)上连接PostgreSQL:
psql --host 172.19.0.4 -U postgres
Password for user postgres:
psql (12.2 (Ubuntu 12.2-4),server 12.3 (Debian 12.3-1.pgdg100+1))
Type "help" for help.
postgres=#
因此,我知道PostgreSQL正在其容器外部进行侦听,因此Traefik应该能够绑定该流。 我还检查了外部traefik是否可以到达服务器:
sudo tcpdump -i ens3 port 5432
tcpdump: verbose output suppressed,use -v or -vv for full protocol decode
listening on ens3,link-type EN10MB (Ethernet),capture size 262144 bytes
09:02:37.878614 IP x.y-z-w.isp.com.61229 > example.com.postgresql: Flags [S],seq 1027429527,win 64240,options [mss 1452,nop,wscale 8,sackOK],length 0
09:02:37.879858 IP example.com.postgresql > x.y-z-w.isp.com.61229: Flags [S.],seq 3545496818,ack 1027429528,options [mss 1460,sackOK,wscale 7],length 0
09:02:37.922591 IP x.y-z-w.isp.com.61229 > example.com.postgresql: Flags [.],ack 1,win 516,length 0
09:02:37.922718 IP x.y-z-w.isp.com.61229 > example.com.postgresql: Flags [P.],seq 1:9,length 8
09:02:37.922750 IP example.com.postgresql > x.y-z-w.isp.com.61229: Flags [.],ack 9,win 502,length 0
09:02:47.908808 IP x.y-z-w.isp.com.61229 > example.com.postgresql: Flags [F.],seq 9,length 0
09:02:47.909578 IP example.com.postgresql > x.y-z-w.isp.com.61229: Flags [P.],seq 1:104,ack 10,length 103
09:02:47.909754 IP example.com.postgresql > x.y-z-w.isp.com.61229: Flags [F.],seq 104,length 0
09:02:47.961826 IP x.y-z-w.isp.com.61229 > example.com.postgresql: Flags [R.],seq 10,ack 104,win 0,length 0
所以,我想知道为什么连接无法成功。 Traefik和PostgreSQL之间一定有问题。
SNI不兼容?
即使删除了TLS配置,问题仍然存在,因此我不希望TLS成为此问题的根源。
然后我进行了搜索,发现很少有类似问题的帖子:
据我了解,PostgreSQL的SSL协议是自定义协议,目前不支持SNI,并且可能永远不支持它。如果正确,它将确认Traefik目前无法代理PostgreSQL,这是一个限制。
通过写这篇文章,我想确认我的观察,同时在Stack Overflow上留下一个可见的记录,以供面临相同问题并寻求帮助的任何人使用。然后我的问题是:是否可以使用Traefik代理PostgreSQL?
更新
有趣的观察,如果使用HostSNI('*')
并让我们加密:
labels:
- "traefik.enable=true"
- "traefik.docker.network=backend"
- "traefik.tcp.routers.postgres.entrypoints=postgres"
- "traefik.tcp.routers.postgres.rule=HostSNI(`*`)"
- "traefik.tcp.routers.postgres.tls=true"
- "traefik.tcp.routers.postgres.tls.certresolver=lets"
- "traefik.tcp.services.postgres.loadBalancer.server.port=5432"
在Dashboard中,一切都被标记为成功,但我们当然不能对通配符*
执行DNS挑战,它会在日志中抱怨:
time="2020-08-12T10:25:22Z" level=error msg="Unable to obtain ACME certificate for domains \"*\": unable to generate a wildcard certificate in ACME provider for domain \"*\" : ACME needs a DNSChallenge" providerName=lets.acme routerName=postgres@docker rule="HostSNI(`*`)"
当我尝试以下配置时:
labels:
- "traefik.enable=true"
- "traefik.docker.network=backend"
- "traefik.tcp.routers.postgres.entrypoints=postgres"
- "traefik.tcp.routers.postgres.rule=HostSNI(`*`)"
- "traefik.tcp.routers.postgres.tls=true"
- "traefik.tcp.routers.postgres.tls.domains[0].main=example.com"
- "traefik.tcp.routers.postgres.tls.certresolver=lets"
- "traefik.tcp.services.postgres.loadBalancer.server.port=5432"
该错误从日志中消失,并且在两种设置中,仪表板似乎都正常,但流量未路由到PostgreSQL(超时)。无论如何,从配置中删除SSL会使流程完成(并且不安全):
labels:
- "traefik.enable=true"
- "traefik.docker.network=backend"
- "traefik.tcp.routers.postgres.entrypoints=postgres"
- "traefik.tcp.routers.postgres.rule=HostSNI(`*`)"
- "traefik.tcp.services.postgres.loadBalancer.server.port=5432"
然后可以连接PostgreSQL数据库:
time="2020-08-12T10:30:52Z" level=debug msg="Handling connection from x.y.z.w:58389"
解决方法
我正在使用Traefik代理PostgreSQL,所以答案是肯定的。但是我没有使用TLS,因为我的设置有些不同。首先,如果PostgreSQL不支持SNI,那么我建议尝试修改标签,尤其是HostSNI规则:
"traefik.tcp.routers.postgres.rule=HostSNI(`*`)"
那是说:忽略SNI,只是将指定入口点中的任何名称视为有效。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。