是什么
HashiCorp Vault 是一個(gè)用于管理密碼、密鑰和證書等秘密的系統(tǒng),同時(shí)還可提供有身份認(rèn)證和授權(quán)保護(hù)的加密服務(wù)。利用 Vault 提供的UI、CLI或HTTP API,可以在嚴(yán)格的控制和審計(jì)下安全地存儲(chǔ)和管理敏感數(shù)據(jù)。
為什么
在現(xiàn)代系統(tǒng)中,需要諸如數(shù)據(jù)庫(kù)訪問(wèn)憑證、外部API訪問(wèn)密鑰、服務(wù)間調(diào)用憑證等多種大量秘密信息,這些信息分散存儲(chǔ)在純文本、配置文件、源代碼或其他位置。如果沒(méi)有專門的解決方案,僅靠散落在各平臺(tái)自身的機(jī)制,很難弄清楚誰(shuí)在訪問(wèn)哪些密鑰,很難做好安全存儲(chǔ)、密鑰輪換和安全審計(jì)等工作。
Vault 通過(guò)將所有這些憑據(jù)集中起來(lái),在一個(gè)地方定義,減少了不必要的暴露,并提供了訪問(wèn)的安全性和審計(jì)的方便性。
工作原理
Vault 主要通過(guò)與安全策略關(guān)聯(lián)的令牌來(lái)控制客戶端對(duì)秘密的訪問(wèn)。安全策略由一組描述路徑及其操作可訪問(wèn)性的安全規(guī)則組成。令牌可以手動(dòng)創(chuàng)建并授予客戶端,也可以由客戶端通過(guò)登錄自行獲取。。

Vault 的核心工作流為:
- 客戶端提供身份識(shí)別信息
- Vault 通過(guò) LDAP、GitHub、AppRole 等可信第三方驗(yàn)證客戶端
- Vault 將定義好的安全策略關(guān)聯(lián)到令牌,并授予客戶端
- 客戶端通過(guò)令牌訪問(wèn)秘密

