是否可以使用 Traefik 通过 SSL 代理 PostgreSQL?
Posted
技术标签:
【中文标题】是否可以使用 Traefik 通过 SSL 代理 PostgreSQL?【英文标题】:Is it possible to use Traefik to proxy PostgreSQL over SSL? 【发布时间】:2020-11-30 22:14:04 【问题描述】:动机
我在尝试使用 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,nop,nop,sackOK], length 0
09:02:37.879858 IP example.com.postgresql > x.y-z-w.isp.com.61229: Flags [S.], seq 3545496818, ack 1027429528, win 64240, options [mss 1460,nop,nop,sackOK,nop,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, ack 1, win 516, 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, ack 1, win 516, length 0
09:02:47.909578 IP example.com.postgresql > x.y-z-w.isp.com.61229: Flags [P.], seq 1:104, ack 10, win 502, length 103
09:02:47.909754 IP example.com.postgresql > x.y-z-w.isp.com.61229: Flags [F.], seq 104, ack 10, win 502, 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 是这个问题的根源。
然后我搜索了一下,发现很少有类似问题的帖子:
Introducing SNI in TLS handshake for SSL connections Traefik 2.0 TCP routing for multiple DBs;据我了解,PostgreSQL 的 SSL 协议是自定义协议,暂时不支持SNI,可能永远不会支持。如果正确,它将确认 Traefik 目前无法代理 PostgreSQL,这是一个限制。
通过写这篇文章,我想确认我的观察结果,同时在 Stack Overflow 上留下一个可见的记录,给任何面临同样问题并寻求帮助的人。那么我的问题是:是否可以使用 Traefik 代理 PostgreSQL?
更新
有趣的观察,如果使用 HostSNI('*')
和 Let's Encrypt:
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"
仪表板中的所有内容都标记为成功,但 Let's Encrypt 不能执行通配符 *
的 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"
【问题讨论】:
PostgreSQL 根本不使用 http,因为...它不是 Web 服务器。那么 - 这个“traefik”东西可以像 haproxy 一样路由普通的 tcp 吗? @RichardHuxton,据我了解:是的,如果两个规则都存在,“traefik”确实完全支持 HTTP 之前的 TCP(请参阅docs.traefik.io/routing/routers/#configuring-tcp-routers)。在我的配置中,您可以检查这是纯 TCP 而不是 HTTP。 恐怕我根本不知道 traefik,但是 postgres 路由器上的 TLS 东西对我来说没有意义。如果你只是转发 tcp,你为什么要摆弄它? @RichardHuxton,好吧,我不想在网络上清楚地公开 postgresql 端口(在通过此流程发送所有凭据之后),我想从 postgresql 然后从 Traefik 撤回 TLS 配置Let's Encrypt 可以自动接管它。另一方面,即使我删除了 TLS 配置,流量也不会以任何一种方式路由,这似乎与 SNI 问题有关。 @khashashin,还没有。我认为从现在开始唯一的解决方法是像往常一样加密 PSQL 连接并设置 Traefik 让 SSL 通过。但是你失去了 LE 的好处。 【参考方案1】:我正在使用 Traefik 代理 PostgreSQL,所以答案是肯定的。但我没有使用 TLS,因为我的设置有点不同。首先,如果PostgreSQL不支持SNI,那么我建议尝试修改标签,特别是HostSNI规则到这个:
"traefik.tcp.routers.postgres.rule=HostSNI(`*`)"
也就是说:忽略 SNI,只取指定入口点中的任何名称为有效名称。
【讨论】:
感谢您的分享,我确实测试了它,但启用了 SSL 并且它不起作用。但是如果删除 SSL,那么流程就完成了。我已经相应地更新了我的帖子。如果您有其他想法来实现 SSL,如果没有,我将需要求助于 PostgreSQL SSL 配置和 pass_trough 开关。最好的问候, 对于 TLS,我认为问题在于让我们加密,因为您没有 SNI,所以没有域名就无法获得证书......但似乎有可能定义域名使用路由器,请参阅docs.traefik.io/routing/routers/#domains_1 - 也许您需要使用通配符域名进行 DNS 质询 感谢您的帮助,尚未工作,但正在取得进展。我确实尝试过域,关于 * 的错误消失了,但 Traefik 不再路由到 PostgreSQL。我已经更新了我的帖子以反映测试。以上是关于是否可以使用 Traefik 通过 SSL 代理 PostgreSQL?的主要内容,如果未能解决你的问题,请参考以下文章