7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

AWS App RunnerAdvent Calendar 2022

Day 18

AWS App Runnerプライベートサービスにインターネットからアクセスし、WAFで保護する

Posted at

本記事は、AWS App Runner Advent Calendar 2022の18日目の記事です。

App Runnerなにもわからんマンです。

AWSでコンテナベースのアプリケーションを開発する場合、
なんらかのコンテナオーケストレーションサービスを選定すると思います。

AWSでの選択肢は、ECSやEKS、App Runnerに、ROSAなんかもありますね。

App Runnerについては、正直「Production用途にはまだまだ」、ぐらいのぼんやりとしたイメージしかなかったので、2022年末時点での状況を調べておこうと思いました。

お世話になったブログ記事等

こちらのブログ、2022年2月に書かれた記事なのですが、
App RunnerとECSの違いがわかりやすく纏めてあり、
且つ2月以降のアップデートについても追記してあります!(すごい!)
本当に勉強になりました。ありがとうございました。

こちらのクラスメソッドのTakuya Shibataさんの記事もとても勉強になりました。
2022年11月にリリースされたApp Runnerのプライベートサービス化のブログ、設定手順等も丁寧に書かれており、すごく実装イメージがわきました。ありがとうございました。

これで、App Runnerについては完全に理解した(気分になった)。

ペインポイント

App Runner、プライベートサービス化がサポートされたことで、それまでの「サービスはインターネットに全公開(アクセス制限の機能なし)」の状態が回避できるようになったことは喜ばしいです。

喜ばしいですが・・・

プライベートサービス化は要するに「VPCの中だけで閉じて使えますよ」と理解しました。もう一声ほしいところです。

App Runnerはプライベート化しつつ、なんとかしてアプリをWAFで保護しつつインターネットに公開できないか。

その謎を解明すべく、我々調査隊はアマゾンの奥地へと向かった。

対応策案

案1: え?普通にELBとか経由して公開できないの?⇒失敗

App RunnerでPrivate endpointを有効化すると、自分たちで用意したVPCにVPC Endpointを生やすことができます。
app_runner_proxy_01.png
さらに作成したApp Runnerのサービスのドメイン名は

のようなドメインが割り当てられますが、
このドメインがプライベートIPを返すようになるので、ELBからそこにアクセスすればええやんと思いました。

図にすると以下のとおりです。
app_runner_proxy_03.png

で、設定してみたのですが・・・

ALBのTarget Groupを設定しても、TargetはUnhealthyのままですね。

検証用VPCにEC2を立ててcurlしてみます。

VPC内で名前解決はできてます。

$ nslookup
> xxxxxxxxxx.ap-northeast-1.awsapprunner.com
Server:         10.0.0.2
Address:        10.0.0.2#53

Non-authoritative answer:
Name:   xxxxxxxxxx.ap-northeast-1.awsapprunner.com
Address: 10.0.1.240
Name:   xxxxxxxxxx.ap-northeast-1.awsapprunner.com
Address: 10.0.2.150

App RunnerのURLにcurlすると、VPC内から普通にアクセスできます。
※今回は、App RunnerのイメージとしてECR Public Galleryのpublic.ecr.aws/nginx/nginx:stable-alpine を使いました。

$ curl https://xxxxxxxxxxx.ap-northeast-1.awsapprunner.com
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
(中略)
</body>
</html>

プライベートIPにcurlすると、TLSのセッション開始の途中で失敗していることがわかります。そりゃそうか。

$ curl https://10.0.1.240
curl: (35) OpenSSL SSL_connect: Connection reset by peer in connection to 10.0.1.240:443

単純にHostヘッダーつけてもダメな模様。そりゃそうか。

$ curl -H 'Host:xxxxxxxxxx.ap-northeast-1.awsapprunner.com' https://10.0.1.240/curl: (35) OpenSSL SSL_connect: Connection reset by peer in connection to 10.0.1.240:443

Target Groupの問題なので、NLBもダメそう。ELBはあきらめた。

