Edited at

SSHログインを二段階認証(ワンタイムパスワード)したお話。


SSHログインを二段階認証化したお話。


はじめに

最近、複数人でRaspberry Piを使ってIotをするようになった。

ただ、置いている場所の都合上、スタッフ以外の一般人からもアクセスできるAPに接続しなければならない環境にある。

SSHにログインする際、いつも不安がつきまとう。鍵ファイルが漏れたら終わりだ。

そんな不安を少しでも和らげようと、SSHを二段階認証にしてみることに。

しかし、いざ始めようにも、情報が少ない。

そんな中で、最近、DjangoとLINE Notifyを使った二段階認証を作成した事例を見かけた。

これを参考に、SSHログインに応用できないか考えてみた。

まず、以下のようなモデルを作る必要がある。

上記モデルは、想定人数が何百人の話になっている。

では、今回のモデルについて考えてみる。

・SSHにログインするユーザは10人未満である。

  ・管理ページや管理サイトを作る必要がない。

・二段階認証は強制にしたい。

・Rasbianのユーザはrootとpiの二つのみ。

  ・このアカウントに誰がログインしたかという情報の通知。

  ・接続元の情報(IPアドレスなど)をログイン完了後に通知。

今回↑上記を元に二段階認証を構築したいと思う。


LINE Notifyを使う。

二段階認証をする際、メールでアクセスコードを送信して認証をさせるシステムが一般的だと思う。

しかし、今時、うちうちで使用するシステムにメールを使うことはほとんどないのではないだろうか。

そこで、普段から使っているコミュニケーションツールである、LINEやSlackにアクセスコードを送信する方法にしたいと思う。

また、LINE Notifyを使った二段階認証が実際に大学で使われている事例があるので、問題ないと思われる。

個人的にはSlackを使いたかったのだが、LINEしかみない人がいるので、やはり、同様にLINE Notifyを用いることにした。


・LINE Notifyにアクセス

まずは、LINE Notifyにアクセスしてみた。↓

https://notify-bot.line.me/ja/

ログインする。

ログインできたら、My pageへ移動。

そして、Generate tokenをクリック。

Access tokenを取得できたら、閉じる前にtokenをきちんとコピーしておくこと。

これでLINE Notifyを使う準備はできた。

bashで使うには、curlなどを用いてやれば良い。1


LineNotify


$ curl -X POST -H "Authorization: Bearer ここにアクセストークン" -F "message=${code}" https://notify-api.line.me/api/notify



bashログイン時にスクリプトを実行したい。

次に考えなければならないのは、どのタイミングでスクリプトを実行させるかという問題である。

SSHログインができれば即時実行できるようにしたい。

これを可能にしてくれるのが、.profileである。2

2019/01/30追記〜

プロファイルスキップ

$ ssh -t user@host bash --noprofile

プロファイルのスキップは簡単である事をご指摘いただいた。

上記コマンドで簡単にプロファイルをスキップすることができるようだ。3

この話は飛ばしてください。

この.profileはホームディレクトリにある。

このファイルの先頭にスクリプトを加えてあげれば良い。


Ctrl + C  や Ctrl + Z で回避される問題。

ここで、問題になるのが、Ctrl + C や Ctrl + Z で強制的に認証処理を終了することが出来てしまう。

この問題を回避するために、trapというものを使う。

trap コマンドは送出されたシグナルを捕捉し、あらかじめ指定されていた処理を実行するコマンドである。4

このtrapコマンドを応用して、5


trap

trap 'exit' SIGINT

trap -- SIGINT


上記を加えてあげれば、 Ctrl + C や Ctrl + Z でSIGINTを補足した時、exitコマンドを実行してくれる。


アクセスコードの生成

ランダムにアクセスコードを生成して、認証に使いたい。

そこで、opensslを使ってランダムにコードを生成させたい。67

四桁程度にしたかったので、以下のようにパイプ処理した。


openssl

$ openssl rand -base64 12 | fold -w 4 | head -1



誰がログインしたか知りたい

次にユーザ登録をどうするかの問題だが、管理サイトを作らないで、ユーザを管理者が登録する形にしたいと思う。

login_user.txtといったように適当にファイルを作って、一行ごとにログインできるユーザを追加してあげれば良い。


login_user.txt

od

ai
buki
ozki
sara

このファイルを一行ずつ比較するようにした。89


login_user_process


