Hashcorp Vault 基礎使用教程
我們在工作中是如何管理大量的 Secret 信息的?(比如筆者的項目中會涉及到對 OpenSSH 的祕鑰及口令存儲、以及對此的定時輪轉及外部調用)
-
以配置文件的形式固化,存放於服務器文件或者 Database 中
-
以代碼的方式存在於
git私有倉庫上,並嚴格控制此庫的訪問權限 -
以 KMS(Key Management Service,雲服務居多)方式託管在公有云服務上
-
來自雲上的挑戰:
Unlocking the Cloud Operating Model(https://www.hashicorp.com/cloud-operating-model)
我們需要實現的通用密碼倉庫需要滿足如下特性:
-
密文存儲(兼顧複雜性)
-
通用性,減少人工修改 Secret 的工作量,即提供 RestfulAPI
-
權限控制
本文要介紹 Vault 這款開源的 Secret 管理工具(口令、token、私鑰及證書等等),是管理代碼中口令、祕鑰(防止明文泄漏)極佳的應用實踐。此外,KMS(Key Management Service,雲服務居多)也是較好的 Secret 管理實踐。vault 項目源碼 在此(https://github.com/hashicorp/vault)
針對此類產品,需要着重關注以下幾點:
-
Secret 的存儲方式,支持的存儲後端
-
Secret 的加密方式及算法
-
系統的權限控制、權限分配(哪些人 / 客戶端能訪問哪些機器的 Secret)
-
系統的認證方式,客戶端使用什麼方式訪問 RestfulAPI
-
系統的 Secret 存儲方式及過期機制
-
系統的高可用性如何保證
-
系統對外接口的 QPS 及併發性能
0x01 Vault 基本原理
本小節參考官方文檔(https://www.vaultproject.io/docs/internals/architecture)。vault 的基礎應用場景如下:
Vault 的使用場景一般爲:
-
系統用戶(如運維同事)通過
HTTP-Vault-API、Vault 命令行工具等將 Secret data 寫入 Vault -
Vault 再將加密的數據存儲到後端
-
外部用戶(如開發人員,各類腳本或者應用程序)通過
HTTP-Vault-API、Vault 命令行工具等方式來獲取到僅僅與自己賬號相關聯的 Secret data,這裏就涉及到 Valut 的權限細粒度管理
vault 的架構如下:
從架構圖可以看出,Vault 分爲 Storage Backend、安全屏障(Barrier) 和 HTTP API 三個部分,Storage Backend 和 Vault 之間的所有數據流動都需要經過 Barrier,Barrier 確保只有加密數據會被寫入 Storage Backend,加密數據在經過 Barrier 被讀出的過程中被驗證與解密。
其他主要組件的功能如下:
-
HTTP(s) API: -
Storage backend: -
Token Store: -
Auth Method: -
Core:負責處理審覈代理(Audit brok)的請求及響應日誌,將請求發送到所有已配置的審覈設備(Audit devices) -
Policy store:負責管理和存儲 ACL Policies,由Core進行 ACL Policy 的檢查
Vault 的數據流
0x02 Vault 的主要運行流程
Step1:數據存儲及加密解密
瞭解幾個名詞:1、Storage Backend(後端存儲): Vault 自身不存儲數據,需要爲其配置 Storage Backend。注意!!Storage Backend 是不受信任的,只用於存儲加密數據
2、Initialaztion: Vault 在首次啓動時需要初始化,這一步生成一個加密密鑰(Encryption key)用於加密數據,加密完成的數據才能被保存到 Storage Backend
3、Unseal(解封): Vault 啓動後,因爲不知道加密密鑰(Encryption Key),它會進入封印(Sealed)狀態,在被解封前無法進行任何操作。Encryption Key 被 Master key 保護,必須提供 Master key 才能完成 Unseal 操作
Master key 和 Encryption Key 的關係如下圖所示:
Step2:認證 && 權限管理
在 Unseal(解封) 操作完成後,Vault 纔可以處理客戶端請求,客戶端首次連接 Vault 時,需要先完成身份認證。客戶端的身份認證方式有:
-
適合用戶:用戶名 / 密碼、LDAP 認證等,同時用戶需要被授予合適的權限用來訪問 Vault
-
適合應用:Public/Private keys、Tokens 或者 Jwt Token 等
此一般流程如下:
-
客戶端發起身份驗證請求,該請求流經
Core模塊並進入Auth methods,Auth methods確定請求是否有效並返回關聯策略(Policies)的列表。在通過Auth methods完成了身份認證,並且檢查的關聯策略也符合授權之後,Token Store將會生成並管理一個新的 Token, 這個 Token 會被返回給客戶端,用於進行後續請求。需要注意的是,此 token 也都存在一個 Lease 租期(有效期),同時 Token 關聯了相關的策略 Policies,這些策略將被用於驗證請求的權限。 -
請求經過驗證後,將被路由到
Secret engine模塊。如果Secret engine返回了一個 Secret(由 Vault 自動生成的 Secret),Core會將其註冊到Expiration manager,並給它附加一個lease ID。lease ID被客戶端用於更新(Renew)或吊銷(Revoke)它得到的 Secret。如果客戶端允許租約(Lease)到期,Expiration manager將自動吊銷這個 Secret Token
Step3:Secret Engine(重要)
Secret Engine 是 Vault 系統保存、生成或者加密數據的組件。Secret Engine 類似一個虛擬文件系統,所有的 read/write/delete/list 操作等都在它下面進行的,然後 Secret Engine 可以自己決定如何來響應請求。從代碼設計角度而言,Secret Engine 一種抽象,它對上層(調用方)提供統一的接口,如物理文件系統、數據庫等等,都可以統一使用增刪改查這些操作接口。常用的 Engine 有如下幾種:
-
kv:鍵值存儲。可看作一個加密的 Redis,只是單純地存儲 / 讀取一些靜態的配置 / 數據 -
Transit Secrets Engine:提供加密即服務的功能,只負責加密和解密,不負責存儲。主要應用場景是提供 App 加解密數據,但是數據仍舊存儲在 MySQL 等數據庫中 -
證書管理:最常見的場景是將根證書(root)存入 Vault,業務證書通過此 Engine 簽發
可以通過指令 vault secrets list 查看 Vault 中當前開啓了哪些 Secret Engine:
[root@VM_120_245_centos ~/vault]# vault secrets enable -path=secret_bifrost kv
Success! Enabled the kv secrets engine at: secret_bifrost/
[root@VM_120_245_centos ~/vault]# vault secrets list
Path Type Accessor Description
---- ---- -------- -----------
cubbyhole/ cubbyhole cubbyhole_e86bac2b per-token private secret storage
identity/ identity identity_19b16864 identity store
kv/ kv kv_988a3c7e n/a
secret/ kv kv_4a27cb62 n/a
secret_bifrost/ kv kv_0b5b6ac3 n/a
sys/ system system_8d02021f system endpoints used for control, policy and debugging
vault/ kv kv_e26a68a4 n/a
從指令輸出可知:有 4 個類型爲 kv(鍵值對加密存儲) 的引擎,分別加載到了 kv/、 secret/、secret_bifrost/ 及 vault/ 路徑上;其他幾個 Engine 是 Vault 內部支撐。之所以能夠向指定的路徑進行讀寫,是因爲有 Secret Engine 的支持,未加載的路徑是不能訪問的(會報錯),不能同時開啓相同的路徑。
此外,同一個 Secret Engine 可以被加載到不同的路徑下(一個 Secret Engine 類的多個實例),每個路徑下的數據都是彼此獨立的。
0x03 Vault Details
本小節介紹 Vault 實現上的一些細節。
Shamir 密鑰分享算法(shamir secret sharing)
Vault 中給出了 Shamir 算法的 實現,該密鑰分享算法的基本思想是分發者通過祕密多項式,將祕密 Secret 分解爲 n 個祕密持有者,其中任意至少於 k 個祕密均能恢復密文,即某一個祕密通常不能由單個持有者保存,必須將祕密分由多人保管並且只有當多人同時在場時祕密才能得以恢復。
Vault 認證方法
Vault 支持 如下 的身份認證機制:1、Token 方式 Token 是 Vault 內置的驗證方法,啓動時即被加載,且不能禁用。例如,服務器初始化時會輸出 Root Token,使用 Root Token 登錄的用戶具有系統最高的訪問權限。在 Vault 中,Token 是可繼承的樹型結構,此 <繼承> 包括兩方面的含義:
-
持有 Token 的用戶創建新的 Token(
Child Token),默認Child Token權限和父 Token 相同(如需特別指定權限) -
當撤銷(Revoke)某個 Token 時,其所創建的
Child Token,以及Child Token的Child Token(等等) 都會被一併刪除,從樹的角度不難理解
2、AppRole 方式 AppRole 是 Vault 爲 App 應用提供的一種較爲安全的認證方式,推薦使用。使用 AppRole 的一般流程爲(From 官網):
#1、創建只讀 policy 文件模板 readonly
[root@VM_120_245_centos /vault]# cat readonly
# Read-only permission on secrets stored at 'secret/data/mysql/webapp'
path "secret/data/mysql/webapp" {
capabilities = ["read"]
}
#2、加載 policy
[root@VM_120_245_centos /vault]# vault policy write readonly readonly<br>
Success! Uploaded policy: readonly
#3、創建帶 TTL 的 token
[root@VM_120_245_centos /vault]# vault write auth/approle/role/readonly token_policies="readonly" token_ttl=1h token_max_ttl=4h
Success! Data written to: auth/approle/role/readonly
#4、查看 readonly 的認證信息
[root@VM_120_245_centos /vault]# vault read auth/approle/role/readonly
Key Value
--- -----
bind_secret_id true local_secret_ids false secret_id_bound_cidrs <nil>
secret_id_num_uses 0
secret_id_ttl 0s
token_bound_cidrs []
token_explicit_max_ttl 0s
token_max_ttl 4h
token_no_default_policy false token_num_uses 0
token_period 0s
token_policies [readonly]
token_ttl 1h
token_type default
#5、查看 rold-id,vault read auth/approle/role/readonly/role-id
[root@VM_120_245_centos /vault]# vault read auth/approle/role/readonly/role-id
Key Value
--- -----
role_id 12afcbf7-33c9-d86f-e678-dc2beeb3fabd
#6、查看 secret-id
[root@VM_120_245_centos /vault]# vault write -force auth/approle/role/readonly/secret-id
Key Value
--- -----
secret_id 7c9e58d5-8af2-176b-8fbc-572db2f8c872
secret_id_accessor ff5c6dca-7b5c-587d-41e2-adaae932a669
secret_id_ttl 0s
#7、根據 role-id 和 secret-id 換取 token
[root@VM_120_245_centos /vault]# vault write auth/approle/login role_id="12afcbf7-33c9-d86f-e678-dc2beeb3fabd" secret_id="7c9e58d5-8af2-176b-8fbc-572db2f8c872"
Key Value
--- -----
token s.qIcsK6N6lm4TffWWcRIRfRSQ
token_accessor hDt3u9o9hjfHZwQ7Zisun7Vw
token_duration 1h
token_renewable true token_policies ["default" "readonly"]
identity_policies []
policies ["default" "readonly"]
token_meta_role_name readonly
#8、應用新 token 訪問集羣
[root@VM_120_245_centos /vault]# export VAULT_TOKEN=s.qIcsK6N6lm4TffWWcRIRfRSQ
[root@VM_120_245_centos ~/vault]# vault kv get secret/data/mysql/webapp
== Data ==
Key Value
--- -----
a 1
#9、越權訪問失敗(無法寫入)
[root@VM_120_245_centos /vault]# vault kv put secret/data/mysql/webapp key=abcd
Error making API request.
URL: GET http://127.0.0.1:8200/v1/sys/internal/ui/mounts/secret/data/mysql/webapp
Code: 403. Errors:
* permission denied
#9、Token 過期後訪問失敗
[root@VM_120_245_centos /vault]# vault kv get secret/data/mysql/webapp
Error making API request.
URL: GET http://127.0.0.1:8200/v1/sys/internal/ui/mounts/secret/data/mysql/webapp
Code: 403. Errors:
* permission denied
從實現上看,role-id 與 secret-id 相當於應用程序的用戶名和密碼,但是實際上不是如此。Vault 希望用此設計來解決 Secret Zero 問題。
3、各類認證方式比較 Token 方法比較簡便易用,但其目的是爲了支持自身的運行,安全性並不高。對於真正的用戶 / 機器認證場景,Vault 官方推薦使用其他更加成熟的機制,例如 LDAP,Github,AppRole 認證方式。
Vault 授權機制
存儲
Vault 支持多種存儲後端:https://github.com/hashicorp/vault/tree/master/plugins/database,生產架構的後端存儲使用 HA 方式進行部署,比如 consul/etcd/mysql 集羣等。
- mysql:
https://github.com/hashicorp/vault/blob/master/plugins/database/mysql/mysql.go
0x04 Vault 基本功能使用
1、配置 Mysql 作爲存儲後端,啓動 Vault
[root@VM_120_245_centos ~/vault]# cat vault.hcl
disable_mlock = true ui=true storage "mysql" {
address = "127.0.0.1:3306"
username = "root"
password = "xxxxxx"
database = "vault"
table = "vault"
}
listener "tcp" {
address = "127.0.0.1:8200"
tls_disable = 1
}
[root@VM_120_245_centos ~/vault]# vault server -config=vault.hcl
2、初始化 vault,得到 5 個子祕鑰及 root Token
[root@VM_120_245_centos ~/vault]# vault operator init
2021-08-20T21:34:41.973+0800 [INFO] core: security barrier not initialized
2021-08-20T21:34:42.002+0800 [INFO] core: security barrier initialized: stored=1 shares=5 threshold=3
2021-08-20T21:34:42.031+0800 [INFO] core: post-unseal setup starting
2021-08-20T21:34:42.060+0800 [INFO] core: loaded wrapping token key
2021-08-20T21:34:42.060+0800 [INFO] core: successfully setup plugin catalog: plugin-directory=""
2021-08-20T21:34:42.060+0800 [INFO] core: no mounts; adding default mount table
2021-08-20T21:34:42.080+0800 [INFO] core: successfully mounted backend: type=cubbyhole path=cubbyhole/
2021-08-20T21:34:42.081+0800 [INFO] core: successfully mounted backend: type=system path=sys/
2021-08-20T21:34:42.081+0800 [INFO] core: successfully mounted backend: type=identity path=identity/
2021-08-20T21:34:42.136+0800 [INFO] core: successfully enabled credential backend: type=token path=token/
2021-08-20T21:34:42.137+0800 [INFO] rollback: starting rollback manager
2021-08-20T21:34:42.137+0800 [INFO] core: restoring leases
2021-08-20T21:34:42.138+0800 [INFO] expiration: lease restore complete 2021-08-20T21:34:42.163+0800 [INFO] identity: entities restored
2021-08-20T21:34:42.164+0800 [INFO] identity: groups restored
2021-08-20T21:34:42.165+0800 [INFO] core: usage gauge collection is disabled
2021-08-20T21:34:42.174+0800 [INFO] core: post-unseal setup complete 2021-08-20T21:34:42.200+0800 [INFO] core: root token generated
2021-08-20T21:34:42.200+0800 [INFO] core: pre-seal teardown starting
2021-08-20T21:34:42.200+0800 [INFO] rollback: stopping rollback manager
2021-08-20T21:34:42.200+0800 [INFO] core: pre-seal teardown complete Unseal Key 1: xxx1
Unseal Key 2: xxx2
Unseal Key 3: xxx3
Unseal Key 4: xxx4
Unseal Key 5: xxx5
Initial Root Token: xxx-root-token
3、解封 vault,查看解封狀態
[root@VM_120_245_centos ~/vault]# vault operator unseal xxx1
[root@VM_120_245_centos ~/vault]# vault operator unseal xxx2
[root@VM_120_245_centos ~/vault]# vault operator unseal xxx3
[root@VM_120_245_centos ~/vault]# vault status
Key Value
--- -----
Seal Type shamir
Initialized true Sealed true Total Shares 5
Threshold 3
Unseal Progress 2/3
Unseal Nonce 95ba53e7-63e2-c998-e1b8-4df4bba20ea3
Version 1.8.1
Storage Type mysql
HA Enabled false
4、使用 root Token 登錄(首次)
[root@VM_120_245_centos ~/vault]# vault login root-token
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.
Key Value
--- -----
token xxx
token_accessor xxx
token_duration ∞
token_renewable false token_policies ["root"]
identity_policies []
policies ["root"]
5、開啓 vault 組件,測試寫 / 讀 / token 生成等操作
[root@VM_120_245_centos ~/vault]# vault secrets enable kv
2021-08-20T21:44:37.837+0800 [INFO] core: successful mount: namespace="" path=kv/ type=kv
[root@VM_120_245_centos ~/vault]# vault kv put kv/test api_key=abc1234 api_secret=1a2b3c4d^C
[root@VM_120_245_centos ~/vault]# vault kv get kv/test
======= Data =======
Key Value
--- -----
api_key abc1234
api_secret 1a2b3c4d
[root@VM_120_245_centos ~/vault]# vault token create -ttl 1h
Key Value
--- -----
token s.6sfhKw2J2dFfNSMdIyWQGqiS
token_accessor o8MjM5SuLewdJk0WSZ0oPPrF
token_duration 1h
token_renewable true token_policies ["root"]
identity_policies []
policies ["root"]
[root@VM_120_245_centos ~/vault]# export VAULT_TOKEN=s.6sfhKw2J2dFfNSMdIyWQGqiS
[root@VM_120_245_centos ~/vault]# vault kv get kv/test
======= Data =======
Key Value
--- -----
api_key abc1234
api_secret 1a2b3c4d
6、根據策略模板創建 token 創建 kv/test 路徑下只讀的策略 test-read-policy:
[root@VM_120_245_centos ~/vault]# cat limit-token.hcl
path "kv/test"{
capabilities = ["read"]
}
[root@VM_120_245_centos ~/vault]# vault policy write test-read-policy ./limit-token.hcl
Success! Uploaded policy: test-read-policy
根據策略創建 token:
[root@VM_120_245_centos ~/vault]# vault token create -policy=test-read-policy
Key Value
--- -----
token s.NMD47aWmzSUWC1bAqalQCWYw
token_accessor gi6WVfxwJnGqMXhDVoJXI5AU
token_duration 768h
token_renewable true token_policies ["default" "test-read-policy"]
identity_policies []
policies ["default" "test-read-policy"]
測試 token 的操作情況,只讀,寫報錯,符合既定策略:
[root@VM_120_245_centos ~/vault]# export VAULT_TOKEN=s.NMD47aWmzSUWC1bAqalQCWYw
[root@VM_120_245_centos ~/vault]# vault kv get kv/test
======= Data =======
Key Value
--- -----
api_key abc1234
api_secret 1a2b3c4d5e6f
[root@VM_120_245_centos ~/vault]# vault kv put kv/test api_key=foo api_secret=bar
Error writing data to kv/test: Error making API request.
URL: PUT http://127.0.0.1:8200/v1/kv/test
Code: 403. Errors:
* 1 error occurred:
* permission denied
7、查看和關閉 Secret engine
[root@VM_120_245_centos ~/vault]# vault secrets list
Path Type Accessor Description
---- ---- -------- -----------
cubbyhole/ cubbyhole cubbyhole_e86bac2b per-token private secret storage
identity/ identity identity_19b16864 identity store
kv/ kv kv_988a3c7e n/a
secret/ kv kv_a1c65202 n/a
sys/ system system_8d02021f system endpoints used for control, policy and debugging
[root@VM_120_245_centos ~/vault]# vault secrets disable secret/ #關閉 secret/
Success! Disabled the secrets engine (if it existed) at: secret/
[root@VM_120_245_centos ~/vault]# vault secrets list
Path Type Accessor Description
---- ---- -------- -----------
cubbyhole/ cubbyhole cubbyhole_e86bac2b per-token private secret storage
identity/ identity identity_19b16864 identity store
kv/ kv kv_988a3c7e n/a
sys/ system system_8d02021f system endpoints used for control, policy and debugging
8、開啓 V2 的 Secret Engine
[root@VM_120_245_centos ~]# vault secrets enable -path=secretv2 -version=2 kv
Success! Enabled the kv secrets engine at: secretv2/
[root@VM_120_245_centos ~]# vault secrets list
Path Type Accessor Description
---- ---- -------- -----------
bifrost_vault/ kv kv_962069cd n/a
cubbyhole/ cubbyhole cubbyhole_e86bac2b per-token private secret storage
identity/ identity identity_19b16864 identity store
kv/ kv kv_988a3c7e n/a
secret/ kv kv_4a27cb62 n/a
secret_bifrost/ kv kv_0b5b6ac3 n/a
secretv2/ kv kv_6e9e3b5b n/a
sys/ system system_8d02021f system endpoints used for control, policy and debugging
vault/ kv kv_e26a68a4 n/a
更多的使用可以參考官方文檔:https://learn.hashicorp.com 瞭解更多。
原文鏈接:https://pandaychen.github.io/2020/01/03/A-HASHCORP-VAULT-INTRO/
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/ijrkjUVLfN8SPdFVI-frFg