コンテンツにスキップ

STS@AWSリソース

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. STSとは:Security Token Service

認証済みのIAMユーザーに対して、特定のAWSアカウントのAWSリソースに認可スコープを持つ一時的な認証情報 (アクセスキーID、シークレットアクセスキー、セッショントークン) を持つIAMユーザーを発行する。

STS


02. スイッチロールの仕組み

スイッチロールとは

AssumeRole (権限委譲) によって、ユーザーのIAMロールを動的に切り替える。

1. IAMロールに信頼ポリシーを紐付け

必要なポリシーが設定されたIAMロールを作成する。

その時信頼ポリシーでは、IAMユーザーのARNを信頼されたエンティティとして設定しておく。

これにより、そのIAMユーザーに対して、IAMロールを紐付けできるようになる。

この時に使用するユーザーは、IAMユーザーではなく、AWSリソースやフェデレーテッドユーザーでもよい。

{
  "Version": "2012-10-17",
  "Statement":
    [
      {
        "Effect": "Allow",
        "Principal":
          {"AWS": "arn:aws:iam::<AWSアカウントID>:user/<ユーザー名>"},
        "Action": "sts:AssumeRole",
        "Condition": {
            # 完全一致
            "StringEquals": {"sts:ExternalId": "<適当な文字列>"},
          },
      },
    ],
}


2. IAMロールを引き受けた認証情報をリクエスト

信頼されたエンティティから、STSのエンドポイント (https://sts.amazonaws.com) に対して、ロールの紐付けをリクエストする。

OIDCによるフェデレーションユーザーの場合は、--external-idオプションの代わりに、--web-identity-tokenオプションを使用する。

このオプションに、発行されたJWTを設定する必要がある。

#!/bin/bash

set -xeuo pipefail

# 事前に環境変数に実行環境名を代入する。
case "${ENV}" in
    "tes")
        aws_account_id="<テスト環境アカウントID>"
        aws_access_key_id="<テスト環境アクセスキーID>"
        aws_secret_access_key="<テスト環境シークレットアクセスキー>"
        aws_iam_role_external_id="<信頼ポリシーに設定した外部ID>"
    ;;
    "stg")
        aws_account_id="<ステージング環境アカウントID>"
        aws_access_key_id="<ステージング環境アクセスキーID>"
        aws_secret_access_key="<ステージング環境シークレットアクセスキー>"
        aws_iam_role_external_id="<信頼ポリシーに設定した外部ID>"
    ;;
    "prd")
        aws_account_id="<本番環境アカウントID>"
        aws_access_key_id="<本番環境アクセスキーID>"
        aws_secret_access_key="<本番環境シークレットアクセスキー>"
        aws_iam_role_external_id="<信頼ポリシーに設定した外部ID>"
    ;;
    *)
        echo "The parameter "${ENV}" is invalid."
        exit 1
    ;;
esac

# 信頼されたエンティティの認証情報を設定する。
aws configure set aws_access_key_id "$aws_account_id"
aws configure set aws_secret_access_key "$aws_secret_access_key"
aws configure set aws_default_region "ap-northeast-1"

# https://sts.amazonaws.com に、ロールの紐付けをリクエストする。
aws_sts_credentials="$(aws sts assume-role \
  --role-arn "arn:aws:iam::${aws_access_key_id}:role/"${ENV}"-<紐付けしたいIAMロール名>" \
  --role-session-name "<任意のセッション名>" \
  --external-id "$aws_iam_role_external_id" \
  --duration-seconds "<セッションの失効秒数>" \
  --query "Credentials" \
  --output "json")"


3. 返信されたレスポンスから認証情報を取得

STSのエンドポイントから一時的な認証情報が発行される。

また同時に、この認証情報は、ローカルマシンの~/.aws/cli/cacheディレクトリ配下にもjsonファイルで保管される。

認証情報の失効時間に合わせて、STSはこのjsonファイルを定期的に更新する。

