是否可以使用 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?的主要内容,如果未能解决你的问题,请参考以下文章

Traefik:无法使用摘要身份验证登录服务

traefik使用etcd存储配置--实例演示

使用 Traefik 时如何使用 Nginx 和 Django Gunicorn 提供静态内容

traefik代理背后的Gitlab“网关超时”

traefik 配置自动申请ssl免费证书

需要 SSL 来验证 traefik 后面的 keycloak