シリアルコンソールでのログイン
シリアルコンソールでのログインを有効にした。ttyS0を非常用のポートとして確立することができた。
総当りの懸念
物理的にサーバーを強奪・押収されたとき、シリアルコンソール接続のメンテナンス専用機の乗っ取りなどで、「総当たり」で攻撃をできてしまう。
物理的に取られてしまうと、sshやシリアルコンソールに対して、expect
などのコマンドを用いて無限にパスワードの総当りを許すことになる。
sshであればファイアウォールでパケット制限を掛ける事もできる。それを手軽に実現するfail2banのようなソフトウェアもある。fail2banはIPアドレスをBANするものでありシリアルコンソールでは動かない。
シリアルコンソールで総当り対策を入れるのはどうすればいいのか。
ロックアウト機構を入れる。
総当りに対する対策は、「ロックアウト」が挙げられる。真っ先に使われるのはロックアウトである。
iphone のパスコードや、キャッシュカードの暗証番号など、数回間違うとしばらく入力不可能にする。など、あれと同じことをLinuxできたらいいなと。
Linuxのログインの仕組みに処理を挟むPAMという機構があり、そのプラグインとしてfaillockが用意されている。
Debian/11で faillock を使う
debian/11 以降では CentOS/Fedoraと同じ PAM faillock を使う。10までは tally だった。11/bullseyes以降は faillockである。
参考資料に書いてあッた。
failock 概要
failock は PAM のプラグインとして動作する。ログインの失敗が閾値(失敗回数/時間)を超えると、ユーザをロックアウトする。
変更するファイルと注意点
作業前の注意
PAMファイルは、設定を間違えると大変。新規ログインが不可になる。ログイン不可になって詰む。
PAMミスによるログイン不可は、sudo も然りである。設定ミスはsudoすも使えなくなる。なので細心の注意が要求される。
作業前にバックアップ
/etc/pam.d バックアップをとっておく。もしくは仮想マシンで十分に検証する。
sudo 済みのセッションを開けておく
sudo 不可になって慌てないために、root にパスワードを設定してrootユーザーでログインできるようにしておく。
また、ログイン不可になっても、認証確立済みのログイン済みセッション(SSHログイン済み)は有効なので、ログイン済みセッションをターミナルに作っておく
緊急メンテ用のOSがあるといい
ログイン不可になって慌てないために、USBメモリにUbuntu、LiveUSBから起動してマウントして、LUKSパスワードを入力し、バックアップからPAMのミスを書き戻す。。そのための緊急マウント手段を確保しておく
事前の準備。
作業前の、安全な退路の確保をする。
- /etc/pam.d をバックアップとっておく
- root にパスワードを一時付与している
- ログイン済み(sudo su or root)の セッションを開けている。
- USBメモリで緊急ブートOSを用意している。
PAMの設定ミスは即死なので注意すること。
設定ファイル
対象になる設定ファイルは次の3つ
- /etc/pam.d/common-auth
- /etc/pam.d/common-account
- /etc/security/faillock.conf
このうち、pam.d が PAM の設定で、faillock.conf は pamから呼ばれたときの設定である。
/etc/pam.d/common-auth
設定は、次のとおりに変えた。
common-auth / before
root@d01:~# cat /etc/pam.d/common-auth | grep -vE '^#' auth [success=1 default=ignore] pam_unix.so nullok auth requisite pam_deny.so auth required pam_permit.so
common-auth / after
root@d01:~# cat /etc/pam.d/common-auth | grep -vE '^#' auth required pam_faillock.so preauth auth [success=2 default=ignore] pam_unix.so nullok auth required pam_faillock.so authfail auth requisite pam_deny.so auth required pam_permit.so
/etc/pam.d/common-account
debian は enterpriseを特に意識してないので、common-account にすべてをまとめている。
/etc/pam.d/common-account | Before
account [success=1 new_authtok_reqd=done default=ignore] pam_unix.so account requisite pam_deny.so account required pam_permit.so
/etc/pam.d/common-account / After
アカウントに処理で、各アカウントで unix ログインの手前で、failock を使ってロック状態を操作する処理を挟む。
account required pam_faillock.so account [success=1 new_authtok_reqd=done default=ignore] pam_unix.so account requisite pam_deny.so account required pam_permit.so
設定ポイント
1ファイル目common-auth 初期設定は、次のようになっている。
1 auth [success=1 default=ignore] pam_unix.so nullok 2 auth requisite pam_deny.so 3 auth required pam_permit.so
1行目 auth はpam_unix へ流し、成功したら次の1行をスキップ、失敗したら順番に実行で次行へ。
2行目 auth で ここに来たら pam_deny に流してその後をスキップ
3行目 auth でここに来たら、pam_permit に流しておわり
ここに処理を挟む。
挟んだ処理は2行。ただし、success時にスキップ先が変わるため、変更箇所は3点である。
1 auth required pam_faillock.so preauth 2 auth [success=2 default=ignore] pam_unix.so nullok 3 auth required pam_faillock.so authfail 4 auth requisite pam_deny.so 5 auth required pam_permit.so
1 行目 pam_faillock の preauth 処理をする(現在のロック状態ですね)
2 行目 pam_unix に流す、成功したら2行スキップする(5行目に行く)
3 行目 faillock に失敗を流す(記録ですね)
4 行目 pam_deny に処理を流す。(deny = 拒否です)
5 行目 pam_permit に処理を流す。 ( permit = 許可です)
2ファイル目 /etc/pam.d/common-account の設定は次のようになった。
account required pam_faillock.so account [success=1 new_authtok_reqd=done default=ignore] pam_unix.so
pam_unix に流す前に、failock を起動してアカウントの状態を保存する。
もう一つの別の方法
参考資料によると、common-accunt
を編集せずに、common-auth
だけで済ませることも出来る。
今回、使ったのは、次の組み合わせである。
auth pam_faillock.so preauth auth pam_faillock.so authfail account pam_faillock.so
それとは、別に、次の組み合わせで使うことも出来る。
auth pam_faillock.so preauth auth pam_faillock.so authfail auth pam_faillock.so authsucc
こちらは、common-auth だけで完結する。
実際にロックアウトされてみる
ログイン画面をだして、実際にロックアウトを試す。
デフォルト設定では、3回のログイン失敗でログインから締め出される(ロックアウト)状態になる。
再起動すればロックアウトは解除される(失敗回数はリセットされる)
ロックアウトの解除
締め出されたユーザーに再びログインを可能にするには、専用のコマンドで失敗記録をリセットする。
faillock --dir /var/log/faillock --user takuya --reset
ロックアウトの解除の別の方法
ロックアウト解除は、デフォルトで /var/run/faillock/takuya
ファイルに記載された失敗回数と時刻を元に算出される。そのためファイルを消せば失敗記録を消すことが出来る。
また、そのファイルは/var/run
に存在する。再起動で消失する揮発性記録である。
再起動後にログインが出来るのが便利と考えるか、ゆるい設定と考えるかは利用者次第だと思う。再起動後もロック継続する方法もある(dir設定)
失敗回数の確認
失敗回数の確認もリセット同じコマンドで行う
faillog --user takuya
ファイルを見ることで、失敗回数がわかる。
失敗と判定される条件
失敗回数/時間
で判定される。初期値では失敗頻度は、3回/15分間に設定されている。
15分以上前に失敗したものは、期限切れで失敗とみなされない。総当り対策なので、単位時間あたりのログイン失敗件数を重視するようです。
ログインの失敗記録は、記録上限回数分だけ保存される。 上限回数を超えた分は、ログがローテーションされる。
失敗件数を faillog で確認してたときの表示例
root@d01:~# faillock --user takuya takuya: When Type Source Valid 2022-02-05 20:39:42 RHOST I 2022-02-05 20:39:47 RHOST I 2022-02-05 21:14:51 RHOST V
記録されたログは、その失敗時刻を評価される。
デフォルトのfail_interval = 900
に設定している場合、failog の中から、900sec(15min) の失敗分をカウントする。
表示例をもう一度確認する。
When | Type | Source | Valid |
---|---|---|---|
2022-02-05 20:39:42 | RHOST | I | |
2022-02-05 20:39:47 | RHOST | I | |
2022-02-05 21:14:51 | RHOST | V |
Valid と Invalid であろうと思われる I / V
があるのがわかる。
これについて時間を変えて調べてみたところ、fail_interval に含まれる失敗ログが V 、失敗があるが、fail_intervalを超過したのでノーカウントになったのが I
で記載されるようです。
なので、例に出てきた。 I/V
は次のようにtrue/false
になっていると思われる。
When | Valid | 経過時間 | fail_interval内か? |
---|---|---|---|
2022-02-05 21:14:42 | I | 16min | false |
2022-02-05 21:14:47 | I | 16min | false |
2022-02-05 21:39:51 | V | 1min | true |
設定ファイル/etc/security/faillock.conf、閾値の確認
閾値の設定は、pam に引数として記載することも可能であるが、専用の設定ファイルを使ったほうが簡単だと思われる。
設定ファイルはdebianの場合 /etc/security/faillock.conf
に設置されていた。
重要な設定は、unlock_timeとfail_intervalだと思います。
fail_interval=900
が初期値で fail_interval間に何度失敗したら、それ以上の試行を止めるか、unlock_time=600
が初期値で、何分間ロックアウトするか。である。
この設定がややこしいのであるが、fail_intervalは、現在時刻から何分前までの失敗を、有効な失敗(カウント対象)とするかであり、3回失敗してても16分前であればカウント対象外になる
問題点・懸念点
sudo 失敗時にも記録される。
sudo でパスワードが訊かれる際も PAMを経由するので、sudoで失敗もカウントされるので注意が必要である。sudoersをうまく設定しておきたい。
閾値の設定と、期限の設定
unlock_timeは、何分間締め出すか、ユーザーにパスワードを入力させない時間を作るものである。この値をunlock_time=0
にすると失敗件数が一定に達するとユーザは二度とログインできなくなる。resetで解除するまでパスワードを受け付けない。
たとえば、fail_interval=60 でunlock_time=120
ならば、1分間に3回失敗すると次にログイン出来るのは、最後の失敗から2分後である。
たとえば、fail_interval=300 でunlock_time=600
ならば、1分間に3回失敗すると次にログイン出来るのは、最後の失敗から10分後である。この場合は、600秒後に、ログインが出来るようになった時点で、fail_interval=300秒も経過しているので、失敗回数はリセットされているはずである。
たとえば、fail_interval=100 でunlock_time=10
ならば、100秒間に3回失敗すると次にログイン出来るのは、最後の失敗から10秒後である。この場合は、10秒後に、ログインが出来るようになった時点で、fail_interval=100を超過していないので、失敗回数は継続でカウント加算されるはずである。
この設定の調整は上手に使い方を考えておきたい。
パスワードの複雑性、人間の失敗傾向と、expect等による機械的ログインの速さを、これらを勘案し、上手に区別して総当りでパスワードが割り出されないバランスを見極めた調整が必要かと思う。
SSHでのロックアウト済みを表示
sshd はUsePAPM設定では、PAMに処理を流し、PAMから結果を受け取るだけなので、エラー・メッセージ(ロック済で認証に移れない場合)が「ロックされてます」を表示できない。解決策としてはChallengeResponseを使ってPAMにすべてを投げることにすればよいが、一部のSSHの機能を犠牲にすることになる( PermitRootLogin prohibit-passwd など)
SSH公開鍵でログインした場合
パスワードなしでSSH公開鍵でログインした場合、sshdがusePAMをしてfaillockに処理が映る場合、common-accountが反応して、失敗件数はすべてリセットされる。
再起動でデータが消える。
再起動でリセットされるのは、 /var/run に保存するからであり、別の場所に設定を移せば良い。たとえば、/var/log/faillock
を作りそちらに保存することで再起動後も失敗件数を継続することが出来る。ただしこの場合、失敗件数の確認とリセットのコマンドが変化するので注意が必要。
例:再起動後もロック維持するため /var/log/faillock に保存する
/etc/security/faillock.conf にディレクトリの指定をする
root@d01:~# cat /etc/security/faillock.conf | grep dir # The directory where the user files with the failure records are kept. dir = /var/log/faillock
コマンドから使うときも --dir
オプションを必ず付ける。
faillock --dir /var/log/faillock --user takuya faillock --dir /var/log/faillock --user takuya --reset
紛らわしいもの
- faillock
- faillog
- fail2ban
似たような機能をもつ、似たような名前のツールだが、全て違うもので違うレイヤで動こいてる。
failog はpam tally用コマンドだけが残っていて、実質には動かせない。
fail2ban はIPアドレスベースなのでSSHやSFTPには有効だが、シリアルコンソールなど物理的な強奪・押収には無力である。
ディスクの暗号化
failock を実施していても、物理的にHDDからデータが抜かれると無力である。そのためfaillockはLUKSのような暗号化と組み合わせない限り、効力を発揮しないと思われる。
リモートアクセスのセキュリティを考えるのであれば、 fail2banで十分であると思われる。
時刻の偽装
ロックアウトはタイムベースで行われる。ハードウェア・クロックを操作されると効果は落ちるかもしれない。
NTPを偽装されて時刻を狂わされる事もあるかもしれない。それらの場合でもハードウェアクロックを操作しながら総当りを行うのはなかなか大変であると思われる。
failockは失敗記録が時刻を過ぎても残されるので、仮に時刻を変えられたとしても前の失敗記録は残るので総当り対策をかなり防ぐことが出来る。
もし時刻が不安ならGPSモジュールを使って自分でStratum1になる手もある。そこまでしなくてもハードウェア構成に変化があれば起動しないようにTPMとUEFIでロックを掛けておけば、仮想マシンで起動されて、ハードウェア時刻をいじられる心配も無いだろう。
ログインを代替手段にする。
またパスワードでロックアウトせずとも、ユーザー鍵をTPMキーやメモリで管理で行い、その鍵を指紋などでかんたんに取り出せるようにしておけばいい。それらを簡易に使えるようにしたのが、macのTouchIDであり、WindowsのPINコード/Heloである。鍵をハードウェアと紐付けてしまえば、ストレージを強奪されても仮想マシン起動によるオンライン攻撃にさらされる危険性は減る。オフラインでストレージの暗号化キーを探すしか手段がなくなる。
まとめ
faillock 自体の設定
faillock自体の設定はファイルでやる。
dir を /var/run にすると再起動でリセットできる。(ただし再起動自体に時間がかかる)
/etc/security/faillock.conf
+ dir = /var/log/faillock + deny = 3 + fail_interval = 7200 + unlock_time = 600 + even_deny_root
failログ保存先を作る。
mkdir -p /var/log/faillock/
リセットと確認
# dir変更している場合 faillock --dir /var/log/faillock --user takuya --reset # dir 変更してないとき。 faillock --user takuya --reset
PAM設定
pam 設定は慎重にやる。設定前に認証済みセッションを用意しておく。
/etc/pam.d/common-auth
+ auth required pam_faillock.so preauth - auth [success=1 default=ignore] pam_unix.so nullok + auth [success=2 default=ignore] pam_unix.so nullok + auth required pam_faillock.so authfail auth requisite pam_deny.so auth required pam_permit.so
/etc/pam.d/common-account
+ account required pam_faillock.so account [success=1 new_authtok_reqd=done default=ignore] pam_unix.so account requisite pam_deny.so account required pam_permit.so
ssh 経由のとき
ssh がPAMを使うよう構成する
usePAM yes
ロックされたとき
次のようにログに出てくる。
tail -f /var/log/auth.log Feb 8 21:30:48 raspi-ubuntu sshd[2576]: pam_faillock(sshd:auth): Consecutive login failures for user takuya account temporarily locked
2023-05-12
debian でこの機能を入れていたが、unattended upgrades で自動更新されてしまい、common-auth が書き戻された。書き換わったためにログインができなくなっていた。
具体的には、つぎのように書き換わっていた。 このように書いた箇所が
# here are the per-package modules (the "Primary" block) auth [success=2 default=ignore] pam_unix.so nullok # here's the fallback if no module succeeds auth required pam_faillock.so authfail auth requisite pam_deny.so
このように変わっていた。
# here are the per-package modules (the "Primary" block) auth [success=1 default=ignore] pam_unix.so nullok # here's the fallback if no module succeeds auth required pam_faillock.so authfail auth requisite pam_deny.so
よりによって、 success=2
に変えている箇所がsuccess=1
に変わってしまった。
Diff/patchでも当てられたのだろうか。いつ書き換わったのか。発火時期がわからない。
非常にめんどくさい。何かいい方法はないのかしら。