boto3と1Passwordを使って、AWSのMFA認証とスイッチロールを自動化してみた!

boto3と1Passwordを使って、AWSのMFA認証とスイッチロールを自動化してみた!

1Passwordとboto3でMFA認証を自動化し、AWSでロールを安全にスイッチする方法を紹介!
Clock Icon2024.11.12

こんにちは!今回は、AWSのMFA(Multi-Factor Authentication)認証を使い、IAMロールにスイッチしながらS3バケットにアクセスするPythonスクリプトを紹介します。このスクリプトは、AWSのセキュリティベストプラクティスに従い、MFAを使用して一時的なセッションを取得し、S3バケットのリストを取得する方法を示しています。

さらに、1Passwordを活用してワンタイムパスワード(OTP)を自動的に取得する仕組みも組み込んでいるため、非常に便利で安全なアクセスが可能です。AWSのセキュリティを強化したい方や、MFA認証を自動化したい方に役立つ内容になっています。

背景

AWSでは、セキュリティを強化するためにMFAを使用することが推奨されています。MFAを使用すると、ユーザーは通常の認証情報(アクセスキーやシークレットキー)に加えて、ワンタイムパスワード(OTP)を入力する必要があります。これにより、認証情報が漏洩した場合でも、攻撃者がアクセスするのを防ぐことができます。

さらに、AWSのAssumeRole機能を使用することで、特定のIAMロールに一時的にスイッチして、そのロールの権限でAWSリソースにアクセスできます。これにより、最小限の権限を付与しつつ、必要な操作を行うことが可能です。

今回紹介するスクリプトでは、これらのセキュリティ機能を組み合わせて、MFA認証を行いながらIAMロールにスイッチし、S3バケットにアクセスする方法を実装しています。

スクリプトの全体像

まずは、スクリプトの全体像を見てみましょう。

import os
import boto3
from boto3.session import Session
from botocore.config import Config
from onepassword import OnePassword

# IAMユーザー名を指定(このユーザーのMFAデバイスを使用する)
IAM_USER_NAME = os.getenv("IAM_USER_NAME")

# AssumeRoleする際に使用するターゲットのAWS IAMロールARN
TARGET_ROLE_ARN = os.getenv("TARGET_ROLE_ARN")

# 1Passwordで保管されているAWSのアクセスキーに関連するタイトル
TITLE = '<AWS Access Key>'

# AWSのリージョン(ここでは東京リージョンを使用)
TARGET_REGION = 'ap-northeast-1'

def get_one_time_password() -> int:
    """
    1Passwordからワンタイムパスワード(OTP)を取得する関数。
    このOTPはMFA認証時に使用される。

    Returns:
        int: ワンタイムパスワード
    """
    # 1Passwordのインスタンスを作成
    op = OnePassword()

    # 1Password内のアイテムリストから、指定したタイトル(TITLE)に一致するアイテムを取得し、そのUUIDを取得
    uuid = [item['id'] for item in op.list_items() if item['title'] == TITLE][0]

    # 取得したUUIDを使って、OTP(ワンタイムパスワード)を取得
    one_time_password = op.get_item_otp(uuid=uuid)

    return one_time_password

def get_mfa_device() -> str:
    """
    IAMユーザーに紐づけられたMFAデバイスのシリアル番号を取得する関数。
    このシリアル番号はMFA認証時に使用される。

    Returns:
        str: MFAデバイスのシリアル番号
    """
    # IAMクライアントを作成
    client = boto3.client("iam")

    # 指定したIAMユーザーに紐づけられたMFAデバイスのリストを取得し、最初のデバイスのシリアル番号を取得
    mfa_device = client.list_mfa_devices(UserName=IAM_USER_NAME)['MFADevices'][0]['SerialNumber']

    return mfa_device

