Root Access Management で AWS におけるメンバーアカウントのルートユーザーの認証情報を削除してみた

に公開

こんにちは、CSC の平木です!

今回は、昨年 11 月頃に登場した Root Access Management を用いてルートユーザーの認証情報を削除する手順を確認してみたいと思います。

https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_root-enable-root-access.html

早速やってみた

Root Access Management の有効化

まずは機能の有効化を実施します。

Organizations の管理アカウントにログインします。
権限があればルートユーザーではなく IAM ユーザーで問題ないです。
必要な権限は下記ドキュメントの前提条件をご確認ください。

https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_root-enable-root-access.html#enable-root-access-management_prerequisite

ログイン後、 IAM へ遷移し、ナビゲーションペインからルートアクセス管理を押し、「有効化」を押します。

機能欄の「ルート認証情報管理」と「メンバーアカウントでの特権ルートアクション」にチェックを入れます。

また、必要に応じて委任管理アカウントに指定したいアカウント ID を入力し「有効化」を押します。

成功のログが出たら機能の有効化は完了です。

ルートユーザーの認証情報の削除

続いて認証情報を削除します。

削除したい対象のアカウントを選択し、「特権的なアクションを実行する」を押します。

特権的なアクションにて「ルートユーザー認証情報を削除」を押し、橙の「ルートユーザー認証情報を削除」を押します。

内容を確認し、問題なければ「削除」をクリックします。

成功のメッセージが出れば完了です。

ルートユーザーのパスワードの再設定を許可したい場合

なんらかの理由でやはりルートユーザーのログインを許可したいケースもあるかと思います。
その場合、次の設定を行うことでパスワードの再設定ができるようになります。

再設定したいアカウントを選択し、「特権的なアクションを実行する」を押します。

特権的なアクションにて「パスワード許可を回復」を押し、橙の「パスワード許可を回復」を押します。

以上で完了です。

AWS CLI で一括でルートユーザーの認証情報を削除したい場合

AWS CLI でも実行可能です。

account_id.listに対象のアカウント ID をリストで記載し、同じディレクトリでスクリプトを実行してください。

こちらは、既に機能有効化済みかつ管理アカウントまたは委任管理アカウントの CloudShell での実行を想定しています。

削除を実行したいアカウント ID をまずaccount_id.listに記載してください。

account_id.list
111122223333
222233334444
333344445555

下記が実際のスクリプトです。

削除実行時には必ず確認プロンプトが表示されます。実行に問題がなければ「Y」を、問題がある場合は「n」または「Y以外」のキーを押してください。

delete.sh
#!/bin/bash

# ==============================================================================
# 設定変数
# ==============================================================================

# 対象アカウント ID リストファイル
ACCOUNT_LIST_FILE="account_id.list"

# assume-root を実行するリージョン
AWS_REGION="us-east-1"

# アクセスキーのリストと削除、MFA/パスワードの削除をカバーするポリシー
TASK_POLICY_ARN="arn:aws:iam::aws:policy/root-task/IAMDeleteRootUserCredentials"

# ==============================================================================
# ヘルパー関数: 削除確認
# ==============================================================================
# ユーザーに y/n で確認を求め、n が入力されたら処理をスキップする
confirm_deletion() {
    local prompt_message="$1"
    local response
    
    read -r -p "${prompt_message} (y/n): " response </dev/tty
    
    if [[ "$response" =~ ^[Yy]$ ]]; then
        return 0 # 0 は成功(削除実行)
    else
        echo "   -> スキップしました。"
        return 1 # 1 は失敗(削除スキップ)
    fi
}

# ==============================================================================
# メイン処理: ファイルからアカウント ID を読み込みループ実行
# ==============================================================================

if [ ! -f "${ACCOUNT_LIST_FILE}" ]; then
    echo "❌ ERROR: アカウントリストファイル (${ACCOUNT_LIST_FILE}) が見つかりません。"
    exit 1
fi

