SHIINBLOG

JAWS DAYS 2017にスカラシップ参加で行ってきた

JAWS DAYSって何?

AWSを利用するユーザの日本のユーザグループ JAWS-UGが主催で行われる全国イベント。毎年東京で行われ今年は5回目。
全国の支部のメンバーが企画や準備を行う。
事前申し込みで1700人、入場1200人という非常に大きなイベント。
最新技術や導入事例、ハンズオンにライフスタイルまで幅広いテーマでセッションなどが行われた。

スカラシップ参加?

今回からできた制度で、地方で活躍しているルーキーメンバーに対して、JAWS-UGがJAWS DAYSに参加するための交通費の一部と宿泊費をサポートする企画。
九州エリアの推薦に自分が選ばれたので、喜んで行きました。
(もともと興味はあったんだけど、会場が東京なので諦めてました)

参加してみて

とにかく熱気がすごい
1200人もの人がいるわけだし、当然物理的にも暑い。
でも、それ以上にセッションが熱い。
タイムテーブル見て、どうしようかな~とか思ってたセッションでも引き込まれる何かがあった!
非常に残念ながら自分の文才では熱気や何かを表現できるだけの力はないので、残念な限りです。

個人的に気になったセッション

さすがに、とりとめもなく終わるのものどうかと思ったので、気になったセッションを。

サーバレスでシステムを開発する時に大切なこと

サーバレスアーキテクチャーで実運用を行った上で、分かった課題などについてのセッション。
運用を考えて開発してね、という結論なので非常に耳に痛かったです。

サーバレスの今と未来

サーバレスの入門によさそうなセッション。
SAM(Serverless Application Model)がちょっと気になる。

API Gatewayのカスタム認証でワンタイムパスワードを使ってみた

概要

API Gatewayの認証でなんとなく(時間ベースの)ワンタイムパスワード(以後、TOTP)を使いたくなったので作ってみる。
基本的にnodejsを使用して構築する。

認証方法

API Gatewayのカスタム認証でTOTPの認証を行う。
TOTPのライブラリにはSpeakeasy(GitHub)を使う。
また、Google Authenticatorなどに登録するためのQRコードの生成にはQRCode Terminal Edition(GitHub)を使用する。

構築

TOTP用の鍵(?)とAuthenticator登録用のQRコードを生成する

まずは、必要なライブラリをインストールする。

$ mkdir make-key
$ cd make-key
$ npm install speakeasy
$ npm install qrcode-terminal

インストールしたら、鍵を作りQRコードを表示するgenerate.jsを作成する。

"use strict";
const fs = require('fs'); // 鍵のテキストを保存する
const speakeasy = require('speakeasy');
const qrcode = require('qrcode-terminal');

// TOTPの鍵の情報を生成する
const secret = speakeasy.generateSecret({length: 20});

// TOTPの鍵をテキスト保存する
const key = secret.base32;
fs.writeFile("totp_secret_base32.txt", key);

// 登録用QRコードを表示する。
const url = secret.otpauth_url;
qrcode.generate(url);

実行すると以下のようになる。 f:id:luciferous_report:20170301133650p:plain (Warningが出ているが気にしない) このQRコードが登録用のものになる。 TOTPの鍵は次のように作られている。

$cat totp_secret_base32.txt
KJ4DGPZ4LM7DU2KWPVOV222YLNEFKJLT

この値は次のカスタム認証用Lambda関数で使用する。

カスタム認証用Lambda関数の作成

こちらもまずはライブラリのインストールを行う。

$ mkdir auth-method
$ cd auth-method
$ npm install speakeasy

次に認証用のLambda関数index.jsを書きます。

'use strict';
const speakeasy = require('speakeasy');

const generatePolicy = function(principalId, isAllow, resource) {
  const effect = isAllow? "Allow": "Deny";
  // IAM ポリシーを生成する
  return {
    principalId: principalId,
    policyDocument: {
      Version: '2012-10-17',
      Statement: [{
        Action: 'execute-api:Invoke',
        Effect: effect,
        Resource: resource
      }]
    }
  };
};

