小ネタ: 2段階認証でssh

Debian系でGoogle Authenticatorを使って2段階認証をする方法についてのメモ。間違ってたらTwitterかFacebookで教えて

OATH (OAuthじゃない) という規格があり、Debian系のOSにはlibpam-oathというのがあるので、それを使えば良い

Debian系 (環境は2013-10-24時点のUbuntu 13.10) を元に書く

http://spod.cx/blog/two-factor-ssh-auth-with-pam_oath-google-authenticator.shtml

そもそもPAMというのはLinuxマシンで認証(てか認可も？)が必要なときにモジュール (.so) を再利用して認証そのもの、パスワードの強度検証、認証後の後処理 (loginuidを設定するとか) を出来るようにするもん。

http://www.linux-pam.org/Linux-PAM-html/Linux-PAM_SAG.html

例えば「ローカル(/etc/passwd, もしくは/etc/shadow) に保存されたパスワードで認証するかLDAPで認証するか、といったルールを/etc/pam.confとか/etc/pam.d/ 等で設定する。これで、ローカルでログインしたときにもsshしたときにも似たようなことを独立したC言語とかで書く必要はなく、pam_unix.soとかpam_ldap.soを個別に呼びだせば良いようになってる。

例えばこんなん 

```
auth    [success=2 default=ignore]      pam_unix.so nullok_secure
auth    [success=1 default=ignore]      pam_ldap.so minimum_uid=1000 use_first_pass
auth    requisite                       pam_deny.so
```
   
これは「ローカル認証失敗してLDAP認証も失敗したら、そのユーザはダメ」という感じになる。
気になるかもしれんので書いとくと、success=2は「ここでうまく行ったら、この行の下2行文の設定は無視」という意味。すげぇ

でま、/etc/pam.d/common-auth (正確にはそれの自動生成元である /usr/share/pam-configs/ 配下) に色々書くのは今回はさけ、単純に ssh するときだけ2段階認証を試すことにした。

読んでる人は、よほどPAMやsshd_configとかに熟知しない限り、本番環境では試さないことだ。

```
# apt-get install libpam-oath oathtool
```

試した環境では公開鍵認証を使ってお手軽ログインしてたので、それを殺す。

```
PubkeyAuthentication no
ChallengeResponseAuthentication yes
PasswordAuthentication no
UsePAM yes
```

(今回の環境ではUsePAMはyesだった)

次にPAMの設定を書く。ここは何をしたいかによるが、今回は/etc/pam.d/sshdに1行足す。

```
# 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

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

/etc/pam.d/common-auth の元になる /usr/share/pam-configs/ 配下に oath とかそういうのを追加してpam-auth-updateを回すとsuやsudoやloginやら「全部に」影響を及ぼしたりするので、そこらへんはPAMを良く分かってる人がやってね。

で、鍵を作る。これは色々方法があると思うが 

```
# head -10 /dev/urandom | md5sum | cut -b 1-30
15ad027b56c81672214f4659ffb432
```

/etc/users.oath に上記のも含めて記述

```
HOTP/T30/6  yourusername    -   15ad027b56c81672214f4659ffb432
```

パーミッションを設定

```
# chmod go-rw /etc/users.oath
```

ツールでGoogle Authenticatorにぶち込む種を作る

```
#  oathtool --totp -v 15ad027b56c81672214f4659ffb432
Hex secret: 15ad027b56c81672214f4659ffb432
Base32 secret: CWWQE62WZALHEIKPIZM77NBS
..
```

TOTPは時間ベース認証。

ながーいBase32をGoogle Authenticatorの「キー」に入れる。

![2013-10-24 20.59.46.png](https://qiita-image-store.s3.amazonaws.com/0/14365/923f0de0-1335-f630-f68b-ae1c7ef823d2.png)

```
# service ssh restart
```

うまくいくと

```
> ssh host.example.com
Password:  (パスワードを入れる
One-time password (OATH) for `amedama': (OTPを入れる)
```

てな感じで2段階認証になる。

参照先の記事で

```
auth [success=1 default=ignore] pam_access.so accessfile=/etc/security/access-local.conf
auth required pam_oath.so usersfile=/etc/users.oath window=30
```

とやってるのは、要は「accessfileにOKってあったらOATHしない。でもそうでなかったらOATHする」という意味。"The Linux-PAM System Administrators' Guide"には個別の(昔の)PAMモジュールの一覧がmanっぽいのも含めて書いてあるので読むと良いよ。man pam_hogehoge も便利だ。

# 用途？

筆者は複数のLinux環境を利用している。その際、一部の環境では、公開鍵認証のみのログインとし、パスワード認証を切っている、というホストがある。

この場合、秘密鍵を持っているホストからしかその環境へは入れない。万が一鍵を失うと、通常の方法で外部から入ることは不可能になってしまう。例えばMacBookの東芝の128GBのSSDがぶっとぶとか (むしろバックアップの必要性を再認識したわけだが)

この「パスワード認証」部分を完全にdisableするのではなく、OTPを要求するようにすると、「たまに外部から入る可能性がある」ホストに対するそれなりにマイルドなセキュリティは提供できる可能性がある。万が一秘密鍵をなくしても、OTPを使えるモバイル端末があれば大丈夫。パスワードを盗まれても、まぁすぐにどうこうという問題はなくなる。

ただ、今回の例ではOTPを設定していないユーザに関する準備は全くしていない上、基本的にrootによるかなりあぶなっかしい作業が必要になる。

# ポイント

OATHという規格がまとまっているため、自分が2段階認証を使いたい環境でそのライブラリがあるかを調べれば良い、ということになる。

今回はLinuxのsshの背後で動く認証認可の仕組みをいじる必要があった。既存の仕組みであるPAMに対してOATHのモジュールを利用するよう指定した。

ちなみに、今回調べた時間のほとんど全てはOATHのことではなくPAMの仕組みだったりする。筆者はPAM自体についてはWikiに項目を設けている

https://mowa-net.jp/wiki/PAM

PAMはそれなりに丁重に扱わないとシステム自体に全くログインできなくなるので気をつけよう。

規格自体はオープンであるため、OATHのライブラリ自体を自作する可能性もありえる。ただし、暗号のエクスパートでないのならせめて暗号部分は外部ライブラリに頼ること (当然だが)。

# おまけ

CentOSだとpam_oath, oathtool が該当すると思われる。試してないけど