You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DD-WRT路由下Docker+Traefik+Let's Encrypt暴露服务遇TLS握手错误

Traefik + Let's Encrypt (DuckDNS) TLS握手错误排查

背景

我打算把Tiny Tiny RSS、ownCloud这类自托管服务暴露到公网,所以选了Traefik做反向代理,搭配Let's Encrypt来获取HTTPS证书。在部署正式服务前,先在Odroid XU-4的Debian系统上搭了个测试栈,包含Traefik、Let's Encrypt和whoami容器。路由器刷了DD-WRT,已经把80、8080、443端口转发到Debian主机,DuckDNS的动态域名更新也由路由器搞定了。

用docker-compose build --no-cache && docker-compose up --build启动容器后,从外网访问http://my_duck_dns.duckdns.org/whoami/时,80端口能正常重定向到443,但一直报TLS握手错误。

Docker Compose配置

version: '3.6'
services:
  traefik:
    container_name: traefik
    image: traefik:1.6.1-alpine
    ports:
      - 80:80
      - 443:443
      - 8080:8080
    networks:
      - proxy
    environment:
      - DUCKDNS_TOKEN=my_duck_dns_token
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./traefik/traefik.toml:/traefik.toml
      - ./traefik/acme/acme.json:/etc/traefik/acme.json
      - ./log:/var/log/traefik
    labels:
      - traefik.enable=true
      - traefik.port=8080
      - traefik.frontend.rule=Host:my_duck_dns.duckdns.org
    restart: always
  whoami:
    container_name: whoami
    image: hypriot/rpi-whoami
    ports:
      - 8000
    networks:
      - proxy
    labels:
      - traefik.frontend.rule=Host:my_duck_dns.duckdns.org;PathPrefixStrip:/whoami/
      - traefik.frontend.entryPoints=https
      - traefik.docker.network=proxy
      - traefik.protocol=http
      - traefik.enable=true
      - traefik.port=8000
    restart: always
networks:
  proxy:
    name: proxy

Traefik.toml配置

debug = true
logLevel = "DEBUG"
checkNewVersion = true
defaultEntryPoints = ["http", "https"]

[proxy]
address = ":8080"

[entryPoints]
  [entryPoints.http]
  address = ":80"
    [entryPoints.http.redirect]
    entryPoint = "https"
  [entryPoints.https]
  address = ":443"
    [entryPoints.https.tls]

#[traefikLog]
# filePath = "/var/log/traefik/traefik.log"
# format = "json"
# logLevel = "DEBUG"

#[accessLog]
# filePath = "/var/log/traefik/access.log"
# format = "json"
# logLevel = "DEBUG"

[docker]
endpoint = "unix:///var/run/docker.sock"
domain = "my_duck_dns.duckdns.org"
exposedbydefault = false
watch = true

[acme]
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
email = "my_email_address@gmail.com"
storage = "/etc/traefik/acme.json"
entryPoint = "https"
acmeLogging = false
[acme.httpChallenge]
entryPoint = "http"
[acme.dnsChallenge]
provider = "duckdns"
delayBeforeCheck = 0

[[acme.domains]]
main = "my_duck_dns.duckdns.org"
sans = ["my_duck_dns.duckdns.org"]

错误日志

traefik | time="2018-05-18T16:15:05Z" level=debug msg="http: TLS handshake error from 151.58.32.33:65175: read tcp 172.27.0.3:443->154.47.32.66:64175: read: connection reset by peer"

完整DEBUG日志片段:

whoami | Listening on :8000
traefik | time="2018-05-18T18:30:55Z" level=info msg="Using TOML configuration file /traefik.toml"
traefik | time="2018-05-18T18:30:55Z" level=info msg="Traefik version v1.6.1 built on 2018-05-14_07:16:56PM"
traefik | time="2018-05-18T18:30:55Z" level=info msg="
Stats collection is disabled.
Help us improve Traefik by turning this feature on :)
More details on: https://docs.traefik.io/basics/#collected-data
"
traefik | time="2018-05-18T18:30:55Z" level=debug msg="Global configuration loaded {\"LifeCycle\":{\"RequestAcceptGraceTimeout\":0,\"GraceTimeOut\":10000000000},\"GraceTimeOut\":0,\"Debug\":true,\"CheckNewVersion\":true,\"SendAnonymousUsage\":false,\"AccessLogsFile\":\"\",\"AccessLog\":null,\"TraefikLogsFile\":\"\",\"TraefikLog\":null,\"Tracing\":null,\"LogLevel\":\"DEBUG\",\"EntryPoints\":{\"http\":{\"Address\":\":80\",\"TLS\":null,\"Redirect\":{\"entryPoint\":\"https\"},\"Auth\":null,\"WhitelistSourceRange\":null,\"WhiteList\":null,\"Compress\":false,\"ProxyProtocol\":null,\"ForwardedHeaders\":{\"Insecure\":true,\"TrustedIPs\":null}},\"https\":{\"Address\":\":443\",\"TLS\":{\"MinVersion\":\"\",\"CipherSuites\":null,\"Certificates\":null,\"ClientCAFiles\":null,\"ClientCA\":{\"Files\":null,\"Optional\":false}},\"Redirect\":null,\"Auth\":null,\"WhitelistSourceRange\":null,\"WhiteList\":null,\"Compress\":false,\"ProxyProtocol\":null,\"ForwardedHeaders\":{\"Insecure\":true,\"TrustedIPs\":null}}},\"Cluster\":null,\"Constraints\":[],\"ACME\":null,\"DefaultEntryPoints\":[\"http\",\"https\"],\"ProvidersThrottleDuration\":2000000000,\"MaxIdleConnsPerHost\":200,\"IdleTimeout\":0,\"InsecureSkipVerify\":false,\"RootCAs\":null,\"Retry\":null,\"HealthCheck\":{\"Interval\":30000000000},\"RespondingTimeouts\":null,\"ForwardingTimeouts\":null,\"AllowMinWeightZero\":false,\"Web\":null,\"Docker\":

排查与解决方案

这个TLS握手错误我碰到过好几次,大概率和证书生成失败、网络连通性或者配置冲突有关,咱们一步步来定位:

1. 先确认ACME证书是否成功生成

Traefik没法完成TLS握手,最常见的原因就是没拿到有效的证书。

  • 先检查acme.json的权限:Traefik需要读写这个文件,而且必须是600权限(因为Alpine镜像里Traefik以非root用户运行,权限太宽会报错),执行chmod 600 ./traefik/acme/acme.json。
  • 打开这个文件看看,如果是空的或者没有包含Certificates字段的有效JSON,说明证书获取失败了。

另外,你的ACME配置同时启用了httpChallenge和dnsChallenge,这两种方式会冲突,Traefik可能不知道用哪种来验证域名。建议只保留一种:

  • 既然你已经转发了80端口,优先用httpChallenge,把[acme.dnsChallenge]整个区块注释掉;
  • 如果http挑战有问题(比如ISP封了80端口),就保留dnsChallenge,注释掉[acme.httpChallenge]。

还有你用的是Let's Encrypt的staging服务器,虽然 staging证书浏览器不认可,但至少能完成TLS握手,如果连这个都失败,肯定是证书没生成的问题。

2. 验证端口转发与网络连通性

  • 确认路由器的端口转发是否真的生效:可以在Debian主机上用nc -l 443监听端口,然后从外网用telnet my_duck_dns.duckdns.org 443测试,如果能连通说明转发没问题,否则检查路由器配置或者主机防火墙(比如ufw是否允许443端口入站)。
  • 检查Traefik容器是否在监听443端口:执行docker exec traefik netstat -tulpn,看看:443是不是处于LISTEN状态。

3. 优化Traefik TLS配置

你的[entryPoints.https.tls]是空的,虽然Traefik会自动加载ACME证书,但可以添加TLS最小版本配置,避免和老客户端的兼容性问题:

[entryPoints.https.tls]
MinVersion = "VersionTLS12"

4. 清理Docker网络冗余配置

  • whoami容器的ports: - 8000完全没必要,Traefik是通过Docker内部网络直接访问容器的,映射到主机端口反而可能带来安全隐患,直接删掉这一行。
  • 确认两个容器都在proxy网络里:执行docker network inspect proxy,查看容器列表,确保traefik和whoami都在其中。

5. 打开ACME日志排查证书问题

把acmeLogging = true加到[acme]区块里,重启Traefik后看日志,里面会有证书获取的详细错误信息,比如域名验证失败、网络超时之类的,这能帮你快速定位问题。

6. 局域网内测试排除外网因素

先在局域网里直接访问https://<Debian主机局域网IP>/whoami/,如果能正常握手,说明问题出在路由器转发或者ISP限制(比如有些ISP会封锁443端口);如果局域网内也失败,那就是本地配置的问题,回到前面的步骤继续排查。


内容的提问来源于stack exchange,提问作者William Nameless

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:42:29