三 月
14
火曜日
2017

打刻システムの構築(後編)

打刻システムの構築(前編)からの続き。

前編にてSNSを基点に打刻情報をGoogleスプレッドシートへ書き込むところまでが完成した。

後編ではSNSの前面にAPI GatewayとCloudFrontを配置し実際にIFTTTアプリから打刻APIを呼び出せるようにする。


API Gateway

API GatewayをSNSの前面に配置。以下の役割を担う。

  • REST APIの呼び出しをトピックへのパブリッシュに変換する
  • APIキー、使用量プラン等によるAPI呼び出しのスロットリング

打刻API用ポリシーの作成

API GatewayからSNSへパブリッシュするためのポリシーを作成する。

AWS マネージメントコンソールから IAM を開き ポリシー から ポリシーの作成 Policy Generatorで以下を作成。

効果
許可
AWSサービス
Amazon SNS
アクション
Publish
Amazon リソースネーム(ARN)
打刻システムの構築(前編)で作成した打刻トピックのARN
ポリシー名
dakoku-sns-publisher

ロギングロールを作成

API GatewayからCloudWatchへログを出力するためのロールを作成する。

AWS マネージメントコンソールから IAM を開き ロール から新しいロール

ロール名
api-gateway-logging

AWSサービスロール Amazon API Gateway から、ポリシー AmazonAPIGatewayPushToCloudWatchLogs を選択する。

実行ロールの作成

API GatewayからSNSへパブリッシュするため、先ほど作成した打刻API用ポリシーをアッタチした実行ロールを作成する。

AWS マネージメントコンソールから IAM を開き ロール から新しいロール

ロール名
dakoku-api-gateway

AWSサービスロール Amazon API Gateway から ポリシーを何も選択せずにロールを作成。

ロール作成後 dakoku-api-gatewayロールの アクセス許可 タブ ポリシーのアタッチ から dakoku-sns-publisherを選択しポリシーをアッタチする。

CloudWatchログのロール設定

AWS マネージメントコンソールから API Gateway を開き 設定 から先ほど作成したロギングロールを設定する。

CloudWatch ログのロール ARN
api-gateway-logging

打刻APIの作成

API Gateway API から新しいAPIを作成する。

API名
dakoku

リソースの作成

API Gateway API dakokuリソース からIFTTT経由で打刻イベントを受ける為のRESTリソースを作成する。

プロキシリソースとして設定
オフ
リソース名
events-ifttt
リソースパス
/events-ifttt-<任意の英数字の組み合わせ>
API Gateway CORS を有効にする
オフ

本来はAPIクライアントの種類を意識せずに /events で受けたいところだが、IFTTTはカスタムHTTPヘッダを送信できないため、 x-api-keyによるAPIキーの送信が行えない。

このため第三者に公開しないIFTTT専用のRESTリソースを作成し後述のCloudFront側で同リソースへのリクエストに対しIFTTT用のAPIキーをx-api-keyヘッダ差し込むことで対応する。

Lambda@Edgeでリクエストパラメタに指定されたAPIキーをヘッダに載せ替える事ができれば良いのだが現時点ではリクエストパラメタへのアクセスは出来ない模様。

モデルの作成

特にモデルを利用する機会は無いが、一応リクエスト本文のデータ構造を定義しておく。

API Gateway API dakokuモデル から作成する。

モデル名
Event
コンテンツタイプ
application/json

モデルのスキーマは以下。

{
  "title": "Event",
  "type": "object",
  "properties": {
    "subject": {
      "type": "string"
    },
    "event": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        },
        "modifiers": {
          "type": "array",
          "items" : {
           "type" : "string"
          }
        }
      },
      "required": ["name", "modifiers"]
    }
  },
  "required": ["subject", "event"]
}

各プロパティの意味は以下となる。

subject
打刻を実施する人や物
例)自分、扉
event.name
発生したイベント名
例)office(職場)
event.modifiers
イベントの修飾子。複数定義可能
例)enter(進入)、leave(退出)

メソッドの作成

API Gateway API dakokuevents-ifttt リソースにPOSTメソッドを作成する。

統合タイプ
AWSサービス
AWSリージョン
ap-northeast-1
AWSサービス
Simple Notification Service (SNS)
HTTPメソッド
POST
アクションの種類
アクション名の使用
アクション
Publish
実行ロール
打刻API実行ロールdakoku-api-gatewayのARN
コンテンツの処理
パススルー

作成したPOSTメソッドの メソッドリクエスト を選択。

認証の設定 から

APIキーの必要性
true

リクエスト本文 から

