Dev Study
AWSサービスの内部原理 コース

132. Secrets Managerのローテーションは4段階の手続きである — createSecret・setSecret・testSecret・finishSecretとAWSCURRENT/AWSPENDING/AWSPREVIOUS

これまでのアプリ認証編は「IAMのAWSアクセスキー」を扱ってきましたが、今回は主題を「DBパスワードのようなアプリ自身の秘密」のライフサイクルに移します。Secrets Managerがパスワードを自動で入れ替えるとき、内部では公式に定められた4段階の手続きが順に呼ばれ、3つのラベルが版を渡り歩きます。その様子を図で追っていきます。

① そもそも「シークレットの版」とラベルという道具立て

シークレットprod/db/password
ラベルはどの版を指すかのポインタ
版v1旧い値
版v2現行の値
版v3新しい値
AWSPREVIOUS最後に有効だった版
AWSCURRENTアプリが今取得する版
AWSPENDING作りかけの版
  • 公式は各版のラベルの割り当てをdescribe-secretの出力であるVersionIdsToStagesで確認できると述べる。ラベルは版そのものではなく「どの版を指すか」を表す。

シークレットは版を積み重ねられ、AWSCURRENT/AWSPENDING/AWSPREVIOUSという3つのラベルがどの版を指すかだけで「現在」「作りかけ」「一つ前」が決まります。値を上書きするのではなく、参照(ラベル)を動かして切り替える設計です。

② 4段階の手続き — ローテーションLambdaが順に呼ぶ

createSecret新しい認証情報を生成
ここで初めて本物が変わる
setSecretDB/外部サービス側を新値に変更
read accessで検証
testSecretAWSPENDINGで接続テスト
update_secret_version_stageでAWSCURRENTを新版へ
finishSecretラベルを付け替える
完了
  • createSecretはget_secret_valueで同じClientRequestTokenの版が既にないか確認し、なければcreate_secretで新規作成してからget_random_passwordで新値を生成、put_secret_valueでAWSPENDINGラベルを付けて保存する。
  • 「Storing the new secret value in AWSPENDING helps ensure idempotency. If rotation fails for any reason, you can refer to that secret value in subsequent calls.」——先にAWSPENDINGへ保存するのは冪等性のためで、途中で失敗しても後続の呼び出しが同じ値を参照できる。

①で新値を「作りかけ(AWSPENDING)」として用意し、②で初めてDB本体を書き換え、③で新値の接続を検証し、④でラベルを付け替えて確定する。①〜③が終わるまで、アプリが取得するAWSCURRENTは旧値のままで、動作は途切れません。

③ finishSecret — 1回のAPI呼び出しでラベルが版を渡り歩く

版v2AWSCURRENT
アプリが今使う旧値
版v3AWSPENDING
テスト済みの新値
版v2AWSPREVIOUS
自動で付く。最後の既知の正常版
版v3AWSCURRENT
AWSPENDINGは同じ呼び出しで外れた
  • 引用: "moves the label AWSCURRENT from the previous secret version to this version, which also removes the AWSPENDING label in the same API call"
  • 公式の注意: "Don't remove AWSPENDING before this point, and don't remove it by using a separate API call, because that can indicate to Secrets Manager that the rotation did not complete successfully"。この時点より前に、あるいは別のAPI呼び出しでAWSPENDINGを外すと、Secrets Managerはローテーション未完了とみなす。
  • 成功後、AWSPENDINGはAWSCURRENTと同じ版に付くか、どの版にも付かないかのいずれか。もしAWSPENDINGがAWSCURRENTと違う版に残っていると、次回のローテーションは「前回がまだ進行中」と判断してエラーを返す。

finishSecretは1回のupdate_secret_version_stageで、AWSCURRENTを旧版から新版へ移し、同じ呼び出しでAWSPENDINGを外し、旧版には自動でAWSPREVIOUSが付きます。だからロールバックは「AWSPREVIOUSが指す版に戻す」だけで済む——最後の既知の正常版が常に残されているからです。

④ ローテーション戦略は2種類 — single userとalternating users

single user1ユーザーだけ更新
alternating usersオリジナル+クローンの2ユーザー
以降
毎回同じ値を入替
最も単純・most use casesに適す
初回: クローン作成
AWSCURRENTはクローンへ
低確率で新旧どちらか拒否
リトライ戦略で軽減
2回目オリジナル→3回目クローン
交互に更新を繰り返す
高可用性は弱め
常にどちらかが有効
高可用性が必要な場面向け
  • single userは公式で"This is the simplest rotation strategy, and it is appropriate for most use cases"と明記。開いているデータベース接続はローテーションでも切断されないが、DB側のパスワード変更とシークレット側の更新の間には短いタイムラグがあり、その間は新しい認証情報を使った呼び出しがdenyされる低リスクがある(適切なリトライ戦略で緩和可能)、と公式は述べる。
  • alternating usersは"applications that require high availability"に適し、ローテーション中にアプリが取得しても"still gets a valid set of credentials"、"After rotation, both user and user_clone credentials are valid"。公式は"even less chance of applications getting a deny than single user rotation"とも明記。
  • クローンユーザーの作成にはsuperuserの認証情報を別シークレットで用意する必要があり、クローンは元ユーザーと同じ権限で作成されるため、元ユーザーの権限を後から変更した場合はクローン側の権限も変更する必要がある。クローンが元ユーザーと同じ権限を持たない場合、および一時的・対話的ユーザーの認証情報にはsingle userを推奨。

single userは1ユーザーのパスワードを毎回入れ替える最も単純な戦略で、大半の用途に適します。alternating usersはオリジナルとクローンを交互に更新するため、ローテーションの最中でもどちらかが常に有効で、高可用性が要る場面に向きます。

⑤ 既習事項との接続 — 「実体は追加、参照を動かして切り替える」の再登場

共通原理実体は不変・追加するだけ / 参照を動かして切り替える
CFn変更セット新スタック状態→安全に切り替え
Lambda版+エイリアス版は不変、エイリアスという参照だけ動かす
Secrets Managerローテーション版を追加しAWSCURRENTという参照を動かす
ロールバック=AWSPREVIOUSに参照を戻すだけ
  • コンテナ編で見た「タスク実行ロールがSecrets Managerから秘密を取得する」構図の、その秘密自身がどう更新されているかの内側の解剖にあたる。取得側(アプリ)は常にAWSCURRENTを引くだけで、裏で版とラベルが動いていることを意識しなくてよい。

Lambdaのバージョン+エイリアスが「実体は不変、参照だけ動かす」だったのと同じく、Secrets Managerのローテーションも「版を追加し、AWSCURRENTという参照を付け替える」だけ。この一貫した原則を知っていれば、ロールバックがなぜ「AWSPREVIOUSに戻すだけ」で済むのかが自然に腑に落ちます。

公式ドキュメントで詳しく ↗