__uname() {
read uname
cat
login_user.txt | while read user
do
if
[ "$uname" = "$user" ]; then
userstatus="t"
echo $userstatus
break
else
userstatus="f"
fi
done

linesend=`curl -X POST -H "Authorization: Bearer ここにLineNotifyアクセストークン" -F "message=${uname}からのアクセス。" https://notify-api.line.me/api/notify > /dev/null 2>&1`

return 0
}
main() {
echo please input user name:
ustatus=`__uname`
if [ "$ustatus" = "t" ]; then
echo ok.
else
echo false.
exit
fi
}

main $@



アクセスコード送信、比較

次に、アクセスコードの送信と比較。

・opensslで四桁のアクセスコード生成。

・LINE Notifyでアクセスコードを送信。

・アクセスコード入力。

・認証完了。

↑と言う流れになる。


ac_code

ac_code=`openssl rand -base64 12 | fold -w 4 | head -1`

curl -X POST -H "Authorization: Bearer ここにLineNotifyアクセストークン" -F "message=${ac_code}" https://notify-api.line.me/api/notify > /dev/null 2>&1
echo "\n"

echo please input token:
read ans
if [ $ans = $ac_code ]; then
echo OK,Passed.
else
echo false.
exit
fi


また、アクセスコードを入力した際、四桁のコードが見えるのはよくない。

アクセスコードの入力を非表示にする。10


stty_process

stty -echo

echo please input token:
read ans
stty echo


接続元IPアドレス、ポート番号の通知

最後に接続元IPアドレス、ポート番号の通知に関して。

環境変数の$SSH_CONNECTIONで上記の情報を参照できるのでそれを利用して、Line Notifyへ通知させる。


ip_port

echo Please input AccessCode:

read ans
if [ "$ans" = "$ac_code" ]; then
echo complete.
curl -X POST -H "Authorization: Bearer ${line_token}" -F "message=正常ログインしました。" https://notify-api.line.me/api/notify > /dev/null 2>&1
curl -X POST -H "Authorization: Bearer ${line_token}" -F "message=(${SSH_CONNECTION})${uname}:接続情報" https://notify-api.line.me/api/notify > /dev/null 2>&1
else
echo false.
curl -X POST -H "Authorization: Bearer ${line_token}" -F "message=アクセスコードが一致しません。" https://notify-api.line.me/api/notify > /dev/null 2>&1
curl -X POST -H "Authorization: Bearer ${line_token}" -F "message=(${SSH_CONNECTION})${uname}:拒否元接続情報" https://notify-api.line.me/api/notify > /dev/null 2>&1
exit
fi

最終的に完成した.profileが以下。


.profile

#!/bin/sh

__uname() {
line_token="ここにLineNotifyアクセストークン"
read uname
cat
login_user.txt | while read user
do
if
[ "$uname" = "$user" ]; then
userstatus="t"
echo $userstatus
break
else
userstatus="f"
fi
done
linesend=`curl -X POST -H "Authorization: Bearer ${line_token}" -F "message=${uname}からのアクセス。" https://notify-api.line.me/api/notify > /dev/null 2>&1`
return 0
}
main() {
line_token="ここにLineNotifyアクセストークン"
echo -n Please input UserName:
ustatus=`__uname`
if [ "$ustatus" = "t" ]; then
:
else
echo False.
exit
fi
ac_code=`openssl rand -base64 12 | fold -w 4 | head -1`
curl -X POST -H "Authorization: Bearer ${line_token}" -F "message=${ac_code}" https://notify-api.line.me/api/notify > /dev/null 2>&1
echo -n Please input AccessCode:
stty -echo
read ans
stty echo
if [ "$ans" = "$ac_code" ]; then
echo Complete.Welcome to Raspberry Pi.
curl -X POST -H "Authorization: Bearer ${line_token}" -F "message=正常ログインしました。" https://notify-api.line.me/api/notify > /dev/null 2>&1
curl -X POST -H "Authorization: Bearer ${line_token}" -F "message=(${SSH_CONNECTION})${uname}接続情報" https://notify-api.line.me/api/notify > /dev/null 2>&1
else
echo False.
curl -X POST -H "Authorization: Bearer ${line_token}" -F "message=アクセスコードが一致しません。" https://notify-api.line.me/api/notify
curl -X POST -H "Authorization: Bearer ${line_token}" -F "message=(${SSH_CONNECTION})${uname}:拒否元接続情報" https://notify-api.line.me/api/notify > /dev/null 2>&1
exit
fi
ac_code="cleared."
ans="cleared."
line_token="cleared."
}
trap 'exit' SIGINT
main $@
trap -- SIGINT
(以下略)

