ALBでgRPCを使う際にターゲット側もTLSしてみた
はじめに
おはようございます、加藤です。?ついに、ALBのターゲット側がHTTP/2、gRPCに対応しましたね。新機能としてのブログは誰かが書くと思うので、タイトルの通りこの図の赤字の部分でをTLSする方法を検証したのでブログとしてまとめます。
前提
下記のAWS News Blogを既に読んでいる方が対象です。
New – Application Load Balancer Support for End-to-End HTTP/2 and gRPC | AWS News Blog
後半、ALBやECSを作成する箇所がありますが詳しくは説明しません。下記を一度は触ったことがある方を想定しています。また、作成するのはドメインを所持している必要があります。
- ACM
- ALB
- ECS(Fargate)
- ECR
- Route53
E2EでTLSは必須なのか?
The ALB supports both secure and insecure connections for target groups using the gRPC protocol.
とAWS News Blogで書かれておりHTTP/2、gRPCプロトコルで使用する場合はターゲット側はHTTP/SどちらでもOKです。
E2EでTLSできるのか?
はい、できます。しかし、ALBは必ずTLSを終端してしまうので、Client - ALBとALB - Backendは別途証明書(ACM発行以外で証明書を購入、独自発行)を用意してTLSする必要があります。
サンプルコードの場合は server.add_insecure_port('[::]:50051')
の箇所を下記の様に書き換えると、E2EでTLSができます。合わせてTarget Groupを新規作成し、ProtocolをHTTPSにPortを443に設定してください。
# 検証用コードなので、関数内でimportするなど雑に書いています。 import os port = os.environ['PORT'] pkey = open('private_key.pem', 'rb').read() chain = open('cert.pem', 'rb').read() creds = grpc.ssl_server_credentials([(pkey, chain)]) server.add_secure_port(f'[::]:{port}', creds)
実際にE2EでTLSしてみる
AWS News Blogの手順とgRPCのサンプルコードをカスタマイズしてE2EでTLSしてみましょう。
最初にgRPCのリポジトリをクローンし、使いたいサンプルコードのディレクトリに移動します。
git clone https://github.com/grpc/grpc.git cd grpc/examples/python/route_guide
続いて、独自証明書を発行します。
openssl req -x509 -nodes -newkey rsa:2048 -days 3650 -keyout private_key.pem -out cert.pem -subj "/CN=localhost" openssl x509 -in cert.pem -out root.crt
route_guide_server.py
を編集します。serve関数を下記に書き換えます。
def serve(): server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) route_guide_pb2_grpc.add_RouteGuideServicer_to_server( RouteGuideServicer(), server) # 検証用コードなので、関数内でimportするなど雑に書いています。 import os port = os.environ['PORT'] pkey = open('private_key.pem', 'rb').read() chain = open('cert.pem', 'rb').read() creds = grpc.ssl_server_credentials([(pkey, chain)]) server.add_secure_port(f'[::]:{port}', creds) server.start() server.wait_for_termination()
Dockerfileを作成します。今回は検証なので、証明書もイメージの中に含めてしまいます。
FROM python:3.7 RUN pip install protobuf grpcio COPY ./ / CMD python route_guide_server.py EXPOSE 50051
ECRにリポジトリを作成し、作成したコンテナイメージをPushします。YOUR_AWS_ACCOUNTはAWSアカウントIDに書き換えてください。
AWS_ACCOUNT_ID=YOUR_AWS_ACCOUNT TAG=v0.1 docker build -t route-guide . docker tag route-guide:latest ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/route-guide:${TAG} aws ecr create-repository --repository-name route-guide docker push ${AWS_ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/route-guide:${TAG}
ALBを作成します。ListenersにはHTTPSを設定します。Certificateを求められるので、所有しているドメイン用のCertificateをACMで作成して設定します。
ターゲットグループを作成します。Target typeは必ずIPにします。Protocol versionがHTTP2、gRPCの場合はInstanceとIPをサポートしていますが、Fargateの場合はIPを指定する必要があります。
また、Health checksのProtocolもHTTPSにPathを/に変更してください。
具体的にターゲットを指定せずに作成を完了します。
ECSクラスタ(Network Only)を作成します。
タスク定義を作成します。TCPポートの50051番を開放し、環境変数PORT=50051に設定します。CPU、MEMは最低でOKです。
このタスク定義を使いサービスを定義します。そのときに作成したALBとTarget Groupをアタッチします。Security GroupのIngressで50051/TCPを開放することを忘れないようにしてください。ソースは0.0.0.0/0かALBでのSGを選択してください。
DNSサーバーでALBに対してALIASを設定します。私はドメインをRoute 53に登録しているのでRoute 53で設定を行いました。
最後にクライアントからアクセスしてみましょう。
route_guide_client.py
のrun関数を下記に書き換えます。YOUR_DNS_NAMEはALBにALIASしているドメイン名に置き換えてください。
def run(): # NOTE(gRPC Python Team): .close() is possible on a channel and should be # used in circumstances in which the with statement does not fit the needs # of the code. cred = grpc.ssl_channel_credentials(root_certificates=None, private_key=None, certificate_chain=None) with grpc.secure_channel('YOUR_DNS_NAME:443', cred) as channel: stub = route_guide_pb2_grpc.RouteGuideStub(channel) print("-------------- GetFeature --------------") guide_get_feature(stub) print("-------------- ListFeatures --------------") guide_list_features(stub) print("-------------- RecordRoute --------------") guide_record_route(stub) print("-------------- RouteChat --------------") guide_route_chat(stub)
必要なライブラリをインストールしてから、Clientを実行します。私はグローバル環境を汚したくないので、asdfとpipenvを使用し環境を切り替えてから下記のコマンドを実行しました。
pip install grpcio google-api-python-client python ./route_guide_client.py
無事にレスポンスが帰ってきましたー
-------------- GetFeature -------------- Feature called Berkshire Valley Management Area Trail, Jefferson, NJ, USA at latitude: 409146138 longitude: -746188906 Found no feature at -------------- ListFeatures -------------- Looking for features between 40, -75 and 42, -73 Feature called Patriots Path, Mendham, NJ 07945, USA at latitude: 407838351 longitude: -746143763 (中略) Feature called 3 Hasta Way, Newton, NJ 07860, USA at latitude: 410248224 longitude: -747127767 -------------- RecordRoute -------------- Visiting point latitude: 415830701 longitude: -742952812 (中略) Visiting point latitude: 405002031 longitude: -748407866 Finished trip with 10 points Passed 10 features Travelled 704831 meters It took 0 seconds -------------- RouteChat -------------- Sending First message at Sending Second message at longitude: 1 Sending Third message at latitude: 1 Sending Fourth message at Sending Fifth message at latitude: 1 Received message First message at Received message Third message at latitude: 1
感想
念願のALBのHTTP2、gRPC対応ということで、ターゲット側がTLS必須なのか?どうやって設定するのかを検証しました。gRPC関係なく、独自証明を発行して(ryって手順が面倒だなーってのが素直な思いですね、本番だとコンテナイメージに証明書を含めずに起動時にS3からダウンロードするとか、Secrets ManagerにJSONとして保持していてファイルに書き起こすとか必要で、証明書の期限まで考えるととっても大変そう。ALBは管理下の信頼できるリソースなので、どうしてもっていう要件が無ければALB - Backendは平文で良いかなーって思いました。
今までgRPCは全く触ったことがないのですが、このブログを書くにあたってほんの少しは理解できて良かったです。
まとめ
- HTTP/2、gRPCプロトコルのターゲットグループをアタッチするならリスナー側はHTTPSが必須です
- ターゲット側はHTTP/2、gRPCプロトコルだとしてもHTTP/SどちらでもOKです
- ターゲット側をTLSする場合は別途証明書が必要になります(追加の管理コストが発生します)
- ALB→BackendへgRPC/TLSでアクセスする場合に証明書のCommon Nameはlocalhostで良い(恐らくなんでも良い)
引用元
- New – Application Load Balancer Support for End-to-End HTTP/2 and gRPC | AWS News Blog https://aws.amazon.com/jp/blogs/aws/new-application-load-balancer-support-for-end-to-end-http-2-and-grpc/
- AWS Elastic Load Balancer Demos https://exampleloadbalancer.com/albgrpc_grpc_overview.html
- Target groups for your Application Load Balancers - Elastic Load Balancing https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-target-groups.html#target-group-protocol-version
- gRPC — gRPC Python 1.33.1 documentation https://grpc.github.io/grpc/python/grpc.html#server-object
- ALBとバックエンドEC2間をHTTPS通信させてみた | Developers.IO https://dev.classmethod.jp/articles/alb-backend-https/
- 自己証明書でsecureなgrpc server/client をつくる - Qiita https://qiita.com/obilixilido/items/8850d2b3adadbabc8280
- macOSでオレオレ認証局を立てて証明書を発行する | インフラ関係のメモ書き http://rikuga.me/2017/12/24/oreore-ca-and-ssl-cert/