ServerlessFramework 30分くらい Lambda + node.js + AWS KMS( Key Management Service ) でAPIキーの暗号化と復号まで

![スクリーンショット 2017-03-29 20.45.45.png](https://qiita-image-store.s3.amazonaws.com/0/124921/c43f5bf2-74dc-a067-d3f2-e09c9645d4f0.png)

###Lambdaでサーバレスでやりたいなー、という案件、いろいろあるんじゃ?
* 定期的にMackarelの監視パラメータを変更したいとか
* Backlogに変更があったらSlackに通知して欲しいとか
* Slackにつぶやくと、EC2サーバに処理が走ったり( git pullとか )

###でも『APIキーとか平文でソースコードに書いちゃっていいの?』という課題が
仮に自分で何らかの手段で暗号化を行っても
「 暗号化、復号の悩みとして『鍵を使って暗号化するのは技術的に可能』だが『鍵の保管場所』に困る」という点は残るかな、と。
AWS KMSはその辺、AWSにお任せしたい、というサービス、というのが私の認識です。
( 逆にこんな『超重要な鍵の管理をサービスとしてやっちゃうAWS』って凄い。肝が座っているというか流石だ )

###ただ、はじめてみると...
「うぉ、IAMロール割り当てる必要がある?　いやまぁ　そうか...」「なんかIAMロール割り当て面倒だな( 必要なのはわかるよ... )」などありまして、沢山かきづらい感じなんですよね。その辺のコストを減らしたい。できればServerlessFrameworkにやっていただきたい。

暗号化、復号の処理は意外と頻繁に行う作業なので、いま一度まとめてみます。

KMSで暗号化した情報にアクセスするためにIAM Roleの設定が必要ですが、これはServerlessFrameworkにやらせます。

鍵の作成などは[クラスメソッド様のページ](http://dev.classmethod.jp/cloud/decrypt-sensitive-data-with-kms-on-lambda-invocation/)を参考にさせていただきました:bow:

###前提
*AWS CLIインストール、aws configure( AWSのクレデンシャル情報の登録 )は済み

###作業の流れ
*マスターキー作成
*AWS CLIで暗号化・復号の作業
*lambdaで復号する関数( の説明 )

###マスターキー作成(既存のマスターキーを使う場合、この項は飛ばす)
AWS WebUI ->[ IAM ]->[ 暗号化キー ]->[ リージョン: ]のプルダウンメニューから[ アジアパシフィック(東京) ]を選択->[ キーの作成 ]

[ エイリアス(必須) ]と[ 説明 ]を入力して[ 次へ ]
( 必要であれば )[ タグキー ]と[ タグ値 ]を入力して[ 次へ ]
キー管理者を選択します( 最低限、自分を選択 )
[ キーの使用アクセス許可の定義 ]( 最低限、自分を選択 )[ 次へ ]
[ キーポリシーのプレビュー ]すみません、ざっと眺めて[ 完了 ]

できあがったキーをクリック、ARNの値( 例: arn:aws:kms:ap-northeast-1:****************:key/********-****-****-****-**********)をコピー

###AWS CLIで暗号化・復号の作業
```bash:環境変数にマスターキーのARNをセット
export KEYID=arn:aws:kms:ap-northeast-1:139332511982:key/********-****-****-****-************
```

```bash:暗号化(この暗号化された文字列はbase64でエンコードされた状態、この文字列を使う)
aws kms encrypt --region ap-northeast-1 --key-id $KEYID --plaintext 'naisho_mojiretu!' --query CiphertextBlob --output text  > angoukaBase64
```
リダイレクトで出力した angoukaBase64 の中身は後ほどスクリプト内に記載するため使います。

```bash:復号(複合できるかの確認です！この復号の処理をnode.jsで行うのがこのエントリの目的)
aws kms decrypt --region ap-northeast-1 --ciphertext-blob fileb://<(cat angoukaBase64 | base64 --decode) --query Plaintext --output text | base64 --decode
```

###lambdaで復号する関数( の説明 )
下記の[ naishoAngoukaZumi ]は上記の[ angoukaBase64 ]の中身を入れてください！

```javascript:kms_test.js
'use strict';
const AWS = require('aws-sdk');
const kms = new AWS.KMS({ region: 'ap-northeast-1' });
const naishoAngoukaZumi = 'angoukaBase64の中身！';

/**
  * AWS kmsに対して、復号化した文字列を取得する
  * @param {string} kmsEncyptedToken - 鍵
  * @return {string} 復号化された鍵
  */
function getToken(kmsEncyptedToken) {
  return new Promise((resolve, reject) => {
    const encryptedBuf = new Buffer(kmsEncyptedToken, 'base64');
    const cipherText = { CiphertextBlob: encryptedBuf };
    kms.decrypt(cipherText, (err, kmsData) => {
      if (err) {
        console.log(`token string decrypt error: ${err}`);
        reject(new Error(`${err}`));
      } else {
        try {
          resolve(kmsData.Plaintext.toString('ascii'));
        } catch (kmsDataErr) {
            console.error((kmsDataErr && kmsDataErr.stack) || kmsDataErr);
        }
      }
    });
  });
}

module.exports.kmsTest = (event, context, callback) => {
  getToken(naishoAngoukaZumi)
    .then((naishoMojiretu) => {
      console.log(naishoMojiretu);
    });
}
```

```bash:実行する前にpackage.jsonがないディレクトリの場合は作る
npm init -y
```

```bash:必要モジュールのインストール
npm install --save aws-sdk
```

###lambda-local でローカルでテスト
lambdaは何かイベントで発動しますが、適当なダミーのデータを作成します。

```bash:ダミーのイベントファイルを作成します
mkdir event-samples
vi event-samples/testEvent.json
```

```bash:testEvent.jsonの内容
{
  "key3": "value3",
  "key2": "value2",
  "key1": "value1"
}
```

```bash:ローカル環境でテスト
 ./node_modules/lambda-local/bin/lambda-local -l kms_test.js -h kmsTest -t 5 -e event-samples/testEvent.json
```

コンソールに秘密の文字列がでてきたでしょうか。これをlambda環境側で動作させたい訳です。

###ServerlessFrameworkでデプロイするための設定
IAMロールに『kms:Decrypt』を追加しています！　復号するのに必要です。

```serverless.yml
service: kms-test
provider:
  name: aws
  runtime: nodejs4.3
  iamRoleStatements:
    - Effect: Allow
      Action:
        - cloudwatch:*
        - kms:Decrypt
      Resource: "*"

package:
  exclude:
    - jsconfig.json
    - event-samples
    - package.json
    - node_modules/**

functions:
  slackPostFunction:
    handler: kms_test.kmsTest
    events:
      - schedule: cron(5 12 ? * MON-FRI *)
```

```bash:デプロイ！
./node_modules/serverless/bin/serverless deploy --region ap-northeast-1 --stage dev
```

lamba環境でもテストしてみましょう。 いやIAMロールが足りないとかいうエラー出がちなので、実環境でのテスト必須です...(いやホント皆さんどうしてるんでしょう)

前のエントリは時間で起動するようにしていましたが( それでも良いのですが )
待つのが面倒なので、AWS WebUI->[ lambda ]->[ 目的の関数名 ]をクリック->[ テスト ]をクリック->サンプルのままででok->実行
をすると即テスト実行できます。 

結果のコンソール出力ですが、lambda関数事にあるページの[ モニタリング ]のタブ->[ CloudWatch のログを表示 ]で確認できます。
いかがだったでしょうか、割とルーチンワークになるので簡単にまとめてみました。
( 正直、面倒ではあるんですが )必要な事ではあるので、なるべく楽になるようにしていきたいですね。

それと[弊社、アオイゼミ](https://corporate.aoi-zemi.com/mission/)ですが絶讃エンジニア募集中です！
今だとAndroidエンジニア。いや隣に弊社 近藤(Androidエンジニア)がいるのですが、
なんだか時々ため息をついたり、最近若干お疲れのご様子。。。増えるといいなー、と思っております！:bow:
[弊社コーポレートサイト](https://corporate.aoi-zemi.com/recruit/)などご覧いただき『なんか見に行ってもいいかなー』と思う方はぜひぜひご応募いただけると幸いですー！