在雲端原生架構下,傳統的 RBAC 顯得力不從心,ABAC 根據屬性的彈性控制更能滿足複雜的存取需求。本文將探討如何結合 OPA 和 Rego 語言,構建更精細、動態的 ABAC 系統。文章將涵蓋 Rego 語言的例項解析,並剖析其如何處理使用者、資源和環境屬性,以及如何設計資料模型來支援策略評估。此外,文章也將探討 OPA Bundle 伺服器的搭建、組態和測試流程,並提出大規模 Bundle 管理的策略,以確保策略的版本控制和一致性。最後,文章將提供最佳實踐和注意事項,協助讀者構建安全可靠的 ABAC 系統。
深入解析根據屬性的存取控制(ABAC)與開放策略代理(OPA)
前言
在現代化的雲端運算環境中,存取控制是資訊安全的重要一環。根據屬性的存取控制(Attribute-Based Access Control, ABAC)提供了一種比傳統根據角色的存取控制(Role-Based Access Control, RBAC)更為靈活的存取控制機制。本文將探討ABAC的實作原理,並以開放策略代理(Open Policy Agent, OPA)為例,展示如何使用Rego語言編寫存取控制策略。
ABAC與RBAC的比較
RBAC主要根據使用者的角色來決定其對資源的存取許可權。相較之下,ABAC則是根據使用者、資源以及環境的多種屬性來動態決定存取許可權。這種機制使得ABAC在複雜的雲端環境中更具彈性。
RBAC的侷限性
- 角色定義僵化:當系統複雜度增加時,角色定義可能無法滿足多變的存取需求。
- 許可權管理困難:隨著角色數量的增加,許可權管理變得更加複雜。
ABAC的優勢
- 動態許可權控制:根據實時屬性進行存取控制。
- 更高的靈活性:能夠適應複雜多變的存取需求。
- 細粒度控制:能夠對資源進行精細的存取控制。
OPA與Rego語言
OPA是一種開源的策略引擎,用於統一管理跨多個系統的存取控制策略。OPA使用Rego語言來編寫策略,這是一種專為策略定義設計的宣告式語言。
Rego語言範例解析
allow if {
user_is_employee
user_is_senior
action_is_update
user_is_on_shift
}
allow if {
user_is_customer
action_is_read
not pet_is_adopted
}
user_is_owner if data.user_attributes[input.user].title == "owner"
user_is_employee if data.user_attributes[input.user].title == "employee"
user_is_customer if data.user_attributes[input.user].title == "customer"
user_is_senior if data.user_attributes[input.user].tenure > 8
action_is_read if input.action == "read"
action_is_update if input.action == "update"
pet_is_adopted if data.pet_attributes[input.resource].adopted == true
# 時間處理邏輯
tz := "America/New_York"
now := time.now_ns()
clock := time.clock([now, tz])
day := time.weekday(now)
shift := data.user_attributes[input.user].shift
user_is_on_shift if {
is_weekday
is_valid_time
}
is_valid_time if {
clock[0] >= data.shifts[shift].start
clock[0] < data.shifts[shift].end
}
is_valid_time if {
clock[0] >= data.shifts[shift].start
clock[0] == data.shifts[shift].end
clock[1] == 0
clock[2] == 0
}
is_weekday if day in ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
#### 內容解密:
此Rego程式碼定義了根據屬性的存取控制規則。主要包含以下幾個方面:
- 允許條件:定義了兩種允許存取的情況:員工在班且為高階員工時可進行更新操作;客戶可讀取未被領養的寵物資訊。
- 使用者屬性判斷:根據使用者的屬性(如職稱、任職時間)來判斷其角色和資歷。
- 動作判斷:判斷使用者的操作是讀取還是更新。
- 資源屬性判斷:判斷寵物是否已被領養。
- 時間處理邏輯:根據員工的班次和當前時間判斷員工是否在班。
資料模型解析
OPA使用JSON格式儲存相關屬性資料,如下所示:
{
"user_attributes": {
"alice": {
"tenure": 20,
"title": "owner",
"shift": "D"
},
"bob": {
"tenure": 15,
"title": "employee",
"shift": "N"
}
},
"shifts": {
"D": {
"start": 9,
"end": 17
},
"N": {
"start": 15,
"end": 23
}
},
"pet_attributes": {
"dog123": {
"adopted": true,
"age": 2,
"breed": "terrier",
"name": "toto"
}
}
}
#### 內容解密:
此JSON資料結構儲存了使用者屬性、班次資訊和寵物屬性。這些資料被OPA用於評估存取控制策略。
- 使用者屬性:包含使用者的任職時間、職稱和班次等資訊。
- 班次資訊:定義了不同班次的起始和結束時間。
- 寵物屬性:記錄了寵物的領養狀態、年齡、品種和名稱等資訊。
ABAC的實作與優勢
透過OPA和Rego語言,我們可以實作根據屬性的動態存取控制。這種方法相較於傳統的RBAC具有更高的靈活性,能夠更好地適應複雜的雲端環境。
ABAC在雲端環境中的實作要點
- 標籤和標記的管理:在雲端環境中,通常使用標籤和標記來為資源新增中繼資料。這些標籤和標記會被ABAC策略評估,因此必須嚴格控制誰可以建立或修改它們。
- 與現有系統整合:ABAC可以與人力資源系統、資產管理系統等現有系統整合,實作動態的屬性更新。
策略自動化與管理
為了有效地管理跨多個系統的存取控制策略,需要實作策略自動化。這包括:
- 策略封裝與分發:使用OPA的bundle機制,將策略封裝並分發到各個OPA例項。
- Bundle伺服器:建立一個bundle伺服器來託管和分發策略包。
使用Golang建立Bundle伺服器範例
// 使用gorilla/mux建立HTTP路由器和處理器
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
)
func main() {
router := mux.NewRouter()
router.HandleFunc("/bundles/{bundle_name}", getBundle).Methods("GET")
log.Fatal(http.ListenAndServe(":8080", router))
}
func getBundle(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
bundleName := vars["bundle_name"]
// 邏輯:根據bundleName提供對應的bundle
}
#### 內容解密:
此Golang程式碼片段展示瞭如何使用gorilla/mux套件建立一個簡單的HTTP伺服器,用於提供OPA的bundle。
- 路由器設定:使用mux.NewRouter()建立路由器,並定義了一個處理GET請求的路由,用於提供bundle。
- 處理函式:getBundle函式根據請求中的bundle_name引數提供對應的bundle。
OPA Bundle 伺服器設定與測試流程詳解
伺服器啟動與基本測試
當伺服器以偵錯模式啟動時,它會監聽在 10.0.2.2:8443
這個 socket 上,並產生以下啟動日誌:
{"level":"info","time":"2022-12-11T20:05:26.159931-05:00","message":"Service started successfully."}
{"level":"info","time":"2022-12-11T20:05:26.16005-05:00","message":"Flags: map[], Args: [./main.bin]"}
{"level":"info","time":"2022-12-11T20:05:26.163098-05:00","message":"Listening on socket 10.0.2.2:8443"}
使用 10.0.2.2
這個迴路位址別名是為了讓在 Docker 中執行的 OPA 伺服器能夠連線到在 Docker 外部的本地 HTTPS 伺服器。這是因為在 Docker 虛擬機器和桌面環境中,localhost
和 127.0.0.1
的上下文是不同的。
測試伺服器連線
首先,使用 curl
命令來測試伺服器的 info
端點,傳送一個帶有 Bearer Token 的 HTTPS GET 請求:
$ TOKEN=`cat token`
$ curl -k -H 'Accept: application/json' -H "Authorization: Bearer ${TOKEN}" https://10.0.2.2:8443/info
{"service-name":"opa-bundle-api","service-id":"078f5...d2954"}
這裡使用了 -k
引數來忽略自簽署的 TLS 憑證組態。
接著,測試 bundle 下載功能,同樣使用 -k
引數:
$ TOKEN=`cat token`
$ curl -k -H "Authorization: Bearer ${TOKEN}" https://10.0.2.2:8443/bundles/signed-main.tar.gz -o signed-main.tar.gz
成功下載檔案後,我們可以開始組態 OPA 伺服器來連線到 bundle 伺服器。
組態 OPA 伺服器連線 Bundle 伺服器
以下是 OPA 伺服器連線 bundle 伺服器的組態檔案內容:
keys:
project-bundle-key:
key: |
-----BEGIN PUBLIC KEY-----
<PUBLIC_KEY>
-----END PUBLIC KEY-----
services:
- name: opa-bundle-api
url: https://10.0.2.2:8443/v1/bundles
allow_insecure_tls: true
credentials:
bearer:
token: "eyJ..."
bundles:
project:
service: opa-bundle-api
resource: signed-main.tar.gz
persist: true
polling:
min_delay_seconds: 10
max_delay_seconds: 20
signing:
keyid: project-bundle-key
使用以下命令啟動 OPA 伺服器並載入組態檔案:
$ opa run -s --config-file opa-conf.yaml
或者,可以使用 --set-file
引數在啟動時直接從檔案載入公鑰:
$ opa run -s --config-file opa-conf.yaml --set-file "keys.project-bundle-key.key=keys/key.pem.pub"
Docker 中的 OPA 伺服器執行
在 Docker 中執行 OPA 伺服器時,需要對映兩個卷來提供組態檔案和 bundle 儲存:
$ docker run -it --rm -p 8181:8181 -v $(pwd):/config -v $(pwd)/.opa:/.opa --platform linux/amd64 openpolicyagent/opa:0.62.0 run -s --config-file=config/opa-conf.yaml
OPA 伺服器啟動日誌分析
OPA 伺服器啟動後的日誌輸出如下:
{"addrs":[":8181"],"diagnostic-addrs":[],"level":"info","msg":"Initializing server.","time":"2022-11-25T23:32:17-05:00"}
{"level":"info","msg":"Starting bundle loader.","name":"project","plugin":"bundle","time":"2022-11-25T23:32:17-05:00"}
{"level":"info","msg":"Bundle loaded and activated successfully. Etag updated to 6...c.","name":"project","plugin":"bundle","time":"2022-11-25T23:32:17-05:00"}
從日誌中可以看到,bundle 設定成功,bundle 被下載並啟用,且 ETag 被更新。
Bundle 請求處理流程
在 bundle 伺服器的日誌中(偵錯模式),可以看到 OPA 的 bundle 請求被處理的記錄:
{"level":"debug","time":"2022-11-25T23:50:54.627103-05:00","message":"HTTP request URI: /v1/bundles/signed-main.tar.gz, HTTP request headers: map[Accept-Encoding:[gzip] Authorization:[Bearer eyJ...] If-None-Match:[6...c] Prefer:[modes=snapshot,delta] User-Agent:[Open Policy Agent/0.46.1 (darwin, arm64)]]"}
ETag 的作用機制
ETag(Entity Tag)用於減少伺服器的頻寬需求,透過檢查快取內容是否有變更來決定是否需要重新下載。更多關於 ETag 的資訊可以參考 Mozilla 開發者檔案。
大規模 Bundle 管理方案探討
雖然 bundle 伺服器和 OPA 的 bundle 組態能夠有效地提供策略和資料,但手動管理和分發策略在大規模系統中難以擴充套件。因此,需要更完善的解決方案來實作策略的集中管理和安全分發。
可擴充套件的工具和服務
後續章節將介紹四種解決方案,以幫助擴充套件 bundle 管理和交付。這些工具和服務各有其優缺點,有些提供企業級支援和成熟的管理工具,有些則是開源軟體,需要更多的自定義工作。選擇合適的工具時,需要考慮其是否符合需求以及願意接受的權衡。
程式碼解析:OPA 組態檔案結構說明
以下是一個典型的 OPA 組態檔案範例,展示瞭如何組態服務、bundle 和驗證金鑰:
services:
- name: opa-bundle-api
url: https://example.com/v1/bundles
allow_insecure_tls: true
credentials:
bearer:
token: "your_bearer_token"
bundles:
project:
service: opa-bundle-api
resource: signed-main.tar.gz
persist: true
signing:
keyid: project-bundle-key
組態檔案重點解析
- 服務定義:定義了名為
opa-bundle-api
的服務,指定了 URL 和認證方式。 - Bundle 組態:指定了要下載的 bundle 名稱和儲存方式。
- 簽名驗證:定義了用於驗證 bundle 簽名的金鑰 ID。
詳細步驟說明
- 在
services
部分定義遠端服務的 URL 和認證資訊。 - 在
bundles
部分指定要使用的服務和資源名稱。 - 設定
persist
為true
以儲存下載的 bundle。 - 在
signing
部分指定用於驗證的金鑰 ID。
圖表說明:OPA 工作流程示意圖
graph LR A[OPA Server] -->|下載 Bundle| B[Bundle Server] B -->|傳回 Bundle| A A -->|驗證簽名| C[驗證成功] C -->|載入策略| D[更新策略] D -->|生效| E[應用策略]
圖表翻譯: 此圖示展示了 OPA Server 與 Bundle Server 的互動流程:
- OPA Server 向 Bundle Server 發起下載請求。
- Bundle Server 傳回所需的 Bundle。
- OPA Server 對收到的 Bundle 進行簽名驗證。
- 驗證成功後,載入並更新內部策略。
- 最終應用新的策略組態。
最佳實踐與注意事項
- 安全組態:確保使用安全的連線方式(HTTPS)並正確組態認證資訊。
- 定期更新:設定合理的輪詢間隔以保持策略的即時更新。
- 日誌監控:定期檢查 OPA 和 Bundle Server 的日誌以發現潛在問題。
- 備份機制:確保對關鍵組態和資料進行適當備份。
詳細實作建議
- 在生產環境中使用有效的 TLS 證書。
- 使用安全的 Token 管理機制。
- 組態適當的日誌記錄和監控告警機制。
- 建立完善的變更管理和稽核流程。