exports.handler = (event, context, callback) => { 
  // トークンを取得
  const token = event.authorizationToken;
  const key = process.env.TOTP_SECRET;
  const result = speakeasy.totp.verify({
    secret: key,
    encoding: 'base32',
    token: token,
    window: 2
  });
  if (result){
    callback(null, generatePolicy('user', true, event.methodArn));
  }else{
    callback(generatePolicy('user', true, event.methodArn));
  }
};

Lambdaに登録するためのzipファイルsource.zipを作成します。

$ zip -r source.zip index.js node_modules/

このsource.zipをLambda関数作成時にアップロードします。
この際、環境変数を設定します。
キー"TOTP_SECRET“にtotp_secret_base32.txtの中身の文字列を入力します。
Lambda関数の作成ができました。

API Gatewayにカスタム認証を設定する

API Gatewayで新しいAPIを作成する(ここは既存のAPIでも問題はない)。 ここでは"totp“という名前で作成したことにする。 f:id:luciferous_report:20170302113305p:plain APIを作成したら左端のカラムを下にスクロールさせて、作成したAPIのオーソライザーを選択します。
Lambda関数のリージョンや名前などを入力し作成ボタンを押します。
(キャッシュの仕組み等は検証してないので結果のTTLを”0“に設定しています)
f:id:luciferous_report:20170302113800p:plain 作成すると下にオーサライザーのテストというものができるので、IDトークンにワンタイムパスワードを入れてどういう動きになるかを見るといいかもしれません。

次に、APIのリソースを作成します(リソースの作成は、左端のカラムのtotpのリソースです)。
ここでは、

  1. CORSを有効にしたgetリソースを作成し、
  2. getリソースにGETメソッドを作成し、統合タイプをMockにする。

ということにします。 f:id:luciferous_report:20170302114939p:plain メソッドリクエストをクリックし、認証に作成したオーソライザーを選択します。 f:id:luciferous_report:20170302115111p:plain オーソライザーを設定したら、APIをデプロイします。
これでAPIの構築は完了です。

curlで叩いてみる

curlコマンドで叩いてみます。
f:id:luciferous_report:20170302123006p:plain 最初はワンタイムパスワードを渡してないので当然失敗しています。
二度目は正規のワンタイムパスワードを渡しているので成功しています。

やってみて

結局のところは、カスタム認証に使うLambda関数の分岐条件にワンタイムパスワードを使うだけなので検証前のワクワク感がすぐなくなってしまいました。
これにユーザ認証まで噛ませようとすると、JSON文字列を渡してパースするほうがいいのかなぁとも思うところです。 個人用途のちょっとした認証だったら便利そうです。

Google Authenticatorを使ってSSHに二段階認証を組み込んでみる

最近、ボットネットによるDDos攻撃とかいろいろ耳にするので、自宅サーバの(SSHの)セキュリティの強化を本気で考えてみる。

とりあえず、Password認証から公開鍵暗号方式に変更。
それだけだと、面白みもないので前々から考えてた二段階認証を組み込んでみる。

二段階認証にはGoogle Authenticatorを使用する。
とはいえ、いきなり自宅サーバに組み込んでログインできませんでした、では洒落にならないのでいったんクラウド上で検証を行った。

検証は以下の環境で行った。

また、構築は以下のサイト(英語記事)を参考にした。

1. Google Authenticator用のPAMのインストール

Google AuthenticatorをSSHで使用するためのPAMはUbuntuリポジトリにあるのでapt-getで普通にインストールできる。

sudo apt-get update
sudo apt-get install libpam-google-authenticator

2. Google Authenticatorの設定ファイルを生成

libpam-google-authenticatorをインストールしたらgoogle-authenticatorコマンドで設定ファイルを対話的に作成する。
ここでは対話をひとつひとつ記述していくことにする。

