IT初心者のSORACOMジャーニー　SORACOM Kryptonで認証キーを手放す

***本記事はSORACOM Advent Calendar 2019の6日目のものです！***


「クラウドって何？サーバーって何？」の状態で、2018年8月からPython・AWSを勉強し始めて、今年はJAWS-FESTA2019にて「IT初心者のクラウドジャーニー」というテーマでプレゼンして、2019年11月12日　SORACOM-UG＃1 札幌初開催！　勢いのままに、Qiita初投稿です！


#初心者は何から始めるか？
 プログラムもデバイスもクラウドサービス利用も全部わからないけど、自己満足できるレベルのアウトプットを求めて、IoT〜通信〜クラウドまで一連で勉強するテーマを決めました。
　***「定期的に写真を撮るデバイスを作って、簡単なホームページを開設してみる」***

　
##初期の構成要素
|構成| デバイス| 備考|
|------------------|------------------|:------------------:|
| デバイス　　　　　 |  Raspberry Pi|  3B+ 　　|
| 通信手段          |  SORACOM SIM　|  Plan−D     |
| クラウドサービス 　 |  AWS　　　　　|   S3、CLI、IAM  |
| プログラミング言語  | Python　　　　|  3.5.3         |
| オープンソースモジュール | OpenCV  　|   4.1.1       |


#開発の変遷　
*【第一段階】*
1. ラズパイからPythonでPiカメラをOpenCVで起動させて、画像を撮影。
2. S3へのアクセス権限をIAMユーザから認証キーを払い出す。
3. AWS CLIで認証して、Boto３でS3へアップロード。
4. S3を公開にして、「index.html」に画像を埋め込んで、HTTPSにて静的サイトのURLを払い出す。
5. AWS Certificate Managerからドメインを購入し、静的サイトの独自ドメイン化を図る。
6. S3　→　CloudFront　→　Route53　にて独自ドメインURLの払い出し。


～簡単な構成図～
![最初のアーキテクチャ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416381/fc298bbb-7865-2039-0c0c-782576e525f2.png)

　この構成で撮影画像をS3の静的サイト上で見れました！

#セキュリティを考える
　セキュリティの知識ゼロで、デバイスを作成していました。見直した際の問題点を下記に記載。
　　１．ラズパイにIAMユーザ権限を直接払い出し
　　２．ラズパイのホームをPi設定、初期パスワードのまま利用
　　３．S3のポリシーを全公開

　もうガバガバの状態でした！知らないって怖いです。
　少しずつ整理をしていくことにしました。
　

#セキュリティに対して、Kryptonに至る経緯
***【まずAWS側でできるセキュリティを確認してみた】***
　１．AWS Cognitoを用いて、ユーザープールからIDを払い出し、承認キーの取得。
　　　AWS CLIから、Pythonのboto3を用いてS3へアップロード完了！
　２．AWS STSで認証をやってみようと思ったけどわからなかったので、やめる。
　　
　　　***Cognitoは便利な機能であることがわかった。が、認証キーの管理はいずれにせよ発生する。***



***【続いて、SORACOMのサービス内容を確認】***
今回、作成したカメラデバイスとの相性がいいSORACOMサービスを選ぶ必要がある。
クラウドへのデータ転送を実施する上では、下記のサービスが存在する。

|サービス名称       |評価　|結果|
|:---------------|------------------|:--------------:|
| Beam　　    　 |送信前後のプロトコル変換があり|△ ※1|
| Funnel        |AWS IoTへの接続可能 　 　　 |✖︎ ※2|
| Krypton　     |AWS Cognitoの認証を肩代わり |○ ※3|

