Yuanchieh's Blog

Yuanchieh's Blog

生命是長期而持續的累積

23 Apr 2020

Vault 教學-集中化管理機敏資料(上)

內容摘錄自影片介紹,要討論 Vault 的功能之前,先退一步回來看最一般的機敏資料的定義與管理方式

機敏資料主要分成 驗證(Authentication)授權(Authorization),一者表明身份一者決定操作的權限,可能是 DB 的權限 / 第三方服務的 API Token / 用來加解密的對稱金鑰等等,這些資料可能被放在原始碼當中 / config 文件 / 環境變數 / 版本控制等等四散各地,之前 Github 有提出許多開發者都誤上傳機敏資料;
另一方面時間一久沒有人知道到底發出去哪些 key 又或是誰有在用,糟糕的是可能一組 key 有多個人使用

Vault 提出三個層級的解決方案
1. 集中化管理
搭建 Vault Server 集中管理所有的機敏資料,在 Vault Server 中確保所有的機敏資料都是被加密儲存,同時 Client 來跟 Server 要機敏資料時傳輸過程也是加密的,安全性大幅提升;
且有 Vault 管理,可以定期 Rotate,並隨時查看目前的機敏資料使用狀況
2.動態生成
透過動態生成方式,每一個 Client 來要即使權限需求相同,也動態生成不同的 key,如果有異常操作,就能很清楚是哪一個 Client 有問題,接著直接 revoke 掉就行了;
如果大家都共用一把 key,就查不出是誰也不能直接 revoke,造成安全上的漏洞
3.Encrpyt as Service
API Server 設計時,我們常需要加解密資料,所以會需要金鑰與實作加解密演算法,但是一方面金鑰外流到 API Server 上多一層風險,再者如果加解密演算法有漏洞又是另一個風險;
所以 Vault Server 本身也提供加解密 API,讓金鑰與演算法的保障都留在 Vault Server 上,大幅降低風險。

架構上,Vault 拆成幾個模組增加相容性

  1. Authentication
    基於人員的驗證,可以透過第三方的架構如 AWS/LDAP,或是K8S
  2. Audit
    將所有的操作都記錄下來,可以存放在 system logs 或是其他儲存方式
  3. Secret
    機敏資料的型態,可能是 Key-Value,也可能是第三方服務如 Database / AWS 等
  4. Storage
    機敏資料的儲放位置

以下教學將初步探索 Vault 功能為主,只適用於學習與測試環境使用!
下一篇實作篇,以 AWS 為主,動態 Launch API Server 時主動索取機敏資料,被配與動態 MongoDB 金鑰與 AWS Role

Vault 初探

Vault 官方教學 是一個互動式的教學,蠻簡單明瞭的,以下是整理的筆記,僅適用於學習,完全不建議用在正式環境!

先到 官方載點下載並安裝,設定好路徑之後可以安裝 auto complete $vault -autocomplete-install,vault 會偵測 shell 安裝對應的插件

啟動測試環境的 Vault server

$ vault server -dev 接著會在 terminal 打印出 Root Token: 的字樣,此時 vault server 會跑在前景;
開啟另一個 terminal tab,將其輸出至環境變數等等會使用上 export VAULT_DEV_ROOT_TOKEN_ID="{替換 Root Token}",接著指定 vault server url $export VAULT_ADDR='http://127.0.0.1:8200'
透過 $ vault status 檢查是否指定正確

取得/新增/更新/刪除 secret

先前提到 Secret 有很多種,從最簡單的 key value 開始,Vault 通過以下指令設定

  1. vault kv put secret/{path} {key}={vaule} {key2}={value2}
  2. vault kv get -format=json secret/{path}
    format 是選擇性回指定回傳格式
  3. vault kv delete secret/{path}

可以把 Vault Server 儲存密碼的方式想成 RESTful API Server,指定資源的路徑,採用最長匹配方式,secret 是預設的前綴,後面的path 用 / 分隔,接著的 kv 可以設定多組,put 同時代表新增與更新;

啟用 Secret 並設定 path

預設用 dev 啟動後 secret/ 路徑會對應到 KV 儲存方式,可以透過 $vault secrets list 查看,接著如果我們想要自訂其他的 Secret,或是指定新的路徑儲放 KV,可以透過 $vault secrets enable -path={path} {secret engine} 例如 $vault secrets enable -path=kv kv 就能指定 kv/ 為新的 key-value 儲存路徑

接著下 $vault kv put kv/hello foo=bar 可以得到相同的結果,透過 $vault secrets list 可以看出來多了一組設定

如果不要用了,可以透過 $vault secrets disable {path}/$vault secrets disable kv/ 刪除,注意底下儲存的密碼也都會一並消失

啟動 AWS Secret Engine 取得動態 AccessKey / AccessSecret