Do you want authentication tokens to be time-based (y/n) y

二段階認証に使うワンタイムパスワードを時間ベースのものにするかを設定する。
Google Authenticatorのスマホアプリは時間ベースのものなのでyを入力。
ちなみに入力したらターミナルの画面にQRコードが表示されるので、それをGoogle Authenticatorのスマホアプリなどで読み込めば、二段階認証用のワンタイムパスワードを取得できるようになる。

Do you want me to update your "/home/ubuntu/.google_authenticator" file (y/n) y

設定ファイルを書き出すかを設定する。 書き出さないと使うことができないので当然yを入力する。

Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) n

ワンタイムパスワードを一度しか使用できないようにするかを設定する。
複数セッションターミナルを開くことも想定されるため、ここではnを入力。
(セキュリティを強固にするだけならyの方がいい気もする)

By default, tokens are good for 30 seconds and in order to compensate for
possible time-skew between the client and the server, we allow an extra
token before and after the current time. If you experience problems with poor
time synchronization, you can increase the window from its default
size of 1:30min to about 4min. Do you want to do so (y/n) n

ワンタイムパスワードの有効期限の設定(?)だと思われる。
実は自分もよくわかっていない。
おそらく、有効期限の幅がデフォルトだと1.5分だが、4分に伸ばすかという意味だと思う。自身はない。
参考にしたページではnだったのでnを入力。

If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting (y/n) y

30秒間に3回のレートリミットを設定するかどうか。
同じワンタイムパスワードを複数回使えることにしているので、ここはyでセキュリティを強化しておく。

これで対話式の設定は終了。
設定ファイルはホームディレクトリに.google-authenticatorという隠しファイルで存在する。

3. PAMの設定を変更する

Google Authenticator側の設定ファイルの準備ができたので、次にPAMの設定を変更する。

sudo vi /etc/pam.d/sshd
# Standard Un*x authentication.
#@include common-auth
# Standard Un*x password updating.
@include common-password
auth required pam_google_authenticator.so nullok

まず、4行目の@include common-authコメントアウトする。
理由はよくわからない。わからないが、参考にしたサイトはそう書かれてたんだ。

次に、末尾にauth required pam_google_authenticator.so nullokを追記する。
Google Authenticatorによるワンタイムパスワードによる認証を追加しつつ、nullokオプションをつけることで、Google Authenticatorを設定していないユーザはワンタイムパスワードを使用せずにログインすることができる。

4. SSHの設定を変更する

次にSSHサーバの設定を変更する。

sudo vi /etc/ssh/sshd_config
# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication yes
# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the ChallengeResponseAuthentication and
# PasswordAuthentication.  Depending on your PAM configuration,
# PAM authentication via ChallengeResponseAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and ChallengeResponseAuthentication to 'no'.
UsePAM yes
AuthenticationMethods publickey,keyboard-interactive

まず、49行目のChallengeResponseAuthenticationの値をyesにする。
次に、88行目のUsePAMの値をyesにする。 最後に、末尾にAuthenticationMethodsの値をpublickey,keyboard-interactiveにする。

上二つでPAMによるチャレンジレスポンス認証(ここではワンタイムパスワードによる認証)を可能にし、最後の一つで公開鍵認証とチャレンジレスポンス認証の二つを通らないとログインできないように設定している。
AuthenticationMethodsでは、publickeyが公開鍵認証、keyboard-interactiveがチャレンジレスポンス認証を意味する。
ちなみに、パスワード認証はpasswordと記述する。

5. SSHサーバの再起動

SSHサーバの設定変更を反映させるために、SSHサーバを再起動する。
このとき、かならずSSHのセッションを一つ以上つないだ状態で再起動すること。
最悪の場合、SSHでログインできなくなる可能性があるため、設定をもとに戻すために残しておくこと。
(まぁ、自分はそれが怖かったからこそクラウド上で検証を行ったわけですが)

sudo systemctl restart sshd

6. SSHログイン

