【手順画像付き】Amazon Cognito の認証に Active Directory ユーザーを使用する方法

Amazon Cognito の認証に Active Directory ユーザーを使用する方法
  • Amazon Cognito の認証に Active Directory ユーザーを活用し、セキュアで効率的なアクセス管理を実現。
  • IAM Identity Center 経由でシームレスに、Amazon Cognito と Active Directory を統合。
  • ALB と合わせてターゲットのLambda 関数実行に、ユーザー認証を設定する方法を紹介します。

背景

AWS Client VPN のコスト削減のため、必要な時に社員自身で AWS Client VPN の関連付けをできるようにしようと考えました。
方法として、Lambda 関数で関連付けを実行することとし、その Lambda 関数の実行を全社員から可能なようにしたいと考えました。

ただ、社員以外の人間は実行できないようにしたいため、Lambda 関数の実行に際してユーザー認証を設けることにしました。
以下の記事より、弊社では Active Directory で社員のユーザー ID を管理しています。
この Active Directory のユーザーを使って、認証を用意できないか検討を始めました。

失敗

実際の構成に行く前に失敗構成をここで供養させてください。

最初に浮かんだ方法は、API Gateway で Lambda 関数を実行し、API Gateway に Cognito の認証を使用することでした。
Cognito のコールバック URL に API Gateway を指定すれば、Cognito のサインイン URL から ユーザーを認証し、API Gateway の実行が可能になるのではと考えました。

ただ、これには問題がありました。
Cognito のサインイン URL で認証に成功したあとには、コールバック URL のクエリ部分に id_token が付与されます。
ただ、API Gateway の認証では、Authorizarion ヘッダーでトークンを含めてリクエストする必要があります。

つまり、Cognito のコールバック URL に API Gateway を指定しても、API Gateway での認証は成功しないということです。
Cognito ではなく Lambda 関数でのカスタム認証を使用すればいけそうではありますが、作りこみが必要なのでいったん保留にしました。

失敗した構成

方法

そこで代替構成として、API Gateway の代わりに ALB を使用することにしました。
ALB でも同様に Cognito を用いたユーザー認証が可能です。
また、ターゲットに Lambda 関数を指定することで、ユーザー認証後にリクエストを送信し、Lambda 関数を実行させることも可能になります。

また、Cognito のユーザーとして、IAM Identity Center を統合することが可能です。
IAM Identity Center では、Active Directory をソースとして指定できるため、IAM Identity Center を通じて Cognito の認証にも使用できます。

IAM アイデンティティセンターを Amazon Cognito ユーザープールと統合するには、どうすればよいですか?
https://repost.aws/ja/knowledge-center/cognito-user-pool-iam-integration

構成としては以下になります。

管理者側の手順

実は、既に別の記事で IAM Identity Center と Active Directory の連携は実施済みです。
なので、実際にやったことは、Amazon Cognito や ALB の設定になります。
既に実施していた、青線の手順項目についてはざっくり用途を記載し、詳細な手順については以下の記事をご参照ください。

Active Directory と IAM Identity Center の連携

IAM Identity Center とは、ユーザーを AWS サービスに連携させるためのサービスになります。
IAM Identity Center 内に直接ユーザーを作成することも可能ですが、Active Directory のユーザーを同期させることも可能です。
弊社では、別の件で既に Active Directory を IAM Identity Center に同期しています。
こちらの手順の詳細は以下の別の記事をご参照ください。

ドメインの用意

ALB の Cognito 認証では、HTTPS リスナーが必須なので、ACM にパブリック証明書を発行する必要があります。
ドメインを持っていると手っ取り早いので、本記事では awsexample.com を取得し使用していきます。

  1. 以下の URL から Route 53 でドメインを取得します。
    https://us-east-1.console.aws.amazon.com/route53/domains/home?region=us-east-1#/DomainSearch
  2. 予定のドメインが取得可能か確認します。
    • ドメインが使用可能かを確認する:awsexample.com
    • 取得可能であれば 選択 しチェックアウトに進みます。
画像
  1. [ステップ1] 料金
    • 期間:1年
    • 自動更新:☑️ オン
画像
  1. [ステップ2] 連絡先情報
    • 連絡先をアルファベットで入力していきます。
    • プライバシーの保護:☑️
      • チェックを付けないと記入した連絡先が公開されることになります。
画像
  1. [ステップ3] 確認して送信