先前提到 Vault 一大特色是支援多種 Secret,而且 Secret 可以是動態生成的,每一個 Client 來索取都能要到獨立的 Secret

透過 aws secret engine,可以指定 Role 與權限並給予動態 secret,首先讓我們啟動 secret $vault secrets enable -path=aws aws,接著設定 aws 帳號

$ vault write aws/config/root \
    access_key=${AWS Access Key} \
    secret_key=${AWS Secret key} \
    region=${AWS Region}

絕對不建議在生產環境如此使用,因為 shell command 會被記錄在 log 中

後續的操作,Vault 就會用這組帳密去管理 IAM

接著創建角色

$ vault write aws/roles/my-role \
        credential_type=iam_user \
        policy_document=-<<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1426528957000",
      "Effect": "Allow",
      "Action": [
        "ec2:*"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
EOF

這裡給予了 ec2 全部的權限

接著要產生 Secret 通過 $vault read aws/creds/my-role,此時就會回傳一組新的 role,每次呼叫都會回傳獨立的 Secret (為什麼用 read 代表創建動作就不確定設計的原理了 :thinking_face),從 IAM Console 可以看到被創建的用戶

如果要 revoke 的話,通過 $vault lease revoke aws/creds/my-role/{lease id} 即可

剛剛想要查找有沒有指令可以條列出所有的 credentials,但看來是沒有這項指令,如果當初沒有記得 lease id 的話,就要去 IAM Console 手動刪除了

所有的指令可以從這邊看到 AWS Secret 文件,或是用 $vault path-help aws 查看,path-help 可以指定不同的路徑層次,例如 $vault path-help aws/config/lease

Authentication & Authorization

現在我們知道要如何建立與管理金鑰,但重點回到誰能來要金鑰以及權限劃分,Authentication 部分 Vault 可以用 token / AppRule / Github / AWS IAM / LDAP 等等,Authorization 部分可以制定 Policy,限縮用戶能夠操作的 Secret 權限

Token

Token 驗證機制是 Vault 核心、不能被關閉的驗證功能,像我們一開始啟動 Vault Server 後會產生一個 root token,接著會用 root token 去制定 Policy,並產生多組 Token,文件提到如果設定完就應該 revoke root token,最好是有 root token 時大家一起盯著螢幕操作後就 revoke,後續有需要可以在動態生成

  1. $vault operator init
  2. $vault operator generate-root
  3. 用 root token 生成其他 root token

在 Vault 中,Token 管理是階層化的,用母 Token 去生成的 Token 會自動變成子 Token,為了管理方便,如果母 Token 被 Revoke 那麼以下所有的子 Token 會全數被 Revoke,如果希望 Token 不受母 Token 影響,可以用設定為 Orphan Token,跳脫階層獨立管理

除了 Root Token,其餘的 Token 都有 TTL,TTL 設定有幾個地方

  1. Vault Server 啟動時的 Config
  2. 透過 mount tuning 去更改
  3. 根據母 Token 的Policy

在有些長駐的程式中,需要長時間持有 Token 可以設定為 Periodic,指定更新週期只要在這期間內去 renew 就能繼續使用 Token,同時可以指定 TTL 或是設為永不失效,等 TTL 到了會自動 revoke

Token 建立後會回傳 id / accessor / policy 等資訊,accessor 可以用來操作 token 相關的指令例如 renew / revoke / 反查 token id 等等,不太確定為什麼不直接用 token id 而是要多產生另一組 accessor 代號,但目前看到要條列出所有 token 只能透過列出 accessors 再去反查 token

以上的 Token 是指 Service Token,也就是預設最常使用的 Token 形式;另外還有 Batch Token 用在 Vault 操作上,暫時沒有看到用處就先略過

接著實際操作看看

// 透過 root 生成的 token,ttl 預設為無過期
$ vault token create

----- 開啟另一個 shell,試著用剛剛的 token 登入
$ export VAULT_ADDR='http://127.0.0.1:8200'
$ vault login {token}
------

// 刪除剛才的 token
$ vault token revoke {token}

// 新增一個 ttl 為 1h 的 token
$ vault token create -ttl="1h"

其他的部分,覺得透過 api 下指令比較方便,東西也比較全面,可以參考文件

// 查看所有的 accessors
$ curl \
    --header "X-Vault-Token: {token}" \
    --request LIST \
    http://127.0.0.1:8200/v1/auth/token/accessors

// 查看各別 accessor
$ curl \
    --header "X-Vault-Token: {token}" \
    --request POST \
    --data '{"accessor": "{accessor id}"}' \
    http://127.0.0.1:8200/v1/auth/token/lookup-self


// 透過 accessor revoke token
$ curl \
    --header "X-Vault-Token: {token}" \
    --request POST \
    --data  '{"accessor": "{accessor token}"}'\
    http://127.0.0.1:8200/v1/auth/token/revoke-accessor

設定 Policy

接著就是設定 Policy 部分,限制不同 Token 能夠操作 Secret 的權限,Vault 撰寫 policy 的格式是採用 hcl,也就是 HashiCorp 自己內部的 Config 描述語言,在路徑下建立檔案 my-policy.hcl 並貼上以下內容

path "kv/foo" {
  capabilities = [ "create", "update" ]
}

path "secret/data/foo" {
  capabilities = [ "create", "update" ]
}

上述文件的意思是這個 Policy 只能操作 secret/foo 的新&更新,其他的操作都一率被禁止,/data/ 指的是預設 Key Value 的 Secret 路徑,如果是另外啟動的如 $vault secrets enable -path=kv kv,則可以不用加 /data 變成 kv/foo 即可

接著實際操作

$ vault policy write my-policy my-policy.hcl
$ vault token create -policy=my-policy

可以看到 token 的 policy 窩了自定義的 my-policy

接著開另一個 shell 登入

$ vault login {token}
$ vault kv put secret/foo bar=baz

// permission denied
$ vault kv read secret/foo
$ vault kv put secret/foo/bar bar=baz

後來發現 $vault login 會影響所有的 shell,這點在測試要稍加留意

Vault Policy 設定可以很彈性,主要有幾項關鍵字

1. path

路徑選擇,可以用 * 代表以下所有路徑 / + 代表此路徑匹配任何字元等方式,如果有多組 path,Vault 採取最長匹配的原則

2. capabilities

權限選擇,有 [read, create, update, delete, list, sudo, deny],文件提到 Vault 沒有很仔細區分 create/update 的使用場景,所以要給就一起給;
sudo 指的是受 root 保護的權限 / deny 是禁止任何操作包含 sudo 也不行

3. allowed_parameters & denied_parameters & required_parameters

針對參數的設定,可以條列允許/不允許/必備的參數,如果沒有特別設定,則代表不受任何限制

4. min_wrapping_ttl & max_wrapping_ttl

產生子 Token 的最長與最短 TTL

以下是綜合範例

path "secret/foo" {
  capabilities = ["create"]
  allowed_parameters = {
    "foo" = []
    "bar" = ["zip", "zap"]
  }
  min_wrapping_ttl = "1s"
  max_wrapping_ttl = "90s"
}

path "secret/bar/*" {
  capabilities = ["create"]
  required_parameters = ["bar", "baz"]
}

path "secret/baz/+/data-*" {
  capabilities = ["create"]
  denied_parameters = {
    "deny-*" = []
  }
}

針對 secret/foo,在創建時只允許參數 foo / bar,其中 foo 的 value 不受限,但是 bar 只能是 zip 或是 zap

第二條是指 secret/bar 底下的任意路徑適用,包含 secret/bar/baz 和 secret/bar/baz/foo…. 等等,並且必須包含 bar, baz 這兩個參數

第三條是路徑會匹配如 secret/baz/foo/data-bar,且不允許 deny- 開頭的 key

多重 Policy 與衝突

在 Token 創建時可以同時綁定多組 Policy,不免讓人好奇如果兩組 Policy 對同一路徑產生衝突時的狀況,實測發現是 capacities 相同路徑下會給予最綜合的權限,而不是按照順序覆蓋之類的

可以用 $vault token capabilities {token} {path} 查詢

改變 Policy

當改變 Policy 時,原本已經建立好的 Token 不會動態套用,需要刪除重建,而新增/更新 Policy 的方式用 $ vault policy write {policy_name} my-policy.hcl即可

統整

僅僅介紹基本功能就花了不少篇幅,涵蓋了基本的 Secrets / Auth 的功能介紹與操作

  1. 啟動 dev server,關掉資料則全部消失
$ vault server --dev
  1. 登入與設定路徑
$ vault login {token}
$ export VAULT_ADDR='http://127.0.0.1:8200'
  1. Secret Engine 是與路徑匹配,secret/data 是預設 Key Store Secret Engine,可以另外開啟新的路徑
$ vault secrets enable -path=kv kv
// 條列
$ vault secrets list -detailed
$ vault secrets disable {path}
  1. 新增&更新/取得/刪除 Token
$ vault kv put kv/foo foo="123"
$ vault kv get kv/foo
$ vault kv delete kv/foo
  1. Authentication
$ vault token create -ttl="1h" -policy="my_policy"
$ vault token revoke {token id}
  1. Authorization
    另外用 hcl 檔儲存 Policy,並匯入設定中
$ vault policy write my-policy my-policy.hcl
$ vault token capabilities {token} {path}
$ vault policy read my-policy
$ vault policy list
$ vault policy delete my-policy  

想不到初步介紹就花這麼大的篇幅,下一篇要實作結合 AWS 與搭配 API Server 的流程