jla*_*rcy 9 postgresql proxy tcp sni traefik
尝试使用 Let's Encrypt 通过 SSL 使用 Traefik 代理 PostgreSQL 时,我遇到了一个问题。我做了一些研究,但没有很好的记录,我想确认我的观察并给每个面临这种情况的人留下记录。
我使用最新版本的 PostgreSQL v12 和 Traefik v2。我想使用 Let's Encrypt从->通过 TLS构建纯 TCP 流。tcp://example.com:5432tcp://postgresql:5432
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
Run Code Online (Sandbox Code Playgroud)
使用静态设置:
[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"
Run Code Online (Sandbox Code Playgroud)
PostgreSQL 服务配置如下:
[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"
Run Code Online (Sandbox Code Playgroud)
看来我的 Traefik 配置是正确的。日志中一切正常,仪表板中的所有部分都标记为成功(无警告,无错误)。所以我对上面的 Traefik 配置充满信心。完整的流程是关于:
EntryPoint(':5432') -> HostSNI(`example.com`) -> TcpRouter(`postgres`) -> Service(`postgres@docker`)
Run Code Online (Sandbox Code Playgroud)
但是,它在 PostgreSQL 方面可能有一个限制。
问题是我无法连接 PostgreSQL 数据库。我总是收到超时错误。
我已经检查过 PostgreSQL 是否正在正常监听(超时错误的主要原因):
# - Connection Settings -
listen_addresses = '*'
port = 5432
Run Code Online (Sandbox Code Playgroud)
我检查了我可以在主机上(容器外)连接 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:
Run Code Online (Sandbox Code Playgroud)
因此,我知道 PostgreSQL 正在其容器外侦听,因此 Traefik 应该能够绑定流。我还检查了外部 traefik 可以访问服务器:
EntryPoint(':5432') -> HostSNI(`example.com`) -> TcpRouter(`postgres`) -> Service(`postgres@docker`)
Run Code Online (Sandbox Code Playgroud)
所以,我想知道为什么连接不能成功。Traefik 和 PostgreSQL 之间一定有问题。
即使我去掉了TLS配置,问题依然存在,所以我不指望TLS是这个问题的根源。
然后我搜索,发现很少有关于类似问题的帖子:
据我了解,PostgreSQL 的 SSL 协议是自定义协议,暂时不支持SNI,可能永远不会支持。如果正确,将确认 Traefik 暂时无法代理 PostgreSQL,这是一个限制。
通过写这篇文章,我想确认我的观察,同时在 Stack Overflow 上留下一个可见的记录给任何面临同样问题并寻求帮助的人。那么我的问题是:是否可以使用 Traefik 来代理 PostgreSQL?
更新
有趣的观察,如果使用HostSNI('*')和让我们加密:
# - Connection Settings -
listen_addresses = '*'
port = 5432
Run Code Online (Sandbox Code Playgroud)
一切都在仪表板中标记为成功,但当然 Let's Encrypt 无法执行通配符的 DNS 挑战*,它在日志中抱怨:
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=#
Run Code Online (Sandbox Code Playgroud)
当我尝试以下配置时:
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
Run Code Online (Sandbox Code Playgroud)
错误从日志中消失,在两种设置中,仪表板似乎都可以,但流量未路由到 PostgreSQL(超时)。无论如何,从配置中删除 SSL 会使流程完整(并且不安全):
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"
Run Code Online (Sandbox Code Playgroud)
然后就可以连接PostgreSQL数据库了:
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(`*`)"
Run Code Online (Sandbox Code Playgroud)
小智 5
在此PR中,已将带有 STARTTLS 的 postgres 的 SNI 路由添加到 Traefik 中。现在 Treafik 将监听 postgres 发送的初始字节,如果要发起 TLS 握手(请注意,postgres TLS 请求首先创建为非 TLS,然后升级为 TLS 请求),Treafik 将处理握手,然后能够从 postgres 接收 TLS 标头,其中包含正确路由请求所需的 SNI 信息。这意味着您可以HostSNI("example.com")与 tls 一起使用来公开不同子域下的 postgres 数据库。
在写这个答案时,我能够使用图像v3.0.0-beta2(参考)