開發與維運

一文搞定快速使用 Docker Compose 玩轉 Traefik v2(二)

Let's Encrypt 證書,HTTP challenge


微信圖片_20220611113528.png

  • 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: [email protected]
      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: [email protected]
      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


微信圖片_20220611113629.png

  • 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: [email protected]
    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: [email protected]
      storage: acme.json
      dnsChallenge:
        provider: cloudflare
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"

在 .env 文件中添加所需的變量


我們知道根據支持的提供商列表添加哪些變量

對於 cloudflare 變量是

  • CF_API_EMAIL - cloudflare login
  • CF_API_KEY - global api key

.env


MY_DOMAIN=example.com
DEFAULT_NETWORK=traefik_net
[email protected]
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


微信圖片_20220611113732.png

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

Leave a Reply

Your email address will not be published. Required fields are marked *