LoginSignup
0
0

More than 1 year has passed since last update.

JS7® JobScheduler - HashiCorp Vaultを利用したSecret対応処理

Last updated at Posted at 2023-03-06

概要

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

処理の流れ

本記事では、下記のWorkflowを作成します。
image.png

  • JobResource
    • JS7_VAULT_CONNECT
      vault のhttp(s)接続情報および認証情報を定義
  • 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の破棄

※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を新規作成します。
image.png

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を新規作成します。
あとは下記のように設定を行います。
image.png

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をしましたら、実際に実行してみてください。

image.png

もしエージェントを複数台利用している場合は、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上にも、実行スクリプト上にも、機密情報を極力保持しない運用が可能です。
また機密情報が一か所管理されることで、機密情報の定期更新などの対応も容易となります。
より安全な環境を検討する際の参考にしていただければ幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0