また、今後チームの人数が増えたら、管理サイトを作りたいなと思う。

2019/01/30追記


プロファイルスキップ

$ ssh -t user@host bash --noprofile


プロファイルのスキップは簡単である事をご指摘いただいた。

上記コマンドで簡単にプロファイルをスキップすることができるようだ。3

現在PAMを使って再構築中。

2019/02/17追記

pam_execを使ってみた。

PAMとは?

IBM developerWorksによると、11




  • 認証 (auth) モジュールは、ユーザーの認証や、クレデンシャルの設定と破棄に使われます。


  • アカウント管理 (account) モジュールは、アクセスや、アカウントとクレデンシャルの有効期限切れ、パスワードの制限やルールなどに関連するアクションを実行します。


  • セッション管理 (session) モジュールは、セッションの初期化と終了のために使われます。


  • パスワード管理 (password) モジュールは、パスワードの変更と更新に関連するアクションを実行します。




上記のことができる。

同じく、モジュールには以下のようなものがある。11




  • pam_access は /etc/security/access.conf の中に事前定義されたルールに基づいて、ログイン名とドメイン名を使ってログ・デーモン・スタイルのログイン・アクセス制御を行います。

  • pam_cracklib はパスワード・ルールに基づいてパスワードのチェックを行います。

  • pam_env は /etc/security/pam_env_conf の環境変数の設定 (set) と設定解除 (unset) を行います。

  • pam_debug は PAM をデバッグします。

  • pam_deny は PAM モジュールをロックアウトします。

  • pam_echo はメッセージを出力します。

  • pam_exec は外部コマンドを実行します。

  • pam_ftp は匿名アクセスのためのモジュールです。

  • pam_localuser を利用するためにはユーザーが /etc/passwd のリストに載っている必要があります。

  • pam_unix は /etc/passwd による従来のパスワード認証を行います。

  • その他にも、pam_userdb、pam_warn、pam_xauth など多くのモジュールがあります。



pam_execを使って、ログイン完了後に通知などを出すことができることが分かった。

ただし、これだけでは物足りなかった。

この情報を頼りに色々と調べていった。

次にSSHにはChallengeResponseAuthenticationというものがあるという事を知った。12

SSH ChallengeResponseAuthenticationにはワンタイムパスワードの利用も想定されているとの事であった。13

だから、これを使って二段階認証をLINENotifyを用いてできないか模索を始めた。

ChallengeResponseAuthenticationとGoogle Authenticator14を組み合わせて使っている事例を多く発見した。

今回はGoogle Authenticatorを使う事なく、ChallengeResponseAuthenticationとLINENotifyまた、PAMなどを用いてLINEグループへワンタイムパスワードを送信して、二段階認証を実現したい。

というのも、Google Authenticatorを使うにはアプリケーションをインストールしてもらわなければならないので、今回のLINE Notifyを使って、ユーザが手軽に二段階認証できるという達成目標に反している。

ただ、Google Authenticatorを使わない方法はどこを探しても見つからず、まして、LINE Notifyを使った事例はだいぶ探したのだが見つけられなかったので、新規性という面ではこのお話も少しは参考になるかもしれない。

まず、PasswordAuthenticationとChallengeResponseAuthenticationの違いについて見ていかなければならない。

結論を言えば15


  • PasswordAuthentication: RFC 4252 で定義されている “Password Authentication Method (password)” を有効にする。


  • ChallengeResponseAuthentication: RFC 4256 で定義されている “Generic Message Exchange Authentication (keyboard-interactive)” 認証を有効にする。


ということだが、もう少し詳しく見れば15


  • Password Authentication Method (password) では、クライアントがユーザ名とパスワードをサーバに送り、サーバは成功したか失敗したかを返す。


  • Generic Method Exchange Authentication (keyboard-interactive) では、まずクライアントがサーバにユーザ名を送る(パスワードは送らない)。それに対してサーバがクライアントに instruction や prompt 等を送り、クライアントが応答する。認証に必要な回数だけ、サーバはクライアントとやり取りすることが出来る。


との事である。

