AWSとオンプレミス間通信をローカルIPでマウントする構成を考える

:::note alert
今回は、かなり実用はできるけど趣味に走った構成で長くなります。
必要な所だけかいつまんでお読みください。
:::

# Introduction

以前、[こちらの記事](https://qiita.com/yuri_snowwhite/items/19c9afd9ce7dfa6557a9)で書いたMountpoint for Amazon S3＋IAM Roles Anywhere＋Site to Site VPNを用いてオンプレミスのサーバから「エンドポイント」でマウントをするのを目的に成功した備忘録になります。

# 今回で使ったもの
## AWSサービス
- VPC
- S3 Endpoint(Interface/Gateway)
- IAM
- IAM Roles Anyware
- Site to Site VPN
- Security Group
- AWS Private Certificate Authority
- Route 53 Inbound Resolver Endpoint
- Cloudshell (設定に利用します）
- EC2(通信確認用）
## オンプレミスの環境
- VMWare上に建てているUbuntu最新安定版DesktopEdition　※AWSCLIは導入しておくこと
- YAMAHA RTX-1210(プロバイダは固定IPアドレス付きになります）

~~かなり色々なものを使いましたね~~

# 実際の設定
## AWS環境の準備
1. VPC作成
    1. 最後、漏れがなければ、さくっと、これはCloudFormationを用いました。サンプルがAmazonQやClaudeでも作成出来ますので仕様を記載します。
        - Public Subnet 2つ
        - Private Subnet 2つ
        - IGWを作成
        - NATGatewayを作成
    となります。
    2. VPNの接続設定を作成
    3. 通信テスト用にPrivateSubnetにEC2インスタンスを起動
## オンプレミス環境の準備
1. VMWareにUbuntuをインストールする（GUIが有ればなお良し）
2. YAMAHAの設定を変更してAWSとVPNを接続する。
3. pingでもなんでも良いので一旦疎通を確認する。

## IAM Roles Anywareを設定
### IAM Roles Anywareとは
AWS外部稼働しているワークロードにIAMロールを使用できるようにするもの
これによりIAMユーザなどで`長期的なCredentialを発行したままにせず漏洩を防ぐことができる`
要するに、よりりセキュアにVM等AWS外からAWSサービスへ直接接続出来るようにしようというもの
詳しくは[AWSのブログ](https://aws.amazon.com/jp/blogs/news/extend-aws-iam-roles-to-workloads-outside-of-aws-with-iam-roles-anywhere/)を参照ください。
また、本記事は[こちら](https://aws.amazon.com/jp/blogs/news/extend-aws-iam-roles-to-workloads-outside-of-aws-with-iam-roles-anywhere/)も参考に設定を行っています。
### オンプレミス側で設定
1.　認証用のCSRと秘密鍵の作成
   一昔前だとコマンドで使ってたと思うのですが念の為コマンド記載します。  
```shell
    # 作成
    openssl req -out csr.pem -new -newkey rsa:2048 -nodes -keyout private-key.pem
    # 確認
    openssl req -in csr.pem -text -noout
```
作成時、国コード等を設定する形になりますがこの後の作業で認証を取る際に
`Organization Unit(部署名)`と`CommonName(サイト名をいつも入力してた所)`が必要なりますので入力しておきましょう。

:::note info
本手順ではOrganization Unitを入力漏れのままで進めてたため、認証用の名前を消すことで対応したので少し脆弱となっています
:::
できた、各Pemファイルの内容を`cat`コマンドなどで表示ができることも確認しておいてください。
### AWS側で設定
2.　AWSマネジメントコンソールから「AWS Private Certificate Authority」にアクセスして認証局を作成します。
![AWS_Private_Certificate_Authority.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3553392/558f1aa2-e287-4212-af43-b193ffecf664.png)
から
![Create_PrivateCA.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3553392/d0cc7a84-17f7-4be1-aeb9-92a09a206703.png)
3.　認証局の設定
モードオプションで有効期間を選択します。
今回は、検証というのもありましたので短期間としました。また、ユースケースや料金に会わせて汎用等設定してください
![Short-live-Certificate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3553392/6bab833a-78f7-4d87-99fc-0e4916a870a1.png)
サブジェクトの設定を行います
![Subject_Settings.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3553392/75766743-4061-4c3b-88b5-50f8d3a1298f.png)
今回は、オンプレミスで作成したものと同じ物を入力しました。
4.　CAルート証明書の作成
できあがった認証局のアクションからCA証明書のインストールでインストールを行います。
暗号化方式は一般的SSL証明書と同じRSA2048にしていますが、暗号強度は高い方が良いですよ！
また、CAルート証明書は基本的に10年がデフォルトの最短期限です。AWSでも同様でした。
![Certification_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3553392/817c3245-c741-4754-835c-57fb747d42e0.png)
できあがった認証局の`ARN`をコピーしておきましょう
5.　エンティティ証明書の作成
もの凄く簡単に言うと、署名リクエストから作る証明書です。この証明書を用いてオンプレミスのサーバから認証を行います。
残念ながら、この証明書はCLIじゃないと作れません。また、認証局にアクセスする関係で、先の手順で認証局にアクセス出来る場所から行いましょう。
今回は`Cloudshell`から行いました。
また、オンプレミスで作成したCSRをここで、vi等でコピー&ペーストで保存するか、アップロードしてください
```shell
作成コマンド
aws acm-pca issue-certificate \
--certificate-authority-arn "CAarn" \
--csr fileb://"CSRのあるディレクトリ" \
--signing-algorithm "SHA256WITHRSA" \
--validity Value=7,Type="DAYS"
書き出しコマンド
aws acm-pca get-certificate \
--certificate-authority-arn CAarn \
--certificate-arn CertificateArn | \
jq -r .'Certificate' > "保存ファイル名".pem
```
有効期限は7日（今回作れる最長期間）にしています。
また、書き出した証明書はダウンロードしてください。オンプレミスのサーバにセットします。(アップロード時同様vi等を利用してコピー&ペーストでも可です）
6.　オンプレミスに設定するIAMRoleの準備
引き続きCloudshellで以下のコマンドで証明書の情報を確認します。
```shell
openssl x509 -text -noout -in "先ほど保存した証明書"
```
IAMロールの設定画面で「カスタム信頼ポリシー」を選択して、下記のカスタムポリシーを入れます。
![CustomIdentityPolicy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3553392/2178c376-36cb-493e-b715-3fa297bb7663.png)
```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
            "Service": "rolesanywhere.amazonaws.com"
            },
            "Action": [
                "sts:AssumeRole",
                "sts:SetSourceIdentity",
                "sts:TagSession"
            ],
            "Condition": {
                "StringEquals": {
                    "aws:PrincipalTag/x509Issuer/CN": "************",
                    "aws:PrincipalTag/x509Issuer/OU": "************",
                    "aws:PrincipalTag/x509Subject/CN": "************",
                    "aws:PrincipalTag/x509Subject/OU": "************"
                }
            }
        }
    ]
}
```
ここで`StringsEquals`に入れている値ですが、先にopensslで確認した内容をそれぞれ入力してください。

また、必要な権限は適宜入れてください。
今回はSSM（後述）の権限とS3の権限を付与しました。
できあがったIAMロールのARNをコピーしておいてください
7.　IAM Roles Anywareの使用準備

:::note info
IAMはグローバルサービスですが、IAM Roles Anywareは、`リージョンサービス`になります。設定するときは認証局を作成したリージョンに合わせてください
:::

信頼アンカーとプロファイルを作成します。
作成箇所はIAMロールのトップページの管理から設定可能です。
![RolesAnyware.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3553392/4efa2e63-4fc1-43c6-99a5-e039128691fe.png)
作成するのは`信頼アンカー`と`プロファイル`です。
![Trust_Unker.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3553392/08f43501-a14c-4031-9684-a037af59c51c.png)
![profile.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3553392/63ed89c1-e96f-464a-985e-5d4e77bde47d.png)
プロファイル作成時、引き受け可能なロールが出てきますので設定を忘れないようにしましょう
![createprofile.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3553392/b271b0a4-39a3-4735-a07f-3e3d8a615216.png)
作成後のそれぞれのARNはメモしておいてください。

8.　オンプレミスへIAMロール設定の導入
- IAM_signin_helperを導入して、IAMロールを利用できるようにします。
[Get temporary security credentials from IAM Roles Anywhere](https://docs.aws.amazon.com/rolesanywhere/latest/userguide/credential-helper.html)から、`IAM_signing_helpe`のインストーラをダウンロードして必要であればパスも通します。
この後に記載する小技的なものの関係で私は下記にしました。
```shell
wget "downloadURL"
sudo cp -rp aws_signing_helper /usr/sbin/
sudo chown root.root /usr/sbin/aws_signing_helper
sudo chmod 755 /usr/sbin/aws_signing_helper
ls -la /usr/sbin/aws_signing_helper
aws_signing_helper -v
```
完了後、下記のコマンドを実行するとAssumeロールが引き受け可能になります。
```shell
aws_signing_helper credential-process \
--certificate ${ダウンロードしたPem} \
--private-key ${CSR作成した際に一緒に作成した秘密鍵} \
--trust-anchor-arn ${アンカーのARN} \
--profile-arn ${IAM Roles AnywareのプロファイルARN} \
--role-arn ${プロファイルに設定した引き受けるロールのARN}
```
#### 小技

最後のコマンドを毎回打つというのは中々大変だったりします。
そのため`~/.aws/config`へ以下を追記することで、毎回、実行しなくて済みます。
また、そのためにsbinへの導入をしています。
```shell
[profile ${profilename}]
credential_process = aws_signing_helper credential-process -certificate ${ダウンロードしたPem} --private-key ${CSR作成した際に一緒に作成した秘密鍵} -trust-anchor-arn ${アンカーのARN} --profile-arn ${IAM Roles AnywareのプロファイルARN} --role-arn ${プロファイルに設定した引き受けるロールのARN}
region = ${region}
```
ワンライナーで書くように記載してますが、改行したため`mount-s3`の検証の際に要らないエラーを出してしまい~~無駄な時間を使い~~ましたという感想を書いておきます。。。~~

確認を行う為何でも良いのでawsコマンドを実行します。
一例
```shell
aws s3 ls
```
バケット一覧が出てくれば、IAM Roles Anywareの設定も完了です。

### ちょっと寄り道（オンプレミスサーバでSSMセッションでアクセスする）

#### Sub Introduction

ここまでやったならSystemsManagerも導入しちゃえば、もしかしてAWSマネジメントコンソールで全部管理出来るんじゃないの？と思ってしまったので、やってみました。

1.　マネジメントコンソールからAWS Systems Managerへアクセスして、ハイブリットアクティベーションを作成します。
2.　アクティベーションを作成しますが、作成完了後添付画像の様な緑色の通知が出てきます。オンプレミスサーバにSSM-Agentのアクティベーション時に必要なのでメモしておきましょう
![ssm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3553392/794abc83-fbf8-4932-b769-4ca714b56c27.png)
3.　[SSM-Agentのインストール](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/hybrid-multicloud-ssm-agent-install-linux.html)を参照に、インストールをします。
4.　インストール完了後、下記のコマンドでアクティベーションを行います。
```shell
sudo /tmp/ssm/ssm-setup-cli -register -activation-code "activation-code" -activation-id "activation-id" -region "region"
```
上記で設定は完了です。
あとは、マネジメントコンソールからアクセスをしてみるだけです。
セッションを張る際、課金が上がると言った警告が出ます
![Advanced_tier.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3553392/c9a915a3-7bd9-4864-906a-fa9fab47ffcd.png)
同意を行うとセッションが張れました
![sessionmanager_local.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3553392/87dd4432-9f6d-41c0-a3f0-a6984b840749.png)

##### 所感
少しもっさり感があります。具体的には応答が一呼吸遅れるかな？といった感じです。
また、IAMRole（Anyewareに許可しているRole）に通常EC2でSystems Manager接続を許可するものと同様の権限を付与する必要がありますのでご注意ください。
今回、北部バージニア(us-east-1)リージョンを使ってるのでもしかしたら日本リージョンを使えば多少違うかもしれません

### mount-s3のVPN越しでローカルIPでマウント
長かったこの記事も終わりに近づいてきました。
最後の仕上げです。UbuntuでS3をマウントします。
#### AWS側で設定
1.　AWSマネジメントコンソールのVPCからS3のGatewayEndpointを作ります(理由は後述します）
2.　Route 53インバウンドResolverを設定します。
3.　コレを作った上でInterfaceEndpointを作成します。
この際、インバウンドエンドポイントのためにプライベートDNSを有効にするのチェックボックスを入れましょう。
![Endpoint_Inbound.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3553392/a124caa2-08cf-43ce-b3d6-12ff5445e9e6.png)
この際1番の設定を行っていないとエラーとなります。
[公式ブログ](https://aws.amazon.com/jp/blogs/news/introducing-private-dns-support-for-amazon-s3-with-aws-privatelink)にも追記みたいな形で記載されていたため、少し悩みました。
また、こちらをを設定してなかったためこの後のMountS3がグローバル経由になる（digで確認してた）形になってました。
4.　ここで、肝心でS3バケットを作成します。
#### オンプレミス側で設定
5.　DNSサーバの参照先の変更
※本来は、社内のDNSサーバで社内のシステムの参照先にない場合の転送先に設定を行うのが正しい姿ですが内部DNSが今回の環境は無いため、直接指定しています。
![resolveconf.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3553392/3bbe9412-1a49-468a-b0f9-caec68d3eade.png)
6.　`mount-s3`の設定をします。
こちらは前回の記事を参考にセットアップします。
前回はRHELベースだったので[公式サイト](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/mountpoint-installation.html)からDEBベースでインストールを行います。
無事、インストール完了・・・・と思ったのですが、どうやら、IAM Roles Anyware経由ではうまく認証出来ない・・・？
と思ったら、上述した、1行に書いていた無かったためでした。
```bash
unable to use cert store signer on linux
sh: 2: --certificate: not found
sh: 3: --private-key: not found
sh: 4: --trust-anchor-arn: not found
sh: 5: --profile-arn: not found
Error: Failed to create S3 client

Caused by:
    0: initial ListObjectsV2 failed for bucket "bucketname" in region us-east-1
    1: Client error
    2: No signing credentials available, see CRT debug logs
Error: Failed to create mount process
```
書き直ししまして、再度挑戦してみます。
```bash
y-kalen@ubuntu-player:~$ mount-s3 --allow-delete "BUCKETNAME" s3-mountpoint
bucket "BUCKETNAME" is mounted at s3-mountpoint
```
おっ？
```bash
mountpoint-s3                                     8.0E     0  8.0E   0% /home/y-kalen/s3-mountpoint
```
うまく行ったー！！！
書き込み測定してみる
```bash
dd if=/dev/zero of=/home/y-kalen/s3-mountpoint/test.data bs=1M count=1024
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 50.7829 s, 21.1 MB/s
```
21MB/Secだと・・・？！結構に遅いかもしれない。
今回は、北部バージニア（us-east-1）リージョンを利用しているので、実際の速度より早いと思います。
この検証してる際に、実はグローバル経由でネットワークで先にマウントできたため速度測定してます。
```bash
y-kalen@ubuntu-player:~$ dd if=/dev/zero of=/home/y-kalen/s3-mountpoint/test.data bs=1M count=1024
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 31.6737 s, 33.9 MB/s
```
若干、グローバル経由のが早いんですよね。なんでだろうと思っていたのですが、「暗号化と複合化」の回数がグローバル通信と比べると多いので、その分遅いのかなと思いました。

### お片付け
使ったままにすると結構、良い金額になります。きちんとお掃除しましょう。
Cloudformationで関わってる部分はCloudformationで消す際にエラーが出るのでその当たりで判断した方が良いかもしれないです。もしくはアカウントを別に作成して、アカウントごと消してしまう感じのがオススメです。

### ユースケースを考えてみる
本検証でちょっとユースケースを考えてみました。
1. イントラ通信しかできない特殊な環境でのHPトップページ（その後はイントラのアプリサーバに繋がる的な環境）用のトップページ置き場
2. 大容量データの直接バックアップ（StorageGatewayを利用すれば良いとか言う突っ込みもありそうですが）用途
3. イントラのアプリケーションサーバから生成されるログやアプリケーションサーバが必要なデータ置き場用途
あたりが妥当なのかなと思っています。

### 所感として
つかってみた所感は、リージョンが北米とは言え、それなりに早いなとは思いました。
個人的にこれをやりたかったのは実はユースケースの3番なんです。
まだ、東京（ap-northeast-1）リージョンでは試せてないので、後日実施したときに追記しますね。
低額でできるならありかなーとは思ってます。


### 追記　-20250505-
東京(ap-northeast-1)リージョンで試してみました。
```
$ dd if=/dev/zero of=./test.data bs=1M count=1024
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 24.587 s, 43.7 MB/s
```
43MB？！？！かなりはやいなぁ・・・。
ということは、これで音楽ストリーミングサーバは使えそうなので、検討してみようかな。
jpsonicは一度アプリ側でキャッシュしてくれるので、必要な時だけS3にアクセスしに行くという感じになるのでそういったアプリには使えると思います。