功能特性
Vault 在對(duì)秘密數(shù)據(jù)持久存儲(chǔ)之前會(huì)對(duì)其進(jìn)行加密,因此其原始存儲(chǔ)也是安全的。
Vault 可為 AWS、SQL 數(shù)據(jù)庫(kù)等系統(tǒng)提供臨時(shí)訪問(wèn)憑據(jù),并在到期后作廢。
Vault 也可以根據(jù)安全團(tuán)隊(duì)定義的加密參數(shù)提供公共的數(shù)據(jù)的加密、解密服務(wù),而不必存儲(chǔ)加解密數(shù)據(jù)。
Vault 使用租期管理存儲(chǔ)其中的各種秘密,到期前沒(méi)有被續(xù)租的秘密將廢棄。
Vault 允許主動(dòng)廢棄單個(gè)、相關(guān)、特定類型的秘密,實(shí)現(xiàn)密鑰輪換或鎖定系統(tǒng),以防范入侵。
版本
Vault 共有開(kāi)源版、云托管版和企業(yè)版3種版本。
開(kāi)源版需要自托管,提供動(dòng)態(tài)秘密管理、加密和數(shù)據(jù)保護(hù)等基本功能特性,需要通過(guò)社區(qū)獲得支持。
云托管版本,名為 HCP Vault(HashiCorp Cloud Platform Vault),與自托管 Vault 具有相同的二進(jìn)制文件,在保證一致用戶體驗(yàn)的同時(shí),允許組織快速使用 Vault。
企業(yè)版需要自托管,比開(kāi)源版多了擴(kuò)展、容災(zāi)等企業(yè)特性,并由HashiCorp提供服務(wù)支持。
快速體驗(yàn)
安裝
# 安裝 yum-config-manager 來(lái)管理存儲(chǔ)庫(kù)。
sudo yum install -y yum-utils
# 使用 yum-config-manager 添加官方的 HashiCorp Linux 存儲(chǔ)庫(kù)。
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
# 安裝
sudo yum -y install vault
# 或者安裝企業(yè)版
sudo yum -y install vault-enterprise
# 驗(yàn)證
vault
啟動(dòng)
# 查看啟動(dòng)幫助
vault server -help
# 以開(kāi)發(fā)模式啟動(dòng),數(shù)據(jù)仍然加密,但保存在內(nèi)存中,使用http偵聽(tīng),僅用于快速體驗(yàn),不能用于生產(chǎn)
vault server -dev
# 記錄日志中輸出的VAULT_ADDR、Unseal Key、Root Token
# 在一個(gè)新終端中進(jìn)行如下操作
# 留存Unseal Key到某個(gè)地方
echo "把這段文字用Unseal Key替換" > unseal.key
# 根據(jù)server啟動(dòng)日志中的信息設(shè)置環(huán)境變量VAULT_ADDR,表示server的訪問(wèn)地址
export VAULT_ADDR='http://127.0.0.1:8200'
# 根據(jù)server啟動(dòng)日志中的信息設(shè)置環(huán)境變量VAULT_TOKEN,表示root訪問(wèn)令牌
export VAULT_TOKEN="用Root Token替換這段文字"
# 查看服務(wù)狀態(tài)
vault status
處理鍵值對(duì)secret
# 查看處理鍵值對(duì)的命令幫助
vault kv -help
# 查看put鍵值對(duì)命令幫助
vault kv put -help
# 創(chuàng)建一個(gè)名為hell、包含foo、excited兩個(gè)Key的鍵值對(duì)secret。
# 每針對(duì)同名secret put一次,其元數(shù)據(jù)中的version就會(huì)遞增
vault kv put -mount=secret hello foo=world excited=yes
# 讀取名為hello的鍵值對(duì)secret,響應(yīng)中會(huì)顯示其元數(shù)據(jù)和鍵值數(shù)據(jù)
vault kv get -mount=secret hello
# 讀取kv中excited鍵的值
vault kv get -mount=secret -field=excited hello
# 以json格式輸出,并使用jq命令提取excited這個(gè)key的數(shù)據(jù)
vault kv get -mount=secret -format=json hello | jq -r .data.data.excited
# 刪除名為hello的鍵值對(duì)secret
vault kv delete -mount=secret hello
# 恢復(fù)無(wú)意刪除的名為hello的secret到第2個(gè)版本
vault kv undelete -mount=secret -versions=2 hello
引擎
秘密引擎是 Vault 用于儲(chǔ)存、生成和加解密的插件化的組件,鍵值只是其中一種,其它存儲(chǔ)引擎還有數(shù)據(jù)庫(kù)、Transit(加解密服務(wù)引擎)、SSH、Time-baseed OTP、AWS、Consul等,也可以自定義。
# 秘密引擎需要啟用后才能使用。
# 同一種存儲(chǔ)引擎可以在不同的路徑(path)上啟用。
# 開(kāi)發(fā)模式預(yù)設(shè)啟用了鍵值引擎。
vault secrets enable -path=kv kv
# 啟用秘密引擎的路徑默認(rèn)為秘密引擎的名稱
vault secrets enable kv
# 以下命令用于列出所有秘密引擎
vault secrets list
# 以下命令使用 path/秘密名稱 的形式設(shè)置名為hello的鍵值數(shù)據(jù)
vault kv put kv/hello target=world
# 以下命令可禁用路徑為kv下的秘密引擎,并刪除關(guān)聯(lián)的秘密和配置
vault secrets disable kv/
生產(chǎn)模式安裝
建立配置文件
Vault使用HCL文件進(jìn)行配置,首先創(chuàng)建一個(gè)HCL配置文件,可以命名為config.hcl。
storage "raft" {
path = "./vault/data"
node_id = "node1"
}
listener "tcp" {
address = "[::]:8200"
cluster_address = "[::]:8201"
tls_cert_file = "/etc/certs/vault.crt"
tls_key_file = "/etc/certs/vault.key"
}
api_addr = "https://127.0.0.1:8200"
cluster_addr = "https://127.0.0.1:8201"
ui = true
storage 指示后端存儲(chǔ)方式,raft 是一種適合生產(chǎn)模式的后端存儲(chǔ)。
listener 用于配置偵聽(tīng) API 請(qǐng)求的地址,生產(chǎn)環(huán)境應(yīng)啟用 TLS 。
api_addr 用于備用服務(wù)器向主服務(wù)器重定向客戶端的地址。
cluster_addr 為同一集群下各node互相通信的地址,用于備用服務(wù)器forward客戶端請(qǐng)求到主服務(wù)器,必須使用TLS。
ui用于啟用默認(rèn)不啟用的Web UI。
不支持內(nèi)存鎖定時(shí),需要添加配置:disable_mlock = true
建立raft工作目錄
啟動(dòng)服務(wù)器
vault server -config=config.hcl
初始化
啟動(dòng)一個(gè)新的終端,并執(zhí)行如下命令以初始化服務(wù)器。
# 設(shè)置服務(wù)API地址
export VAULT_ADDR='http://127.0.0.1:8200'
# 初始化服務(wù)
vault operator init
初始化過(guò)程會(huì)輸出5個(gè)解封密鑰和1個(gè)root初始Token,需要安全的分發(fā)密鑰,避免一個(gè)人持有所有密鑰,以便當(dāng)Vault密封時(shí),至少需要3個(gè)密鑰才能解封。
解封
Vault在初始化后,以及每次重啟后,都需要解封才能讀取秘密數(shù)據(jù)。需要在不同的計(jì)算機(jī)上,分別執(zhí)行如下命令,并各提供一個(gè)不同的密鑰,才能完成解封。
登錄
使用初始化后獲得的Root令牌登錄Vault服務(wù)。
其它運(yùn)維命令
任一運(yùn)維人員可通過(guò)執(zhí)行 vault operator seal 命令再次密封Vault,可以通過(guò)執(zhí)行 pgrep -f vault | xargs kill 命令終止 Vault 進(jìn)程,通過(guò)執(zhí)行 rm -r ./vault/data 命令清除 Vault 數(shù)據(jù)。
使用Transit引擎進(jìn)行加解密
以下演示使用Vault Transit引擎進(jìn)行數(shù)據(jù)加解密(不存儲(chǔ))的過(guò)程,涉及管理員和客戶端兩個(gè)角色。
# (管理員) 以開(kāi)發(fā)模式及root token啟動(dòng) Vault
# 注:生產(chǎn)環(huán)境中,應(yīng)使用具有某些安全策略的其它Token執(zhí)行本任務(wù)
vault server -dev -dev-root-token-id root
# (管理員) 使用環(huán)境變量聲明Vault服務(wù)的地址,方便后續(xù)操作
export VAULT_ADDR=$VAULT_ADDR
# (管理員) 使用環(huán)境變量聲明要使用的令牌,方便后續(xù)操作
export VAULT_TOKEN=root
# (管理員) 啟用transit引擎
vault secrets enable transit
# (管理員) 啟用名為orders的加密環(huán)
vault write -f transit/keys/orders
# (管理員) 為客戶端創(chuàng)建名為app-orders的安全策略
vault policy write app-orders -<<EOF
path "transit/encrypt/orders" {
capabilities = [ "update" ]
}
path "transit/decrypt/orders" {
capabilities = [ "update" ]
}
EOF
# (管理員)為app-orders安全策略創(chuàng)建一個(gè)令牌
vault token create -policy=app-orders
# (客戶端)使用環(huán)境變量存儲(chǔ)管理員分配的令牌
export APP_ORDER_TOKEN="hvs.CAESIOVYYt5Cq5E0zZX3QVHEv5EYj94pGkGipUX48rI_f2wFGh4KHGh2cy5kdlhPSEhyR2pLa0hlODQwRDVkMzVuMWE"
# (客戶端)對(duì)明文base64編碼后,再調(diào)用Vault服務(wù)進(jìn)行加密
VAULT_TOKEN=$APP_ORDER_TOKEN vault write transit/encrypt/orders \
plaintext=$(base64 <<< "4111 1111 1111 1111")
# (客戶端)調(diào)用Vault服務(wù)對(duì)密文進(jìn)行解密
VAULT_TOKEN=$APP_ORDER_TOKEN vault write transit/decrypt/orders \
ciphertext="vault:v1:cZNHVx+sxdMErXRSuDa1q/pz49fXTn1PScKfhf+PIZPvy8xKfkytpwKcbC0fF2U="
# (客戶端)對(duì)解密后的文本進(jìn)行base64解碼
base64 --decode <<< "NDExMSAxMTExIDExMTEgMTExMQo="
# (管理員)執(zhí)行如下命令進(jìn)行密鑰輪換
vault write -f transit/keys/orders/rotate
# (客戶端)再次調(diào)用加密服務(wù),此時(shí)密文開(kāi)頭會(huì)變?yōu)関ault:v2
vault write transit/encrypt/orders \
plaintext=$(base64 <<< "4111 1111 1111 1111")
# (客戶端)使用新密鑰包裝舊密文。Vault會(huì)自行先解密再用新密鑰加密。
vault write transit/rewrap/orders \
ciphertext="vault:v1:cZNHVx+sxdMErXRSuDa1q/pz49fXTn1PScKfhf+PIZPvy8xKfkytpwKcbC0fF2U="
# (管理員)啟用密鑰的24小時(shí)自動(dòng)輪換
vault write transit/keys/orders/config \
auto_rotate_period=24h
# (管理員) 查看密鑰信息,其中 auto_rotate_period 為輪換設(shè)置,默認(rèn)值0s表示禁用。
vault read transit/keys/orders
# (管理員) 設(shè)置最小可解密密鑰版本,默認(rèn)從1開(kāi)始所有版本的密鑰均可以解密
vault write transit/keys/orders/config \
min_decryption_version=5
# (管理員)導(dǎo)出密鑰(包括明文和密文),密文可以保存到Vault的鍵值引擎,
# 用于在Vault外對(duì)大文件進(jìn)行加解密
vault write -f transit/datakey/plaintext/orders
# (管理員)導(dǎo)入一個(gè)已有的外部密鑰
vault read -field=public_key transit/wrapping_key
使用數(shù)據(jù)庫(kù)引擎獲得動(dòng)態(tài)憑證
以下演示使用Vault的數(shù)據(jù)庫(kù)引擎來(lái)管理數(shù)據(jù)庫(kù)訪問(wèn)憑證,涉及管理員和應(yīng)用程序兩個(gè)角色。
# 首先做一些準(zhǔn)備工作,本例中使用docker方式啟動(dòng)一個(gè)postgres數(shù)據(jù)庫(kù)實(shí)例
# 拉取postgres的docker鏡像
docker pull postgres:latest
# 啟動(dòng) postgres 容器
docker run \
--detach \
--name learn-postgres \
-e POSTGRES_USER=root \
-e POSTGRES_PASSWORD=rootpassword \
-p 5432:5432 \
--rm \
postgres
# 通過(guò)docker在名為learn-postgres的容器中執(zhí)行psql命令,
# 來(lái)創(chuàng)建一個(gè)名為 ro 的角色,用于完成示例
docker exec -i \
learn-postgres \
psql -U root -c "CREATE ROLE \"ro\" NOINHERIT;"
# 為 ro 角色授予一些權(quán)限
docker exec -i \
learn-postgres \
psql -U root -c "GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"ro\";"
#--------以下進(jìn)入正式環(huán)節(jié)-----
# (管理員)以dev模式啟動(dòng)一個(gè)Vault服務(wù),方便演示
vault server -dev -dev-root-token-id root
# (管理員)設(shè)置環(huán)境變量,方便后續(xù)操作
export VAULT_ADDR=http://127.0.0.1:8200
export VAULT_TOKEN=root
export POSTGRES_URL=127.0.0.1:5432
# (管理員)在Vault中啟用數(shù)據(jù)庫(kù)引擎
vault secrets enable database
# (管理員) 配置 postgreql-database-plugin,包括連接信息
# connection_url支持以,分割的多個(gè)實(shí)例,插件將依次嘗試連接
vault write database/config/postgresql \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@$POSTGRES_URL/postgres?sslmode=disable" \
allowed_roles=readonly \
username="root" \
password="rootpassword"
# (管理員)創(chuàng)建一個(gè)可在postgres中建立角色的sql模板
tee readonly.sql <<EOF
CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}' INHERIT;
GRANT ro TO "{{name}}";
EOF
# (管理員)創(chuàng)建名為readonly的數(shù)據(jù)庫(kù)角色,創(chuàng)建語(yǔ)句將使用上面的sql模板
vault write database/roles/readonly \
db_name=postgresql \
creation_statements=@readonly.sql \
default_ttl=1h \
max_ttl=24h
# (應(yīng)用程序) 從Vault獲取一個(gè)只讀角色
# 返回結(jié)果中包括username、password,以及租約id和租期等信息
vault read database/creds/readonly
# 可使用如下命令驗(yàn)證獲得的角色是否存在
docker exec -i \
learn-postgres \
psql -U root -c "SELECT usename, valuntil FROM pg_user;"
# (管理員)列出所有租約
vault list sys/leases/lookup/database/creds/readonly
#(管理員)使用環(huán)境變量保存第一份租約的id
LEASE_ID=$(vault list -format=json sys/leases/lookup/database/creds/readonly | jq -r ".[0]")
# (管理員)續(xù)約
vault lease renew database/creds/readonly/$LEASE_ID
# (管理員)在租約到期前主動(dòng)撤銷
vault lease revoke database/creds/readonly/$LEASE_ID
# (管理員)按照前綴撤銷所有符合條件的租約
vault lease revoke -prefix database/creds/readonly
Vault還提供數(shù)據(jù)庫(kù)用戶名模板、密碼策略等功能。