読者です 読者をやめる 読者になる 読者になる

Make Local Happiness

自分の幸せは自分で作る!!!

raspberryPiの初期セットアップ

f:id:iwate_takayu:20170408172520j:plain

必要なもの

SDカードにRaspbian(OS)を焼く

こちらはMacでの作業になります

raspbianをMacにダウンロード

以下のページからダウンロードできます

https://www.raspberrypi.org/downloads/raspbian/

1.SDカードのディスクを確認します

$ diskutil list
/dev/disk2 (internal, physical):
  #:                       TYPE NAME                    SIZE       IDENTIFIER
  0:     FDisk_partition_scheme                        *15.9 GB    disk2
  1:             Windows_FAT_32 boot                    66.1 MB    disk2s1
  2:                      Linux                         4.3 GB     disk2s2

2.先程ダウンロードした.imgのファイルを指定してSDカードに書き込みます

$ sudo dd bs=1m if=2017-03-02-raspbian-jessie.img of=/dev/disk2


4190+0 records in
4190+0 records out
4393533440 bytes transferred in 3563.665218 secs (1232869 bytes/sec)

3.sdカードを取り出します

sudo diskutil eject /dev/disk2

WIFI設定

SSID, PASSWORDは適宜変更する

sudo sh -c 'wpa_passphrase SSID PASSWORD >> /etc/wpa_supplicant/wpa_supplicant.conf'

Wifiのパスワードが残っているので消す

sudo vi /etc/wpa_supplicant/wpa_supplicant.conf

network={
        ssid=SSID
        #psk=PASS # 生パスワードなんで消しておく
        psk=01b...
}

SSIDが表示されれば成功

$ iwconfig wlan0

IPを固定にする

現在のIPを確認する

$ ifconfig wlan0
wlan0     Link encap:Ethernet  HWaddr b8:27:eb:46:79:7d  
          inet addr:192.168.0.205  Bcast:192.168.0.255  Mask:255.255.255.0

ゲートウェイのアドレスを確認する

$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    303    0        0 wlan0
192.168.0.0     0.0.0.0         255.255.255.0   U     303    0        0 wlan0

DNSサーバを確認する

$cat /etc/resolv.conf 
# Generated by resolvconf
nameserver 8.8.8.8

先程確認したIPを記載する

sudo vi /etc/dhcpcd.conf

interface wlan0
inform 192.168.0.205
static routers=192.168.0.1
static domain_name_servers=8.8.8.8

$ sudo /etc/init.d/dhcpcd reload

各種インストール

アップデート

$ sudo apt-get update
$ sudo apt-get upgrade

$ sudo apt-get install -y git
$ sudo apt-get install -y vim

鍵設定

githubに公開鍵を置く

githubに鍵を予め登録しておく 共通鍵は以下のURLで確認できる

https://github.com/[username].keys

公開鍵を登録する

cd 
mkdir .ssh
cd .ssh 
wget -O authorized_keys https://github.com/ganezasan.keys 
cd ..

sudo chmod 700 .ssh
sudo chmod 600 .ssh/authorized_keys

sshの設定

$sudo vim /etc/ssh/sshd_config

...
PermitRootLogin no # root不可
...
PasswordAuthentication no # パスワード認証を無効に
...
AuthorizedKeysFile     %h/.ssh/authorized_keys #コメントインする

$ sudo /etc/init.d/ssh restart

sshを有効にする

sudo raspi-config

Interfacing options > ssh > Enable or disable ssh server

ディスプレイを上下逆にする

$ lcd_rotate=2

Arduinoをはじめようを読んでみたメモ

本の半分がArduino言語のリファレンスなので、 実際作るときに辞書としては使えそう。 前半は簡単なセンサーのプログラムと回路のサンプルについての説明が書いてあった。

以下メモです。

ティルトスイッチ(傾斜スイッチ) パルス幅変調(PWM)

抵抗器とコンデンサの値の読み方

抵抗器とは?

0
1
2
オレンジ 3
4
5
6
7
8
9
10%
5%

金と銀は精度を表す色 茶、黒、オレンジ、金という並びだったら、 103±5%とよめます。でもこれだと意味がわかりませんね。 実は3本目がゼロの数を表しています。 なので103ならば、10の後ろにゼロが3つあるので、「10000Ω±5%」の抵抗器ということになります。10kとも表せます。