つまり簡単にまとめてみると、


Password Authentication Method

クライアント(ユーザ)がユーザ名、パスワードをセットでサーバ送信。



その値によって判断する。



Generic Method Exchange Authentication

クライアント(ユーザ)がサーバにユーザ名送信。

↓ ←----------------------------------                     

サーバがクライアント(ユーザ)にその他の認証要素を必要な回数だけLISTEN。

↓ ----------------------------------↑

複数の認証要素によって判断する。


という事である。

ちなみにこれを有効にするのは簡単で、sshd_configの設定を書き換えるだけで良い。

今回は公開鍵認証とチャレンジレスポンス認証を使いたい。

手元にRaspberry Piがないので、環境は異なるが、ほぼ同じだと考える。


ここからの検証環境: Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-45-generic i686)


sshd_config


PubkeyAuthentication yes

#~Omission~

MaxSessions 2

#~Omission~

ChallengeResponseAuthentication yes
PasswordAuthentication no

#~Omission~

UsePAM yes

#~Omission~

AuthenticationMethods publickey,keyboard-interactive #Last line



$


$ sudo service sshd restart


UsePAMとの関係14

UsePAM が yes になっている時、PasswordAuthentication も ChallengeResponseAuthentication も PAMを使って認証します。

UsePAM が no の時、PasswordAuthentication は /etc/shadow を読んで認証するはずですが、

error: Could not get shadow information for fugahoge

と言って認証に失敗してますね…

ちなみに、ChallengeResponseAuthentication は PAM を使って認証するという前提の実装になっているので、UsePAM が no だとこれまた認証に失敗します。

UsePAM を no にする必要はないと思いますが、念の為。



上記から、UsePAMもyesに設定する。

次にワンタイムパスワードの発行ですが、私は暗号のエキスパートではないので、ライブラリを用いたい。

OATH規格があり、Debian系のOSにはlibpam-oathというのがあるのでそれを利用する。1216


$

$ sudo apt install libpam-oath oathtool


ここでPAMについておさらいだが、Linuxで認証、認可時 (*.so) を再利用し、認証 (auth)、アカウント管理 (account)、セッション管理 (session)、パスワード管理(password)ができる。1112

上記から、authの設定を/etc/pam.d/sshdに追加する。


/etc/pam.d/sshd

# PAM configuration for the Secure Shell service

# Standard Un*x authentication.
@include common-auth

auth required pam_oath.so usersfile=/etc/users.oath window=30 # Add this line!

# Disallow non-root logins when /etc/nologin exists.
account required pam_nologin.so


また、OATH用にユーザファイルに情報を加えなければならないので、/etc/users.oathとしている。

上述しているが、opensslを使った67

ここでは30桁にしたかったので、以下のようにパイプ処理した。


openssl(yourcode)

$ openssl rand -base64 100 | md5sum | fold -w 30 | head -1

<yourcode>


name

$ hostname

<name>

/etc/users.oath に記述

パーミッションも設定する事。


$

$ sudo chmod go-rw /etc/users.oath


:/etc/users.oath

HOTP/T30/6 <name> - <yourcode>

TOTPは時間ベース認証。

ツールで時間ベース認証の種を生成。


oathtool

$ oathtool --totp -v <yourcode>

Hex secret: ~Omission~
Base32 secret: ~Omission~

Base32 secretをgoogle-authenticatorでは「キー」として使う。1213

今回はgoogle-authenticatorを使わないので、以下のように直接生成する。


$

$ oathtool --totp <yourcode>


この段階で、

$ sudo service ssh restart

新規にプロンプトを立ち上げて、でログインをしてみると、以下のようになる。

ここで気をつけなければならないのが、このままログアウトしてしまうと、時間ベース認証で、送信する部分を作っていないので、ログインできなくなってしまう。

私は、ログインできなくなってしまい、再度、仮想環境を立ち上げなければならなくなってしまった。

上図はexitを打ってしまった様子だ。

くれぐれも本番環境で試さず、仮想環境で作り込んでからのデプロイをオススメする。

次に、送信部分だが、今回、SSHログイン前にサーバ側がアクセスを検知してワンタイムパスワードを飛ばさねばならない。

これに関しては、ほとんど情報がなくて苦労した。

SSHログイン後に通知するのであれば、前述のprofileを使ったり、sshrcを使ったり、pam_execを用いれば良いのだが、これでは、ワンタイムパスワードを送る前に、認証を突破せねばならないので、無理であった。

