はじめに
漏えいしたサポートキーは失効済みです。サービスは利用できますが、集計ジョブの非公開設定には古い値が残り、認証できません。保留中の daily バッチには集計が必要です。
スコープを学び、代替キーを発行し、worker の非公開設定を更新して実際の業務実行を完了します。顧客ディレクトリへのアクセスが拒否され、無関係な監視が正常なことも試します。このラボには独立した封じ込め後の環境があり、前の VM は再利用しません。
読み取り専用の認証情報を試す
このステップでは、無効な認証情報と、有効でも権限が不足する認証情報を区別します。新しい環境は封じ込め後の状態です。key_support_old は失効済み、ジョブは古い秘密値を使用し、daily は未処理です。前のラボのファイルや VM は不要です。 キーとオペレータートークンはこの VM に属します。以前の環境の認証情報を再利用せず、ここで値を取得してください。
Workstation は /home/labex/project/repository の保守用ターミナルです。Integration は /home/labex/project で keyworker として動きます。Key Manager はサービスの保守 UI です。ローカル教材 API は http://127.0.0.1:8000 にあり、その架空の動作はこのコース固有です。
スコープは名前付き操作を行う権限です。最小権限とは、ジョブに必要な操作を許可し、無関係な操作を禁止したままにすることです。集計プログラムはチケットを読み、集計を公開しますが、顧客ディレクトリは読みません。
| 操作 | 必要なスコープ | ジョブに必要か |
|---|---|---|
GET /v1/tickets |
tickets:read |
必要:入力 |
POST /v1/summaries |
summaries:write |
必要:出力 |
GET /v1/customers |
customers:read |
不要 |
GET /v1/health |
health:read |
不要:別の監視 |
GET /v1/results |
tickets:read |
受信済み集計の確認 |
権限エラーを見分ける
準備済みの読み取り専用デモキーは有効ですが、公開する権限がありません。Workstation で公開リクエストを送ります。curl -X POST は書き込み操作を選び、-H はヘッダー、-d は JSON 本文を指定します。-s は進行表示を隠し、-w は状態を表示します。サービスは本文を調べる前に権限を拒否するため、ここでは空オブジェクト {} で十分です。 Content-Type: application/json ヘッダーは、本文が JSON 形式であることをサービスに伝えます。
curl -s -w '\nHTTP %{http_code}\n' -X POST -H 'Authorization: Bearer lab_demo_read_only' -H 'Content-Type: application/json' -d '{}' http://127.0.0.1:8000/v1/summaries
{"error":"Missing scope","required":"summaries:write"}
HTTP 403
この API の 403 は、認証には成功したが必要なスコープがないことを示します。認証情報自体を拒否する 401 とは異なります。読み取り専用キーでは読み書きするジョブを完了できず、全権限キーでは過剰です。代替キーには表の 2 つの業務スコープが必要です。
専用の代替キーを発行する
このステップでは、前のステップで選んだ 2 つのスコープで専用の代替キーを発行します。秘密値を非公開で保存するのは次のステップです。名前は識別用ラベルで、権限を決めるのはスコープだけです。
専用の認証情報を発行する
Workstation で、ファイルを表示する cat を使いオペレータートークンを表示します。
cat /home/labex/.config/keylab/operator-token
Key Manager の Operator token に値を入れ、Sign in、Open Key Manager と進みます。オペレーター認証はアプリケーションキーとは別です。key_support_old が既に Revoked であることを確認します。
Issue a key の Name に Support summary replacement と入力します。tickets:read と summaries:write だけを選び、Issue key を押します。非秘密のキー ID と Secret shown once が表示されます。ページを離れる前に lab_key_ で始まる秘密値をコピーしてください。サービスが保存するのはハッシュで、再取得可能な秘密値ではありません。 ハッシュは一方向の指紋のようなもので、保存した元の値を表示せずに、提示された値と比較できます。
値を失った場合は、未使用キーを ID で失効させ、新しく発行します。漏えいした値を再利用したり、コピー間違いの対処として権限を広げたりしないでください。
非公開の実行時設定を保存する
このステップでは、代替秘密値をサービスアカウントの非公開実行時ファイルに保存し、アクセス権限を確認します。以下のコマンドはすべて Integration で実行してください。
実行時設定は、実行するプログラムが読むファイルです。この worker は呼び出すたびに /home/labex/project/runtime/job.ini を読むため、再起動は不要です。リポジトリのサンプルは別のファイルです。Integration で現在の設定を表示します。
cat /home/labex/project/runtime/job.ini
[integration]
api_key = lab_key_support_exposed
秘密値をリポジトリやシェルのコマンド履歴へ入れないでください。Bash の read はプロンプトで値を受け取り、-r は文字をそのまま保ち、-s は入力を隠し、-p は案内を表示します。API_KEY は一時的なシェル変数です。Integration で実行し、プロンプトに新しい秘密値を貼り付けて Enter を押します。入力は表示されません。
read -r -s -p 'Replacement key: ' API_KEY
printf は書式付きテキストを出力します。\n は改行、%s は変数値の挿入、二重引用符は値を 1 引数に保つ役割で、> は指定ファイルを置き換えます。既存の非公開ファイルは、このターミナルのアカウント keyworker が所有します。
printf '[integration]\napi_key = %s\n' "$API_KEY" > /home/labex/project/runtime/job.ini
chmod でアクセス権限を設定します。モード 600 は所有者だけに読み書きを許可し、グループや他のアカウントには許可しません。
chmod 600 /home/labex/project/runtime/job.ini
ls -l は秘密値を表示せずに所有者と権限を確認できます。
ls -l /home/labex/project/runtime/job.ini
-rw------- と所有者 keyworker を確認します。サイズや日時は変わります。シェル変数だけではファイルは更新されません。実行時ファイルの保存により、次の worker 呼び出しが新しい認証情報を使います。
この Integration ターミナルを開いたままにしてください。API_KEY はこのシェルだけの変数で、すべてのターミナルで使えるわけではありません。次のステップで独立したリクエストに使い、その後削除します。ターミナルを閉じたり再接続したりした場合は、上の cat で job.ini 内の現在の秘密値を確認し、このステップを繰り返してください。
業務実行を完了して検証する
このステップでは保留中の daily を処理し、許可と禁止の両方を試します。worker は準備済みです。/opt/keylab/worker.py を変更しないでください。認証情報を復旧する課題であり、認証を回避するようプログラムを書き換える課題ではありません。
Integration で指定バッチを実行します。
python3 /opt/keylab/worker.py daily
Read batch: daily (3 items)
{"batch":"daily","count":3,"subjects":["Reset password","Invoice copy","Delivery question"]}
Received batch: daily
Receiving key ID: ...
受信側の ID は Key Manager の代替キー ID と一致する必要があります。401 なら保存した秘密値が有効な代替キーのものか確認し、403 なら表とスコープを照合します。必要なら誤ったスコープの代替キーを失効させ、正しく発行し、非公開ファイルを更新して再実行します。同じバッチの再実行は受信記録を置き換え、集計を重複作成しません。
実際の受信サービスを確認する
同じ Integration ターミナルを使い続けます。前のステップの API_KEY はまだ利用できるため、一度だけ表示される秘密値を再入力する必要はありません。
読み取り専用の結果エンドポイントを使います。パイプライン | は応答を次のプログラムへ渡します。python3 -m json.tool は Python の JSON 整形ツールを実行し、読みやすくインデントします。
curl -s -H "Authorization: Bearer $API_KEY" http://127.0.0.1:8000/v1/results | python3 -m json.tool
daily、payload 内の 3 つの件名、代替キーの key_id を探します。これはローカルの成功ファイルではなく受信 API の記録です。バックエンドは、どの認証情報で元データを読んだかも記録します。
禁止された顧客ディレクトリへのリクエストを試します。
curl -s -w '\nHTTP %{http_code}\n' -H "Authorization: Bearer $API_KEY" http://127.0.0.1:8000/v1/customers
{"error":"Missing scope","required":"customers:read"}
HTTP 403
想定どおりの拒否は新しいキーの制限を確認できます。一時的なシェル変数が不要になったら unset で削除します。保存した実行時ファイルは変わりません。
unset API_KEY
Workstation で古いキーが使えず、監視が正常なことを確認します。
curl -s -w '\nHTTP %{http_code}\n' -H 'Authorization: Bearer lab_key_support_exposed' http://127.0.0.1:8000/v1/tickets
{"error":"Invalid or revoked key"}
HTTP 401
curl -s -w '\nHTTP %{http_code}\n' -H 'Authorization: Bearer lab_key_monitor_only' http://127.0.0.1:8000/v1/health
{"status":"ok","integration":"monitor"}
HTTP 200
最後に git status --short は、変更または未追跡のリポジトリファイルを短く表示します。
git status --short
ガイドどおりなら非公開実行時ファイルだけを変更したため、出力はありません。代替秘密値をサンプルやコミットに入れないでください。最初のラボで見たように、見える行が消えても履歴に秘密値が残る可能性があります。
まとめ
認証失敗と権限不足を区別し、読み取りと書き込みのスコープを選び、一度だけ表示される秘密値を非公開で保存して保留中の集計を完了しました。受信記録、古いキーの拒否、顧客アクセスの禁止、監視の可用性を確認しました。有効なローテーションは、正当な作業を復旧すると同時に漏えいした認証情報のアクセスを取り除きます。