　※1　データの暗号化におけるプロトコル変換が初心者の私にとっては、難しい。
　※2　AWS IoTからS３へのデータ容量制限が128kByteであるため、画像をUPする上では不向き。
　※3　認証キーの管理不要。認証の再生成時間も設定可能。
　
#Kryptonを実装
　SORACOMさんのホームページ[(こちら)](https://dev.soracom.io/jp/krypton/what-is-krypton/ "こちら")を参考に実装しました。
　ホームページの内容のまま、SORACOM Kryptonを実装することができました。　
　ただし、画像をS3へUPする際に、PythonのSDKが無い？あるようだけど作る必要あり？とのことで、
　手っ取り早く実装したいので、既にSDKが存在するNode.jsを勉強し、S3へのアップロード用のプログラムを一部書き換えました。

　コード公開です。毎分撮影画像を指定のS3へアップロード。

```Node.js
'use strict';
const AWS = require('aws-sdk');
const exec = require('await-exec');
const credentialsRefreshInterval = 15 * 60 * 1000;
const fs = require('fs');
const cron = require('node-cron')

// Executes krypton client to get open ID token
async function getToken(){
  let execResult = await exec("./krypton-cognito");
  return JSON.parse(execResult.stdout);
}

// Initializes AWS credentials and set timer to refresh periodically
async function initializeCredentials(){
  let data = await getToken();
  AWS.config.credentials = new AWS.CognitoIdentityCredentials({
    IdentityId: data.identityId,
    Logins: {
      'cognito-identity.amazonaws.com': data.token
    }
  }, {region: data.region});
  setInterval(
    async () => {
      data = await getToken();
      AWS.config.credentials.params.Logins['cognito-identity.amazonaws.com'] = data.token;
      AWS.config.credentials.refresh(err => {
        console.log("successfully refreshed AWS credentials");
      });
    },
    credentialsRefreshInterval
  );
}

initializeCredentials().then(() => {
  console.log("Successfully obtained AWS credentials");
 
  const s3 = new AWS.S3();
  const params = {
	Bucket: 'バケット名称',
	Key: "S3に保存する名前の画像名称"
	};
  let v = fs.readFileSync("./撮影画像名称");
  params.Body=v;
  cron.schedule(' 0 * * * * *',() => { 
  s3.putObject(params, function(err, data){
      if(err) console.log(eer, err.stack);
      else{    console.log(data);
               console.log('update image to S3!!');
           }
    	});
    console.log('wait for next action');
    });
})
```
soracom-krypton-client-for-java中の”kryptonExamples”フォルダの中にある”cognito-auth.js”に対して、「定期実行」と「S3へのアップロード」を追加しています。
参照: [Node.js 定期実行](https://qiita.com/n0bisuke/items/66abf6ca1c12f495aa04
 "定期実行")
参照: [Node.js S3へのアップロード](https://qiita.com/hashito/items/dff9adc37fb683e6c738
 "S3へのアップロード")


#最終的な構成

![最終的なアーキテクチャ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416381/f67da179-5be5-58ce-9765-13ac67a544b9.png)

Plan-DのSIMで上記の構成にたどり着きました！
無事に、認証キーを手放すことができました！

さっそく大容量アップロード用のPlan-DU SIMで運用開始しようとしたが、Plan－DU用ドングルを購入しておらず、ドコモのWifiルーターで代用中だったことを思い出す。

あれ、そもそもWifiルーターに設置されたSIMに対してもKryptonって使えるんだけっけ？
よくわかっていないことが判明しました。とりあえず、Plan-DU用のドングルを購入しよう。

そして、Wifiルーターに対するKrypton設定についてご存知の方、教えて頂きたいです。
出来ないような気もしてます。。。

IT初心者が記載したブログとなります。
表現に誤りがある場合には、ご指摘いただけますと幸いです。
Advent Calendar 楽しんで参りましょう〜！


#参照サイト
***【ラズパイの設定に関する資料】***
参照: [【python/OpenCV】カメラの歪み補正を行う方法](http://rikoubou.hatenablog.com/entry/2019/03/26/143556 "【python/OpenCV】カメラの歪み補正を行う方法")

参照: [ラズパイ3にOpenCV3/4を簡単に導入](https://qiita.com/mt08/items/e8e8e728cf106ac83218 "ラズパイ3にOpenCV3/4を簡単に導入")
　*OpenCVがPython2に入ってしまう場合に動きません。python3を起動させてcv2のバージョンをチェックしてみるといいです。*

参照: [boto3を使ってS3にファイルのアップ＆ダウンロード](https://qiita.com/yuni/items/e68c14a63e3a2eaaae71 "boto3を使ってS3にファイルのアップ＆ダウンロード")
*boto3をラズパイに入れる際には、sudo pip3 install boto3*
*pip3で入れないと、Python3で使えないので注意ください。*


***【ラズパイとクラウド連携に関する資料】***
参照: [AWS CLIの設定とS3へのアップロード](https://qiita.com/atmaru/items/8187ea2d6eac4c03b49a "AWS CLIの設定とS3へのアップロード")


***【S3の静的サイト作成に関わる資料】***
　参照: [【図解】AWS S3を静的なWebサーバーとして利用する](https://orizuru.io/blog/aws/s3-01/ "【図解】AWS S3を静的なWebサーバーとして利用する")

　参照: [AWS S3で静的Webページをホスティングする](https://qiita.com/dogwood008/items/a92abae789f4b0466f38 "AWS S3で静的Webページをホスティングする")

***【クラウド上のセキュリティを高める資料】***
参照: [AWSにおける静的コンテンツ配信パターンカタログ（アンチパターン含む）](https://dev.classmethod.jp/cloud/aws/static-contents-delivery-patterns/ "AWSにおける静的コンテンツ配信パターンカタログ（アンチパターン含む）")

参照: [AWS再入門 Amazon CloudFront編](https://dev.classmethod.jp/cloud/cm-advent-calendar-2015-aws-re-entering-cloudfront/ "AWS再入門 Amazon CloudFront編")

参照: [SSL証明書発行時のドメイン認証メールをSESで受け取ってみた](https://dev.classmethod.jp/cloud/aws/acm-verifydomain-ses/ "SSL証明書発行時のドメイン認証メールをSESで受け取ってみた")

参照: [S3と独自ドメインで公開しているサイトをSSL(https)化する](https://cre8cre8.com/aws/https-s3-and-cloudfront.htm
 "S3と独自ドメインで公開しているサイトをSSL(https)化する")