いくつか、実験をして、swatchを使ったり、ループで送りっぱなしにしたり、数多くの失敗を繰り返したがここでは割愛する。

最終的には、SSHアクセスがあったかどうか、SSHのポート(だいたい22番ポートであろうか)を監視する常駐型プログラムを作らねばならなかった。

試行錯誤していたが、結局まとめてShellScriptにすることができた。


LINENotifyOATH.sh

#!/bin/sh

ST=0
while :
do
var=`date "+%S"`
sshnode=`lsof -i:22 | grep ssh | awk '{print $9}' | wc -l`
if [ $var -gt 30 ]; then
rt=`expr 60 - $var`
else
rt=`expr 30 - $var`
fi
code=`oathtool --totp <yourcode>`
if [ $sshnode = 0 ]; then
echo none.
ST=0
elif [ $sshnode = 1 ] && [ $ST = 0 ]; then
echo ok1.
TOKEN=<LINE Notify access token.>
MSG="node1:${code}(残り:${rt}秒)"
curl -X POST -H "Authorization: Bearer ${TOKEN}" -F "message=${MSG}" https://notify-api.line.me/api/notify
ST=1
elif [ $sshnode = 2 ] && [ $ST = 1 ]; then
echo ok2.
TOKEN=<LINE Notify access token.>
MSG="node2:${code}(残り:${rt}秒)"
curl -X POST -H "Authorization: Bearer ${TOKEN}" -F "message=${MSG}" https://notify-api.line.me/api/notify
ST=0
else
:
fi
done


上記を用いてサーバに常駐させれば良い。

常駐化に関しては色々やり方があると思うが、私はcronで10分毎にリセットする方法をとった。


cron

*/9 * * * * sleep 50;sh /killpointer/pam_path.sh

*/10 * * * * sh /etc/profile.d/LINENotifyOATH.sh

またこのままでは、複数のプロセスが動いたままであるので、以前作ったkillpointerを使って狙ってプロセスを落とすようにしている。

killpointerに興味があればGithubに上げているので、参照してもらえれば幸いである。

https://github.com/Iovesophy/killpointer

普通にデーモン化するなら以下の要領で、


デーモン化

$ sudo mv LINENotifyOATH.sh /etc/init.d 

$ sudo chmod 755 /etc/init.d/LINENotifyOATH.sh
$ cd /etc/init.d
$ ln ../init.d/LINENotifyOATH.sh S10LINENotifyOATH.sh

以上でOSを再起動させると LINENotifyOATH.sh がデーモンとして動いているはずである。

ちなみに、本体を解説すると、

$ sshnode=`lsof -i:<port for example 22> | grep ssh | awk '{print $9}' | wc -l`

でSSHで接続要求している台数をみることができる。

この数値がゼロであれば要求なし、ということで、ワンタイムパスワードを送信しないようにできる。

また、上述しているが、SSHセッションを二つまでにしたいので二つ以上の要求があってもワンタイムパスワードを送信しないようにしている。

今回は同一のRaspbianのPiユーザを共有して使うので台数の情報しか必要ないが、書き方によっては、どのユーザに接続要求しているのかということも検知することができる。

また、試行錯誤の結果、ちょうど時刻がXX:XX:30、XX:XX:XX:00になると認証が切り替わるので、その点を考慮して、残り時間も送信することができる。


RT

var=`date "+%S"`

if [ $var -gt 30 ]; then
rt=`expr 60 - $var`
else
rt=`expr 30 - $var`
fi

あとは、ログイン後にpam_execやsshrcなどを使って上述のコードを実行させてあげれば良い。

やはり、普段から使っているLINEに通知がくると大変便利である。


GoogleAuthenticatorも併用可能

2019/02/18追記

LINEを使った二段階認証もできたが、GoogleAuthenticatorを使った二段階認証もどちらも使うことができるので、より多くのユーザに合わせることができる。

今までの作業に加えてあとやることは簡単で、先ほど生成した、oathtoolのBase32 secretのコードを使えばいいだけのことである。


$

$ oathtool --totp -v <yourcode>

Hex secret: ~Omission~
Base32 secret: ~Omission~

これで表示されたBase32 secretキーを手入力でGoogleAuthenticatorに読み込めば完了。

アカウント名は適当で良い。

