Engineer's Way

主にソフトウェア関連について色々書くブログです。

API Gateway + CloudFrontの構成でカスタムドメインを使用すると、IAMでのアクセス制限ができない件

 

AWSAPI Gatewayを使ってプロダクション環境でAPIを公開する時、WAFを噛ませて防御したいと思うことがあります。
今のAPI Gatewayには、残念ながらWAFと直接関連付ける仕組みがないので、そういった場合はAPI Gatewayの前段にCloudFrontを置く必要があります。
(API Gatewayも内部的にはCloudFrontを使っているので、二重になってしまいますが仕方ありません)

ドメイン名もビジネス要件に合ったものを使うことになるので、それをRoute53に登録して、CloudFrontと紐づけることになります。
図で示すと以下のようになりますね。

f:id:matsnow:20171031011417p:plain

一方、API Gatewayにはざっくり以下の3つのアクセス制限方法があります。

  1. IAM認証 (v4署名)
  2. カスタムオーソライザー
  3. Cognitoオーソライザー

docs.aws.amazon.com

このうち、IAM認証を先ほどのAPI Gateway + CloudFront + 任意のドメインの構成上で使おうとしても失敗します。
正確には、v4で署名したリクエストをCloudFront(の先にあるAPI Gateway)に送信しても403エラーで返ってきます。
Amazonのサポートにも問い合わせてみましたが、残念ながら現時点の仕様とのことです。

誠に恐れ入りますが、API Gateway の前段に CloudFront ディストリビューションを配置頂いている場合、 API Gateway 上では生成 SDK による API の IAM 認証をご利用頂くことができません。 すでにご確認を頂いております通り、 SDK 内の invokeUrl を CloudFront にて公開頂いているカスタムドメイン名に編集頂くことで、 署名の生成に使用されるドメイン名と実際の API エンドポイントであるドメイン名が不一致となり、API Gateway 上での署名検証が失敗致します。

よって、API Gateway + WAF + カスタムドメインを使いたい場合、APIの認証はカスタムオーソライザーかCognitoオーソライザーでやることになります。 API Gatewayがバージョンアップして、直接WAFと連携させられるようになるのを期待しましょう。

ほかのサーバレス系の記事

matsnow.hatenablog.com matsnow.hatenablog.com

Angular4で開発した複数環境のSPAに、それぞれ別のGoogleタグマネージャーのスニペットを設定する方法

 

背景

本番環境と検証環境で運用しているAngular4で作ったSPAに、Googleタグマネージャー(GTM)のコードを埋め込むことになりました。

Googleタグマネージャーとは

f:id:matsnow:20170929004023p:plain

Googleタグマネージャー自体の概要や使い方は以下が参考になります。

www.h-nanae.com

www.milab-inc.com

埋め込み手順

埋め込む必要のある環境が本番環境だけであれば、単にGTMが吐き出してくれたスニペットをそのままindex.htmlに書けばいいわけですが、検証環境にも別のコンテナのスニペットを埋め込みたいとなった時に、少し詰まりました。

というのも、スニペットはheadタグの中とbodyタグ直下にそれぞれ埋め込む必要があるので、app-rootの外にあるindex.htmlに何とかして環境変数を反映させないといけないからです。

とりあえず、少し考えて、実際に試したのが以下のやり方です。

1) environment.tsへの追記

environment.prod.tsなど、各環境用の定義ファイルに

environment: {
    production: true,
    googleTagManager: 'GTM-XXXX'
};

みたいに書いておきます。(GTM-XXXXのところは GTMのコンテナIDを記述)

2) main.tsにスニペット埋め込み用のコードを記述

main.tsのif (environment.production) の中に以下を追記します。
本来、コンテナのIDが入っている箇所は、テンプレート記法で環境変数に置き換えています。

if (environment.production) {
  enableProdMode();

  /////////////// ここから //////////////////
  const script = document.createElement('script');
  script.text = `
  (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
  new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
  j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
  'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
  })(window,document,'script','dataLayer','${environment.googleTagManager}');`;
  document.head.appendChild(script);

  const noscript = document.createElement('noscript');
  const iframe   = document.createElement('iframe');
  iframe.src     = `https://www.googletagmanager.com/ns.html?id=${environment.googleTagManager}`;
  iframe.height  = '0';
  iframe.width   = '0';
  iframe.setAttribute('style', 'display:none;visibility:hidden');
  noscript.appendChild(iframe);
  document.body.insertBefore(noscript, document.body.firstChild);
  /////////////// ここまで //////////////////
}

これで後はGTMのコンソール上でタグやトリガーを設定してやれば、問題なく環境別にアクセス解析をすることができるようになります。
なお、GoogleAnalyticsでも同じ手法が使えるはずです。

あまり実施する必要に迫られることは無さそうなことですが、一応備忘録として。

CloudFormationでElasticSearchService 5.x系を構築する時の注意点

 

CloudFormationでElasticSearchServiceを構築しようとしたところ、
Createに異様に時間がかかる上、

Creating Elasticsearch Domain did not stabilize.

というエラーが出て構築に失敗してしまうという状況に出くわしました。 (ロールバックにも異様に時間がかかるという、、)

調べてみたところ、バージョン5.x系のElasticSearchを作る場合、 AdvancedOptionsの指定が必須らしいとのこと。

https://forums.aws.amazon.com/thread.jspa?messageID=768527

YAMLだとこうなります。

      ElasticsearchVersion: "5.3"
      AdvancedOptions:
        rest.action.multi.allow_explicit_index: true
        indices.fielddata.cache.size: ""

公式ドキュメントだと「Required: No」となっているので結構な罠ですね、、。

AWS::Elasticsearch::Domain - AWS CloudFormation