本ページでは、Keeperシークレットマネージャーのローテーションの「NOOP モード」を使用して AWS IAMユーザーアクセスキーをローテーションする方法について解説します。「NOOPモード」は、Keeperレコード内で設定するフラグであり、これによりゲートウェイは主なローテーション方式を飛ばして、ボルト内のPAMユーザーレコードに添付されたポストローテーションスクリプトを直接実行するように指示します。
本ページには、要件、手順、Pythonスクリプトの例が含まれています。このスクリプトは、提供された管理者認証情報 (管理者アカウントのAWSアクセスキー) と EC2インスタンスロール認証の両方をサポートします。このスクリプトでは、以前のユーザーキーを削除するなど、アクセスキーを安全にローテーションします。ローテーションが完了すると、新しいキーはKeeperレコードに保存されます。
3. ボルト内のKeeper PAMユーザーレコードの更新
キーのローテーション後、ローテーションされた PAMユーザーレコードは新しいAWSアクセスキーID、シークレットアクセスキー、作成日、削除されたアクセスキー IDで更新されます。
ゲートウェイがEC2インスタンスで実行されている場合、スクリプトに管理者アクセスキーを提供する必要はありません。ゲートウェイは、VMに割り当てられたAWSインスタンスロールの権限を活用します。
以下の手順では、最小限の権限でゲートウェイEC2インスタンスにEC2インスタンスロールを設定する方法について解説します。
アクセスキーをローテーションするための最小限の権限を持つインスタンスロールを追加/構成する手順
1. AWSでポリシーを作成
[Policies] (ポリシー) を選択し、[Create policy] (ポリシーの作成) をクリックします。
[JSON]を選択して以下を貼り付けます。ご利用のAWSアカウントIDに必ず置き換えてください。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:CreateAccessKey",
"iam:ListAccessKeys",
"iam:DeleteAccessKey"
],
"Resource": "arn:aws:iam::YOUR_AWS_ACCOUNT_ID_HERE:user/*"
}
]
}
2. AWSでIAMロールを作成
[Roles] (ロール) を選択し、[Create role] (ロールの作成) をクリックします。
[AWS service] (AWSサービス) を選択し、[EC2]を選択します。
必要なIAMポリシー (上記で最小限の権限で作成したポリシー) を添付します。
3. EC2インスタンスにロールを割り当てる
EC2マネジメントコンソールでインスタンスを見つけます。
[Actions] (アクション) > [Security] (セキュリティ) > [Modify IAM role] (IAMロールの変更) をクリックします。
作成したロールを選択し、[Update IAM role] (IAMロールを更新) をクリックします。
ゲートウェイEC2インスタンスで以下のコマンドを実行すると、インスタンスロールにIAMと対話するための適切な権限があることを確認できます。
aws sts get-caller-identity
このロールを構成すると、EC2 インスタンス (この場合は Keeperゲートウェイ) はスクリプトに添付されたロール認証情報を自動的に使用するため、アクセスキー認証情報を必要とせずにIAMアクセスキーの作成や削除などを実行できるようになります。
ボルトからのローテーション構成
上記のフィールドとカスタム フィールドを使用して、共有フォルダにPAMユーザーレコードを作成します。
ゲートウェイがまだ存在しない場合は、Keeperボルトのシークレットマネージャータブでゲートウェイ用の新しいアプリケーションを作成します。
アプリケーションに上記で作成した共有フォルダに対して編集権限があることを確かにします。
EC2インスタンスでゲートウェイ (アプリケーションを選択した後のゲートウェイタブ) をプロビジョニングします。
EC2インスタンスで、Keeperボルトによって提供されるinstallコマンドを実行し、EC2インスタンスで以下のコマンドを実行することでboto3とkeeper_secrets_manager_coreがインストールされていることを確かにします。
pip3 install boto3
pip3 install keeper_secrets_manager_core
Keeperボルトのシークレットマネージャータブで、PAM構成タブに移動します。必要に応じて、新しいPAM構成を作成します。
[環境]で[ローカルネットワーク]か[AWS]を選択できます。
「AWS」を選択した場合は、「Access Key」と「Secret Access Key」フィールドを空白にしてください。これらを指定すると、インスタンスロール認証を使用せずに、スクリプトで自動的に使用されるようになります。AWS PAM構成にAWSアカウントIDを指定する必要があります。
ゲートウェイを選択し、共有フォルダを選択して、PAM構成を保存します。
前述のPAMユーザーレコードを編集します。
パスワードローテーション設定: 希望するスケジュールと上記で作成したPAM 構成を選択します。
レコードにPAMスクリプトを追加します。以下のファイルを選択し、スクリプトコマンドを指定してください。
ゲートウェイがEC2インスタンスで実行されていない場合は、AWSに対して認証し、別のユーザーのアクセスキーをローテーションするために管理者アクセスキーが必要になります。ここでは、AWS PAM構成で提供される管理者アクセスキーを使用します。
Keeperボルトからの設定
上記のフィールドとカスタムフィールドを使用して、共有フォルダにPAMユーザーレコードを作成します。
ゲートウェイがまだ存在しない場合は、Keeperボルトのシークレットマネージャータブでゲートウェイ用の新しいアプリケーションを作成します。
アプリケーションに上記で作成した共有フォルダに対して編集権限があることを確かにします。
Linuxボックスでゲートウェイ (アプリケーションを選択した後のゲートウェイタブ) をプロビジョニングします。Keeper ボルトによって提供されるinstallコマンドを実行し、Linuxボックスで以下のコマンドを実行することでboto3とkeeper_secrets_manager_coreがインストールされていることを確かにします。
pip3 install boto3
pip3 install keeper_secrets_manager_core
Keeperボルトのシークレットマネージャータブで、PAM構成タブに移動します。必要に応じて、新しいPAM構成を作成します。
[環境]で、[AWS]を選択し、ゲートウェイを選択して、共有フォルダを選択し、「AWS ID」、「Access Key」、「Secret Access Key」を入力します。これは、スクリプトがユーザーアクセスキーをローテーションするために使用する管理者アクセスキーになります。
前述のPAMユーザーレコードを編集します。
パスワードローテーション設定: 希望するスケジュールと上記で作成したPAM構成を選択します。
レコードにPAMスクリプトを追加します。以下のファイルを選択し、スクリプトコマンドを指定してください。
ゲートウェイがEC2インスタンスで実行されていない際は、AWSに対して認証し、別のユーザーのアクセスキーをローテーションするのに管理者アクセスキーが必要になります。ここでは、別のKeeperレコードに保存されている管理者アクセスキーを使用します。このオプションを使用すると、Keeperでユーザーアクセス キーをローテーションするのと同じ方法で、管理者アクセスキーをローテーションすることもできます。
AWSインスタンスロールかAWS PAM構成のいずれかを使用してローテーションを設定した場合、アクセスキーを含むPAM構成かアクセスキーを含まないPAM構成が作成されます。以下の手順に従うと、別のKeeperレコードに保存されている管理者アクセスキーが強制的に使用されます。
Keeperボルトからの設定
PAMスクリプトをPAMユーザーレコードに添付する際、ローテーション認証情報を添付できます。
添付するレコードはレコードタイプでも構いません。ただし、「aws_access_key_id」と「aws_secret_access_key」の2 つのカスタムフィールドが含まれている必要があります。
PAMユーザーのレコードタイプを使用して管理者アクセスキーを保存すると、管理者アクセスキーのローテーションも自動化できます。その場合は、必ず上記の要件に従ってください。
AWS AssumeRoleを使用して他のAWSアカウントでユーザーアクセスキーをローテーション
PAMスクリプト自体にレコードを添付して管理者にAWSアクセスキーを提供することで、AWS AssumeRoleを利用して複数のAWSアカウントでAWSユーザーアクセスキーをローテーションすることもできます。
AWS AssumeRoleの詳細については、こちらのウェブサイトをご覧ください。
この機能を活用するには、PAM スクリプト (ローテーション認証情報) に添付されたレコードに新しいカスタムフィールドを追加する必要があります。
カスタムフィールドラベルは以下となります。
このフィールドには、他のAWSアカウントのアクセスキーをローテーションする権限を持つAWS環境のロールarnが格納されます。
このフィールドが存在する場合、ローテーションスクリプトはこれを利用して、提供された管理者アクセスキーIDおよびシークレットと組み合わせ、新しい一時的なアクセスキーを生成します。この一時的なキーを使用して、別のAWSアカウント内のエンドユーザーのアクセスキーをローテーションします。
スクリプトはその後、通常の追加フィールドなしの場合と同様に、Keeper PAMユーザーレコードを新しいキーおよび関連情報で更新します。
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)