本ページでは、Keeperシークレットマネージャーのローテーションの「NOOP モード」を使用して AWS IAMユーザーアクセスキーをローテーションする方法について解説します。「NOOPモード」は、Keeperレコード内で設定するフラグであり、これによりゲートウェイは主なローテーション方式を飛ばして、ボルト内のPAMユーザーレコードに添付されたポストローテーションスクリプトを直接実行するように指示します。
本ページには、要件、手順、Pythonスクリプトの例が含まれています。このスクリプトは、提供された管理者認証情報 (管理者アカウントのAWSアクセスキー) と EC2インスタンスロール認証の両方をサポートします。このスクリプトでは、以前のユーザーキーを削除するなど、アクセスキーを安全にローテーションします。ローテーションが完了すると、新しいキーはKeeperレコードに保存されます。
キーのローテーション後、ローテーションされた PAMユーザーレコードは新しいAWSアクセスキーID、シークレットアクセスキー、作成日、削除されたアクセスキー IDで更新されます。
import boto3
import sys
import base64
import json
from keeper_secrets_manager_core import SecretsManager
from keeper_secrets_manager_core.storage import FileKeyValueStorage
from botocore.exceptions import ClientError, NoCredentialsError
# sys.stdinは配列ではないため、添字(例えば、sys.stdin[0])を使用することはできません。
for base64_params in sys.stdin:
# ゲートウェイからパラメータを取得する
params = json.loads(base64.b64decode(base64_params).decode('utf-8'))
break
# Keeperでポストローテーションスクリプトに添付されたレコードを取得する
records = json.loads(base64.b64decode(params.get('records')).decode('utf-8'))
# ゲートウェイ構成を使用してKeeperシークレットマネージャーSDKを初期化する
secrets_manager = SecretsManager(config=FileKeyValueStorage('/etc/keeper-gateway/gateway-config.json'))
# ユーザーの新しいアクセスキーを作成する関数
def create_new_access_key(iam_client, user_name: str) -> tuple:
new_key = iam_client.create_access_key(UserName=user_name)
return new_key['AccessKey']['AccessKeyId'], new_key['AccessKey']['SecretAccessKey'], new_key['AccessKey']['CreateDate']
# ユーザーのアクセスキーをローテーションする関数
def rotate_user_access_key(iam_client, user_name: str) -> dict:
if not user_name:
raise ValueError("User name is required for rotation")
try:
# ステップ 1: ユーザーの新しいアクセスキーを作成する
new_access_key_id, new_secret_access_key, create_date = create_new_access_key(iam_client, user_name)
print(f"New Access Key created successfully for user {user_name}")
# ステップ 2: ユーザーの既存のアクセスキーをすべて一覧表示する
existing_keys = iam_client.list_access_keys(UserName=user_name)
deleted_keys = [access_key['AccessKeyId'] for access_key in existing_keys['AccessKeyMetadata']
if access_key['AccessKeyId'] != new_access_key_id]
# ステップ 3: 新しく作成したアクセスキー以外のすべてのアクセスキーを削除する
for access_key_id in deleted_keys:
iam_client.delete_access_key(UserName=user_name, AccessKeyId=access_key_id)
# 結果を返す
return {
'NewAccessKeyId': new_access_key_id,
'NewSecretAccessKey': new_secret_access_key,
'CreateDate': create_date,
'DeletedAccessKeys': deleted_keys
}
except ClientError as e:
print(f"An error occurred: {e}")
raise e
# すべてのアクセスキーを削除し手からユーザーのアクセスキーをローテーションする関数
def delete_then_rotate_user_access_key(iam_client, user_name: str) -> dict:
try:
# ステップ 1: ユーザーの既存のアクセスキーをすべて一覧表示する
existing_keys = iam_client.list_access_keys(UserName=user_name)
deleted_keys = [access_key['AccessKeyId'] for access_key in existing_keys['AccessKeyMetadata']]
# ステップ 2: すべてのアクセスキーを削除する
for access_key_id in deleted_keys:
iam_client.delete_access_key(UserName=user_name, AccessKeyId=access_key_id)
# ステップ 3: ユーザーのために新しいアクセスキーを作成する
new_access_key_id, new_secret_access_key, create_date = create_new_access_key(iam_client, user_name)
print(f"New Access Key created successfully for user {user_name}")
# 結果を返す
return {
'NewAccessKeyId': new_access_key_id,
'NewSecretAccessKey': new_secret_access_key,
'CreateDate': create_date,
'DeletedAccessKeys': deleted_keys
}
except ClientError as e:
print(f"An error occurred: {e}")
raise e
# ターゲットAWSアカウントでロールを引き受ける関数
def assume_role_in_target_account(role_arn, session_name='CrossAccountSession'):
sts_client = boto3.client('sts')
try:
assumed_role = sts_client.assume_role(
RoleArn=role_arn,
RoleSessionName=session_name
)
credentials = assumed_role['Credentials']
return {
'aws_access_key_id': credentials['AccessKeyId'],
'aws_secret_access_key': credentials['SecretAccessKey'],
'aws_session_token': credentials['SessionToken']
}
except ClientError as e:
print(f"Error assuming role: {e}")
raise e
# ゲートウェイからの入力と、KSMを使用した完全なPAMユーザーのクエリ
pam_user_to_update = secrets_manager.get_secrets([params.get('userRecordUid')])[0]
user_name = params.get('user')
# 管理者ユーザーアカウントのアクセスキーを取得する
aws_admin_cred_provided = False
for record in records:
if "role_arn" in record: # KeeperレコードからAssumeRole認証が検出
aws_admin_cred_provided = True
role_arn = record["role_arn"]
aws_access_key_id = record["aws_access_key_id"]
aws_secret_access_key = record["aws_secret_access_key"]
print(f"Admin access key provided along with role arn for AWS AssumeRole. Using Access Key ID {aws_access_key_id}, from record UID {record['uid']}.")
# ロールを引き受けて一時的な認証情報を取得する
assumed_role_credentials = assume_role_in_target_account(role_arn)
# 引き受けたロールの認証情報を使用してIAMクライアントを初期化する
iam_client = boto3.client(
'iam',
aws_access_key_id=assumed_role_credentials['aws_access_key_id'],
aws_secret_access_key=assumed_role_credentials['aws_secret_access_key'],
aws_session_token=assumed_role_credentials['aws_session_token'],
region_name='us-east-1'
)
break
if "aws_access_key_id" in record:
aws_admin_cred_provided = True
aws_access_key_id = record["aws_access_key_id"]
aws_secret_access_key = record["aws_secret_access_key"]
print(f"Admin access key provided. Using Access Key ID {aws_access_key_id}, from record UID {record['uid']}.")
# 管理者の認証情報を使用してIAMクライアントを初期化する
iam_client = boto3.client('iam',
aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
region_name='us-east-1')
break
if "accessKeyId" in record:
aws_admin_cred_provided = True
aws_access_key_id = record["accessKeyId"]
aws_secret_access_key = record["accessSecretKey"]
print(f"Admin access key provided. Using Access Key ID {aws_access_key_id}, from record UID {record['uid']}.")
# 管理者の認証情報を使用してIAMクライアントを初期化する
iam_client = boto3.client('iam',
aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
region_name='us-east-1')
break
else:
try:
# Keeperシークレットマネージャーを使用してPAM構成から管理者アクセスキーを取得しようとする
pam_config = secrets_manager.get_secrets([params.get('providerRecordUid')])[0]
aws_access_key_id = pam_config.field('accessKeyId', single=True)
aws_secret_access_key = pam_config.field('accessSecretKey', single=True)
# キーが正常に取得されたか確認する
if aws_access_key_id and aws_secret_access_key:
aws_admin_cred_provided = True
print(f"Admin access key retrieved from the PAM Config. Using Access Key ID {aws_access_key_id}, from the PAM Config.")
# 管理者の認証情報を使用してIAMクライアントを初期化する
iam_client = boto3.client('iam',
aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
region_name='us-east-1')
else:
print("Access key ID or secret key not found in the retrieved Keeper Secrets Manager record.")
except Exception as e:
# 取得中に発生したエラーを処理する
print(f"Error retrieving admin access key: {e}")
# 管理者のアクセスキーが提供されていない場合、インスタンスロールの認証情報を確認する
if not aws_admin_cred_provided:
print("No admin access key provided. Checking for instance role authentication...")
try:
# 認証情報なしでIAMクライアントを作成し、インスタンスロールの認証情報が利用可能かテストする
iam_client = boto3.client('iam')
iam_client.list_access_keys(UserName=user_name) # APIにアクセスできるかテストする
aws_admin_cred_provided = True
print("Instance role credentials found. Using instance role authentication.")
except NoCredentialsError:
print("No instance role credentials available.")
except ClientError as e:
print(f"Error using instance role credentials: {e}")
# 認証情報が提供されていない場合、終了する
if not aws_admin_cred_provided:
raise RuntimeError("No admin access key or instance role credentials provided. The script will exit.")
# ユーザーが`delete_all_keys_before_rotating`というカスタムフィールドを指定したか確認する
try:
delete_all_keys_before_rotating = pam_user_to_update.custom_field('delete_all_keys_before_rotating', single=True)
delete_all_keys_before_rotating = str(delete_all_keys_before_rotating).lower() in ['true', 'yes', '1']
except Exception as e:
print(f"OPTIONAL field 'delete_all_keys_before_rotating' not found: {e}")
delete_all_keys_before_rotating = False # エラーが発生した場合、`false`に設定する
# ユーザーのパラメータに基づいて、適切なローテーション関数を呼び出す
if delete_all_keys_before_rotating:
result = delete_then_rotate_user_access_key(iam_client, user_name=user_name)
else:
result = rotate_user_access_key(iam_client, user_name=user_name)
if result:
print("\nRotation Result:")
print(f"New Access Key ID: {result['NewAccessKeyId']}")
print(f"Create Date: {result['CreateDate']}")
print(f"Deleted Access Keys: {result['DeletedAccessKeys']}")
# KeeperのPAMユーザーレコードを更新する
pam_user_to_update.custom_field('aws_access_key_id', result['NewAccessKeyId'])
pam_user_to_update.custom_field('aws_secret_access_key', result['NewSecretAccessKey'])
create_date_str = result['CreateDate'].isoformat() if hasattr(result['CreateDate'], 'isoformat') else str(result['CreateDate']) # Convert the create_date to string format
pam_user_to_update.custom_field('CreateDate', create_date_str)
deleted_keys_str = ', '.join(result['DeletedAccessKeys']) # Convert deleted access keys to a comma-separated string
pam_user_to_update.custom_field('Access_Key_ID_Removed_during_previous_rotation', deleted_keys_str)
secrets_manager.save(pam_user_to_update)