コンデンサとは?

コンデンサは電気を蓄えたり放出したりする電子部品です。 直流を通さないで絶縁するはたらきもあります。

http://www.murata.com/ja-jp/campaign/ads/japan/elekids/compo/capacitor

コンデンサキャパシタ)の値の読み方はもう少し簡単です。 電解コンデンサのような樽型の部品は、値がそのまま印刷されています。 コンデンサの値の谷はファラド(F)で、あなたよく使う込んで差はマイクロファラド(μF)単位で図れるものでしょう。

セラミックコンデンサのような円盤形のコンデンサの場合は、μのような単位の記号は印刷されていません。かわりにピコファラド(pF)単位の値が3桁の数字で示されます。

1,000,000pF = 1μF

この3桁の数字の読み方は抵抗に似ていて、3桁目の数字が1〜2桁目の数字の後ろに並ぶゼロの数を表しています。ただし、ゼロの数をそのまま表すのは3桁目が0〜5のときだけで、8の場合は最初の2桁を0.01倍、9の場合は0.1倍にします。6と7は使われません。 例を挙げましょう。104と印刷されていたら100,000pF、つまり0.1μFです。

229ならば2.2μFとなります。

Prototyping Labを読んでArduinoを触ってみた

今回始めてArduinoに触り、電子工作というか回路とかも結構怪しい状態なので、 書籍に書いてることをメモしている感じです。 この本を読みました。

作品紹介

Arduinoで作られたプロトタイプの作品も結構多かった。 どれもプロトタイプからガッツリ工場での量産を意識しているわけではなく、 途中で品質や金額などを考慮して、品質をなんとか担保しつつ量産できるようにする工程があった。

電子回路の基礎知識

電気の流れは水の流れに例えられ、電位の高いところから低いところに向けて電流は流れる。地点の高さ(海抜)の高さは電気ではグラウンド(GND)を基準として比較します。

オームの法則

オームの法則は「電位差が電流に比例し、その比例定数を抵抗という」 2点間の電位差をE、電流をI、比例定数をである抵抗をRで表すと、 E=I×R

R=(電源電位 - LEDの順電圧)/ LEDに流したい電流

LEDの順電圧が1.8V(赤の場合にはこのくらいが多い)、電流が10mAの場合320Ωが適切であることがわかる

R = (5 - 1.8) / 0.01 = 3.2/0.01 = 320

Arudino基礎知識

PWM(Pulse Width Modulation)

オンの区間とオフの区間の割合を変えることで連続的に出力をコントロールすること

analogWrightは限られたデジタルピンでしか使えない

D3,D5,D6,D9,D10,D11の6つのみ

センサ

フォトインタラプタ

ある位置に物体があるかどうかを検知できる。 フォトインタラプタには透過型と反射型があります。 - 透過型: 発光素子と受光素子が対抗にセットされ、間を何かが遮ったかどうかを検出することができます。 - 反射型: 発光素子と受光素子が並行にセットされ、一定の距離に反射する物体があるか否か、また、反射率はどの程度かを検出することができます。

まとめ

センサー試すにはいい感じの本でした。 回路とセンサーの部品、サンプルコードなど丁寧に書かれていたので、始めの1冊としてはいいかもしれないです。 Lチカもやっぱり動くと楽しい!

f:id:iwate_takayu:20170202121939j:plain

CloudFormationでDynamoDBのテーブルを10個以上作る

Subscriber limit exceeded: Only 10 tables can be created, updated, or deleted simultaneously.

CloudFormationからDynamoDB作ると、こんなエラーがでるんですよね。 しかも結構いい加減で、私の環境では14個までは作成できました。

AWSのサポートに連絡すれば、 上限を緩和することとかできるかな〜と思ったのですが、 DynamoDBのAPIで制限されており、今のところは上限緩和できません。

Limits in DynamoDB - Amazon DynamoDB http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Limits.html

In general, you can have up to 10 CreateTable, UpdateTable, and DeleteTable requests running simultaneously (in any combination). In other words, the total number of tables in the CREATING, UPDATING or DELETING state cannot exceed 10.

解決策

Serverlessを使ってDynamoDBを作成していたのですが、 解決策は2つあって、

  1. Serverlessのプロジェクトを2つ以上に分ける
  2. DependsOn属性を使いテーブルに依存関係を付ける

