ALBとECSの間のTLS接続を試す

クラメソさんのこの記事を読んで追加で色々試してみたのでメモ。

dev.classmethod.jp

 

 

[ALBからのHTTPS通信?]

こちらのAWS公式ドキュメントに記載されているように、ロードバランサでTLS接続を終わらせ、EC2やECSへの接続にはHTTPSでなくHTTP通信を利用するだけでも十分であることが多い。一方で、より厳しいルールに従うならばロードバランサからバックエンドへの通信にHTTPSを利用することもできる。

 

以下の記事では、バックエンドにEC2インスタンスを利用し、自己署名証明書HTTPS接続している。

dev.classmethod.jp

 

自己署名証明書を使っているが、ALBのトラストストアに問題の証明書を登録するような手順は確認できなかった。ということは、ALBはバックエンドの証明書がなんであれ受け入れる、ということになる。

 

ここでいくつかの疑問が湧いてくる。

  • 有効期限が切れた証明書の場合はどうだろうか?
  • 利用可能なプロトコルに制限はあるのだろうか?

 

[aws-ecs-patterns]

aws-ecs-patternsはaws-cdkの一部として提供されているモジュールで、コンテナ化されたサービスを数行のコードで定義できるようにしてくれている。

github.com

使い方はこちらのドキュメントに書かれている。

docs.aws.amazon.com

 

わざわざEC2インスタンスを用意するのも面倒だったので、今回はこのaws-ecs-patternsを利用して環境を構築し、先ほどの疑問点を実験してみる。

 

利用したCDKのバージョンは以下の通り。

$ cdk --version
2.19.0 (build e0d3e62)

 

先ほどのドキュメントに記載の手順でTypeScriptのCDKプロジェクトを初期化する。

間違えて実験ではAWS CDK v1のecs-patternsを利用してしまったが、v2だと以下のパスからimportできる。

import * as ecsp from 'aws-cdk-lib/aws-ecs-patterns'

 

以下の行をスタックに追加してcdk deployを実行すると、ロードバランサ経由でパブリックにアクセスできるECSタスクが起動する(参考コミット)。

    new ecsp.ApplicationLoadBalancedFargateService(this, 'MyWebServer', {
      taskImageOptions: {
        image: ecs.ContainerImage.fromRegistry('amazon/amazon-ecs-sample'),
      },
      publicLoadBalancer: true
    });

 

これだとランダムなURLになってしまうので、Route 53で確保しているドメイン経由でアクセスできるように修正する。

続いて、ALBからECSタスクへの通信でHTTPSを利用するように修正する。この時利用しているDockerfileはNginxの最新のイメージをベースにしており、サーバー証明書は手元で作成した自己署名証明書である。

 

そのままドメインにアクセスすると以下のようにnginxのデフォルトページが表示される(現在同ドメインは利用不可)。

 

[nginxで実験]

上記で用意したDockerfileは以下のようになっている。

FROM --platform=linux/amd64 nginx

COPY server.crt /etc/nginx/ssl/server.crt
COPY server.key /etc/nginx/ssl/server.key

COPY ssl.conf /etc/nginx/conf.d/ssl.conf 

 

このserver.crtを有効期限の切れたものにしてcdk deployコマンドでデプロイしてみる。すると、先ほどと同じ画面が問題なく表示された。

自己署名証明書ですらALBが信頼するのであれば、もはや期限が切れていようが関係ないということだろうか。元々のクラメソさんの記事でも自己署名証明書だったので、有効期限切れの証明書が受け入れられたことはあまり驚かない。

 

ssl.confは以下のようになっている。

@@ -0,0 +1,10 @@
server {
    listen 443 ssl;

    root /usr/share/nginx/html;
    index index.html;

    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
}

 

ここにssl_protocols TLSv1;などの行を追加して、いくつかのプロトコルバージョンを試してみる*1

ssl_protocols SSLv3を指定してデプロイを実行したところ、以下のエラーがでた。

tls_setup_handshake:internal error

 

ブラウザでアクセスすると以下のように502 Bad Gatewayが表示された。

 

おそらく以下のStackoverflowで見られるのと同じ問題で、opensslのバージョンの問題と思われたので、NginxのDockerイメージのバージョンを1.8まで戻して再度デプロイをしてみた。

stackoverflow.com

 

結果として502 Bad Gatewayの表示は変わらなかったが、サーバ側のログに変化が見られた。恐らくTLS1.0以降の新しいプロトコルのClientHelloが送信された結果、Nginx側で処理できずにエラーとなったものと思われる。

SSL_do_handshake() failed (SSL: error:14076102:SSL routines:SSL23_GET_CLIENT_HELLO:unsupported protocol) while SSL handshaking, client: 10.0.114.212, server: 0.0.0.0:443

 

[まとめ]

  • AWS-CDKのaws-ecs-patternsで環境を構築しALB-ECS間のTLS接続を実験した
  • ALB-ECS間のTLS通信は証明書の有効期限が切れていても可能
  • ALB-ECS間のTLSプロトコルバージョンの下限はTLS1.0:SSL3.0では通信不可
  • ソースコードは以下のリポジトリにまとめてある

github.com

 

本当は利用可能な暗号スイートをあれこれ制限して実験してみたかったのだけれど、nginx力が足りず断念。またいつか時間がある時に試してみたい。

*1:デフォルトではTLS1.0、1.1、1.2が有効となっている。