def assume_role(one_time_password: int, mfa_device: str, TARGET_ROLE_ARN: str) -> Session:
    """
    指定したロールにスイッチロール(Assume Role)する関数。
    MFA認証を行い、一時的なセッションを取得する。

    Args:
        one_time_password (int): ワンタイムパスワード(OTP)
        mfa_device (str): MFAデバイスのシリアル番号
        TARGET_ROLE_ARN (str): スイッチロールするターゲットロールのARN

    Returns:
        Session: 一時的なセッション情報を持つboto3のSessionオブジェクト
    """
    # STSクライアントを作成
    client = boto3.client("sts")

    # AssumeRole APIを呼び出して、一時的な認証情報を取得
    temp_credentials = client.assume_role(
        RoleArn=TARGET_ROLE_ARN,  # スイッチするロールのARN
        RoleSessionName='assume_role_sample',  # セッションに付ける名前
        SerialNumber=mfa_device,  # MFAデバイスのシリアル番号
        TokenCode=one_time_password  # ワンタイムパスワード(OTP)
    )

    # 一時的な認証情報を使用して新しいセッションを作成
    assume_role_session = Session(
        aws_access_key_id=temp_credentials['Credentials']['AccessKeyId'],
        aws_secret_access_key=temp_credentials['Credentials']['SecretAccessKey'],
        aws_session_token=temp_credentials['Credentials']['SessionToken'],
        region_name=TARGET_REGION,  # 使用するリージョン
    )

    return assume_role_session

def main():
    """
    メイン処理を行う関数。
    MFA認証を行い、指定のロールにスイッチロールした後、S3のバケットリストを表示する。
    """
    # MFAデバイスのシリアル番号を取得
    mfa_device = get_mfa_device()

    # 1Passwordからワンタイムパスワードを取得
    one_time_password = get_one_time_password()

    # スイッチロールを行い、一時的なセッションを取得
    assume_role_session = assume_role(one_time_password, mfa_device, TARGET_ROLE_ARN)

    # スイッチロールしたセッションを使ってS3クライアントを作成
    s3 = assume_role_session.client('s3')

    # S3バケットのリストを取得して表示
    print(s3.list_buckets())

# メイン処理を実行
if __name__ == "__main__":
    main()

スクリプトの流れ

1. 1Passwordからワンタイムパスワードを取得

まず、get_one_time_password関数を使って、1Passwordからワンタイムパスワード(OTP)を取得します。このOTPはMFA認証時に必要です。

2. IAMユーザーのMFAデバイスを取得

get_mfa_device関数を使って、指定したIAMユーザーに関連するMFAデバイスのシリアル番号を取得します。これは、MFA認証を行うために必要です。

3. AssumeRoleでIAMロールにスイッチ

assume_role関数を使って、STSのAssumeRole APIを呼び出し、指定したIAMロールにスイッチします。ここで、MFA認証を行うために、先ほど取得したOTPとMFAデバイスのシリアル番号を使用します。

4. S3バケットのリストを取得

スイッチロールができていることを確認するため、今回は、S3バケットのリストを取得します。

詳細解説

1PasswordからのOTP取得

def get_one_time_password() -> int:
    """
    1Passwordからワンタイムパスワード(OTP)を取得する関数。
    このOTPはMFA認証時に使用される。

    Returns:
        int: ワンタイムパスワード
    """
    # 1Passwordのインスタンスを作成
    op = OnePassword()

    # 1Password内のアイテムリストから、指定したタイトル(TITLE)に一致するアイテムを取得し、そのUUIDを取得
    uuid = [item['id'] for item in op.list_items() if item['title'] == TITLE][0]

    # 取得したUUIDを使って、OTP(ワンタイムパスワード)を取得
    one_time_password = op.get_item_otp(uuid=uuid)

    return one_time_password

この関数では、1PasswordのAPIを使って、指定されたタイトル(TITLE)に一致するアイテムからOTPを取得しています。これにより、1Passwordに保存されたMFAトークンを自動で取得できるため、手動でMFAコードを入力する手間が省けます。

MFAデバイスの取得

