序
久しぶりに刺激的なタイトルの記事ができた。
世の中では、sshd_configでrootログインを禁止し、sudoを使うのが良いとされている。
が、私は思うのだ。
本当にそれ、安全か?
sudoへの理解を深める
まずそもそもsuではなくsudoが良いとされる理由だが、主に2つ。
rootでの実行がログされるrootのシェルプロセスを発生させない
が、考えてみてほしい。
まず、sshは認証がログに残る。 実行もログに残すことができる。
シェルプロセスに関しては、限られたコマンドだけで生活できるならそうだが、サーバー運営ではそうはいかない。 結局シェルが必要だ。
でも
sudo zsh -lするくらいなら最初からrootシェル取ればいいだろう。
SSHはシェル取るだけじゃなくコマンド実行もできるのだし。
しかもそれが
foo ALL=(ALL:ALL) ALL
みたいなことをしてるのなら「シェルを取らなくていい」とか何を言っているんだという感じだ。
認証強度
まず間違いなく、人間が打てるレベルのパスワードよりも公開鍵認証のほうが、認証強度は段違いに高い。 というか、SSHの公開鍵認証は認証の中でも屈指のアルゴリズムだと言って良い。
だいたい、サーバーのユーザーパスワード、そんなに強固なものにしているのか? 128bitですら16文字だぞ。しかも、パスワードは基本的にASCII文字だけだから128bit同士でも強度が全然違う。
さらに秘密鍵にパスワードをかけているなら、これだけで多要素認証になっている。 というか、シェルが取れる鍵にパスワードかけていない人なんていないはずだ。 特に意識の高いChienomi読者なら当然!!!!
実際、sshdに対してはパスワード認証を無効化して公開鍵認証だけにしろと世の中では言われているではないか。 なのになぜroot権限を行使するときはパスワード認証で良いと考えるのか。
しかも、sudoで使うのはユーザーパスワードである。 パスワードログインができればそのままrootが行使できる。 これはよわよわセキュリティではないか。
そもそもの話、パスワード認証というのはオンライン攻撃だけでなくオフライン攻撃にも弱い。 見られたり聞かれたりで人間側を狙うことで結構突破できてしまったりするのだ。
だから、「人の目や耳があるところでパスワードを打たない」自体がセキュリティとして効果がある。 MicrosoftがPINをやりはじめたのはそれが理由のはずなのだが、Windows 11ではPINをパスワードとほぼ等価のものとして扱いはじめているからよくわからない。 PINを盗めば好き放題ではないか。
脱線した。
とにかく、「パスワードを打たない」はそれ自体が防御になるし、パスワードを使うものはパスワードだけでは突破されないようにしたいものだ。
前提の設定
rootには人間には入力不可能なレベルのパスワードを設定しておくこと。
ただし、ASCIIのみにしておいたほうがいい。コンソールからも入ることが不可能になると非常に辛い。
rootのパスワードを使うのは、本当に緊急事態のときだけだ。
もちろん、ユーザーにも強固なパスワードを設定しておくこと。 これは普通に十分にセキュアなパスワードであればそれで良い。
sshdを設定する
まず第一に当たり前の話だが、認証に使う一般ユーザーとrootユーザーの両方に、それぞれ異なる公開鍵を登録しておくこと。 そして、その秘密鍵にはパスワードをかけること。
当たり前だけれど、RSA鍵なんか使わないこと。
# vps-user_ed25519.pubをユーザーのauthorized_keysへ
ssh-keygen -t ed25519 -f ~/.ssh/keys/vps-user_ed25519
# vps-root_ed25519.pubをrootのauthorized_keysへ
ssh-keygen -t ed25519 -f ~/.ssh/keys/vps-root_ed25519さて、じゃあ/etc/ssh/sshd_config.d/以下にそれ用の設定を書いていこう。
私はこんな感じにしている。
PasswordAuthentication no
AddressFamily inet
Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
AuthenticationMethods publickey
PermitRootLogin no
Match Host 127.0.0.1
PermitRootLogin prohibit-password
DenyUsersもあるんだけど、そこは伏せる。
これで外からのrootログインは付加、localhostのみ可となる。
AddressFamily inetしていないと制御が面倒になるけど、インターネット側からIPv6で接続したい要求があるならがんばれ。
一応説明も入れよう。
PasswordAuthenticationは機能しないのでなくてもいい。
AddressFamilyは設定を楽にするためにIPv4限定。
CiphersはAES限定にすることでAES-NIが効いて軽くなる。デフォルトでは優先がAESじゃないのでAES-NI使ってくれない。
128bitはドロップしてもいいと思う。
AuthenticationMethods超重要。公開鍵認証以外を受け入れない。
PermitRootLogin no
はrootログインを禁止する。
Match Host 127.0.0.1
localhost接続についての記述。
PermitRootLogin prohibit-password
rootはパスワード認証禁止。
localhostはデフォルトでパスワードでも入れてしまうので、書いたほうが良い。
これでsshdをリロード。
sshを設定する
まず、ユーザーへの接続。
Host vps
HostName example.com
User john
Port 22
IdentityFile ~/.ssh/keys/vps-user_ed25519
IdentitiesOnly yes
各種値は環境に合わせること。 続いてrootの設定。
Host vps-root
HostName localhost
Port 22
IdentityFile ~/.ssh/keys/vps-root_ed25519
IdentitiesOnly yes
ProxyJump vps-user
ポートは環境に合わせること。
HostNameはlocalhostで良い。vps-userの時点で対象ホストに入っており、そこから見るとrootで入ろうとしているのはlocalhostだからだ。
これにより、まずサーバーに対して一般ユーザーで公開鍵認証でログインすることができる。 これはごく普通だろう。 外からSSHでログインするには公開鍵認証が必須。つまり、手元にある秘密鍵を入手し、なおかつパスワードも解き明かす必要がある。
外からはSSHでrootにログインすることはできない。
ログインするためには当該ホストにログインしている必要がある。
この前提を満たすには一般ユーザーでログインすれば良い。
ProxyJumpを用いているため、rootでログインする際には一般ユーザーでログインした経路を通ってセッションを確立する。
もし仮にサーバーの一般ユーザーとして不正にログインされたとしても、サーバー上にはrootで認証できる鍵がないのでSSH経由で不正にrootにログインすることはできない。
sudoersから外す
この記事の目的はセキュリティを犠牲に利便性を上げることではなく、セキュリティを強化することなので、SSHでrootログインできることを確認したらvisudoしてsudoersから一般ユーザーを外そう。
もし運用上コマンド単発でsudoしたいものがあるのであれば、横着してALLせずにコマンドを明示する。
エージェント
エージェントを使うと少しセキュリティレベルは落ちるが、それでもsudoを使うよりもずっと強固なので、PC側ではエージェントに鍵を追加してもいいと思う。
パスワードを何度も打つ必要がなくなり、利便性も上がる。