今回はマスタとなるテーブルが10個以内だったので、 2のDependsOn属性で解決しました。

DependsOn属性を使いテーブルに依存関係を付ける

詳しくは以下のドキュメントをご参照ください。

DependsOn 属性 - AWS CloudFormation

http://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-attribute-dependson.html

DependsOn 属性を含む DynamoDB テーブル

http://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html#cfn-dynamodb-table-examples-dependson

Resources:
  myFirstTable:
    Type: AWS::DynamoDB::Table
    Properties:
      ...
  mySecondTable:
    Type: AWS::DynamoDB::Table
    DependsOn: myFirstTable
    Properties:
      ...

上記例では、必ず myFirstTable の作成が完了した後に mySecondTable の作成が開始されます。

そのため11個目以降のテーブルに DependsOn 属性をつけることで、 DynamoDB テーブルの同時作成数を 10 個に抑えつつ CloudFormation スタックの作成が可能になります。

Terraformで既存のDynamoDBの設定を取得する

f:id:iwate_takayu:20170112190156p:plain

Terraformのページは本当にわかりにくいので、メモ。 AWSマネジメントコンソールから手動でDynamoDBなどを作り、設定をコード化したい場合に便利です。

Terraform install

バイナリーをダウンロードし、パスが通っているディレクトリに設置します。
こちらのページにリンクがあります。 www.terraform.io

$ curl -LO 'https://releases.hashicorp.com/terraform/0.8.4/terraform_0.8.4_darwin_amd64.zip'
$ unzip terraform_0.8.4_darwin_amd64.zip
Archive:  terraform_0.8.4_darwin_amd64.zip
  inflating: terraform               

$ mv terraform /usr/local/bin/terraform
$ terraform --version
Terraform v0.8.4

AWSの設定

aws.tfファイルを作成し、普段使っているProfileなどを選択します。 defaultを使っている方は、この設定は不要です。

provider "aws" {
  region                   = "ap-northeast-1"
  shared_credentials_file  = "/Users/xxxxxx/.aws/credentials"
  profile                  = "test"
}

DynamoDBをインポート

<table_name>を任意のテーブル名にすれば完了です。 コマンド実行後terraform.tfstateが作成されています。

$ terraform import aws_dynamodb_table.basic-dynamodb-table <table_name>

2016年やってよかったことまとめ(振り返り)

2016年あっとい間ですね! やりたいことや、やりきれなかったことが沢山残っていますが、 とりあえず、振り返りとしてやってよかったことをまとめていきます。

フィリピン留学

3月末から6月までの約3ヶ月間フィリピンのセブ島に英語を勉強しに行ってきました。 詳細はこちらの記事にまとめています。

ganeza.hatenablog.com

ganeza.hatenablog.com

ganeza.hatenablog.com

TOEICの点数としては、フィリピンから帰ってきた次の日に日本で受けた試験で、 745点を取ることができました。 行く前が460点だったので約300点アップです!!

f:id:iwate_takayu:20161231230122p:plain

留学いってよかった事

  1. 外人と飲みに行っても2時間くらいなら話しに困らない
  2. TOEICの試験中に眠くなくならない
  3. 洋画や海外ドラマを見ていて会話が全部ではないが、半分くらいなら聞こえる

もちろん、まだこのレベルでは仕事で使うには不自由すぎるのですが、 一緒に開発をしている方(アメリカ人)と一緒に飲みに行くことができるようになったのは大きな1歩でした。 もちろん、仕事面でも開発中に対面以外でも議論や説明ができるようになりました。

※もう2年ほど一緒にやっているので、かなり思いを汲み取ってもらってます。ただ日本語は使ってません。

筋トレ(交互浴)

最近は週に2日以上ジムに通うのが日課になっています。 最初は5kmくらい走っていたのですが、 筋肉欲しいな。。という憧れがあったので、今は主に胸筋と腹筋、背筋を中心に筋トレしています。 胸板が薄い人は欲しいですよね。

通い初めてやっと4ヶ月経ちましたが、 筋肉が1.5kg増えました。 最初の2ヶ月はなぜか痩せるという悲しい現象が起きたのですが、 やっとベンチプレスなどのメニューで使う重さも+10kg以上変わってきました。 慣れもありますが、いい感じです!

