IAM Identity Centerの導入備忘録

この記事は [Metaps Advent Calendar 2023](https://qiita.com/advent-calendar/2023/metaps) 11日目の記事となります。

# はじめに
### IAM Identity Center(旧AWS SSO)とは？

AWS Organizations組織に属している複数のAWSアカウントへのシングルサインオンを可能にし、各アカウントへの権限を一元管理できるサービスです。
また、SAMLアプリケーションにも対応しており(下記URL参照)、
OU外のアカウントも`External AWS Account`というIdentity Centerアプリケーションを使ってSAML経由でシングルサインオンが可能になります。
https://docs.aws.amazon.com/ja_jp/singlesignon/latest/userguide/saasapps.html

https://aws.amazon.com/jp/iam/identity-center/

弊社メタップスホールディングスでのIAM Identity Center導入の一部始終を記事にしました。


# IAM Identity Centerの導入背景
弊社で導入していたSSOサービスが2023年度末でEOLを迎えるため、新しいSSOの導入を検討していました。

求められた要件は以下のとおり
- 開発者側で日常的に使うサービスはライセンスの都合上、IdP(Identity Provider)の利用が必須
- 自社管理AWSアカウントと、社外管理AWSアカウントの両方に簡単にアクセスできること
    - **自社管理AWSアカウント**: 社内で開発しているサービスのプロダクトアカウント
    - **社外管理AWSアカウント**: 社外で開発しているサービスのプロダクトアカウント
- 移行先ではAWS STSを発行したい
    - プログラムアクセスでIAMアカウントのアクセスキーを使わない方針にするため
    - アクセスキー流出によるリスクはこちらのブログにわかりやすく記載されています

https://dev.classmethod.jp/articles/unauthorized-use-of-aws-access-keys-due-to-leakage/
- AWS以外のSaaSもSSO可能であること
- 現状、Googleアカウント（GoogleWorkspace）が情シス、それ以外で開発に関わるアカウントはSREで管理

### IAM Identity Center
* Organizationsとの連携ができるためAWSのマルチアカウント運用がとても簡単

### Google Workspace SSO
* Google Workspaceの仕組みの上で動作するため、情シスでアカウント管理ができそう

~~
といった感じで検討した結果、IAM Identity Centerを導入することになりました。

※ SREチーム側で対応が必要なのは緑の部分で、青の部分はGoogleWorkspaceと連携されるイメージ
利用ユーザーからだと、[Google認証]→[IAM Identity Centerスタートページ]→[AWSマネジメントコンソール]といった画面遷移

# 導入パターン
当初はこんな感じに設定する予定でしたが...

- 緑の部分: SRE側で管理
- 青の部分: 情シス側で管理

``` mermaid
flowchart LR

subgraph GoogleWorkspace
    GoogleUser["Googleアカウント"]
end

subgraph GC["AWS HQアカウント"]
  subgraph GV[IAM Identity Center]
    IdStore[("Id Store")]
    IdP["IdP Source"]
    PSet["許可セット"]
    UG["ユーザーグループ"]
  end
end

AWSAC["自社管理AWSアカウント"]
AWSEAC["外部AWSアカウント"]
ESV["外部サービスアカウント"]


GoogleUser -- SCIMでユーザー情報を同期 --> IdStore
GoogleUser -- GoogleをIdPとして設定 --> IdP
GV -- AWS SSO自動プロビジョニング --> AWSAC
GV -- 手動でSAML認証設定 --> AWSEAC
GoogleUser -- SAML認証などで連携 --> ESV

classDef SGC fill:none,color:#345,stroke:#345
class GC SGC
class GoogleWorkspace SGC

classDef SGV fill:none,color:#0a0,stroke:#0a0
class GV SGV

classDef SGPrS fill:#def,color:#07b,stroke:none
class GS2 SGPrS

classDef SGPuS fill:#efe,color:#092,stroke:none
class GS1 SGPuS

classDef SDB fill:#46d,color:#fff,stroke:#fff
class GoogleUser SDB
class IdStore SDB
class IdP SDB
class ESV SDB

classDef SST fill:#493,color:#fff,stroke:#fff
class PSet SST
class UG SST
class AWSAC SST
class AWSEAC SST
```

実際に導入したパターンはこのようになりました。

```mermaid
flowchart LR

subgraph GC["AWS 親アカウント"]
  subgraph GV[IAM Identity Center]
    IdStore[("Id Store")]
    IdP["IdP Source"]
    PSet["許可セット"]
    UG["ユーザーグループ"]
  end
end

AWSAC["自社管理AWSアカウント"]
AWSEAC["社外管理AWSアカウント"]
ESV["外部サービスアカウント"]

GV -- AWS SSO自動プロビジョニング --> AWSAC
GV -- 手動でSAML認証設定 --> AWSEAC
GV -- SAML認証などで連携 --> ESV

classDef SGC fill:none,color:#345,stroke:#345
class GC SGC

classDef SGV fill:none,color:#0a0,stroke:#0a0
class GV SGV

classDef SGPrS fill:#def,color:#07b,stroke:none
class GS2 SGPrS

classDef SGPuS fill:#efe,color:#092,stroke:none
class GS1 SGPuS

classDef SDB fill:#46d,color:#fff,stroke:#fff
class ESV SDB

classDef SST fill:#493,color:#fff,stroke:#fff
class PSet SST
class UG SST
class AWSAC SST
class AWSEAC SST
class IdStore SST
class IdP SST
```

# 実際導入してみて気づいたこと
- Terraformで対応していない・対応できない部分が多い
- OU外アカウントでSTSを使うにはsaml2awsの導入が望ましい
- IDソースをGoogleWorkspaceにした時に起きた問題

これらの点を深掘りしていきます。

## Terraformで対応していない・対応できない部分が多い
- SSOインスタンスがTerraform上で設定できない
ssoインスタンスは現状Terraformで作成できないため(v1.4)、data句で持ってくることしかできません。
```main.tf
####################################################################
# (一部抜粋) IAM Identity Center
####################################################################
data "aws_organizations_organization" "this" {}

# SSOインスタンス
## Terraform上で作成不可のため、dataソースで定義
data "aws_ssoadmin_instances" "this" {}

# IDストアグループ
resource "aws_identitystore_group" "this" {
  for_each = local.groups

  identity_store_id = tolist(data.aws_ssoadmin_instances.this.identity_store_ids)[0]
  display_name      = each.value.name
}
```
- `~/.aws/config`に追記する必要がある

下記のIssueにもありますが、AWS Providerバージョン4.50.0現在、
`aws sso login`の設定をしただけではエラーが起こります。

https://github.com/hashicorp/terraform/issues/32465

`terraform init`
```
Initializing the backend...
Initializing modules...
╷
│ Error: error configuring S3 Backend: Error creating AWS session: profile "{AWS Profile}" is configured to use SSO but is missing required configuration: sso_region, sso_start_url
│ 
│ 
```

そのため、`sso_start_url`と`sso_region`を追加する必要があります。
```~/.aws/config
[profile {プロファイル名}]
+ sso_start_url = {AWS SSOのログインページURL}
+ sso_region = ap-northeast-1
sso_session = metaps
sso_account_id = {AWS Account ID}
sso_role_name = {Product}{Role}
region = ap-northeast-1
output = json

[sso-session metaps]
sso_start_url = {AWS SSOのログインページURL}
sso_region = ap-northeast-1
sso_registration_scopes = sso:account:access
```


## OU外アカウントでSTSを使うにはsaml2awsの導入が望ましい
OU外のAWSアカウントでは、`aws sso login`コマンドを使ってsts情報を取得することができません。
そのため、以下の手順でSAML認証を行う必要があります。
- ブラウザでSAMLレスポンスを取得
- SAMLレスポンスをファイルに保存 (samlresponse.log)
- `aws sts assume-role-with-saml`コマンドを入力
(コマンド全文)
```
aws sts assume-role-with-saml --role-arn arn:aws:iam::ACCOUNTNUMBER:role/IAM_ROLE --principal-arn arn:aws:iam::ACCOUNTNUMBER:saml-provider/SAML_PROVIDER --saml-assertion file://samlresponse.log

awk -F:  '
                BEGIN { RS = "[,{}]" ; print "[PROFILENAME]"}
                /:/{ gsub(/"/, "", $2) }
                /AccessKeyId/{ print "aws_access_key_id = " $2 }
                /SecretAccessKey/{ print "aws_secret_access_key = " $2 }
                /SessionToken/{ print "aws_session_token = " $2 }
' 
```
- レスポンスを`~/.aws/credentials`に保存

詳しい手順はAWS公式から出しているマニュアルをご確認ください。
https://repost.aws/ja/knowledge-center/aws-cli-call-store-saml-credentials

ただ、この作業を毎回手作業でやることは現実的ではありません。
そのため、saml2awsというOSSを使うことで、上記の処理をインタラクティブに行ってくれます。

https://github.com/Versent/saml2aws

### saml2awsを使ったログイン手順
i. (初回設定)ターミナルで以下の項目を入力。
```
~ % saml2aws configure -a {saml2awsプロファイル名} -p {AWSプロファイル名}
? Please choose a provider: Browser 
? AWS Profile {AWSプロファイル名}
? URL {先ほどコピーしたURLを入力}
? Username [何も入力せずそのままenter]
? Password [何も入力せずそのままenter]
No password supplied

account {
  URL: https://{censored}.awsapps.com/start/#/saml/default/{IAMRole}/{SSOInstanceID}
  Username: 
  Provider: Browser
  MFA: Auto
  SkipVerify: false
  AmazonWebservicesURN: urn:amazon:webservices
  SessionDuration: 3600
  Profile: {AWSプロファイル名}
  RoleARN: 
  Region: 
}

Configuration saved for IDP account: {saml2awsプロファイル名}
```

ii. アクセスが必要な場合、`saml2aws login`コマンドを都度入力する。
 - インストール直後の初回のみ、 `--download-browser-driver`オプションを入力
```
~ % saml2aws login -a {saml2awsで使うプロファイル名} [--download-browser-driver] 
Using IdP Account {saml2awsで使うプロファイル名} to access Browser {SAMLのログインURL}
To use saved password just hit enter.
? Username   {何も入力せずそのままenter}
? Password  {何も入力せずそのままenter}

Authenticating as   ...
```
iii. ブラウザが立ち上がるので、SSO認証を行う

ターミナルがこの画面になれば成功。  
※1時間で有効期限が切れるため、都度`saml2aws login`を行うこと
```
Selected role: arn:aws:iam::{AWSアカウントID}:role/{ロール名}
Requesting AWS credentials using SAML assertion.
Logged in as: arn:aws:sts::{AWSアカウントID}:assumed-role/{ロール名}/{ユーザー名}

Your new access key pair has been stored in the AWS configuration.
Note that it will expire at {失効時間}
To use this credential, call the AWS CLI with the --profile option (e.g. aws --profile {プロファイル名} ec2 describe-instances).
```

# IDソースをGoogleWorkspaceにした時に起きた問題
**弊社では下記の問題が致命的だったため、IDソースをIAM Identity Centerにすることに決定しました。**
- SCIMを使った自動プロビジョニングは労力の割に得られる効果が薄い
- ユーザー名がメールアドレスと一致する必要がある
- saml2awsと社内認証システムとの相性が悪い

これらの点を深掘りしていきます。

## SCIMを使った自動プロビジョニングは労力の割に得られる効果が薄い
導入パターンの章でもありましたが、
情シス側でGoogleアカウント、SRE側でAWSアカウントを管理しています。
``` mermaid
flowchart LR

subgraph GoogleWorkspace
    GoogleUser["Googleアカウント"]
end

subgraph GC["AWS HQアカウント"]
  subgraph GV[IAM Identity Center]
    IdStore[("Id Store")]
    IdP["IdP Source"]
    PSet["許可セット"]
    UG["ユーザーグループ"]
  end
end

AWSAC["自社管理AWSアカウント"]
AWSEAC["外部AWSアカウント"]
ESV["外部サービスアカウント"]


GoogleUser -- SCIMでユーザー情報を同期 --> IdStore
GoogleUser -- GoogleをIdPとして設定 --> IdP
GV -- AWS SSO自動プロビジョニング --> AWSAC
GV -- 手動でSAML認証設定 --> AWSEAC
GoogleUser -- SAML認証などで連携 --> ESV

classDef SGC fill:none,color:#345,stroke:#345
class GC SGC
class GoogleWorkspace SGC

classDef SGV fill:none,color:#0a0,stroke:#0a0
class GV SGV

classDef SGPrS fill:#def,color:#07b,stroke:none
class GS2 SGPrS

classDef SGPuS fill:#efe,color:#092,stroke:none
class GS1 SGPuS

classDef SDB fill:#46d,color:#fff,stroke:#fff
class GoogleUser SDB
class IdStore SDB
class IdP SDB
class ESV SDB

classDef SST fill:#493,color:#fff,stroke:#fff
class PSet SST
class UG SST
class AWSAC SST
class AWSEAC SST
```

**自動プロビジョニングを行う場合、GoogleWorkspaceグループとIdentity Centerグループの構造を一致させることが望ましいです。** [^1]
[^1]: GoogleSAMLアプリケーションの紐付けは、GoogleWorkspaceグループ単位で行うため

そのため、GoogleWorkspaceでアカウント設計をリファクタリングする必要が出てきました。

(簡略化した弊社のアカウント組織図)
``` mermaid
flowchart LR
subgraph AWS;
    Organization --> AWSAccount1;
    Organization --> AWSAccount2;
    AWSAccount1 --> Account1D["DeveloperRole"];
    AWSAccount1 --> Account1A["AdministratorRole"];
    AWSAccount2 --> Account2D["DeveloperRole"];
    AWSAccount2 --> Account2A["AdministratorRole"];
end

subgraph GoogleWorkspace;
    GoogleAdmin --> OfficerGroup;
    GoogleAdmin --> EmployeeGroup;
    GoogleAdmin --> BusinessPartnerGroup;
end
```
限られた工数で実装するには情シス・SRE共にリソースが足りず、自動プロビジョニングしないという判断に至りました。

## ユーザー名がメールアドレスと一致する必要がある
意外な盲点でした。
自動プロビジョニングを行わない場合、IdentityCenterでユーザーとグループを作成する必要があります。(当たり前ですが)
現在弊社のIAMアカウントの命名規則は `{First name}_{Last name}` のようになっています。

(実際起きたエラー)
IDソースはGoogleWorkspaceで設定しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416385/2ddabe74-498e-ef5e-ea60-d9e5f92e4765.png)