コンテンツタイプ
application/json
モデル名
Event

作成したPOSTメソッドの 統合リクエスト を選択。

URLクエリ文字列パラメータ

TopicArn
打刻システムの構築(前編)で作成した打刻トピックのARN
固定値を渡すのでシングルクォートで囲う点に注意
Message
method.request.body

APIのデプロイ

API Gateway API dakokuアクション から APIをデプロイする。
本番環境を意味するprodというステージ名でデプロイする。

デプロイされるステージ
[新しいステージ]
ステージ名
prod

ステージの設定

API Gateway API dakokuステージ prod からステージの設定を行う。

API キャッシュを有効化
無効
CloudWatch ログ有効化
ログレベル
ERROR
リクエスト/レスポンスをすべてログ
無効
詳細 CloudWatch メトリクス有効化
スロットリング有効化
レート
10
バース
100

スロットリングについては基本APIキーで制御するが、デフォルトの値も念のため小さめにしておく。

使用量プラン

API Gateway 使用量プラン から 打刻APIで使用する使用量プランを作成する。

今回使用量プランは想定外の利用が発生しないための安全装置としての位置付け。

名前
dakoku-basic
スロットリングの有効化
有効にする
レート
1
バースト
10
クォータを有効にする
有効にする
1000 リクエスト数/

次へ

APIステージの追加 から 使用量プランを関連付けるAPIとステージを指定する。

API
dakoku
ステージ
prod

次へ

APIキーを関連付けずに 完了

APIキーの作成

API Gateway APIキー からIFTTTから打刻APIを呼び出す際に利用するAPIキーを作成する。

名前
dakoku-ifttt
APIキー
自動生成

保存

使用量プランに追加 から 使用量プラン dakoku-basic を指定。

テスト

API Gatewayを起点に一連の処理が正しく動作するか確認する。

API Gateway API dakokuリソース events-iftttPOST から テスト を選択。

リクエスト本文に

{
  "subject": "nijohando",
  "event" : {
    "name": "office",
    "modifiers": ["leave"]
  }
}

指定してテストを実行する。

  • CloudWatchログにLambdaの実行ログが正常に出力
  • Googleドライブ上のフォルダ/dakoku の下にdakoku_event_<現在の西暦> というファイル名でGoogleスプレッドシートが作成される
  • スプレッドシートには月ごとのシートが作成され、以下の打刻情報を1行追加される
2017-03-11T21:25:07.857 | nijohando | office | leave

を確認し正常に動作していることを確認する。

SSL/TLS証明書の発行

打刻APIの独自ドメインとHTTPS化で必要となるSSL/TLS証明書をCertificate Managerで発行しておく。

事前確認

Certification Managerで証明書のリクエストを行う際に所有確認のためAWSから以下のメールアドレスに一気にメールが送信される。

  • WhoisのDomain registrant, Technical contact, Administrative contact
  • administrator@your_domain
  • hostmaster@your_domain
  • postmaster@your_domain
  • webmaster@your_domain
  • admin@your_domain

独自ドメインのWhoisの登録情報が代行になっている場合は上記のいずれかのメールアドレスでメールを受信できるようにしておく必要がある。

証明書のリクエスト

AWSマネージメントコンソールから Certificate Manager を開く。
リージョンを バージニア北部 に切り替え、証明書のリクエスト を押下。

注意点としては東京リージョンではなく、バージニア北部リージョンで証明書を作成する必要がある。
これはCloudFrontにはリージョンの概念は無いが管理上バージニア北部リージョンに属するため東京リージョンで作成した証明書はCloudFront側から利用できない。

ドメイン名
dakoku.nijohando.jp

確認とリクエストボタンを押下。
前述のメールアドレスに確認メールが来るのでメール文中のリンクを踏んで承認する。 AWSマネージメントコンソールに戻り続行ボタンを押して証明書の発行が完了。

CloudFront

ディストリビューションの作成

打刻API用ディストリビューションを作成する。

AWS マネージメントコンソールから CloudFront を開き Create Distribution

Webの Get Started を選択。

Origin Domain Name
作成したAPI Gateway prodステージのドメイン
Origin Path
/prod
Origin ID
dakoku-api-gateway
Original SSL Protocols
TLSv1.2
TLSv1.1
TLSv1
Origin Protocol Policy
HTTPS Only
Viewer Protocol Policy
HTTPS Only
Allowed HTTP Methods
GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
Object Caching
Customize
Minimum TTL
1
Maximum TTL
1
Default TTL
1
Alternate Domain Names
dakoku.nijohando.jp
打刻API用に作成する独自ドメイン。
Custom SSL Certificate
dakoku.nijohando.jp
打刻API用に作成したSSL証明書。