まだまだ、至らない点も多いがとりあえず今日はここまでとする。

2019/03/06追記

LINE Notifyをcurlで使うと「Couldn't resolve host」エラーが出ていた。

このため、プログラムが中断されてしまっていた。


nohup.out

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:--
0curl: (6) Could not resolve host: notify-api.line.me


/etc/resolv.confのDNS設定によって発生していた模様。

この場合は以下のようにすれば良い。17


/etc/resolv.conf

nameserver 8.8.8.8


2019/03/13追記

上記のコード(LINENotifyOATH.sh)ではポートマッピングをしているサーバーからのssh接続要求しか見てくれないことが分かった。

ssh接続要求の検知には、


$

$ lsof | grep sshd | awk '{print $3}' | grep sshd | wc -l


でsshdの動きを見てあげる必要がある。

以下のように書き直した。


LINENotifyOATH.sh


#!/bin/sh
ST=0
while :
do
var=`date "+%S"`
sshnode=`lsof | grep sshd | awk '{print $3}' | grep sshd | wc -l`
if [ $var -gt 30 ]; then
rt=`expr 60 - $var`
else
rt=`expr 30 - $var`
fi
code=`oathtool --totp <yourcode>`
if [ $sshnode = 0 ]; then
echo none.
ST=0
elif [ $sshnode = 4 ] && [ $ST = 0 ]; then
echo ok1.
TOKEN=<LINE Notify access token.>
MSG="node1:${code}(残り:${rt}秒)"
curl -X POST -H "Authorization: Bearer ${TOKEN}" -F "message=${MSG}" https://notify-api.line.me/api/notify
ST=1
elif [ $sshnode = 8 ] && [ $ST = 1 ]; then
echo ok2.
TOKEN=<LINE Notify access token.>
MSG="node2:${code}(残り:${rt}秒)"
curl -X POST -H "Authorization: Bearer ${TOKEN}" -F "message=${MSG}" https://notify-api.line.me/api/notify
ST=0
else
:
fi
done






  1. 「LINE Notify API Document」https://notify-bot.line.me/doc/en/ 



  2. 「.bashrc .bash_profile .profile の読み込み順」http://blog.jojo.jp/?eid=1013034 



  3. 「Can I ssh into my account without invoking .profile?」https://askubuntu.com/questions/63741/can-i-ssh-into-my-account-without-invoking-profile 



  4. 「シグナルと trap コマンド」 https://shellscript.sunone.me/signal_and_trap.html 



  5. 「Ctrl+Cとkill -SIGINTの違いからLinuxプロセスグループを理解する」http://equj65.net/tech/linuxprocessgroup/ 



  6. 「opensslコマンドの使い方」 https://qiita.com/hana_shin/items/6d9de0847a06d8ee95cc 



  7. 「OpenSSL」https://wiki.gsi.de/foswiki/bin/view/Linux/OpenSSL 



  8. 「ファイルを一行ずつ処理する方法」http://sweng.web.fc2.com/ja/program/bash/read-each-line.html 



  9. 「bashのwhileループ内の変数をループ外で使う」http://iwsttty.hatenablog.com/entry/2015/01/31/183525 



  10. 「シェルでパスワードとかの入力を非表示にする。」http://d.hatena.ne.jp/slayer845/20100210/1265734761 



  11. 「PAM を理解し、構成する」 https://www.ibm.com/developerworks/jp/linux/library/l-pam/index.html 



  12. 「小ネタ: 2段階認証でssh」 https://qiita.com/amedama/items/c0ae17a5bc33657e492d 



  13. 「[Linux] SSHの認証でワンタイムパスワードを使う(導入編)」 http://muramasa64.fprog.org/diary/?date=20140912 



  14. 「google-authenticator」 https://itunes.apple.com/jp/app/google-authenticator/id388497605?mt=8 



  15. 「OpenSSH の ChallengeResponseAuthentication と PasswordAuthentication」 http://lovepeers.org/2014/06/14/openssh-challengeresponseauthentication/ 



  16. 「Two-factor time based (TOTP) SSH authentication with pam_oath and Google Authenticator」 http://spod.cx/blog/two-factor-ssh-auth-with-pam_oath-google-authenticator.shtml 



  17. 『curlで「Couldn't resolve host」エラーが出た場合の対処』https://qiita.com/bz0/items/7d4bac34c6cdada59b94