Let's Encrypt 证书,HTTP challenge
LE
- Let's Encrypt. 提供免费证书的服务Certificate
- 存储在服务器文件中的加密密钥,允许进行加密通信并确认身份ACME
- 一种协议(精确商定的通信方式),以协商来自 LE 的证书。它是 traefik 的一部分。DNS
- 互联网上的服务器,将域名转换为 IP 地址
Traefik 使用 ACME 向 LE 请求特定域的证书,如 example.com
。LE 用一些随机生成的文本来回答,然后 traefik 把这些文本放在服务器的特定位置。然后,LE 向 DNS 互联网服务器询问 example.com
,结果指向了某个 IP 地址。LE 通过端口 80/443 查找该 IP 地址,查找包含该随机文本的文件。
如果存在,那么这证明了要求证书的人都控制了服务器和域,因为它显示了对 DNS 记录的控制权。证书已颁发,有效期为 3 个月,traefik 将在少于 30 天的时间内自动尝试续订。
现在我们来看看该怎么做。
创建一个具有 600 权限的空 acme.json 文件
该文件将存储证书以及有关证书的所有信息。
touch acme.json && chmod 600 acme.json
在 traefik.yml 中添加 443 入口点和证书解析器
在 entrypoint 部分中,新的 entrypoint 被添加为 websecure,端口 443
certificatesResolvers 是一个配置部分,它告诉 traefik 如何使用 acme resolver 获取证书。
certificatesResolvers: lets-encr: acme: #caServer: https://acme-staging-v02.api.letsencrypt.org/directory storage: acme.json email: whatever@gmail.com httpChallenge: entryPoint: web
- 解析器的名称为
lets-encr
,并使用 acme - 注释掉了 staging caServer 使 LE 颁发了一个 staging 证书,这是一个无效的证书,不会给绿锁,但没有限制,所以很适合测试。如果它在工作,它会说,我们加密。
- Storage 告诉在哪里存储给定的证书 -
acme.json
- 邮件是 LE 发送证书过期通知的地方
- httpChallenge 有一个入口点,因此 acme 在端口 80 上执行 http challenge
这就是 acme 所需要的一切
traefik.yml
## STATIC CONFIGURATION log: level: INFO api: insecure: true dashboard: true entryPoints: web: address: ":80" websecure: address: ":443" providers: docker: endpoint: "unix:///var/run/docker.sock" exposedByDefault: false certificatesResolvers: lets-encr: acme: #caServer: https://acme-staging-v02.api.letsencrypt.org/directory storage: acme.json email: whatever@gmail.com httpChallenge: entryPoint: web
暴露/映射端口 443 并将 acme.json 挂载在 traefik-docker-compose.yml 中
注意:acme.json 不是 :ro
- 只读
traefik-docker-compose.yml
version: "3.7" services: traefik: image: "traefik:v2.1" container_name: "traefik" hostname: "traefik" env_file: - .env ports: - "80:80" - "443:443" - "8080:8080" volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" - "./traefik.yml:/traefik.yml:ro" - "./acme.json:/acme.json" networks: default: external: name: $DEFAULT_NETWORK
向容器添加所需的标签
与第一章中的纯 HTTP 相比,它只是将路由器的入口点从 web
更改为 websecure
, 并将名为 lets-encr
的证书解析器分配给现有路由器
whoami-docker-compose.yml
version: "3.7" services: whoami: image: "containous/whoami" container_name: "whoami" hostname: "whoami" labels: - "traefik.enable=true" - "traefik.http.routers.whoami.entrypoints=websecure" - "traefik.http.routers.whoami.rule=Host(`whoami.$MY_DOMAIN`)" - "traefik.http.routers.whoami.tls.certresolver=lets-encr" networks: default: external: name: $DEFAULT_NETWORK
nginx-docker-compose.yml
version: "3.7" services: nginx: image: nginx:latest container_name: nginx hostname: nginx labels: - "traefik.enable=true" - "traefik.http.routers.nginx.entrypoints=websecure" - "traefik.http.routers.nginx.rule=Host(`nginx.$MY_DOMAIN`)" - "traefik.http.routers.nginx.tls.certresolver=lets-encr" networks: default: external: name: $DEFAULT_NETWORK
运行容器
稍等一分钟
容器现在只能在 https 上工作并且具有 greenlock
检查 acme.json 的内容
如果要重新开始,请删除acme.json
Let's Encrypt 证书在 cloudflare 上 DNS challenge
LE
- Let's Encrypt. 提供免费证书的服务Certificate
- 存储在服务器文件中的加密密钥,允许进行加密通信并确认身份ACME
- 一种协议(精确商定的通信方式),以协商来自 LE 的证书。它是 traefik 的一部分。DNS
- 互联网上的服务器,将域名转换为 IP 地址
Traefik 使用 ACME 向 LE 请求特定域的证书,如 example.com
。LE 用一些随机生成的文本来回答,然后 traefik 把这些文本放在服务器的特定位置。然后,LE 向 DNS 互联网服务器询问 example.com
,结果指向了某个 IP 地址。LE 通过端口 80/443 查找该 IP 地址,查找包含该随机文本的文件。
如果存在,那么这证明了要求证书的人都控制了服务器和域,因为它显示了对 DNS 记录的控制权。证书已颁发,有效期为 3 个月,traefik 将在少于 30 天的时间内自动尝试续订。
与 httpChallenge 相比的好处是能够使用通配符证书。这些是验证所有子域 *.example.com
的证书 另外,无需打开任何端口。
但 traefik 需要能够对 DNS 记录进行自动更改,因此需要管理网站 DNS 的人对此提供支持。这就是为什么选择 cloudflare。
现在我们来看看该怎么做。为所有规划的子域添加类型 A DNS 记录
[whoami, nginx, *] 是示例子域,每个子域都应有一个指向 traefik IP 的 A 记录。
创建一个具有 600 权限的空 acme.json 文件
touch acme.json && chmod 600 acme.json
将 443 入口点和证书解析器添加到 traefik.yml
在 entrypoint 部分中,新的 entrypoint 被添加为 websecure,端口 443
certificatesResolvers 是一个配置部分,它告诉 traefik 如何使用 acme resolver 获取证书。
certificatesResolvers: lets-encr: acme: #caServer: https://acme-staging-v02.api.letsencrypt.org/directory email: whatever@gmail.com storage: acme.json dnsChallenge: provider: cloudflare resolvers: - "1.1.1.1:53" - "8.8.8.8:53"
- 解析器的名称为
lets-encr
,并使用 acme - 注释掉了 staging caServer 使 LE 颁发了一个 staging 证书,这是一个无效的证书,不会给绿锁,但没有限制,所以很适合测试。如果它在工作,它会说,我们加密。
- Storage 告诉在哪里存储给定的证书 -
acme.json
- 邮件是 LE 发送证书过期通知的地方
- dnsChallenge 是由一个 provider 指定的,
在这个例子中是 cloudflare。每个提供程序在 .env 文件中需要不同名称的环境变量, 但这是稍后的内容,这里只需要提供程序的名称
- 解析器是在挑战期间使用的知名 DNS 服务器的 IP
traefik.yml
## STATIC CONFIGURATION log: level: INFO api: insecure: true dashboard: true entryPoints: web: address: ":80" websecure: address: ":443" providers: docker: endpoint: "unix:///var/run/docker.sock" exposedByDefault: false certificatesResolvers: lets-encr: acme: #caServer: https://acme-staging-v02.api.letsencrypt.org/directory email: whatever@gmail.com storage: acme.json dnsChallenge: provider: cloudflare resolvers: - "1.1.1.1:53" - "8.8.8.8:53"
在 .env
文件中添加所需的变量
我们知道根据支持的提供商列表添加哪些变量
对于 cloudflare 变量是
CF_API_EMAIL
- cloudflare loginCF_API_KEY
- global api key
.env
MY_DOMAIN=example.com DEFAULT_NETWORK=traefik_net CF_API_EMAIL=whateverbastard@gmail.com CF_API_KEY=8d08c87dadb0f8f0e63efe84fb115b62e1abc
暴露/映射端口 443 并将 acme.json 挂载在 traefik-docker-compose.yml 中
注意:acme.json 不是 :ro
- 只读
traefik-docker-compose.yml
version: "3.7" services: traefik: image: "traefik:v2.1" container_name: "traefik" hostname: "traefik" env_file: - .env ports: - "80:80" - "443:443" - "8080:8080" volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" - "./traefik.yml:/traefik.yml:ro" - "./acme.json:/acme.json" networks: default: external: name: $DEFAULT_NETWORK
在容器上添加所需的标签
与第一章中简单的 http 相比
- 路由器的入口点从
web
切换到websecure
- 分配给路由器的名为
lets-encr
的证书解析器 - 定义将要获得证书的主域的标签,在这里是 whoami.example.com,域名是从
.env
文件中提取的
whoami-docker-compose.yml
version: "3.7" services: whoami: image: "containous/whoami" container_name: "whoami" hostname: "whoami" labels: - "traefik.enable=true" - "traefik.http.routers.whoami.entrypoints=websecure" - "traefik.http.routers.whoami.rule=Host(`whoami.$MY_DOMAIN`)" - "traefik.http.routers.whoami.tls.certresolver=lets-encr" - "traefik.http.routers.whoami.tls.domains[0].main=whoami.$MY_DOMAIN" networks: default: external: name: $DEFAULT_NETWORK
nginx-docker-compose.yml
version: "3.7" services: nginx: image: nginx:latest container_name: nginx hostname: nginx labels: - "traefik.enable=true" - "traefik.http.routers.nginx.entrypoints=websecure" - "traefik.http.routers.nginx.rule=Host(`nginx.$MY_DOMAIN`)" - "traefik.http.routers.nginx.tls.certresolver=lets-encr" - "traefik.http.routers.nginx.tls.domains[0].main=nginx.$MY_DOMAIN" networks: default: external: name: $DEFAULT_NETWORK
运行容器
docker-compose -f traefik-docker-compose.yml up -d docker-compose -f whoami-docker-compose.yml up -d docker-compose -f nginx-docker-compose.yml up -d
DNS 挑战的全部重点就是获取通配符!
很公平
因此,对于通配符,这些标签将加入 traefik compose。
- 与以前一样使用相同的
lets-encr
证书解析器,它在 traefik.yml 中定义 - 子域(*.example.com)的通配符被设置为要获取证书的主域
- 裸域(只是简单的example.com)设置为sans(主题备用名称)
同样,您确实需要 * .example.com
和 example.com
在 DNS 控制面板中设置为 A 记录,指向 traefik 的 IP
traefik-docker-compose.yml
version: "3.7" services: traefik: image: "traefik:v2.1" container_name: "traefik" hostname: "traefik" env_file: - .env ports: - "80:80" - "443:443" - "8080:8080" volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" - "./traefik.yml:/traefik.yml:ro" - "./acme.json:/acme.json" labels: - "traefik.enable=true" - "traefik.http.routers.traefik.tls.certresolver=lets-encr" - "traefik.http.routers.traefik.tls.domains[0].main=*.$MY_DOMAIN" - "traefik.http.routers.traefik.tls.domains[0].sans=$MY_DOMAIN" networks: default: external: name: $DEFAULT_NETWORK
现在,如果容器想作为子域进行访问,则只需要一个具有 url 规则的常规路由器,位于 443 端口入口点,并使用相同的 lets-encr
证书解析器
whoami-docker-compose.yml
version: "3.7" services: whoami: image: "containous/whoami" container_name: "whoami" hostname: "whoami" labels: - "traefik.enable=true" - "traefik.http.routers.whoami.entrypoints=websecure" - "traefik.http.routers.whoami.rule=Host(`whoami.$MY_DOMAIN`)" - "traefik.http.routers.whoami.tls.certresolver=lets-encr" networks: default: external: name: $DEFAULT_NETWORK
nginx-docker-compose.yml
version: "3.7" services: nginx: image: nginx:latest container_name: nginx hostname: nginx labels: - "traefik.enable=true" - "traefik.http.routers.nginx.entrypoints=websecure" - "traefik.http.routers.nginx.rule=Host(`nginx.$MY_DOMAIN`)" - "traefik.http.routers.nginx.tls.certresolver=lets-encr" networks: default: external: name: $DEFAULT_NETWORK
这是apache,但这次运行在裸域 example.com
上
apache-docker-compose.yml
version: "3.7" services: apache: image: httpd:latest container_name: apache hostname: apache labels: - "traefik.enable=true" - "traefik.http.routers.apache.entrypoints=websecure" - "traefik.http.routers.apache.rule=Host(`$MY_DOMAIN`)" - "traefik.http.routers.apache.tls.certresolver=lets-encr" networks: default: external: name: $DEFAULT_NETWORK
转发 HTTP 流量到 HTTPS
http 停止使用 https 设置,最好将 http(80) 重定向到 https(443)。
Traefik 有专门的中间件 — redirectscheme。
当 traefik.yml
本身设置为文件提供程序时,可以在动态部分的 traefik.yml
中的多个位置声明此重定向。
或在任何正在运行的容器中使用标签,此示例在 traefik compose 中进行操作。
使用 traefik 中的标签添加新路由和重定向方案
- "traefik.enable=true"
在这个 traefik 容器上启用 traefik,不是说这里需要到服务的典型路由,而是说没有它其他标签就不能工作
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
创建一个名为 redirect-to-https
的新中间件,输入 “redirectscheme” 并为其分配方案 https
。
- "traefik.http.routers.redirect-https.rule=hostregexp(`{host:.+}`)"
创建一个名为 redirect-https
的新路由,并使用一个正则表达式规则来捕获所有传入请求
- "traefik.http.routers.redirect-https.entrypoints=web"
声明此路由器在哪个入口点上侦听 - Web(端口80)
- "traefik.http.routers.redirect-https.middlewares=redirect-to-https"
将新创建的 redirectscheme 中间件分配给此新创建的路由。
traefik-docker-compose.yml
version: "3.7" services: traefik: image: "traefik:v2.1" container_name: "traefik" hostname: "traefik" env_file: - .env ports: - "80:80" - "443:443" - "8080:8080" volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" - "./traefik.yml:/traefik.yml:ro" - "./acme.json:/acme.json" labels: - "traefik.enable=true" ## DNS CHALLENGE - "traefik.http.routers.traefik.tls.certresolver=lets-encr" - "traefik.http.routers.traefik.tls.domains[0].main=*.$MY_DOMAIN" - "traefik.http.routers.traefik.tls.domains[0].sans=$MY_DOMAIN" ## HTTP REDIRECT - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" - "traefik.http.routers.redirect-https.rule=hostregexp(`{host:.+}`)" - "traefik.http.routers.redirect-https.entrypoints=web" - "traefik.http.routers.redirect-https.middlewares=redirect-to-https" networks: default: external: name: $DEFAULT_NETWORK