昔、社会人1年目〜2年目でもジムには通ってましたが、 忙しい、面倒くさいと通うのをやめてしまいました。
ですが、今は体力が大事!という思いと、水風呂気持ちいいいいいいいいいという思いが私をジムにつれて行ってくれます。

水風呂発明した人凄いですよね!!!

昔は水風呂とかただ冷たくて、嫌いだったのですが、 今は、ただただ気持ちよくてしかたないです。

お風呂で温まって、水風呂
もっかいお風呂
そして水風呂
その後サウナ
水風呂

と大体3回は水風呂入ってます。

水風呂は筋肉がズタズタにされて、 力がはいらなくなる感じだったのですが、 最近はそれが気持ちいいいと感じるようになってきました。

そして、翌日の目覚めが違います!! 結構病弱な体質ですが、風邪もこの半年は殆ど引いていません。 もうジムに筋トレというよりも、水風呂に入りに行ってる感じです。

水風呂を入るキッカケになったヨッピーさんにはマジで感謝しております。

travel.spot-app.jp

自主サービス開発

今は2つの自主プロジェクトをやっています。

1つは高校の同級生と一緒にリアル脱出ゲームのメディアを作っています。
彼がリアル脱出ゲームを愛していて、友人の結婚式や旅行などで、自分の自作の脱出ゲームの謎を作るほどです。
今は公開しているのはメディアだけですが、簡単なゲームも開発中です!!

私も彼に誘われて一緒にリアル脱出ゲームに参加したのですが結構面白いです!!!

もう1つは、去年から考えていたサービスで、 エンジニアに気軽に相談できるサービスです。

ganeza.hatenablog.com

10月からやっと開発をはじめました。遅いですよね。。

本当はフィリピンから帰って来た後、すぐ開発を始めたかったのですが、 お金の問題もあり、受託もやりつつ開発を進めています。
今年中に公開するのが目標でしたが、まだちょっとかかりそうです。

もっと早くやろうとしていたのですが、 1人だとなかなか進まないことに気づきました。

なので、今は大学の後輩と集まって時間をとって開発をしています。 ただ、これはただ集まっているだけですw

彼は別の作業やプロジェクトをやっていますし、 私は私でもくもくと開発をしています。

いわゆる動機付けですね。 意外と今はいい感じで日曜日に集まって開発するのが習慣になってきています。

他に小さいですが、初めて自分のnpmのパッケージを公開しました。

www.npmjs.com

また自分のプロフィールサイトも公開しました。

Takayuki Ito