# ファイルからアカウント ID を読み込み、ループ処理
while IFS= read -r MEMBER_ACCOUNT_ID || [ -n "$MEMBER_ACCOUNT_ID" ]; do
    
    # 空行またはコメント行をスキップ
    if [[ -z "$MEMBER_ACCOUNT_ID" || "$MEMBER_ACCOUNT_ID" =~ ^# ]]; then
        continue
    fi

    echo "===================================================="
    echo "▶️ 【アカウント ID: ${MEMBER_ACCOUNT_ID}】の処理を開始します。"
    echo "===================================================="

    # ----------------------------------------------------
    # 1. assume-root コマンドを実行し、一時認証情報を取得
    # ----------------------------------------------------
    echo "   ▶️ ルートセッションを確立します..."

    ASSUME_ROOT_OUTPUT=$(aws sts assume-root \
        --target-principal "arn:aws:iam::${MEMBER_ACCOUNT_ID}:root" \
        --task-policy-arn "arn=${TASK_POLICY_ARN}" \
        --region "${AWS_REGION}" \
        --output json)

    if [ $? -ne 0 ]; then
        echo "   ❌ ERROR: assume-root の実行に失敗しました。このアカウントはスキップします。"
        # 環境変数が設定されるのを防ぐため、次のループへ
        continue
    fi

    # 一時認証情報を環境変数にエクスポート
    export AWS_ACCESS_KEY_ID=$(echo "${ASSUME_ROOT_OUTPUT}" | jq -r '.Credentials.AccessKeyId')
    export AWS_SECRET_ACCESS_KEY=$(echo "${ASSUME_ROOT_OUTPUT}" | jq -r '.Credentials.SecretAccessKey')
    export AWS_SESSION_TOKEN=$(echo "${ASSUME_ROOT_OUTPUT}" | jq -r '.Credentials.SessionToken')

    echo "   ✅ ルートセッション認証情報を取得しました。"
    echo "----------------------------------------------------"

    # ==============================================================================
    # 2. 認証情報の参照と削除 (ルートユーザー特権として操作)
    # ==============================================================================

    # ----------------------------------
    # 2-1. 署名証明書の削除 (1 番目)
    # ----------------------------------
    echo "--- 2-1. 署名証明書の削除 ---"
    CERTIFICATES=$(aws iam list-signing-certificates --output json)
    CERTIFICATE_IDS=$(echo "${CERTIFICATES}" | jq -r '.Certificates[].CertificateId')

    if [ -z "${CERTIFICATE_IDS}" ]; then
        echo "   ☑️ 削除対象の署名証明書はありません。"
    else
        for CERT_ID in ${CERTIFICATE_IDS}; do
            if confirm_deletion "   - 証明書 ID: ${CERT_ID} を削除します。よろしいですか?"; then
                aws iam delete-signing-certificate --certificate-id "${CERT_ID}"
                if [ $? -eq 0 ]; then
                    echo "   ✅ 署名証明書 ${CERT_ID} を削除しました。"
                else
                    echo "   ❌ 署名証明書 ${CERT_ID} の削除に失敗しました。"
                fi
            fi
        done
    fi
    echo "----------------------------------------------------"

    # ----------------------------------
    # 2-2. アクセスキーの削除 (2 番目)
    # ----------------------------------
    echo "--- 2-2. アクセスキーの削除 ---"
    ACCESS_KEYS=$(aws iam list-access-keys --output json)
    KEY_IDS=$(echo "${ACCESS_KEYS}" | jq -r '.AccessKeyMetadata[].AccessKeyId')

    if [ -z "${KEY_IDS}" ]; then
        echo "   ☑️ 削除対象のアクセスキーはありません。"
    else
        for KEY_ID in ${KEY_IDS}; do
            if confirm_deletion "   - アクセスキー ID: ${KEY_ID} を削除します。よろしいですか?"; then
                aws iam delete-access-key --access-key-id "${KEY_ID}"
                if [ $? -eq 0 ]; then
                    echo "   ✅ アクセスキー ${KEY_ID} を削除しました。"
                else
                    echo "   ❌ アクセスキー ${KEY_ID} の削除に失敗しました。"
                fi
            fi
        done
    fi
    echo "----------------------------------------------------"

    # ----------------------------------
    # 2-3. MFA デバイスの非アクティブ化 (3 番目)
    # ----------------------------------
    echo "--- 2-3. MFA デバイスの非アクティブ化 ---"
    MFA_DEVICES=$(aws iam list-mfa-devices --output json)
    MFA_SERIAL_NUMBERS=$(echo "${MFA_DEVICES}" | jq -r '.MFADevices[].SerialNumber')

    if [ -z "${MFA_SERIAL_NUMBERS}" ]; then
        echo "   ☑️ 削除対象の MFA デバイスはありません。"
    else
        for SERIAL in ${MFA_SERIAL_NUMBERS}; do
            if confirm_deletion "   - MFA シリアル: ${SERIAL} を非アクティブ化します。よろしいですか?"; then
                aws iam deactivate-mfa-device --serial-number "${SERIAL}"
                if [ $? -eq 0 ]; then
                    echo "   ✅ MFA デバイス ${SERIAL} を非アクティブ化しました。"
                else
                    echo "   ❌ MFA デバイス ${SERIAL} の非アクティブ化に失敗しました。"
                fi
            fi
        done
    fi
    echo "----------------------------------------------------"

    # ----------------------------------
    # 2-4. ログインプロファイルの削除 (パスワードの削除) (4 番目)
    # ----------------------------------
    echo "--- 2-4. ログインプロファイルの削除 (パスワード) ---"

    if confirm_deletion "   - ルートユーザーのログインプロファイル(パスワード)を削除します。よろしいですか?"; then
        aws iam delete-login-profile
        
        if [ $? -eq 0 ]; then
            echo "   ✅ ログインプロファイル (パスワード) を削除しました。"
        else
            echo "   ⚠️ ログインプロファイルの削除は失敗したか、最初から設定されていませんでした。"
        fi
    fi
    echo "----------------------------------------------------"

    # ----------------------------------
    # 3. 環境変数のクリーンアップ
    # ----------------------------------
    unset AWS_ACCESS_KEY_ID
    unset AWS_SECRET_ACCESS_KEY
    unset AWS_SESSION_TOKEN

    echo "✅ アカウント ${MEMBER_ACCOUNT_ID} の処理を完了しました。"
    echo "===================================================="

done < "${ACCOUNT_LIST_FILE}"

echo "✅ すべてのアカウントに対する処理が完了しました。"

おわりに

今回は、Root Access Management によるメンバーアカウントのルートユーザーの認証情報の削除をやってみました。

Security Hubのコントロール「IAM.6」で頻繁に検出される仮想MFAに関する問題を、認証情報自体を持たないことで根本的に解決できます。

この記事がどなたかの役に立つと嬉しいです。

株式会社サイバーセキュリティクラウド テックブログ

Discussion