OpenSSLで自己署名CA証明書を作成し、オンプレからIAM Roles Anywhereを試す

# はじめに

AWS IAM Roles Anywhereはオンプレミス＆外部システムから、PKI (Public Key Infrastructure) 認証を用いて、AWSリソースアクセスすることができます。

本記事ではハンズオンとして、OpenSSLを利用して自己署名CA証明書からクライアント証明書を作成し、IAM Roles AnywhereからS3バケットの一覧表示を試します。

感想についても最後に備忘録として残しておきます。

また、躓いたポイントがありましたので、トラブルシューティングが誰かの助けになれば幸いです。

# 想定するユースケース(記事から抜粋)
- オンプレミスのデータを Amazon Simple Storage Service (Amazon S3) にバックアップする
- オンプレミスの Kubernetes ワークロードに対して、Amazon DynamoDB や Amazon Simple Queue Service (Amazon SQS) などのネイティブ AWS サービスへのアクセスを提供する
- AWS Secrets Manager に保存されているシークレットに対して AWS 外部のワークロードからアクセスする
- オンプレミスのソースから AWS Security Hub にセキュリティ検出結果を送信する
- ハイブリッドワークロードが段階的にマイグレーションしていく過程で AWS サービスにアクセスできるようにする

# ゴール
- AWS IAM Roles Anywhereを利用して、自宅VM上のOSからAWS CLIを叩いてS3バケットが表示されること
- サービスの使い方の全体像が把握できること

# 筆者の環境
- VM環境(ubuntu 24.04.01 arm)
- OpenSSL 3.0.13 30
- AWSのリソースは東京リージョン(ap-northeast-1)で作成しております
- aws_signing_helper 1.4.0