{
  "Credentials":
    {
      "AccessKeyId": "<アクセスキーID>",
      "SecretAccessKey": "<シークレットアクセスキー>",
      "SessionToken": "<セッショントークン文字列>",
      "Expiration": "<セッションの期限>",
    },
  "AssumeRoleUser":
    {
      "AssumedRoleId": "<セッションID>:<セッション名>",
      "Arn": "arn:aws:sts:<新しいアカウントID>:assumed-role/<IAMロール名>/<セッション名>",
    },
  "ResponseMetadata":
    {
      "RequestId": "*****",
      "HTTPStatusCode": 200,
      "HTTPHeaders":
        {
          "x-amzn-requestid": "*****",
          "content-type": "text/xml",
          "content-length": "1472",
          "date": "Fri, 01 Jul 2022 13:00:00 GMT",
        },
      "RetryAttempts": 0,
    },
}


4. 認証情報を取得

レスポンスされたデータから認証情報を抽出する。

この時、アクセスキーID、シークレットアクセスキー、セッショントークン、が必要になる。

代わりに、~/.aws/cli/cacheディレクトリ配下のjsonファイルから取得しても良い。

認証情報を環境変数として出力し、使用できるようにする。

#!/bin/bash

cat <<EOF > assumed_user.sh
export AWS_ACCESS_KEY_ID="$(echo "$aws_sts_credentials" | jq -r ".AccessKeyId")"
export AWS_SECRET_ACCESS_KEY="$(echo "$aws_sts_credentials" | jq -r ".SecretAccessKey")"
export AWS_SESSION_TOKEN="$(echo "$aws_sts_credentials" | jq -r ".SessionToken")"
export AWS_ACCOUNT_ID="$aws_account_id"
export AWS_DEFAULT_REGION="ap-northeast-1"
EOF

環境変数に登録する代わりに、AWSの認証情報ファイルを作成しても良い。

#!/bin/bash

aws configure --profile "${ENV}"-repository <<EOF
$(echo "$aws_sts_credentials" | jq -r ".AccessKeyId")
$(echo "$aws_sts_credentials" | jq -r ".SecretAccessKey")
ap-northeast-1
json
EOF

echo aws_session_token = $(echo "$aws_sts_credentials" | jq -r ".SessionToken") >> ~/.aws/credentials


5. 認証認可の動作確認

ロールを引き受けた新しいアカウントを使用して、AWSリソースに認証認可できるか否かを確認する。

認証情報の取得方法として認証情報ファイルの作成をtfstateファイル択した場合、profileオプションが必要である。

#!/bin/bash

# 認証情報ファイルを参照するオプションが必要がある。
aws s3 ls --profile <プロファイル名> <tfstateファイルが管理されるバケット名>


03. STSで発行されるIAMユーザー

Trusted Entityの事前作成

事前に、元となるIAMユーザー (Trusted Entity) を作成しておく。

AssumeRoleによるスイッチロールの仕組みでは、まずTrusted Entityをコールする。

Trusted Entityを使って、必要なIAMロールをSTSから発行し、一時的なIAMユーザーを作成する。

AssumeRole


IAMユーザーの自動更新

STSで発行されたIAMユーザーには、そのAWSアカウント内のみで使用できるロールが紐付けられている。

この情報には失効秒数が存在し、期限が過ぎると新しいIAMユーザーになる。

秒数の最大値は、該当するIAMロールの概要の最大セッション時間から変更できる。


発行するIAMユーザーの切り替え

IAMユーザーを一括で管理しておき、特定のAWSアカウントでは特定の認可スコープを委譲する。

sts_multi-account


04. IAMユーザーの発行元

フェデレーテッドユーザー

任意のIDプロバイダーで認証済みのユーザー (フェデレーテッドユーザー) にIAMロールを付与することにより、AWSリソースにリクエストを送信できる。


OIDC、Web IDフェデレーション

▼ OIDC、Web IDフェデレーションとは

OIDCまたはWeb IDフェデレーションによる認証/認可を使用する。

▼ CognitoをIDプロバイダーとする場合

CognitoをIDプロバイダーとして使用するように、信頼されたエンティティを設定する。

{
  "Version": "2012-10-17",
  "Statement":
    {
      "Effect": "Allow",
      "Principal": {"Federated": "cognito-identity.amazonaws.com"},
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
          # 完全一致
          "StringEquals": {"cognito-identity.amazonaws.com:aud": "*****"},
          "ForAnyValue:StringLike":
            {"cognito-identity.amazonaws.com:amr": "unauthenticated"},
        },
    },
}