画像
  1. 以下の URL から取得したドメインのパブリックホストゾーンが作成されているか確認します。
    https://us-east-1.console.aws.amazon.com/route53/v2/hostedzones?region=us-east-1#

ACM に証明書発行

  1. 以下の URL から ACM のパブリック証明書をリクエストします。
    https://ap-northeast-1.console.aws.amazon.com/acm/home?region=ap-northeast-1#/certificates/request/public
    • 完全修飾ドメイン名:api.awsexample.com
      • ALB に割り当てるドメイン名を指定します。
      • ここで指定したドメイン名で社員はアクセスすることになります。
    • 検証方法:⦿ DNS 検証 - 推奨
画像
  1. [Route 53 でレコードを作成] をクリックしレコードを作成します。
    • 先ほど作成した awsexample.com のホストゾーンに自動的に検証用のレコードが作成されることになります。
    • 検証が完了したら証明書が発行されます。
画像

Cognito の作成

  1. 以下の URL から Cognito のユーザープールを作成します。
    https://ap-northeast-1.console.aws.amazon.com/cognito/v2/idp/set-up-your-application?region=ap-northeast-1
    • アプリケーションに名前を付ける:ALB for Client VPN
    • サインイン識別子のオプション:☑️ ユーザー名
    • サインアップのための必須属性:email
    • リターン URL:https://api.awsexample.com/oauth2/idpresponse
      • ALB に割り当てる予定のドメイン名に /oauth2/idpresponse をつけたものとしてください。
画像
  1. Amazon Cognito のナビゲーションペインの [概要][ドメイン] から、ユーザープール ID と Cognito ドメインを確認します。
    後の IAM Identity Center との連携や ALB でのリスナーの作成などで必要になります。
    • ユーザープール ID:ap-northeast-1_eXAMPLE
    • Cognito ドメイン:https://ap-northeast-1example.auth.ap-northeast-1.amazoncognito.com
画像

IAM Identity Center の連携準備

  1. 以下の URL から IAM Identity Center でアプリケーションを作成します。
    https://ap-northeast-1.console.aws.amazon.com/singlesignon/applications/home?region=ap-northeast-1#/add
  2. [ステップ1] アプリケーションタイプを選択
    • セットアッププリファレンス:設定するアプリケーションがある
    • アプリケーションタイプ:SAML 2.0
画像
  1. [ステップ2] アプリケーションを設定
    • アプリケーションを設定
      • 表示名:Amazon Cognito
      • 説明:Custom SAML 2.0 application for Amazon Cognito
    • アプリケーションメタデータ:⦿ メタデータ値をマニュアルで入力する
      • アプリケーション ACS URLhttps://ap-northeast-1example.auth.ap-northeast-1.amazoncognito.com/saml2/idpresponse
        • Cognito の作成 [手順2] で確認した Cognito ドメイン に /saml2/idpresponse を付けます。
      • アプリケーション SMAL 対象者:urn:amazon:cognito:sp:ap-northeast-1_eXAMPLE
        • Cognito の作成 [手順2] で確認した ユーザープール ID を使います。
    • また、この時に「IAM Identity Center SAML メタデータファイル」を確認します。
      後の Cognito との連携で必要になります。
      • IAM Identity Center SAML メタデータファイル:https://portal.sso.ap-northeast-1.amazonaws.com/saml/metadata/ExAmPlExAmPlExAmPlExAmPlExAmPlExAmPlExAmPlE
画像
  1. 作成したアプリケーションの詳細から、[アクション] > [属性マッピングを編集] で属性の設定をします。
アプリケーションの
ユーザー属性
IAM Identity Center の
ユーザー属性
形式
Subject${user:subject}persistent
email${user:email}basic
画像
  1. 作成したアプリケーションの詳細から、[ユーザーとグループを割り当てる] でグループを割り当てます。
    ここで割り当てたグループが、Amazon Cognito の認証に成功することとなります。
    ここでは全社員が含まれている IAMIdentityCenterAllUsersAndGroups を割り当てます。
画像