Create Distribution でディストリビューションを作成。

statusがInProgressからDeployedになるまでしばらくかかる。

Originの追加

既に打刻API用のAPI Gatewayをオリジンとして登録済みではあるが、ここで別のオリジンとして定義する。

最初に登録したオリジンdakoku-api-gatewayは単純に打刻API用のAPI Gatewayへ中継するためのオリジンでx-api-keyヘッダを送出できるクライアント(IFTTT以外)向けの口。

それに対して追加のオリジンdakoku-api-gateway-events-iftttはIFTTT専用の口でx-api-keyヘッダにIFTTT用のAPIキーを追加して中継するためのオリジン。

つまりマルチオリジン構成だがオリジンの実態は同じAPI Gateway。違いはHTTPヘッダの加工処理の有無だけとなる。

CloudFrontDistributions から作成した打刻APIディストリビューションを選択。Originsタブから Create Origin

Origin Domain Name
作成したAPI Gateway prodステージのドメイン
Origin Path
/prod
Origin ID
dakoku-api-gateway-events-ifttt
Original SSL Protocols
TLSv1.2
TLSv1.1
TLSv1
Origin Protocol Policy
HTTPS Only

Origin Custom Headers に以下ヘッダを追加

x-api-key
API Gatewayで作成したIFTTT連携用のAPIキー dakoku-ifttt の値

Behaviorの追加

Behaviorを追加し、非公開のIFTTT用URLでアクセスが来た場合はオリジンdakoku-api-gateway-events-iftttを利用するように定義する。

CloudFrontDistributions から作成した打刻APIディストリビューションを選択。Behaviorsタブから Create Behavior で新規に作成。

Path Pattern
events-ifttt-<任意の英数字の組み合わせ>
(API Gateway側で作成したevents-iftttリソースのパス)
Origin
dakoku-api-gateway-events-ifttt
Viewer Protocol Policy
HTTPS Only
Allowed HTTP Methods
GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
Object Caching
Customize
Minimum TTL
1
Maximum TTL
1
Default TTL
1

Route 53に独自ドメインを登録

打刻API用のドメインを追加する。

Hosted zones から自ドメインを選択、Create Record Set でレコードを作成する。

Name
dakoku
Type
A IPv4 address
Alias
Yes
Alias target
Cloudfrontで作成したディストリビューションのFQDN

Create でレコードを作成。

テスト

curlからAPIを叩いて動作を確認する。

curl -H "Content-Type: application/json" \
-X POST \
-d '{"subject": "nijohando", "event": {"name": "office", "modifiers": ["enter"]}}' \
https://dakoku.nijohando.jp/events-ifttt-xxxxxxxxxxxx

Googleスプレッドシートに打刻情報が保存されれば以上でサーバサイドの準備は完了。

IFTTTの設定

最後にクライアントサイドの設定を行う。
iOS用のIFTTTアプリから出勤/退勤用アプレットを作成する。

出勤用アプレットの作成

My Applets から + で新規アプレットを作成。

then Locationサービスを選択。

Choose trigger から You enter an area を選択、職場の付近を設定。

that Makerサービスを選択。

Choose action から Make a web request を選択。

URL
https://dakoku.nijohando.kjp/events-ifttt-xxxxxxxxxxxx
Method
POST 
Content Type
application-json

Bodyに以下のJSONを定義。

{
  "subject": "nijohando",
  "event": {
    "name": "office",
    "modifiers": ["enter"]
  }
}

退勤用アプレット

My Applets から + で新規アプレットを作成。

then Locationサービスを選択。

Choose trigger から You exit an area を選択、職場の付近を設定。

that Makerサービスを選択。

Choose action から Make a web request を選択。

URL
https://dakoku.nijohando.kjp/events-ifttt-xxxxxxxxxxxx
Method
POST 
Content Type
application-json

Bodyに以下のJSONを定義。

{
  "subject": "nijohando",
  "event": {
    "name": "office",
    "modifiers": ["leave"]
  }
}

実地試験開始

何もしなくても出勤、退勤時刻が自動的にGoogleスプレッドシートに記録されていくので精神衛生上非常に良い。

但しジオフェンスの張り方に工夫が必要。
狭すぎると地理的影響(ビル、地下、基地局)などにより測位誤差が大きくなったタイミングで境界を越えイベントが発生してしまう為、最初は大きめ張り、 そこから職場の地理的特性を見ながらフェンスの広さや中心を調整していくのが良さそうだ。