こちらの記事に以下の内容が記載されていました。
(要約) IdentityCenterのユーザー名は、**GoogleWorkspaceメールアドレスをユーザー名として**Identity Centerのユーザーアカウントにマッピングされる。

https://aws.amazon.com/jp/blogs/security/how-to-use-g-suite-as-external-identity-provider-aws-sso/

> When you use Google Workspace to authenticate and manage your users, you have to create a user entity in IAM Identity Center. The user entity is not a user account, but a logical object. It maps a Google Workspace user through its primary email address as the username to the user account in IAM Identity Center. The user entity in IAM Identity Center allows you to grant a Google Workspace user access to accounts and define its permissions in those accounts.

## saml2awsと社内認証システムとの相性が悪い
現在、弊社の社内基盤認証システムはサテライトオフィスをで導入しています。

https://www.sateraito.jp/

saml2awsの認証には`Browser`プロバイダーを使う設定にしています。(認証時にChromiumが開きます。)
このChromium、かなり曲者でした。
シークレットウィンドウで開き、ブラウザ情報が1回の認証ごとに初期化されます。

そのため、ログインごとに端末使用申請をしなければならない[^2]状況に陥ります。
このままではSSOを使うならIDパスワード認証の方が楽という本末転倒な状態になってしまいます。
[^2]:メタップスホールディングスではセキュリティ上、アクセス元が変わるたび端末使用申請を必要としています。

saml2awsの認証に`GoogleApps`プロバイダーを使えばいいじゃないかとも思ったのですが、サテライトオフィスがCLIアクセスに対応しておらず、認証前に弾かれてしまいます。


* * *

以上のことから、IDソースをGoogleWorkspaceにせず、IAM Identity Centerにすることに決定しました。

# 最後に
IAM Identity Center、当初は簡単にGoogleと連携できると思っていたのですが、思った以上に制約が多かったです。
設定は完了したので、これから社員・パートナーにロールを付与していく段階です。
~~アカウント管理が正確に把握できてなかったこともあり適用するのがめちゃしんどいですが~~地道に頑張っていこうと思います。

反響があれば、どのようにTerraformでコード化していったかも記事として書こうと思います。