概要
JobSchedulerのWorkflowでは、実行内容に応じて、ID/Password、API Key、秘密鍵 など、機密情報を取り扱うことも多くあるかと思います。
そこでsecret管理の一例として、Hashicorp Vault を使った対応手段をご紹介します。
(認証連携ではなく、機密情報管理にVaultを利用した事例です)
前提条件
- JS7 jobschedulerの基本操作を習熟していること。※Workflow設計や変数利用の知識が必要です。
まだの方はこちらから JS7® JobScheduler 関連記事の目次 - 本記事はJobScheduler Ver. 2.5.1 で確認した内容です。
今後内容が変わる可能性があります。 - HashiCorp Vault が別途稼働しており、agentではvault、aws、jq コマンドが利用できるものとします。
※本記事ではVaultの細かな操作方法についての説明は行いません。
docker(alpine)環境を利用している場合は、下記操作で各コマンドを利用できます。
apk add --no-cache vault libcap aws-cli jq
setcap cap_ipc_lock=/usr/sbin/vault
処理の流れ
- JobResource
- JS7_VAULT_CONNECT
vault のhttp(s)接続情報および認証情報を定義
- JS7_VAULT_CONNECT
- Workflow(のシェルJob)
- vault_auth
Vault認証を行い、認証Tokenを後続処理に渡す - kv_job
認証Tokenを用いて、Vault KV Secret (ID/Passwordなど)を読み取り、処理を実行 - vault_get_aws_secret
認証Tokenを用いて、Vault AWS Secret (一時IAM AccessKey) を取得し、後続処理に渡す - aws_job
一時IAM AccessKeyを用いて、aws関連処理を実行 - vault_revoke_lease
Vault AWS Secret(一時IAM AccessKey)の破棄 - vault_revoke_token
Vault 認証Tokenの破棄
- vault_auth
※JobResourceの情報は、controller, agentのジャーナルに平文で保存されます。つまり定義した認証情報は、controller,agentにて閲覧できます。
【参考】Workflow実行時のデータの流れ
本記事では、userpass認証を例に取り上げますが、より安全な利用を目指す場合は、Vaultの認証情報が不要となる手段(AWS Auth など)の使用をお勧めします。
また、本記事ではシンプルにagent1台構成とします。
(認証以降の後続処理を別agentで実行することも可能です)
Vault 環境準備
vaultに、本記事で利用するkv, aws secret および userpass認証の設定を追加します。
# kv (version 1) の設定およびPolicy
vault secrets enable -path=kv/myapp -description="kv for myapp" kv
vault kv put kv/myapp/system -<<EOF
{
"hostname": "system",
"ipaddress": "192.168.0.1",
"login_user": "user01",
"login_password": "P@ssw0rd"
}
EOF
vault kv get kv/myapp/system
vault policy write kv_myapp_policy -<<EOF
# kv/myapp/ 以下のkey-value 管理
path "kv/myapp/*" {
capabilities = [ "create", "read", "update", "delete", "list" ]
}
EOF
# vault aws secret の設定、Policy追加 ※別途AWS側でPolicy追加等必要
# 詳しくはこちら https://qiita.com/saitamanokusa/items/24888fdcabaa7b153be5#aws
# ※aws の利用の用途がない場合は飛ばしてください
access_key="xxxxxxxxxxx"
secret_key="xxxxxxxxxxx"
region="ap-northeast-1"
vault secrets enable -path=aws/myapp -description="aws for myapp" aws
vault write aws/myapp/config/root access_key="$access_key" secret_key="$secret_key" region=$region
vault write aws/myapp/config/lease lease=15m lease_max=1h
vault write aws/myapp/roles/s3_full credential_type=iam_user policy_arns=arn:aws:iam::aws:policy/AmazonS3FullAccess
vault policy write aws_myapp_policy -<<EOF
# aws/myapp/ 以下の管理
path "aws/myapp/*" {
capabilities = ["create", "read", "update", "list" , "delete"]
}
path "sys/leases/lookup/aws/myapp/creds/*" {
capabilities = ["create", "update"]
}
path "sys/leases/revoke/aws/myapp/creds/*" {
capabilities = [ "read","update"]
}
EOF
# userpass 認証でのログイン設定追加、認証有効期限1h、上記kv, aws 利用Policyが付与されるように
vault auth enable userpass
vault write auth/userpass/users/js7user password=js7password
vault write auth/userpass/users/js7user token_ttl=1h token_max_ttl=1h token_explicit_max_ttl=4h
vault write auth/userpass/users/js7user policies=kv_myapp_policy,aws_myapp_policy
vault read auth/userpass/users/js7user
vaultの習得は、チュートリアルが充実していますので、そちらをご参照ください。
https://developer.hashicorp.com/vault/tutorials
主なコマンド操作はこちらにも記載があります。
https://qiita.com/saitamanokusa/items/24888fdcabaa7b153be5
JobResource作成
Vault接続情報、および認証情報を環境変数を定義します。
Vault接続、認証関連の情報は、各Workflow、Jobで利用するため、JobResourceで定義することにします。
/Practice
- Workflows
より、JobResourceを新規作成します。
JobResource名 : JS7_VAULT_CONNECT
Arguments : 使用しないため、空のままで
Environment : Variables に下記を設定します。
Name | Value |
---|---|
VAULT_ADDR |
https://vault.saitamanokusa.net:8200 (vaultのURL指定) |
VAULT_SKIP_VERIFY | true (vaultでhttps利用時の証明書チェックをスキップ) |
VAULT_FORMAT | json (vaultコマンドの出力をjson形式に) |
VAULT_USER_ID | js7user (vault userpass認証を使用する場合) |
VAULT_PASSWORD | js7password (vault userpass認証を使用する場合) |
※JobResourceの情報は、controller, agentのジャーナルに平文で保存されます。つまりcontroller,agentにて、Vaultの認証情報を閲覧できます。
【参考】Workflow実行時のデータの流れ
より安全な利用を目指す場合は、Vaultの認証情報が不要となる手段(AWS Auth など)の使用をお勧めします。
workflow 作成
/Practice
- Workflows
より、Workflow名 job_using_vault
を新規作成します。
あとは下記のように設定を行います。
Workflow
JobResourceJS7_VAULT_CONNECT
は各Jobで利用するため、Workflowにて指定します。
Properties | 設定値 |
---|---|
Name | job_using_vault |
Title | Vault利用Job |
JS7_VAULT_CONNECT | /Practice/JS7_VAULT_CONNECT |
Job: vault_auth
Job Properties | 設定値 |
---|---|
Name | vault_auth |
Label | vault_auth |
Agent | primaryAgent |
Job Class | Shell |
Script | (下記参照) |
Scriptは下記のように設定します
※Vault認証を行い、認証Tokenを後続処理に渡します。
#!/usr/bin/env bash
set -euo pipefail
# vault auth token 取得
res=$(vault login -method=userpass -path=userpass username="$VAULT_USER_ID" password="$VAULT_PASSWORD")
[ $? -ne 0 ] && { echo "vaultの応答がありません" >&2 && exit 1; }
vault_token=$(echo "$res" | jq -r '.auth.client_token')
[ ! "$vault_token" ] || [ "$vault_token" = "null" ] && { echo -e "vaultの応答に問題があります\n${res}" >&2 && exit 1; }
# 標準出力でvaultのレスポンスを返す
echo "$res" | jq .
# js7 後続処理向けにorder変数を定義
[ "${JS7_RETURN_VALUES:=}" ] && echo "vault_token=$vault_token" >>"$JS7_RETURN_VALUES"
Job: kv_job
Job Properties | 設定値 |
---|---|
Name | kv_job |
(省略) |
Job環境変数で、vault認証Tokenを定義します ※以降の各Jobでも同様
Environment Variables | |
---|---|
VAULT_TOKEN | $vault_token |
Scriptは下記のように設定します
※認証Tokenを用いて、Vault KV Secret (ID/Passwordなど)をjson形式で取得し、必要な処理を実行します。
#!/usr/bin/env bash
set -euo pipefail
res=$(vault kv get kv/myapp/system)
[ $? -ne 0 ] && { echo "vaultの応答がありません" >&2 && exit 1; }
myapp_hostname=$(echo "$res" | jq -r '.data.hostname')
myapp_ipaddress=$(echo "$res" | jq -r '.data.ipaddress')
myapp_user=$(echo "$res" | jq -r '.data.login_user')
myapp_password=$(echo "$res" | jq -r '.data.login_password')
# secret情報を使った処理を実行(処理は任意)
echo "hostname: $myapp_hostname"
echo "ipaddress: $myapp_ipaddress"
echo "user: $myapp_user"
echo "password: $myapp_password"
Job: vault_get_aws_secret
Job Properties | 設定値 |
---|---|
Name | vault_get_aws_secret |
(省略) |
Environment Variables | |
---|---|
VAULT_TOKEN | $vault_token |
Scriptは下記のように設定します
※認証Tokenを用いて、Vault AWS Secret (s3_full権限をもった一時AccessKey)を取得し、後続処理に渡します。
またAccessKey利用終了後の処理のために、lease_id も後続処理に渡します。
#!/usr/bin/env bash
set -euo pipefail
vault_path="aws/myapp"
vault_role="s3_full"
# vault secret 取得
res=$(vault read "${vault_path}/creds/${vault_role}")
[ $? -ne 0 ] && { echo "vaultの応答がありません" >&2 && exit 1; }
# 動作確認および js7後続処理用に値のチェック
vault_lease_id=$(echo "$res" | jq -r '.lease_id')
access_key=$(echo "$res" | jq -r '.data.access_key')
secret_key=$(echo "$res" | jq -r '.data.secret_key')
security_token=$(echo "$res" | jq -r '.data.security_token')
[ "$security_token" = "null" ] && security_token=""
# 応答内容に問題がある場合はエラーを返す
[ ! "$vault_lease_id" ] || [ "$vault_lease_id" = "null" ] && { echo -e "vaultの応答に問題があります\n${res}" >&2 && exit 1; }
# 即応答を返すと、取得したAccessKeyで認証エラーが出るため、
# aws認証が通るようになるまで待機
export AWS_ACCESS_KEY_ID="$access_key"
export AWS_SECRET_ACCESS_KEY="$secret_key"
export AWS_SESSION_TOKEN="$security_token"
declare count=0
while ! aws sts get-caller-identity >/dev/null 2>&1; do
sleep 1
count=$((count + 1))
[[ $count -gt 60 ]] && { echo "aws 認証タイムアウト" >&2 && exit 1; }
done
# 認証が不安定であるため追加で5秒待機
sleep 5
# 標準出力でvault のレスポンスを返す
echo "$res" | jq .
# JS7 workflowでの実行の場合は、$JS7_RETURN_VALUES にorder変数をセット
[ "${JS7_RETURN_VALUES:=}" ] && {
echo "vault_lease_id=$vault_lease_id"
echo "aws_access_key_id=$access_key"
echo "aws_secret_access_key=$secret_key"
echo "aws_session_token=$security_token"
} >>"$JS7_RETURN_VALUES"
Job: aws_job
Job Properties | 設定値 |
---|---|
Name | aws_job |
(省略) |
Job環境変数に一時AccessKeyを定義することで、aws-cliで利用できるようにします。
※AWS_SESSION_TOKENは、assume_role認証利用時に使用します。
Environment Variables | |
---|---|
AWS_ACCESS_KEY_ID | $aws_access_key_id |
AWS_SECRET_ACCESS_KEY | $aws_secret_access_key |
AWS_SESSION_TOKEN | $aws_session_token |
Scriptは下記のように設定します
※(一時AccessKeyを使って)awsコマンドで必要な処理を行います。
#!/usr/bin/env bash
set -euo pipefail
# aws 関連の処理を実行(処理は任意)
aws sts get-caller-identity
sleep 1
aws s3 ls --output text
sleep 1
Job: vault_revoke_lease
Job Properties | 設定値 |
---|---|
Name | vault_revoke_lease |
(省略) |
Job環境変数に$vault_lease_id
を定義することで、vault lease revoke に使用します。
Environment Variables | |
---|---|
VAULT_TOKEN | $vault_token |
VAULT_LEASE_ID | $vault_lease_id |
Scriptは下記のように設定します
※VAULT_LEASE_IDを指定して、AWS Secret(一時IAM AccessKey)を破棄します。
#!/usr/bin/env bash
set -euo pipefail
# vault lease revoke処理
if ! vault lease revoke "$VAULT_LEASE_ID"; then
echo "vaultの応答がありません" >&2
exit 1
fi
Job: vault_revoke_token
Job Properties | 設定値 |
---|---|
Name | vault_revoke_token |
(省略) |
Environment Variables | |
---|---|
VAULT_TOKEN | $vault_token |
Scriptは下記のように設定します
※指定した認証Tokenを破棄します。
#!/usr/bin/env bash
set -euo pipefail
# vault 認証Token破棄
if ! vault token revoke -self; then
echo "vaultの応答がありません" >&2
exit 1
fi
動作チェック
JobResource、WorkflowのDeployをしましたら、実際に実行してみてください。
もしエージェントを複数台利用している場合は、2つ目以降の処理を別エージェントにしても、上手く処理が流れることを確認してみてください。
必要な変数値が後続処理に渡されるため、エージェント間をまたいだ処理も可能です。
認証は常に特定のエージェントで行い、後続の実務処理は用途に応じた環境で行うことも可能。
【参考】VaultコマンドのWeb API対応について
vaultはWebAPIにも対応していますので、例えば下記のようにも書き換えられます。
(vaultコマンドを追加しなくとも対応可能)
なおvault認証の処理は、vaultコマンドを使ったほうが楽です。
※認証方式によっては、ややこしくなる
# vault kv secret
res=$(curl -s -k \
-H "X-Vault-Request: true" -H "X-Vault-Token: $VAULT_TOKEN" \
"${VAULT_ADDR}/v1/kv/myapp/system")
# vault aws secret
res=$(curl -s -k \
-H "X-Vault-Request: true" -H "X-Vault-Token: $VAULT_TOKEN" \
"${VAULT_ADDR}/v1/${vault_path}/creds/${vault_role}")
# vault lease revoke
curl -s -k -X PUT \
-H "X-Vault-Request: true" -H "X-Vault-Token: $VAULT_TOKEN" \
-d '{"sync":false}' \
"${VAULT_ADDR}/v1/sys/leases/revoke/${VAULT_LEASE_ID}"
# vault token revoke
curl -s -k -X PUT \
-H "X-Vault-Request: true" -H "X-Vault-Token: $VAULT_TOKEN" \
"${VAULT_ADDR}/v1/auth/token/revoke-self"
最後に
今回ご紹介した方法を用いることで、JobScheduler上にも、実行スクリプト上にも、機密情報を極力保持しない運用が可能です。
また機密情報が一か所管理されることで、機密情報の定期更新などの対応も容易となります。
より安全な環境を検討する際の参考にしていただければ幸いです。