Cognito の連携準備

  1. 以下の URL から ID プロバイダーとして IAM Identity Center を追加します。
    https://ap-northeast-1.console.aws.amazon.com/cognito/v2/idp/user-pools/ap-northeast-1_eXAMPLE/authentication/social-and-custom-providers/add?region=ap-northeast-1
    (※ 適宜 ap-northeast-1_eXAMPLE の箇所を、Cognito の作成 [手順2] で確認した ユーザープール ID に置き換えてください)
    • アイデンティティプロバイダー:SAML
    • プロバイダー名:IAMIdentityCenter
    • メタデータドキュメントのソース:⦿ メタデータドキュメントのエンドポイント URL を入力
      • https://portal.sso.ap-northeast-1.amazonaws.com/saml/metadata/ExAmPlExAmPlExAmPlExAmPlExAmPlExAmPlExAmPlE
      • IAM Identity Center の連携準備 [手順3] で確認した IAM Identity Center SAML メタデータファイル を指定します。
    • SAML プロバイダーとユーザープールの間で属性をマッピング
      • ユーザープール属性:email / SAML 属性:email
画像
  1. 以下の URL からアプリケーションクライアント「ALB for Client VPN」の ID プロバイダーを IAMIdentityCenter に変更します。
    https://ap-northeast-1.console.aws.amazon.com/cognito/v2/idp/user-pools/ap-northeast-1_eXAMPLE/applications/app-clients/<App Client ID>/login-pages/edit/hosted-ui-settings?region=ap-northeast-1
    (※ 適宜 ap-northeast-1_eXAMPLE の箇所を、Cognito の作成 [手順2] で確認した ユーザープール ID に置き換えてください)
    (※ 適宜 <App Client ID> の箇所を、アプリケーションクライアント ID に置き換えてください)
    • ID プロバイダー:IAMIdentityCenter
画像

Lambda 関数の作成

ALB のターゲットとなる Lambda 関数を作成していきます。

  1. 以下の AWS CLI コマンドを実行し、Lambda 関数用の IAM ロールを作成します。
# Create IAM role
aws iam create-role \
    --role-name LambdaRole-ClientVPN \
    --assume-role-policy-document '{
       "Version": "2012-10-17",
       "Statement": [
           {
               "Effect": "Allow",
               "Principal": {
                   "Service": "lambda.amazonaws.com"
               },
               "Action": "sts:AssumeRole"
           }
       ]
    }'

# Attach the IAM policy required for Client VPN
aws iam attach-role-policy \
    --role-name LambdaRole-ClientVPN \
    --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess

# Attach the IAM policy required for logging
aws iam attach-role-policy \
    --role-name LambdaRole-ClientVPN \
    --policy-arn arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
  1. 以下の URL から Lambda 関数を作成します。
    https://ap-northeast-1.console.aws.amazon.com/lambda/home?region=ap-northeast-1#/create/function
  2. 以下の設定で作成します。
    • 関数名:AWSClientVPN-AssociatingSubnets
    • ランタイム:Python 3.13
    • 実行ロール:LambdaRole-ClientVPN
画像
  1. 以下の URL からタイムアウトの時間を1分に変更します。
    https://ap-northeast-1.console.aws.amazon.com/lambda/home?region=ap-northeast-1#/functions/AWSClientVPN-AssociatingSubnets/edit/basic-settings?tab=configure
    • タイムアウト:1分
画像
  1. Lambda 関数のコードを記入していきます。
    コード記入後、Deploy (Ctrl+Shift+U) をする必要があります。
    ほとんど Chat GPT に書かせましたがコードは以下です。
import boto3
import os
import json

def lambda_handler(event, context):

    # Initialize the EC2 client
    ec2 = boto3.client('ec2')
    
    # Input parameters
    client_vpn_endpoint_id = '<Your Client VPN endpoint ID>'
    subnet_id = '<Your subnet ID for associating>'
    
    try:
        # Describe the Client VPN endpoint to check its status
        endpoint_status_response = ec2.describe_client_vpn_endpoints(
            ClientVpnEndpointIds=[client_vpn_endpoint_id]
        )
        
        endpoint_status = endpoint_status_response['ClientVpnEndpoints'][0]['Status']['Code']
        print(f"Client VPN Endpoint Status: {endpoint_status}")
        
        # Check if the status is 'pending-associate'
        if endpoint_status.lower() == 'pending-associate':
            # Associate the Client VPN endpoint with the target subnet
            response = ec2.associate_client_vpn_target_network(
                ClientVpnEndpointId=client_vpn_endpoint_id,
                SubnetId=subnet_id
            )
            
            # Return response details
            return {
                'statusCode': 200,
                'headers': {
                    'Content-Type': 'application/json'
                },
                'body': json.dumps({
                    'Message': 'Successfully associated Client VPN endpoint with subnet.',
                    'AssociationId': response['AssociationId'],
                    'Status': response['Status']
                })
            }
        else:
            return {
                'statusCode': 400,
                'headers': {
                    'Content-Type': 'application/json'
                },
                'body': json.dumps({
                    'Message': f'Client VPN Endpoint is not in pending-associate state. Current status: {endpoint_status}'
                })
            }
        
    except Exception as e:
        print(f"Error associating Client VPN endpoint: {str(e)}")
        return {
            'statusCode': 500,
            'headers': {
                'Content-Type': 'application/json'
            },
            'body': json.dumps({
                'Message': 'Failed to associate Client VPN endpoint.',
                'Error': str(e)
            })
        }