def get_mfa_device() -> str:
    """
    IAMユーザーに紐づけられたMFAデバイスのシリアル番号を取得する関数。
    このシリアル番号はMFA認証時に使用される。

    Returns:
        str: MFAデバイスのシリアル番号
    """
    # IAMクライアントを作成
    client = boto3.client("iam")

    # 指定したIAMユーザーに紐づけられたMFAデバイスのリストを取得し、最初のデバイスのシリアル番号を取得
    mfa_device = client.list_mfa_devices(UserName=IAM_USER_NAME)['MFADevices'][0]['SerialNumber']

    return mfa_device

この関数では、指定されたIAMユーザーに紐づけられたMFAデバイスのシリアル番号を取得しています。MFAデバイスは、AWSのMFA認証時に必要な情報です。

AssumeRoleでのロール切り替え

def assume_role(one_time_password: int, mfa_device: str, TARGET_ROLE_ARN: str) -> Session:
    """
    指定したロールにスイッチロール(Assume Role)する関数。
    MFA認証を行い、一時的なセッションを取得する。

    Args:
        one_time_password (int): ワンタイムパスワード(OTP)
        mfa_device (str): MFAデバイスのシリアル番号
        TARGET_ROLE_ARN (str): スイッチロールするターゲットロールのARN

    Returns:
        Session: 一時的なセッション情報を持つboto3のSessionオブジェクト
    """
    # STSクライアントを作成
    client = boto3.client("sts")

    # AssumeRole APIを呼び出して、一時的な認証情報を取得
    temp_credentials = client.assume_role(
        RoleArn=TARGET_ROLE_ARN,  # スイッチするロールのARN
        RoleSessionName='assume_role_sample',  # セッションに付ける名前
        SerialNumber=mfa_device,  # MFAデバイスのシリアル番号
        TokenCode=one_time_password  # ワンタイムパスワード(OTP)
    )

    # 一時的な認証情報を使用して新しいセッションを作成
    assume_role_session = Session(
        aws_access_key_id=temp_credentials['Credentials']['AccessKeyId'],
        aws_secret_access_key=temp_credentials['Credentials']['SecretAccessKey'],
        aws_session_token=temp_credentials['Credentials']['SessionToken'],
        region_name=TARGET_REGION,  # 使用するリージョン
    )

    return assume_role_session

この関数では、AWS STSのassume_role APIを使用して、指定されたIAMロールにスイッチしています。MFA認証を行うために、MFAデバイスのシリアル番号とOTPを引数として渡しています。

S3バケットのリストを取得

def main():
    """
    メイン処理を行う関数。
    MFA認証を行い、指定のロールにスイッチロールした後、S3のバケットリストを表示する。
    """
    # MFAデバイスのシリアル番号を取得
    mfa_device = get_mfa_device()

    # 1Passwordからワンタイムパスワードを取得
    one_time_password = get_one_time_password()

    # スイッチロールを行い、一時的なセッションを取得
    assume_role_session = assume_role(one_time_password, mfa_device, TARGET_ROLE_ARN)

    # スイッチロールしたセッションを使ってS3クライアントを作成
    s3 = assume_role_session.client('s3')

    # S3バケットのリストを取得して表示
    print(s3.list_buckets())

main関数では、MFAデバイスとOTPを取得し、それらを使ってIAMロールにスイッチしています。最後に、スイッチしたセッションを使ってS3バケットのリストを取得しています。

セキュリティ上の考慮点

このスクリプトでは、MFA認証とAssumeRoleを組み合わせることで、セキュリティを強化しています。通常のアクセスキーやシークレットキーだけではなく、MFAデバイスとワンタイムパスワードを使用することで、不正アクセスのリスクを大幅に減少させることができます。

また、1Passwordを使用してOTPを自動的に取得することで、セキュリティを保ちながら利便性を向上させています。

まとめ

今回紹介したスクリプトは、AWSのMFA認証とAssumeRoleを使って安全にS3バケットにアクセスするための手法を示しています。1Passwordを使ってOTPを自動的に取得することで、セキュリティを保ちながら利便性を向上させることができます。

AWSのセキュリティを強化し、MFA認証を効率的に扱いたい方にとって、このスクリプトは非常に有用なツールとなるでしょう。ぜひ、自分のプロジェクトに取り入れてみてください!

参考リンク:

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.