CloudFront + APIGateway 構成の403 ERROR

APIGateway,AWS,CloudFront

概要

はじめに

  • 今回は CloudFront + API Gateway 構成の構築で経験した、403 ERROR について記載します。CloudFront のオリジンが動的コンテンツだからこそ入れたチューニングに罠が潜んでおりました!
  • 先ず前提として、CloudFront + API Gateway の構成を採用した背景をご説明します。本来であればエッジを利用するAPI Gateway に、さらにCloudFront を配置するパターンに疑問を持つ方もいらっしゃるかと思います。求められた構成として、API Gateway + Lambda によるAPI のオリジンがあり、かつ S3(静的コンテンツ)もある、といった複数のオリジンに対して 1つのFQDN でアクセスする必要がありました。そのため、CloudFront + API Gateway の構成を作ります。また、API Gatewayでは細かなキャッシュの設定などできないため、CloudFront の機能が生きてきます。
  • システム構築後、クライアント → API Gateway へは疎通OK ですが、クライアント → CloudFront → API Gateway は 403 ERROR となり、疎通NG に陥りましたー。

 

システム構成図

  • CloudFront を作成し、代替ドメイン名を設定します。オリジンには、API Gateway + Lambda および S3 の複数オリジンを登録し、パスによって振り分けする設定を行います。CloudFront のパス振り分けは、こちらの記事を参照ください。なお、S3 のオリジン構築は本記事の対象外です。
  • API Gateway のEndpoint タイプはエッジ最適化とし、API Gateway にはカスタムドメインを設定します。API Gateway のバックエンドには、API GatewayからのリクエストをトリガーとするLambda があります。

 

 

 

CloudFront + APIGateway 構成の罠!

CloudFront + APIGateway 構成の構築に潜む罠を探せ

  • CloudFront + API Gateway 構成の構築ポイントを記載していきます。ただし、この構築の流れには、1つ罠が潜んでいます。どこに罠があるでしょうか??

 

  • 先ず、API Gateway + Lambda をデプロイします。その後、API Gateway にカスタムドメインを設定します。API Gateway に設定する証明書は、事前にACM で準備します。
  • API Gateway のカスタムドメイン設定方法は、こちらの記事を参照してください。また、ここで使用するLmabda は、こちらの記事でも紹介したHello World を表示するだけのシンプルな構成となります。
  • API Gateway のカスタムドメイン設定後、API Gateway のエンドポイントをRoute 53 にレコード登録します。

 

  • 続いて、CloudFront のディストリビューションを作成します。
  • オリジン設定では、Origin Domain Nameに先ほど設定したAPI Gateway のカスタムドメイン名を設定、Origin Protocol Policy に“HTTPS Only" を設定します。(API Gateway のエンドポイントには、HTTPS プロトコルでアクセスする必要があるため)

 

  • デフォルトキャッシュ動作の設定では、Viewer Protocol Policyに“HTTPS Only" を設定します。今回はオリジンがAPI になるため、Allowed HTTP Methodsに“GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE" のメソッドを設定します。
  • また、オリジンが静的コンテンツではないため、ヘッダのキャッシュは不要であり、Cache Based on Selected Request Headersは“All" としました。
  • オリジン側でCokkie やクエリパラメータを使用する場合は、Forward Cookies を“All" 、Query String Forwarding and Cachingを“All" とします。

 

  • ディストリビューション設定では、Alternate Domain Names (CNAMEs)にCloudFrontの代替ドメイン名を設定、SSL CertificateにACMで取得した証明書を設定します。

  • CloudFrontのディストリビューションを作成後、CloudFront のエンドポイントをRoute 53 にレコード登録します。
  • 初めは、ここで構築完了の想定でした…。

 

ここで、想定外の403 ERROR に遭遇です

  • さあー、ここからが本題ですw
  • ここで、テストのためCloudFront のエンドポイントにcurlを打ち込みましたが、“403 ERROR" となりました。原因は、デフォルトキャッシュ動作の設定でCache Based on Selected Request Headersに"All" を設定したことに起因しておりました。(前述の説明で、黄色ハッチングの箇所です)
  • API Gateway はHTTPS プロトコルでアクセスする必要があり、かつカスタムドメインを設定しております。Cache Based on Selected Request Headersを"All" にした場合、CloudFront はオリジンに"Host"ヘッダを転送するため、API Gatewayのカスタムドメインに設定した証明書とヘッダが一致せず、エラーを招きます。
niikawa@niikawa1:~$ curl https://niikawa-front.example.com
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD><BODY>
<H1>403 ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
Bad request.
We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
<BR clear="all">
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
<BR clear="all">
<HR noshade size="1px">
<PRE>
Generated by cloudfront (CloudFront)
Request ID: vCM1si1-KjhQRnfFbLEMBGR2BRP-wvE3fmpFOYIxKaWYM6UVFqVQeQ==
</PRE>
<ADDRESS>
</ADDRESS>
</BODY></HTML>

 

 

CloudFront + APIGateway 構成の勘所はココ

  • それでは、Cache Based on Selected Request Headersには何を設定すれば良かったのでしょうか? 今回、オリジンはAPI であり静的コンテンツではないため、ヘッダのキャッシュは不要、Cache Based on Selected Request Headersは"All" と判断しました。しかし、オリジンに"Host"ヘッダが転送されたことが逆効果となりました。
  • 下記の通り、Cache Based on Selected Request HeadersにはAllではなく、Whitelist を選択します。かつ、Whitelist Headers にオリジンに転送が必要なヘッダを個別に指定します。(今回は、Accept, Accept-Encoding, Accept-Languageを指定)
  • かつ、APIにはヘッダをキャッシュする必要がないため、TTL を"0″ に設定します。(All ではTTLのカスタマイズができない)

 

 

無事、疎通確認でOK となる

  • 改めて、CloudFront のエンドポイントにcurlを打ち込みました。無事、疎通確認でOK となりました!

 

niikawa@niikawa1:~$ curl https://niikawa-front.example.com
{"statusCode": 200, "body": "\"Hello from Lambda!\""}

 

 

 

APIGateway,AWS,CloudFront

Posted by takaaki