ALB の作成

ターゲットグループから作成していきます。
ALB の作成は本題ではないので、ここから AWS CLI で設定していきます。

  1. 以下の AWS CLI コマンドにてターゲットグループを作成します。
    (※ マネジメントコンソールからターゲットグループを作成すると、リソースベースのポリシーは自動的に作成されるので、三つ目の AWS CLI コマンドは不要になります)
LAMBDA_FUNCTION_NAME="AWSClientVPN-AssociatingSubnets"

# Create a target group
TARGET_GROUP_ARN=$(aws elbv2 create-target-group \
    --name AssociatingSubnets-alb \
    --target-type lambda \
    --query 'TargetGroups[0].TargetGroupArn' \
    --output text)

# Get the ARN of the Lambda function
LAMBDA_FUNCTION_ARN=$(aws lambda get-function \
    --function-name $LAMBDA_FUNCTION_NAME \
    --query 'Configuration.FunctionArn' \
    --output text)

# Add permission for the ALB to invoke the Lambda function
aws lambda add-permission \
    --function-name $LAMBDA_FUNCTION_NAME \
    --statement-id "AWS-ALB_Invoke-targetgroup" \
    --action lambda:InvokeFunction \
    --principal elasticloadbalancing.amazonaws.com \
    --source-arn $TARGET_GROUP_ARN

# Register the Lambda function
aws elbv2 register-targets \
    --target-group-arn $TARGET_GROUP_ARN \
    --targets Id=$LAMBDA_FUNCTION_ARN
  1. 以下の AWS CLI コマンドにて、HTTPS を許可する ALB 用のセキュリティグループを作成します。
VPC_ID="<Your VPC ID>"

# Create a security group for the ALB
SECURITY_GROUP_ID=$(aws ec2 create-security-group \
    --group-name ALB-sg \
    --description "Security group for ALB allowing HTTPS traffic" \
    --vpc-id $VPC_ID \
    --query 'GroupId' \
    --output text)

# Add an inbound rule to allow HTTPS traffic
aws ec2 authorize-security-group-ingress \
    --group-id $SECURITY_GROUP_ID \
    --protocol tcp \
    --port 443 \
    --cidr 0.0.0.0/0
  1. 次に以下の AWS CLI コマンドにて ALB を作成します。
    ALB には2つ以上の AZ のサブネットを指定する必要があります。
PUBLIC_SUBNET1="<1st public subnet ID>"
PUBLIC_SUBNET2="<2nd public subnet ID>"

# Create an ALB
LOAD_BALANCER_ARN=$(aws elbv2 create-load-balancer \
    --name AssociatingSubnets-alb \
    --subnets $PUBLIC_SUBNET1 $PUBLIC_SUBNET2 \
    --security-groups $SECURITY_GROUP_ID \
    --scheme internet-facing \
    --type application \
    --ip-address-type ipv4 \
    --query 'LoadBalancers[0].LoadBalancerArn' \
    --output text)
  1. 以下の AWS CLI コマンドにて HTTPS リスナーを作成します。
    Cognito の作成 [手順2] で確認した ユーザープール IDCognito ドメイン を使います。
DOMAIN="api.awsexample.com"
USER_POOL_ID="ap-northeast-1_eXAMPLE"
COGNITO_DOMAIN="https://ap-northeast-1example.auth.ap-northeast-1.amazoncognito.com"
COGNITO_DOMAIN_PREFIX=$(echo $COGNITO_DOMAIN | awk -F[/:] '{print $4}' | cut -d. -f1)

# Get the ARN of the ACM certificate
CERTIFICATE_ARN=$(aws acm list-certificates \
    --query "CertificateSummaryList[?DomainName=='${DOMAIN}'].CertificateArn" \
    --output text)