まとめ (準備で勝敗は100%決まっている

2016年は起業してから初めて自分の投資に時間を費やした年になりました。
ある意味、会社としての成長よりも個人の能力の成長に力を入れました。 仕事としても、誰かを動かすというよりは、1人のエンジニアとして活動していました。

これは全て準備不足だったからです。

何かをやりたいときに時間とお金は重要で、 私はその準備(お金)を怠っていました。
何のために2年やってきてるんだよ。と思ったりもしました。

ただ、この後も人生は続くので、 ゆっくり行こうと思います。

2016年はいろいろ振り返ることができ、新しい1歩の足がかりを作れた1年でしたので、 個人的には満足しています。

来年は仕事面でも色々変化のある年にしたいと思っています。 今やっているサービスの開発も公開できるように進めていきたいと思います。

2017年もみなさまどうぞよろしくお願いいたします。

よいお年を。

serverlessでCUSTOM AUTHを利用する時はTTLの設定に気をつけて!!

Serverless Advent Calendar 2016 10日目の記事です。 qiita.com

みなさんAmazon API Gateway使っていますか?

API Gatewayとは? サーバなしでAPIを作成できる便利なツールです。 エンドポイントを作成できURLやリクエストのメソッドに応じてLambdaを紐付けることも可能です。

今年はServerless Frameworkがv1に変わりAPI GatewayやLambdaがかなり使いやすくなったと思います。

さらにAPI GatewayではAPIの認証も3つの方法を設定することができます。

  1. APIキーによる認証
  2. IAMによる認証
  3. 独自の認証

今回は、3の「独自の認証」をserverless Frameworkを使って利用した際に、TTLの設定が原因で認証されないことがあった話をしたいと思います。

作成したソースはGithub上に公開しております。

github.com

API Gatewayの認証について

3つのAPI Gatewayについて簡単にご紹介していきます。

1. APIキーによる認証

1つ目はとってもシンプルでx-api-keyをリクエストのヘッダに含め、事前に設定したAPIキーが合えばリクエストを正常に返してくれます。

2. IAMによる認証

2つ目はIAMを使った認証で、IAMのroleにexecute-api:InvokeのActionが適切に設定されており、AWS Signature Version 4を使って認証付きのリクエストを送ると認証されます。

方法としてはAmazon Cognitoを使って一時的なIAM roleを発行し、認証するのがよいかと思います。

もちろん、API認証専用のユーザを作ってもできますが、ユーザを削除するか、ロールを外す以外に認証を外すことができないので、オススメはできないです。 CognitoからIAM Roleを発行してもらうAPIを作成したり、そのためのRole設定だったり、AWS Signature Version 4を使った認証情報の作成だったりと、ものすごく面倒くさいです。

3. 独自の認証

3つ目の独自の認証はLambdaを作成し、独自の認証を作成することができます。 独自のトークンをリクエストのヘッダーに設定し、そのトークンのチェックをLambdaに書くことができます。 デフォルトではAuthorizationをヘッダーに設定します。

私はログイン後にJWT(Json Web Token)のトークンを発行し、ログイン後に利用するAPIの認証に利用しました。

実際にやってみる

以下の4つを作成し、どこでTTLが問題になるかを検証していきます。

  • createToken (node function)
  • jwtAuthorizer (Lambda)
  • customAuthAPI (API Gateway)
  • hello (API Gateway)

serverless のプロジェクトを作成する

serverlessのセットアップは、公式ページにインストールとCredentialsの設定はあるので、こちら参照してください。

プロジェクトを作成し、最初にデプロイしてみます。

$ serverless create --template aws-nodejs --path serverless-custom-auth-example

Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/repos/serverless/serverless-custom-auth-example"
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v1.2.1
 -------'

Serverless: Successfully generated boilerplate for template: "aws-nodejs"


$ cd serverless-custom-auth-example
$ serverless deploy

Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading service .zip file to S3 (733 B)...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
............
Serverless: Stack update finished...

Service Information
service: serverless-custom-auth-example
stage: dev
region: us-east-1
api keys:
  None
endpoints:
  None
functions:
  serverless-custom-auth-example-dev-hello: arn:aws:lambda:us-east-1:xxxxxxxxxx:function:serverless-custom-auth-example-dev-hello

jwtAuthorizerを作成

jsonwebtokenを利用するので、npm でインストールします。

$ mkdir jwtAuthorizer
$ cd jwtAuthorizer
$ npm init
$ npm install --save jsonwebtoken
$ vim handle.js

実際にコードを書いていきます。 CustomTokenでLambdaを使用した際に、リクエストでヘッダに設定されたAuthrizationのパラメータはLambda上ではevent.authorizationTokenに入ってきます。

ヘッダの情報は以下のような形式をを想定するので、Bearerは認証には不要なので、splitで分割し、後ろのトークン部分を使いjwt.verifyでトークンを複合していきます。

Bearer eySDSGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9qZWN0IjoidGVzdCIsImlhdCI6MTQ4MTE3NjI4NiwiZXhwIjoxNDgxMTc5ODg2fQ.X3k9VtnfSASDSQwDDSDSU8ZEdBonRmp7KClokOJHmQ

トークンには鍵の他に幾つかパラーメータを設定することができるので、 今回は鍵の有効期限(expiresIn)と idを設定することを想定して書いていきます。

トークンの認証が通らなかった場合は、500エラーでUnauthorizedで返し、 トークンを複合した際にidがuserではなかった場合は、403エラーで呼び出しを許可しないようにします。 トークンが正しく、デコードした際のidがuserだった場合に、APIの実行を許可するRoleを発行されます。execute-api:InvokeというのがAPIを実行できる権限になります。

const jwt = require('jsonwebtoken');
const jwtOptions = {
  secretOrKey: 'serverless-custom-auth-example',
  expiresIn: { expiresIn: '1h'},
};

const generatePolicy = function(principalId, effect, resource) {
  const authResponse = {};
  authResponse.principalId = principalId;

  if (effect && resource) {
    const policyDocument = {
      Version: '2012-10-17',
      Statement: [
        {
          Action: 'execute-api:Invoke',
          Effect: effect,
          Resource: resource
        }
      ]
    };
    authResponse.policyDocument = policyDocument;
  }
  return authResponse;
};

module.exports.jwtAuthorizer = (event, context) => {
  const jsonWebToken = event.authorizationToken.split(' ')[1];

  jwt.verify(jsonWebToken, jwtOptions.secretOrKey, (err, decode) => {
    if (err) {
      return context.fail("Unauthorized");
    }

    if (decode.id !== 'user') {
      return context.succeed(generatePolicy('user', 'Deny', event.methodArn));
    } else {
      return context.succeed(generatePolicy('user', 'Allow', event.methodArn));
    }
  });
};

APIの認証にjwtAuthorizerを設定する

jwtAuthorizerのlambdaの設定とHelloのAPIの作成をしていきます。

jwtAuthorizerのLambdaを設定

$ vim serverless.yml

  jwtAuthorizer:
    handler: jwtAuthorizer/handler.jwtAuthorizer
    memorySize: 128
    package:
      include:
        - jwtAuthorizer/handler.js
        - jwtAuthorizer/package.json
        - jwtAuthorizer/node_modules

hello APIを作成

以下のようにevents配下を追加していくことで、 APIが設定することができます。

$ vim serverless.yml

  hello:
    handler: handler.hello
    memorySize: 128
    package:
      include:
        - handler.js
    events:
      - http:
          path: hello
          method: post

hello APIにCUSTOM Authの設定を追加

authorizerに先程作成した、jwtAuthorizerの名前を指定するだけです。 すごく簡単です。

※ integrationにlambdaを設定しない場合、 APIのレスポンスが紐付かないことがあったので、お気をつけください。

$ vim serverless.yml

  hello:
    handler: handler.hello
    memorySize: 128
    package:
      include:
        - handler.js
    events:
      - http:
          path: hello
          method: post
          integration: lambda
          cors: true
          authorizer:
            name: jwtAuthorizer

デプロイしてみる

serverless.ymlの設定ができたところで、デプロイしてみます。

$ serverless deploy
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading service .zip file to S3 (1.21 MB)...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
................................
Serverless: Stack update finished...

Service Information
service: serverless-custom-auth-example
stage: dev
region: us-east-1
api keys:
  None
endpoints:
  POST - https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello
functions:
  serverless-custom-auth-example-dev-hello: arn:aws:lambda:us-east-1:xxxxxxx:function:serverless-custom-auth-example-dev-hello
  serverless-custom-auth-example-dev-jwtAuthorizer: arn:aws:lambda:us-east-1:xxxxxxxxxx:function:serverless-custom-auth-example-dev-jwtAuthorizer

AWS マネジメントコンソールからAPI Gatewayを確認するとhello のPOSTメソッドの認証にjwtAuthorizerが設定されていることが確認できます。

f:id:iwate_takayu:20161211000558p:plain

ためしに、curlコマンドで叩いてみます。

curl -X POST https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello
{"message":"Unauthorized"}

はい。ヘッダーに何も設定していないのでうまくいきません。。

トークン作成の関数を作成しログインが成功するか試してみる

createTokenの関数を作成します。

$ mkdir createToken
$ cd createToken
$ npm init
$ npm install --save jsonwebtoken
$ vim index.js

'use strict';

const jwt = require('jsonwebtoken');
const jwtOptions = {
  secretOrKey: 'serverless-custom-auth-example',
  expiresIn: { expiresIn: '1h'},
};

const payload = { id: 'user'};
const token = jwt.sign(payload, jwtOptions.secretOrKey, jwtOptions.expiresIn);

console.log(`token: ${token}`);

実行するとTokenが取得できるので、Hello APIをためしてみる

$ node index.js 
token: dasdasdasdasdasdasdasda.eyJpZCI6InVzZXIiLCJpYXQiOjE0ODEzODAwMDMsImV4cCI6MTQ4MTM4MzYwM30.hhV0IVMWJAZGOixzsyNbluL4Eqe4cgtlk7NtA3SHaRI

$ curl -X POST https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello -H 'Authorization: Bearer dasdasdasdasdasdasdasda.eyJpZCI6InVzZXIiLCJpYXQiOjE0ODEzODAwMDMsImV4cCI6MTQ4MTM4MzYwM30.hhV0IVMWJAZGOixzsyNbluL4Eqe4cgtlk7NtA3SHaRI' | jq

{
  "statusCode": 200,
  "body": "{\"message\":\"Go Serverless v1.0! Your function executed successfully!\"}"
}

はい!いい感じですね!!!

2回目のリクエストが 403エラーになる

タイトルが答えなのですが、 試していきます。

2つ目のエンドポイントを作っていきます。 全然関係ないですが、最近裏サンデーの「Good Night World」がすごい面白くて、 しかも日曜日が更新日なので、今はワクワクしながらこの記事を書いているので、 Helloの反対のGoodNight関数を作ります。

$ vim handler.js

module.exports.goodNgiht = (event, context, callback) => {
  callback(null, { Message: 'Good Night World!'});
};

serverless.ymlにAPIの設定を追加

  goodNight:
    handler: handler.goodNight
    memorySize: 128
    package:
      include:
        - handler.js
    events:
      - http:
          path: good-night
          method: post
          integration: lambda
          cors: true
          authorizer:
            name: jwtAuthorizer

デプロイする

$ serverless deploy

Service Information
service: serverless-custom-auth-example
stage: dev
region: us-east-1
api keys:
  None
endpoints:
  POST - https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello
  POST - https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/good-night
functions:
  serverless-custom-auth-example-dev-hello: arn:aws:lambda:us-east-1:xxxxxxxx:function:serverless-custom-auth-example-dev-hello
  serverless-custom-auth-example-dev-goodNight: arn:aws:lambda:us-east-1:xxxxxxxx:function:serverless-custom-auth-example-dev-goodNight
  serverless-custom-auth-example-dev-jwtAuthorizer: arn:aws:lambda:us-east-1:xxxxxxxx:function:serverless-custom-auth-example-dev-jwtAuthorizer

これで準備はすべて完了です。 ためしてみます。

2回目のリクエストが 403エラーになるか確認

1回目にHello APIを叩くと成功!!

$ curl -X POST https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello -H 'Authorization: Bearer dsdadasdasdasdasdas.eyJpZCI6InVzZXIiLCJpYXQiOjE0ODEzODAwMDMsImV4cCI6MTQ4MTM4MzYwM30.hhV0IVMWJAZGOixzsyNbluL4Eqe4cgtlk7NtA3SHaRI' | jq

{
  "statusCode": 200,
  "body": "{\"message\":\"Go Serverless v1.0! Your function executed successfully!\"}"
}

続いてすぐにGoodNight APIを叩くと、、403エラーになりました。

$ curl -X POST https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/good-night -H 'Authorization: Bearer dsdadasdasdasdasdas.eyJpZCI6InVzZXIiLCJpYXQiOjE0ODEzODAwMDMsImV4cCI6MTQ4MTM4MzYwM30.hhV0IVMWJAZGOixzsyNbluL4Eqe4cgtlk7NtA3SHaRI' | jq

{
  "Message": "User is not authorized to access this resource"
}

TTLの設定を追加する

これ本当になんでだよって凄い悩んだんですが、 TTLを設定するだけで解消する問題でした。 一度目の認証でキャッシュができ、2回目のアクセスでは同じトークンで認証できないようになっていました。 デフォルトの設定だと300秒になっているので(たしか。。)、この時間を短くします。

serverless.ymlにTTLを設定する

authorizerにresultTtlInSecondsを設定します。

authorizer:
  name: jwtAuthorizer
  resultTtlInSeconds: 1

これでデプロイすれば、2回目の認証も成功できます。 めでたし、めでたし!

※たまに他の人のコードで ttlを設定していますが、v1になってからなのか、もともとなのかはわかりませんが、 これだと設定できていません。騙されないように気をつけてください。
まあ、短い方がいいので、こっちで設定できたほうがいいんですけどね。

ttl: 1

まとめ

API Gatewayの Custom AuthはIAM認証よりも簡単に設定できるので、かなり使いやすいのではないかと思います。 ただ、Serverless Frameworkはまだまだドキュメントが少なく今回のようなTTLの設定だけでかなりハマったりするので、今後沢山の人が使い、事例ができてきて安定してくるといいなと思っています。

ではよいServerlessライフを。