▼ AWS EKSをIDプロバイダーとする場合

AWS EKS_oidc

AWS EKSをIDプロバイダーとして使用するように、FederatedキーでAWS EKS Clusterの識別子を設定する。

これにより、AWS EKS Cluster内で認証済みのServiceAccountにIAMロールを紐付けることができるようになる。

また、Conditionキーで特定のServiceAccountを指定できるようにする。

{
  "Version": "2012-10-17",
  "Statement":
    [
      {
        "Sid": "",
        "Effect": "Allow",
        "Principal":
          {
            "Federated": "arn:aws:iam::<AWSアカウントID>:oidc-provider/<AWS EKS ClusterのOpenID ConnectプロバイダーURL>",
          },
        "Action": "sts:AssumeRoleWithWebIdentity",
        "Condition": {
            # 完全一致
            "StringEquals":
              {
                "<AWS EKS ClusterのOpenID ConnectプロバイダーURL>:sub":
                  ["system:serviceaccount:<Namespace名>:<ServiceAccount名>"],
              },
          },
      },
    ],
}

KubernetesのServiceAccountを作成し、IAMロールのARNを設定する。

ServiceAccountは、Terraformではなくマニフェストで定義した方が良い。

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    AWS EKS.amazonaws.com/role-arn: <IAMロールのARN>
  name: <信頼されたエンティティで指定したユーザー名内のServiceAccount名>
  namespace: <信頼されたエンティティで指定したユーザー名内のNamespace名>

IRSAにより、ServiceAccountを介してPodとAWS IAMロールが紐づく。

▼ その他のIDプロバイダー

  • Auth0
  • Keycloak
  • Google Cloud Auth


SAMLベースフェデレーション

SAMLによる認証/認可を使用する。


05. IAMロールの委譲先ユーザー

IAMロールの委譲先ユーザー

IAMユーザー、AWSリソース、フェデレーテッドユーザー、にIAMロールを委譲できる。

aws_sts_assumed-user


IAMロールの委譲先ユーザーの種類

▼ IAMユーザー

IAMロールと同じ/異なるAWSアカウントのIAMユーザーに委譲できる。

IAMユーザーの場合、外部IDが必要になる。

▼ AWSリソース

IAMロールと同じ/異なるAWSアカウントのAWSリソースに委譲できる。

IAMリソースの場合、外部IDが必要になる。

▼ フェデレーテッドユーザー

OIDC、SAML、によって発行されたユーザーに委譲できる。

OIDCのフェデレーテッドユーザーの場合、発行されたJWTが必要になる。


フェデレーテッドユーザー

▼ AWS OIDC

IAMロールの信頼されたエンティティに、AWS OIDCで発行されたユーザーを設定する。

フェデレーテッドユーザーは任意のIPプロバイダーで発行する。

{
  "Version": "2012-10-17",
  "Statement":
    {
      "Effect": "Allow",
      "Principal": {"Federated": "cognito-identity.amazonaws.com"},
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
          # 完全一致
          "StringEquals": {"cognito-identity.amazonaws.com:aud": "*****"},
          "ForAnyValue:StringLike":
            {"cognito-identity.amazonaws.com:amr": "unauthenticated"},
        },
    },
}

▼ 外部OIDC

IAMロールの信頼されたエンティティに、外部OIDCサービスで発行されたユーザーを設定する。

{
  "Version": "2012-10-17",
  "Statement":
    {
      "Effect": "Allow",
      "Principal": {"Federated": "accounts.google.com"},
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
          # 完全一致
          "StringEquals": {"accounts.google.com:aud": "*****"},
          "ForAnyValue:StringLike":
            {"accounts.google.com:amr": "unauthenticated"},
        },
    },
}

▼ AWS SAML

IAMロールの信頼されたエンティティに、AWS SAMLで発行されたユーザーを設定する。

{
  "Version": "2012-10-17",
  "Statement":
    [
      {
        "Effect": "Allow",
        "Principal":
          {
            "Federated": "arn:aws:iam::<AWSアカウントID>:saml-provider/<プロバイダー名>",
          },
        "Action": "sts:AssumeRole",
        "Condition": {
            # 完全一致
            "StringEquals": {"SAML:aud": "https://signin.aws.amazon.com/saml"},
          },
      },
    ],
}