【AWS CDK】CDKを用いたALB mTLS認証の実装

## はじめに
re:invent2023でALBがmTLS認証をサポートすることが発表されました。
[Application Load Balancer での TLS を使用した相互認証](https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/mutual-authentication.html)

ALBのmTLS認証を実装し、mTLS認証のログを出力する場合は、以下3点を実装する必要があります。
- CA証明書をトラストストアにあげる
- ALB Listenerの設定
- Connections Logsの有効化
※Connections Logsの有効化は必須ではありません

今回は、CDKを用いたALBのmTLS認証の実装を上記3つのポイントに分けてご紹介させて頂きます。
※ 本ブログに記載した内容は個人の見解であり、所属する会社、組織とは全く関係ありません。

## CA証明書をトラストストアにあげる
ALBのListenerでmTLS認証を有効化するために、検証先としてトラストストアを指定します。
そのために、CA証明書が格納されたトラストストアを作成する必要があります。
このトラストストアを作成するために以下3ステップ踏む必要があります。
- CA証明書格納用のS3バケットの作成
- 作成したCA証明書格納用のS3バケットにCA証明書を格納
- CA証明書格納用のS3バケットをCA証明書のソースとしたトラストストアを作成

それぞれのパーツでサンプルと共にご紹介します。

### CA証明書格納用のS3バケットの作成
これは普通にS3バケットを作成すれば大丈夫です。
~~~typescript:mtls-sample.ts
    const myCertBucket = new s3.Bucket(this, "certBucket", {
      bucketName: `cert-bucket-hogehoge`,
      enforceSSL: true,
      autoDeleteObjects: true,
      encryption: s3.BucketEncryption.S3_MANAGED,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    });
~~~

### CA証明書格納用のS3バケットをCA証明書のソースとしたトラストストアを作成
S3にCA証明書を上げるのには、BucketDeployment Constructsを使用します。
このConstructsを利用するとCDK実行環境にあるファイルをDeployプロセスの中でアップロードしてくれます。

~~~typescript:mtls-sample.ts
    const caCertupload = new s3deploy.BucketDeployment(this, "caCertupload", {
      sources:[ 
        s3deploy.Source.data(
          "CA_cert.pem",
          fs.readFileSync(path.join(__dirname, "../../ssl/CA_cert.pem"), "utf8")
          ),
          ],
          destinationBucket: myCertBucket,
    });
~~~~

sourcesのところでS3にアップロードするファイルを指定しています。
s3deploy.Source.data()の第一引数でS3に配置する際のObject名を指定しています。
第二引数では、アップロードしたいファイルのローカル格納先を指定しています。
ここでは、Node.jsのfsとpathモジュールを活用しています。

**path.join(__dirname, "../../ssl/CA_cert.pem")**
__dirnameはこのコードが配置されているディレクトリパスとなります。ここからの相対パスとして、 ../../ssl/CA_cert.pemを結合し、最終的なファイルパスを生成しています。

**fs.readFileSync(path, "utf8")**
ファイルをutf8形式でテキストとして読み込んでいます。

### CA証明書のソースをCA証明書格納用のS3バケットとしてトラストストアを作成
トラストストアは現在L1のみ提供されているため、L1 Constructsを利用します。
[CDK Constructsに関して](https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/constructs.html)

~~~typescript:mtls-sample.ts
    const trustStore = new elb.CfnTrustStore(this, "cATrustStore",{
      caCertificatesBundleS3Bucket: caCert.deployedBucket.bucketName,
      caCertificatesBundleS3Key: "cA_cert.pem",
      name :"CA-trust-store"
    });
~~~

CA証明書の格納されているS3をソースバケットとして、アップロードした際のobject名をkeyとして指定します。

以上で、「CA証明書をトラストストアにあげる」部分は完了です。

## ALB Listenerの設定
mTLS認証の有効化は、Listener毎に行います。
そのうえで、現在mTLS認証の設定は、L1 Constructsのみの提供となります。
そのため、L2で作成したListenerをエスケープハッチで追加設定をしてきます。

設定には、以下2ステップを踏む必要があります。
- ALB Listenerの作成
- 作成したListenerにエスケープハッチで追加設定