# Get the ARN of the Cognito User Pool
COGNITO_USER_POOL_ARN=$(aws cognito-idp describe-user-pool \
    --user-pool-id $USER_POOL_ID \
    --query 'UserPool.Arn' \
    --output text)

# Get the App Client ID for the User Pool
COGNITO_APP_CLIENT_ID=$(aws cognito-idp list-user-pool-clients \
    --user-pool-id $USER_POOL_ID \
    --query 'UserPoolClients[0].ClientId' \
    --output text)

# Create an HTTPS listener with Cognito authentication
aws elbv2 create-listener \
    --load-balancer-arn $LOAD_BALANCER_ARN \
    --protocol HTTPS \
    --port 443 \
    --certificates CertificateArn=$CERTIFICATE_ARN \
    --default-actions Type=authenticate-cognito,Order=1,AuthenticateCognitoConfig="{UserPoolArn=$COGNITO_USER_POOL_ARN,UserPoolClientId=$COGNITO_APP_CLIENT_ID,UserPoolDomain=$COGNITO_DOMAIN_PREFIX}" Type=forward,Order=2,TargetGroupArn=$TARGET_GROUP_ARN
  1. 最後に api.awsexample.com にアクセスしたときに ALB へルーティングされるように、Route 53 ホストゾーンにレコードを作成します。
    事前に以下の公式ドキュメントで、ALB DNS のホストゾーン ID を確認します。

サービスエンドポイント

リージョン名リージョンエンドポイントRoute 53 ホストゾーン ID (Application Load Balancer、Classic Load Balancer)Route 53 ホストゾーン ID (Network Load Balancer)
アジアパシフィック (東京)ap-northeast-1elasticloadbalancing.ap-northeast-1.amazonaws.comZ14GRHDCWA56QTZ31USIVHYNEOWT
https://docs.aws.amazon.com/ja_jp/general/latest/gr/elb.html
HOSTEDZONE_ID_ALB="Z14GRHDCWA56QT"

# Get hosted zone ID of awsexample.com
HOSTEDZONE_ID_R53=$(aws route53 list-hosted-zones-by-name \
    --dns-name awsexample.com \
    --query "HostedZones[0].Id" \
    --output text)

# Get domain name of ALB
ALB_DNS=$(aws elbv2 describe-load-balancers \
    --query "LoadBalancers[?LoadBalancerName=='AssociatingSubnets-alb'].DNSName" \
    --output text)

# Create record for ALB
aws route53 change-resource-record-sets \
    --hosted-zone-id $HOSTEDZONE_ID_R53 \
    --change-batch '{
      "Comment": "Creating ALB alias for api.awsexample.com",
      "Changes": [
        {
          "Action": "UPSERT",
          "ResourceRecordSet": {
            "Name": "api.awsexample.com",
            "Type": "A",
            "AliasTarget": {
              "HostedZoneId": "'"$HOSTEDZONE_ID_ALB"'", 
              "DNSName": "'"$ALB_DNS"'",
              "EvaluateTargetHealth": false
            }
          }
        }
      ]
    }'

ユーザー側の手順

社員が Lambda 関数経由で、AWS Client VPN エンドポイントの関連付けを実施します。
具体的には以下の手順で実施してもらいます。

  1. ブラウザから以下の URL にアクセスしてもらいます。
    URL: https://api.awsexample.com
  2. Active Directory に登録してある、自身のユーザー ID とパスワードでログインしてもらいます。

上記の手順にて AWS Client VPN エンドポイントの関連付けが実施されます。
なお、もし、初回ログインの場合には、IAM Identity Center で MFA の登録がデフォルトで有効なので、 MFA デバイスの登録が必要になります。
その場合には、初回時のみ以下の手順を実施してもらいます。

  1. 自前のスマートフォンに Google Authenticator(MFA 認証アプリ)をインストールしてもらいます。[Google Play / App Store]
  2. MFA デバイスの登録 で認証アプリを選択し以下を進めます。
    • QR コードを表示
    • スマートフォンで Google Authenticator を起動
    • アプリ右下の + から QR コードをスキャン
    • アプリに追加された AWS SSO 欄の6桁の認証コードを入力

社員が AWS Client VPN を使うときには、上記の手順を踏んでもらうことを周知しました。
これにより、関連付けを最小限の時間で済ませることで、コストを削減することができました。
関連付けを自動で解除する仕組みも実装しようと思いますが、それは次の記事で紹介します。

タイトルとURLをコピーしました