案2: LambdaでApp Runnerにアクセスして結果をAPI Gatewayに返す⇒できた

じゃあ、LambdaをVPCにアクセスできるようにして、リバースProxy的に動かせばいいのでは?と考えました。

こういうイメージ。
app_runner_proxy_05.png

Lambdaを書く

疎通できればいいので、細かいところはご容赦を。

例えば"apprunner-proxy"というLambda関数を書きます。
環境変数URLには、App RunnerのURLを設定して下さい。

from urllib import request
import urllib
import os
import json

def get_apprunner(event, context):
    apprunner_url = os.environ['URL']
    
    try:
        request_get = request.Request(url=apprunner_url)
        with request.urlopen(request_get) as res:
            content = res.read().decode()
        return {
            'statusCode': 200,
            'body': json.dumps(content),
            'isBase64Encoded': False,
            'headers' : {}
        }
    
    except (urllib.error.HTTPError) as error:
        status_code = error.code
        print(status_code)

LambdaをVPCに接続する

Lambda関数のConfiguration=>VPCから、接続するVPCと、Security Groupを設定します。

設定したVPCにENIが生えます。
app_runner_proxy_06.png

Lambdaをテストする

Test eventはデフォルトのEvent JSONで作成し、Lambda関数をテスト実行します。

app_runner_proxy_07.png

うまくいったようです。
Response bodyにnginxのコンテナから返されたHTMLが含まれています。

App Runnerのマネジメントコンソールの画面、Application Logsにて、Lambda(Python-urllib)からのアクセスを確認できます。
ソースIPは、わたしが作った検証用VPCでは使ってないアドレスなので、おそらくApp Runner内部のリクエストルーターのIPアドレス?ではないかと。

app_runner_proxy_08.png

APIを作成する

Lambda関数からApp Runnerにアクセスできればもうこっちのもんです。
API GatewayでREST APIを作成しましょう。

Actionsから、Create Method。
app_runner_proxy_09.png

GETリソースを作成し、

Integration typeはLambda Function、
Use Lambda Proxy integrationはチェック、
Lambda Functionには作成したLambda関数名(今回はapprunner-proxy)

を入力し、Saveします。

ActionsからDeploy API、
Deployment stageは[New Stage]、
Stage nameはdevとかstgとか設定し、Deployします。
app_runner_proxy_10.png

stgを作成すると、Invoke URLが発行されます。
ブラウザで、インターネット経由でアクセスしてみましょう。
app_runner_proxy_11.png

bodyに、nginxのコンテナから返されたHTMLが含まれています。
app_runner_proxy_12.png

WAFを設定する(IP sets)

今の状態だと、App RunnerプライベートサービスにAPI Gateway経由でインターネットから誰でもアクセスできてしまいます。

最低限、アクセス元のソースIPアドレスは絞りたいところです。

この手順、がんばって書こうと思いましたが、クラメソさんのブログに丁寧に解説してくださっている記事がありましたので、ご紹介いたします。
IP rulesを活用してソースIPアドレスの制限を行いましょう。
(手抜きですいません・・)

というわけで

かなり無理矢理感のあるやり方で、AWS App Runnerプライベートサービスにインターネットからアクセスしてみました。

今回は疎通検証のための簡易なコードだったり設定ですので、(大丈夫と思いますが)Productionでの利用を検討されている場合はキチンと各種セキュリティや可用性、パフォーマンス系の設計・実装・テストをしましょう。

API GatewayがあるのでCloudFrontとも連携できますが、CloudFrontを利用するぐらいのアクセス数のあるユースケースだと、Lambdaがボトルネックになりそうです。
そういう場合は、素直にECS/Fargateでアプリを稼働させた方がよいかと。

App Runner、コンテナオーケストレーション部分が隠蔽されて、気軽にアプリを公開できる非常に面白いサービスだと思います。

Security GroupやWAFのネイティブ対応だったり、利用できるスペックの組み合わせが増えると、かなりユースケースも増えてくるんじゃないかと思うので、今後のRoadmapに注目しましょう!!!

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?