# 前提条件
- AWSの基本的な知識
- PKI認証が概略レベルでわかること。(PKIの認証のおさらいとして[こちら](https://www.nic.ad.jp/ja/newsletter/No23/080.html)が分かりやすかったです。)
- AWS アカウントを持っていること
- IAMに関連する基礎的な知識があること
- IAM ロールと IAM Roles Anywhere に対する権限が付与されたIAMユーザーで操作していること

# 準備
## aws_signing_helperをVM環境にダウンロード

- IAM Roles Anywhereを利用してAWSリソースの操作をするために、一時的なセキュリティ資格情報(セッション情報)の取得をします
- 取得にはAWSが提供する IAM Roles Anywhere の資格情報ヘルパーツールを使用します
- 詳細については、[Temporary security credentials in IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html)をご参照ください

### 以下のリンクからaws_signing_helperをダウンロード
> 自環境ではLinux Aarch64をダウンロードしました

[ダウンロード先](https://docs.aws.amazon.com/rolesanywhere/latest/userguide/credential-helper.html)

### ダウンロードしたaws_signing_helperに実行権限の付与
```
$chmod +x aws_signing_helper 
```

### aws_signing_helperが実行できることを確認
```
$ ./aws_signing_helper version
1.4.0
```

### OpenSSLがubuntu上でインストールされているかを確認する
```
$openssl version
OpenSSL 3.0.13 Jan 2024 (Library: OpenSSL 3.0.13 30 Jan 2024)
```

### 適当なS3バケットを作成する
(作成方法は省略します)

![スクリーンショット 2025-02-11 10.37.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/786bd464-5a57-e702-9d0a-5418e3b5caea.png)
- デモ用のバケット: s3-qiita-iamrolesanywhere

# 手順

## 1.OpenSSLで自己署名CA証明書を作成

- 信頼アンカーには ACM Private CA で発行した証明書または自己署名CA証明書 を登録できます。※信頼アンカーについては4の手順にて説明します
- ACMは利用せずに、opensslで自己署名CA証明書を作成します

### 1-1.自己署名CA用の秘密鍵を作成

- 自己署名CA用の 2048-bit RSA 秘密鍵を生成します
    ```
    $openssl genrsa -out aws-ca-private-key.pem 2048
    ```

### 1-2.自己署名CA証明書の作成

- 作成する前に`/etc/ssl/openssl.cnf`を以下のように修正or追加します。※設定をミスするとクライアント証明書の作成が上手にいかない可能性があります

- ```:/etc/ssl/openssl.cnf
    [ v3_ca ]
    subjectKeyIdentifier=hash
    authorityKeyIdentifier=keyid:always,issuer
    basicConstraints = critical,CA:true
    keyUsage = cRLSign, keyCertSign

    ...

    [ v3_client ]
    basicConstraints = CA:FALSE
    keyUsage = digitalSignature, keyEncipherment
    extendedKeyUsage = clientAuth
    ```
    
- 「1-1.自己署名CA用の秘密鍵を作成」で作成した秘密鍵を使用し、1年間有効な認証局 (CA) の自己署名CA証明書を作成します

    ```
    openssl req -x509 -new -key aws-ca-private-key.pem -sha256 -days 365 -out aws-ca-cert.pem -extensions v3_ca -config /etc/ssl/openssl.cnf
    ```
    
    ![スクリーンショット 2025-02-11 14.16.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/51748346-d499-5a78-0b92-722201df1d9c.png)

- 識別名は適当な値を入力します。私は以下の通り入力しました
    ```:識別名
    C: JP
    ST: AWS
    L: Tokyo
    O: ARI
    OU: DEV
    CN: ATSUSHI
    E: None
    ```

### 1-3. 自己署名CA証明書の内容を表示 

- 「1-2.自己署名CA証明書の作成」で作成した自己署名CA証明書の内容を表示します
    ```
    $openssl x509 -in aws-ca-cert.pem -text -noout
    ```
    
    ![スクリーンショット 2025-02-11 14.17.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/e6fe00ad-e709-ed71-49ec-385e4dfbdff4.png)

:::note info
作成したリソース
- aws-ca-private-key.pem : 自己署名CA用の秘密鍵
- aws-ca-cert.pem: 自己署名CA証明書 
:::

## 2.クライアントのCSR(証明書署名要求)を作成

### 2-1.クライアントの秘密鍵を作成
クライアント用の 2048-bit RSA 秘密鍵を生成します

```
$openssl genrsa -out aws-client-private-key.pem 2048
```

### 2-2.クライアントのCSR(証明書署名要求)を作成
- クライアントのCSRを作成します

```
openssl req -new -key aws-client-private-key.pem -out aws-client-csr.pem
```
![スクリーンショット 2025-02-11 14.33.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/121984d4-1c9c-08e8-c8f6-2f666e8d9d48.png)

- 識別名は適当な値を入力します。私は以下の通り入力しました
```:識別名
C: JP
ST: AWS
L: Kyoto
O: ARI
OU: OPS
CN: TARO
E: None
```

:::note info
作成したリソース
- aws-client-private-key.pem : クライアント用の秘密鍵
- aws-client-csr.pem: クライアントのCSR(証明書署名要求)
:::

## 3.自己署名CA証明書からクライアント証明書を作成

### 3-1. クライアント証明書を自己署名CA証明書で作成

- 自己署名CAの秘密鍵と自己署名証明書を使用し、1年間有効なクライアント証明書を作成します

```
$openssl x509 -req -in aws-client-csr.pem -CA aws-ca-cert.pem -CAkey aws-ca-private-key.pem -CAcreateserial -out aws-client-cert.pem -days 365 -extensions v3_client -extfile /etc/ssl/openssl.cnf 
```

![スクリーンショット 2025-02-11 14.34./04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/81a0f674-7fe1-d0cc-838d-498b96216e93.png)

### 3-2.クライアント証明書の内容を表示

```
$openssl x509 -in aws-client-cert.pem -text -noout
```

![スクリーンショット 2025-02-11 14.34.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/c779a0da-e3c0-1a40-8924-b63e4bb8e0af.png)
![スクリーンショット 2025-02-11 14.35.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/b42ad777-b99f-e5d4-ab07-ddca74055264.png)

### 3-3.クライアント証明書の検証
- 以下のコマンドからクライアント証明書の検証を行います(OKだったら大丈夫です)
```
openssl verify -CAfile aws-ca-cert.pem aws-client-cert.pem
```
![スクリーンショット 2025-02-11 14.36.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/77bbae51-9e81-43b7-b2ac-001335bc07fd.png)

:::note info
作成したリソース
- aws-client-cert.pem : クライアント証明書
:::

## 4.AWS IAM Roles Anywhereの設定

### 4-1.信頼アンカーの作成

:::note info
信頼アンカーとは、認証局(CA)の証明書をAWSに登録し、CAが発行した証明書を信頼する仕組みです。これにより、適切に署名されたクライアント証明書を使用して、IAMロールを引き受けることが可能になります。
:::

- IAMロールの画面に移動。「信頼アンカーを作成する」を押下します

![スクリーンショット_2025-02-11_15_18_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/5b99a981-e975-4741-809b-316cd7e5a71e.png)

- 「信頼アンカー名」に値を入力します ※ハンスオンでは`aws-ca-cert-achor`とします
-  ACMで発行した証明書の場合は「AWS証明書管理プライベートCA」にチェックをしますが、今回は自己署名CAなので「外部証明書ハンドル」を押下します
-  外部証明書ハンドルには「1-2.認証局 (CA) の自己署名証明書を作成」で作成済みの`aws-ca-cert.pem`の中身を転記します。空白など余計な文字を入力するとエラーが発生するので、ご注意ください
-  Notifications settingsは証明書の有効期限が切れた時、有効期限が切れる前にEvent Bridgeなどで通知ができる設定です
    -  CA certificate expiry event: 自己署名CA証明書の期限
    -  End Entity Certificate Expiry Event: クラアント証明書の期限

![スクリーンショット_2025-02-11_15_33_54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/6dca71e4-4419-4905-8702-cf43d397bef6.png)

- 信頼アンカーが作成されたことを確認
![スクリーンショット 2025-02-11 15.34.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/a1ef3532-1821-d95d-d181-988cd86e3211.png)

### 4-2.IAMロールを作成

- S3のバケットを表示するためのIAMロールを作成します(リソース名は`s3-readonly-anywhereroles`)
- 信頼されたエンティティタイプは「AWSのサービス」を選択
- サービスまたはユースケースには「Roles Anywhere」を選択
- 「次へ」を押下します

![スクリーンショット_2025-02-11_15_40_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/c3b605ec-618d-4169-bf82-7baedb417ab9.png)

- ポリシーには「AmazonS3ReadOnlyAccess」をチェック
- 「次へ」を押下します

![スクリーンショット_2025-02-11_15_41_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/4c506f3e-2bba-4043-b02d-2ee2a7b04b67.png)

- 信頼ポリシーに以下のConditionセクションを追加します

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Service": "rolesanywhere.amazonaws.com"
            },
            "Action": [
                "sts:AssumeRole",
                "sts:SetSourceIdentity",
                "sts:TagSession"
            ],
            "Condition": {
                "ArnEquals": {
                    "aws:SourceArn": "arn:aws:rolesanywhere:ap-northeast-1:<アカウントID>:trust-anchor/08520eee-d730-41f5-a589-d60cefade923"
                }
            }
        }
    ]
}
```

- 以下の設定でリソースの権限範囲をクライアント証明書に限定することができます。証明書で設定した組織名を指定することも可能です

![スクリーンショット_2025-02-11_15_53_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/5e94b490-c8ef-45cc-b989-047be1e8bcc6.png)

- IAMロールの作成を確認

![スクリーンショット 2025-02-15 12.08.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/32d9e58b-9dd9-41fd-9c91-e6d291266d3f.png)

### 4-3.プロファイルを作成

:::note info
プロファイルとはIAMロールと信頼アンカーを紐付けて、セッション情報を取得する設定です。期限や、追加でポリシーの付与が可能です。
::: 

- 「プロファイルを作成」を押下

![スクリーンショット_2025-02-11_15_18_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/88d346cc-d52e-4d08-961c-ab0cf0b091fd.png)

- プロファイル名に適当な値を入力します。※ハンスオンでは`aws-ca-cert-profile`とします
- IAMロールには「4-2.IAMロールを作成」で作成したIAMロール`s3-readonly-anywhereroles`を指定します。IAMロールを複数指定できますが、指定されたIAMロールは他のプロファイルで利用することができません
- セッションポリシーはIAMロールにアクセス制限を追加で設定することができます。※ハンズオンでは設定しませんでした
- Session Durationはセッション情報の時間設定です。15分 ~ 12時間の間で設定することができます。ハンズオンでは1時間で設定しました
- Custom Role Session nameはセッション名に名前を付けられます。※ハンズオンでは設定しませんでした
- 「プロファイルを作成」を押下

![スクリーンショット_2025-02-11_16_05_19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/8f6731db-e155-4a39-8224-f453d4040679.png)

- プロファイルが作成されたことを確認

![スクリーンショット_2025-02-11_16_07_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/a8de373c-6e0d-47a3-a849-e1db7d14e4fe.png)

:::note info
作成したリソース
- aws-ca-cert-achor : 信頼アンカー
- aws-ca-cert-profile : プロファイル
- s3-readonly-anywhereroles : IAMロール
:::

## 5.AWS CLIからS3バケットを表示

### 5-1. セッション情報を取得する設定

- aws_signing_helper credential_processでセッション情報を取得します
- セッション情報は実行するたびに変更されます
- 以下は引数の説明です
```: 
aws_signing_helper credential_process
    -- certificate: クライアント証明書ファイルのパスを指定します
    -- private-key: クライアント秘密鍵ファイルのパスを指定します
    -- trust-anchor-arn: 作成した信頼アンカーのARNを指定します
    -- profile-arn: 作成したプロファイルのARNを指定します
    -- role-arn: IAM Roles Anywhereで利用するIAMロールのARNを指定します