SSHを再起動したら、RLoginなどのターミナルエミュレータでログインしてみよう。
Google Authenticatorのスマホアプリなどに表示されるワンタイムパスワードを入力してログインできれば成功だ。
ダメだった場合は、残しておいたセッションから設定ファイルを修正するなり、サーバを一から立て直すなりしてみるといいだろう。

おまけ) Google Authenticatorの設定をコマンドオプションで非対話式に書き出させる。

ChefやAnsibleなどからサーバの自動構築をかける際、対話式では困るのでコマンドオプションを試してみる。 幸い、クラスメソッドの記事の中に、コマンドオプションが載ってたので、google-authenticatorコマンドのヘルプも参考に自分の設定に近いと思われるオプションを試してみた。

google-authenticator -t -D -f -r 3 -R 30 -W

よーしログインしてみるぞ~、っとやってみるけどログインできない。

ということで、対話的に作成した場合と、オプションから作成したときで.google-authenticatorファイルの比較をしてみることにしてみた。

まず、対話的に作成した場合。

2B4W5FOSFMPQJCVN
" RATE_LIMIT 3 30
" TOTP_AUTH
41375110
40302207
67243582
78149499
99954973

そして、コマンドオプションで作成した場合。

3L5WLDSSSXJYCXPI
"RATE_LIMIT 3 30
" WINDOW_SIZE -1
" TOTP_AUTH
61414616
16375568
36481341
59544280
16815552

ん? なんかコマンドオプションのほうは" WINDOW_SIZE -1という行が追加されている。
指定しているオプションから考えると、対話的に作成した場合の4番目の質問、オプションでいう-Wが怪しい。
-w NというオプションでWINDOW_SIZENにできるみたい。
とはいえ、デフォルトの値はなんだ?

Google先生の力によりGoogle AuthenticatorのGithub issueに答えを見つける。

By default, tokens are good for 30 seconds. In order to compensate for possible time-skew between the client and the server, we allow an extra token before and after the current time. If you experience problems with poor time synchronization, you can increase the window from its default size of +-1min (window size of 3) to about +-4min (window size of 17 acceptable tokens). Do you want to do so? (y/n)

そうか、デフォルトのWINDOW_SIZEは3なのか。
ということで、再度コマンドオプションを試してみる。

google-authenticator -t -D -f -r 3 -R 30 -w 3

今度はうまくいったので、Chefによる自動構築も夢じゃない、と思う。

ふくあず 春のIoT祭り(04/25)に参加してきました

以前 Microsoft MVP Communication Camp に参加した時から興味があったふくあずの勉強会に参加してきました

jazug.doorkeeper.jp

Microsoftが提供するクラウド(Azure)の話だけでも興味深いのですが、

今回はIoTハンズオン(体験)もあるので会社の研修(新入社員対象、自由参加)をサボって参加させていただきました。

まぁ、サボってと言いながらちゃんと研修担当の先輩に許可もらって休んではいるんですけどね。

 

今回の勉強会はIoTハンズオンと複数の講演の二部構成でした。

ハンズオンは、GR-SAKURAとセンサーボードを利用した温度計測と、

Azureへのデータ送信集計でした。

今回使用した言語はC# ( .Micro Framework )で、

ほとんどコピペだったので近いうちに読むつもりです。

測定したデータはAzureのMobile Serviceに送信し蓄積し、

蓄積したデータをASP.NETを用いて更に集計する人の元へ送信する、

という内容でした。

 

第二部ではAzureやVisual Studioについての講演でした。

資料が上がっているようなので、そちらの紹介です。

 

www.slideshare.net

Atsushi Kojima

 

speakerdeck.com@nakansukeさん

 

 

www.slideshare.net

@Airish9さん

 

LTでは、

www.slideshare.net

@wanto1101さん

 

非常に面白い話を聞けました。

@Airish9さんが紹介していた5/15C#勉強会の方に参加するつもりなのでそちらのリンクも。

eventon.jp