それぞれのパーツでサンプルと共にご紹介します。

### ALB Listenerの作成
これは普通にListenerを作成すれば大丈夫です。
今回のサンプルでは、前段にALBを構成していたため、addListener Methodを利用しています。
~~~typescript:mtls-sample.ts
    const sListener = alb.addListener("HttpsListener",{
      port: 443,
      protocol: elb.ApplicationProtocol.HTTPS,
      certificates: [acmCert],
      sslPolicy: cdk.aws_elasticloadbalancingv2.SslPolicy.RECOMMENDED_TLS,
      defaultTargetGroups: [fargateTG],
    });
~~~

### 作成したListenerにエスケープハッチで追加設定
L2の「ApplicationListener」では現状mTLSに関連する設定はありません。
そのため、作成したListenerからL1 Constructsを取り出し(エスケープハッチ)、追加設定する必要があります。
~~~typescript:mtls-sample.ts
    const cfnSListener = sListener.node.defaultChild as elb.CfnListener;
    cfnSListener.mutualAuthentication = {
      ignoreClientCertificateExpiry: true,
      mode: "verify",
      trustStoreArn: trustStore.ref,
    };
~~~

**const cfnSLister = sListener.node.defaultChild as elb.CfnListener;**
ListenerのdefaultchildをCfnListenerとして、取り出します。
これでL1 ConstructsのCfnListenerを活用出来ます。

**cfnSLister.mutualAuthentication**
CfnListenerのmutualAuthenticationを設定し、mTLS認証を実装します。
トラストストアは、L1 Constructsで作っているため、trustStoreArnはClouFormationのRef関数経由で設定します。

以上で、「ALB Listenerの設定」部分は完了です。

## Connections Logsの有効化
必須ではありませんが、クライアント認証ログは欲しいですよね。
そのためのログが、Connections Logsです。
Connection LogsはALBの属性を編集することにより、実装されます。
ログの配信先も必要なので、以下2ステップを踏む必要があります。
- Conndection Logs配信先バケットの作成
- ALB属性の追加

それぞれのパーツでサンプルと共にご紹介します。

### Conndection Logs配信先バケットの作成
これは普通にS3バケットを作成すれば大丈夫です。
~~~typescript:mtls-sample.ts
    const albConnectionLogBucket = new s3.Bucket(this, "albConnectionLogBucket", {
      bucketName: `alb-connectionlog-bucket-hogehoge`,
      enforceSSL: true,
      autoDeleteObjects: true,
      encryption: s3.BucketEncryption.S3_MANAGED,
      objectLockEnabled: true,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    });
~~~~

### ALB属性の追加
追加する必要のある属性は以下3種類です。
|Key|Value|
|---|---|
|connection_logs.s3.enabled|true|
|connection_logs.s3.bucket|Conndection Logs配信先バケット名|
|connection_logs.s3.prefix|prefix|
~~~typescript:mtls-sample.ts
    interface connnectionLogSet {
    Key: string,
    Value: string
    };
    const connnectionLogSets:connnectionLogSet[] = [
      {Key: "connection_logs.s3.enabled", Value: "true"},
      {Key: "connection_logs.s3.bucket", Value: albConnectionLogBucket.bucketName},
      {Key: "connection_logs.s3.prefix", Value: "hoge"}
      ];
      
    connnectionLogSets.forEach((param) => {
      alb.setAttribute(param.Key, param.Value)
    });
~~~
ALBの属性追加は、ALBのsetAttribute Methodを利用しています。

以上で、「Connections Logsの有効化」部分は完了です。

## まとめ
ALBのmTLS認証はリリースされてから日が浅いこともあり、CDKでの実装はちょっとめんどくさいです。
トラストストア作成したり、L1 CfnListenerを使う必要があったり、ALBの属性いじったり。。。
Constructsなり、Methodなり早めにリリースされることを祈ります。

直近で、ALBのmTLS認証の実装をご検討の方は参考にして頂けると幸いです。
※ 本ブログに記載した内容は個人の見解であり、所属する会社、組織とは全く関係ありません。