```

- コマンドが長いため、以下のスクリプトを作成しております
```:sts.sh
#!/bin/bash

# aws_signing_helper credential-process
./aws_signing_helper credential-process \
  --certificate <クライアント証明書ファイル（aws-client-cert.pem） \
  --private-key クライアント秘密鍵ファイル（aws-client-private-key.pem） \
  --trust-anchor-arn 信頼アンカーARN(aws-ca-cert-achor) \
  --profile-arn プロファイルARN(aws-ca-cert-profile) \
  --role-arn IAMロールARN(s3-readonly-anywhereroles)
```

### 5-2. セッション情報を取得

- 「5-1.セッション情報を取得する設定」で作成した`sts.sh`を実行します
```
$./sts.sh
```

-  実行結果から`AccessKeyId`、`SecretAccessKey`、`SessionToken`を控えます
-  `Expiration`はセッションの期限を表しています
![スクリーンショット 2025-02-11 16.16.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/5a5a5e26-2874-3a3d-4f4e-9fa513b20593.png)

- もう一度実行すると違うセッション情報を取得します
![スクリーンショット 2025-02-11 16.17.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/56405d9b-7e09-a233-16b3-2e3e28806f7e.png)

- 取得したセッション情報を`~/.aws/config`に追加
![スクリーンショット 2025-02-11 16.22.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/84893ae1-0414-52f9-3a5a-fc92ee81e38e.png)

### 5-3. AWS CLIでS3バケットを表示

- CLIからS3バケットの一覧を表示します
```
$aws s3 ls --profile roleanywhere-profile
```
- S3バケットの表示を確認
![スクリーンショット 2025-02-11 16.27.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/c7bf1e6c-1f5d-47ec-1060-45e6eb67f1ee.png)

### 5-4. クライアント証明書の有効期限が過ぎた時の表示

- クライアント証明書の有効期限が過ぎた時に、再度セッション情報を取得しようとすると下記のように画面が出力されます

```
$./sts.sh
2025/02/15 05:32:50 AccessDeniedException: Untrusted signing certificate
```
![Ubuntu_64_ビット_ARM_Server_24_04_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972157/83db6d4c-8af3-4a1f-9a7a-1e72d822bd12.png)

# 最後に
IAM Roles Anywhereのハンスオンを試してみました。サーバの台数が多くなると証明書の管理やaws_signing_helperでセッション情報を取得する必要があるため、運用の負担は増えます。また、料金についても実運用だと証明書にACMを利用するのでコスト面でもやはり考慮する必要があります。

しかし、セキュリティはPKIを利用するため高く、要件が厳しいところでは機会があるのかなと思いました。

# トラブルシューティング(追記予定)

## aws_signing_helper credential-processの実行時にUntrusted certificateyやInsufficient certificateエラーが出力される場合。

- 設定(`/etc/ssl/openssl.cnf`)にミスがあるかもしれません
- 以下の設定と一緒に正しい記載かご確認ください
```:/etc/ssl/openssl.cnf
    [ v3_ca ]
    subjectKeyIdentifier=hash
    authorityKeyIdentifier=keyid:always,issuer
    basicConstraints = critical,CA:true
    keyUsage = cRLSign, keyCertSign

    ...

    [ v3_client ]
    basicConstraints = CA:FALSE
    keyUsage = digitalSignature, keyEncipherment
    extendedKeyUsage = clientAuth
```

# 参考記事
- https://docs.aws.amazon.com/rolesanywhere/latest/userguide/introduction.html
- https://aws.amazon.com/jp/blogs/news/extend-aws-iam-roles-to-workloads-outside-of-aws-with-iam-roles-anywhere/

# 仲間を募集しています！
ARIではエンジニア・ITコンサルタント・PM職全方位で仲間を募集しております。
カジュアル面談、随時受付中です！
ご興味ある方はこちらをご覧ください。

https://note.com/ari_hr/n/n4aed9ba75421