SSHにて公開鍵認証+TOTP

07/12

SSHの認証をMFAに

目次

経緯

最近、ランサムやらサイバー攻撃によるシステムへの侵入によって企業の個人情報が漏洩する事件を目の当たりにする。
こういうのを見ていると、こっちもなんかせんといかんかなと不安になる。
ということで、SSHを多要素認証にしてみる。
煩わしくなったらすぐやめよう。

仕組み

サーバ側にTOTPの仕組みを入れて、SSHの認証方式にそれを追加するだけ。
具体的には、Google Authenticator(以下、GA)を入れて、サーバにて生成アルゴリズムを入れて準備しておく。
スマホにもGAを入れておき、このサーバのセキュリティコードを読ませて、同じアルゴリズムでOTPを生成すると。
あとは、SSHDの認証方式の変更と、認証に使っているPAMにGA認証を追加して、振る舞いを決めてあげればよい。
LPICの勉強していれば簡単に構築できるやつ。
PAMなんて久しぶりに触るなぁ、requiredとrequisiteとsufficientとか勉強したな。
ただもう全然覚えてない。

flowchart TD
    a[SSHD]
    b[/Auth Methods\]
    c([Google Authenticator])
    d(Pubkey)
    e([Password])

    a---b
    b-.-od
    b-.-o|PAM|c
    b-.-x|PAM|e

    style a fill:#F79D28,color:#666
    style b fill:#996
    style c fill:#bbb,color:#555
    style e fill:#2893F7
    style d fill:#914D91

要件

基本的に私しか使わないので、MFAをグローバルな設定にしてもよいが、何かの拍子でログイン出来なくなるのを避けるために、内部アクセスは通常通り公開鍵認証とする。

必要なもの

構築

さて、構築していく。
まずはスマホに上のGAは入れておいて、セットアップしておく。

Google Authenticator

さて、次にサーバにGAを入れる。
ここではdebian 11での例。
以下のコマンドで導入。

$ sudo apt install libpam-google-authenticator

続いて、個人ユーザ(ここでは私)のセットアップ。

$ google-authenticator

Do you want authentication tokens to be time-based (y/n) y
Warning: pasting the following URL into your browser exposes the OTP secret to Google:
  https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/me@hostname%3Fsecret%3DXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%26issuer%3Dhostname
[                                     ]
[                                     ]
[                                     ]
[                                     ]
[                                     ]
[                                     ]
[                                     ]
[     ここにQRコードが表示される      ]
[                                     ]
[                                     ]
[                                     ]
[                                     ]
[                                     ]
[                                     ]
[                                     ]
[                                     ]
Your new secret key is: XXXXXXXXXXXXXXXXXXX
Enter code from app (-1 to skip): 123456
Code confirmed
Your emergency scratch codes are:
  12345678
  91234567
  89123456
  78912345
  67891234
Do you want me to update your "/home/me/.google_authenticator" file? (y/n) 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) y  … 1

By default, a new token is generated every 30 seconds by the mobile app. 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. This allows for a time skew of up to 30 seconds between authentication server and client. If you experience problems with poor time synchronization, you can increase the window from its default size of 3 permitted codes (one previous code, the current code, the next code) to 17 permitted codes (the 8 previous codes, the current code, and the 8 next codes). This will permit for a time skew of up to 4 minutes between client and server. Do you want to do so? (y/n) y  … 2

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  … 3

どデカいQRコードが表示されるので、スマホのGAにて読み取る。
すると、GAのTOTPの一覧に新しく現れるので、そこに表示されるOTPを入力すると認証されるので、バックアップコードが表示されるはず。
そのあとのインストラクションのことを説明。

  1. 同じ認証トークン(OTP)を何回も利用することを拒否するかを聞いている。この制限により30秒ごとに1度しか使うことが許されなくなる。ただ、セキュリティは格段に上がるので、これはYesにしたほうが良いと思う。
  2. 既定では認証トークンは30秒ごと生成されると。クライアント・サーバ間の時間のずれの可能性を補うため、現在のトークンだけではなくその前後1つも許可することによって最大30秒のずれが許容されると。既定ではね。これを前後8つまで許容するかと聞いている。前後最大4分が許容される仕組み。これはちょっとどうなんだろう、結構寛大な設定に感じる。Noにすると、既定。ただここでは一旦Yesにして様子をみることに。
  3. ログイン試行回数を30秒毎に3回までに制限される。これはYesでよい。

で、Secret Keyとバックアップコード、この3つの設定は$HOME/.google_authenticatorに保存される。

SSHD

さて次に、SSHDの設定を変更する。
基本は公開鍵認証でログインしているので、PAMの有効化とキーボードインタラクティブの設定を入れていく。
LAN内のログインでは公開鍵認証のみにする。

$ sudo vi /etc/ssh/sshd_config
PAM yes
ChallengeResponseAuthentication yes

AuthenticationMethods publickey,keyboard-interactive
Match Address 192.168.0.0/16, 127.0.0.0/8
    AuthenticationMethods publickey
Match all
$ sudo systemctl restart ssh
$ 

PAM

次に、sshのPAM認証部分を設定していく。
ちなみに、requiredは必ず成功を要求する条件で処理を後ろに流す。
requisiteは必ず成功を要求する条件で失敗したらその場で処理を打ち切る。
sufficientは成功していると他のrequiredが失敗していない限り成功という結果になりそれ以降は処理されない。
LPICで勉強したやつ。懐かしい。
設定の仕方としては、GAの認証部分のみを切り出して別ファイルにしてSSHDの認証部分に置き換える。
今回はもともとのUnix認証部分を思い切って止めて、GAに置き換えてみる。

$ sudo vi /etc/pam.d/google-auth    #ファイルを新規作成
auth        required      pam_env.so
auth        sufficient    pam_google_authenticator.so echo_verification_code
auth        requisite     pam_succeed_if.so uid >= 1000 quiet
auth        required      pam_deny.so
$ sudo vi /etc/pam.d/sshd           #該当箇所をコメントアウトして新規追加
#@include common-auth
@include google-auth
$ 

テスト

さて準備ができているはずなので、試してみる。
クライアントはWindowsであればWSL、MacBookならTerminal、Linuxはまぁ、Gnome-Terminalかな。

$ ssh hostname
(me@hostname) Verification code: 649819
Linux hostname XXXXXXXXX-amd64 #1 SMP Debian XXXXXXXXXXXXXXXX x86_64
Welcome to the fantasy world
You have no mail.
Last login: XXXXXXXXXXXXXXXXXXXXXXXXXX
$ 

なんかできてる。よいのでは。
細かい設定が必要になった場合は、もっと頭悩ませることになるのかも。


コメント: