Traefik 2 基礎授權驗證(前篇)

Traefik 2 基礎授權驗證(前篇)
我們經常會看到在訪問應用前,系統提示用戶進行鑑權操作,或出於某些原因,內部提供公網服務的應用需要藏在一些基礎的鑑權認證後,避免直接向大衆公開。

除了使用各種語言來實現鑑權外,使用 Traefik 也可以簡單快速的滿足這些需求。

準備基礎的 Web 服務Demo
我們先以 whoami 爲例,啓動一個 Web 服務,配置如下:

version: '3'

services:

  whoami:
    image: containous/whoami
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=traefik"
   # 參考 https://soulteary.com/2020/12/02/easier-way-to-use-traefik-2.html
      - "traefik.http.routers.test-auth-web.middlewares=https-redirect@file"
      - "traefik.http.routers.test-auth-web.entrypoints=http"
      - "traefik.http.routers.test-auth-web.rule=Host(`whoami.lab.com`, `whoami.lab.io`)"
      - "traefik.http.routers.test-auth-ssl.entrypoints=https"
      - "traefik.http.routers.test-auth-ssl.tls=true"
      - "traefik.http.routers.test-auth-ssl.rule=Host(`whoami.lab.com`, `whoami.lab.io`)"

    networks:
      - traefik

networks:
  traefik:
    external: true

爲了進一步保障數據傳輸安全,推薦使用 HTTPS 進行數據交互。可以使用前文中的 https-redirect 中間件,將 HTTP 請求自動轉發到 HTTPS 協議上。

將配置保存爲 docker-compose.yml 後,使用 docker-compose up -d 啓動服務後,可以看到類似下面的頁面。

Traefik 2 基礎授權驗證(前篇)
Basic Auth
使用 Traefik 爲應用添加 Basic Auth 非常簡單,只需要定義一個包含 basicAuth 用戶名密碼的中間件聲明,然後在需要使用 Basic Auth 驗證的服務路由上引用它即可,像是下面這樣:

labels:
...
  - "traefik.http.middlewares.test-auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
  - "traefik.http.routers.test-auth-ssl.middlewares=test-auth@docker"
...

重新使用 docker-compose up -d 啓動服務後,會看到一個彈出框,要求我們輸入密碼。
Traefik 2 基礎授權驗證(前篇)

隨便輸入賬號密碼,或者取消輸入,會獲得 401 Unauthorized 的錯誤提示,如果我們輸入賬號和密碼爲 test 的內容,點擊確定,則可以正常看到 Demo 服務的頁面內容。

如何生成 Basic Auth 賬號密碼
如果你是 macOS 用戶,系統默認攜帶了 apache htpasswd 工具,可以直接生成上面配置中的賬號密碼。


htpasswd -nb test test
test:$apr1$lH3nyBaa$/wCu0V3.1kYdpZPHRbiyv/

如果你的系統中找不到這個命令行,你也不想安裝 apache utils,那麼可以使用 Docker 來生成賬號密碼:

docker run --rm -it --entrypoint /usr/local/apache2/bin/htpasswd httpd:alpine -nb test test

但是需要注意的是,在 compose 中使用的話,密碼中的 $ 需要使用 $$ 來進行替換,解決轉義問題。

如何配置多個賬號密碼

配置多個賬號密碼可以使用兩種方式:

  • 使用包含多個賬號的配置文件
  • 使用包含多個賬號的環境變量
    如果你有多個應用都希望使用 Basic Auth 來進行基礎保護,那麼可以在 Traefik 的動態配置中添加這個“驗證中間件”,如果你還不瞭解如何配置 Traefik,可以參考這篇文章。

使用文件來定義、管理用戶密碼,需要聲明下面的內容到 labels 字段中:

  - "traefik.http.middlewares.test-auth.basicauth.usersfile=/path/to/my/usersfile"

並在一個文件中使用換行來保存我們生成的用戶名和密碼:


test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/
test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0

如果你希望每個應用有其獨立的賬號密碼,不希望用戶賬號混在一起存放、管理,那麼可以使用環境變量和項目環境配置文件來解決這個問題。

先定義一個讀取環境變量的驗證中間件:


  - "traefik.http.middlewares.test-auth.basicauth.users=$AUTH_USER_LIST"

然後在 compose 同級目錄中創建一個 .env 文件,以英文逗號爲分隔符,傳入生成的用戶鑑權信息即可:


test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0

手動選擇是否要將驗證信息透傳
默認情況下,當我們登錄後,Traefik 會將授權後的驗證頭髮送至後方的服務,我們在 header 中能看到類似下面的信息:


Authorization: Basic dGVzdDp0ZXN0

有一些應用支持使用請求頭中的數據作爲鑑權登錄信息,而我們定義的用戶信息很可能和系統的鑑權信息是不同的(也不推薦使用這個方案做爲多數情況下應用鑑權方案),所以造成應用無法正常登陸,所以此刻我們要將這個鑑權操作的作用範圍做一個限制,讓它僅僅生效在首次訪問應用前,流量到達 Traefik 時:


  - "traefik.http.middlewares.test-auth.basicauth.removeheader=true"

在添加了上面內容後,我們可以看到輸入賬號密碼後,Traefik 不會再進行 Authorization 請求頭的透傳。

用還是不用,這是個問題
雖然相對詳細的介紹了 Basic Auth,但是並不推薦大範圍或者將其作爲唯一鑑權手段。

因爲在標準規範中,它使用 Base64 對用戶名密碼進行編碼,然後傳遞給其他應用。衆所周知 Base64 是可逆編碼的,所以我們使用 Basic Auth 來保護應用其實並不安全,比如我們將前文中的 Authorization: Basic dGVzdDp0ZXN0 最後一段內容 dGVzdDp0ZXN0 進行解碼,能夠直接得到明文的 test:test。

但是如果你的系統未公開暴露於網絡,並且使用人員有限,或提供開放服務,但是單純不希望被搜索引擎抓取,可以在應用前端套一層 Basic Auth,相比較用戶、爬蟲能夠直接訪問到機器,這樣還能夠節約大量不必要的計算資源浪費。

不要單純聽從網絡人云亦云,一刀切完全不用,剋制的使用在適合的場景下,事半功倍。

Digest Auth
在詳細介紹了 Basic Auth 後,我們來了解 Digest Auth 會輕鬆不少。前文提到 Basic Auth 存在一些安全問題,所以有了這個“升級版本”,支持使用 MD5 / SHA 系列加密算法來替換簡單的 Base64 “加密”。

有一件有意思的事情,目前 Mozilla 社區 buglist 中 不支持 SHA 加密 的反饋已經維持打開 12 年,最近幾天有一位仁兄提了 PR,或許火狐瀏覽器不久之後可以支持使用 Digest Auth(SHA)。

Traefik 中的 Digest Auth 中間件和 Basic Auth 中間件使用基本一致,所以你基本可以將上文中類似下面配置中的 basicauth 替換爲 digestauth 來達到相同目的:

# 使用 Basic Auth
 - "traefik.http.middlewares.test-auth.basicauth.users=$AUTH_USER_LIST"
# 使用 Digital Auth
- "traefik.http.middlewares.test-auth.digestauth.users=$AUTH_USER_LIST"

如何生成 Digital Auth 賬號密碼
如果你是 macOS 用戶,系統同樣默認攜帶了 apache htdigest 工具,可以直接生成上面配置中的賬號密碼,不過相比 htapasswd 使用起來會複雜一些,需要在使用過程中手動輸入賬號密碼,因爲默認工具會生成文件,我們的場景下,其實不一定需要它創建文件,所以這裏將輸出指向 /dev/stdout 即可在運行的完畢展示結果。


htdigest -c /dev/stdout test test                                                   
Adding password for test in realm test.
New password: 
Re-type new password: 

test:test:3c7ca779a9504185a7b86c8b1c388e90

類似的,如果你的系統中找不到這個命令行,你也不想安裝 apache utils,那麼可以使用 Docker 來生成賬號密碼:

docker run --rm -it --entrypoint /usr/local/apache2/bin/htdigest httpd:alpine -c /dev/stdout test test 
Adding password for test in realm test.
New password: 
Re-type new password: 

test:test:3c7ca779a9504185a7b86c8b1c388e90

用還是不用,是個問題嗎
上文提到,目前瀏覽器對於這個類型的驗證有各種各樣的“兼容性”問題,有不支持 SHA1 的,有不支持 SHA 256 的,有隻支持 MD5 的…而且坦白說,目前如果使用摘要算法做鑑權,SHA 256 以下其實差異不大,SHA 系列相比 MD5 的好處目前來看僅剩硬件(CPU指令集)計算加速,以及***成本更高一些,相對更不易碰撞。

如果你想選擇使用 Digest 作爲鑑權,同樣是不建議的,如果有 Basic Auth 最後一小節中的需求理由,可以直接使用 Basic Auth。

Forward Auth

Forward Auth 相比上面兩種方案,其實有質的不同,上面兩種加密中間件本質提供的是RFC標準下的交互協議,而這個中間件提供的一個通用的鑑權業務能力:你可以自由對接任何你自己的鑑權系統、用戶數據來源,甚至實現一個通用的 SSO 授權頁面。

限於篇幅,這部分內容,我們放到下篇來聊。

最後
原本我只是想聊聊基於開源代碼快速搭建適合用於個人、團隊、基礎設施使用的 SSO,萬萬沒想到,需要展開鋪墊如此這麼多前置知識。

--EOF

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章