作業の前提と用意するもの

この記事では、手元のPC(クライアント)からLinuxサーバーへSSHでログインする環境を想定します。サーバーには今はパスワードでログインできていて、そのユーザーがsudoで管理者権限を使えることが前提です。クライアントはLinuxかmacOSを想定しています。どちらにもOpenSSHの ssh-keygen と ssh-copy-id が通常入っています。Windows標準のOpenSSHには ssh-copy-id が含まれていません。その場合の代わりの方法は手順2で触れます。

公開鍵認証では、秘密鍵と公開鍵のペアを使います。公開鍵はサーバーに置き、秘密鍵は手元から出しません。ログインのときは、秘密鍵を持っていることをサーバーに証明します。パスワードがネットワークを流れないため、総当たり攻撃に強くなります。

作業前に知っておく3つの注意点

既存のSSHセッションを閉じずに作業する

sshd_config(SSHサーバーの設定ファイル)の変更を誤ると、新しいログインができなくなります。すでにログイン中のセッションは、設定を再読み込みしてもそのまま使えます。そこで、作業用とは別にログイン済みのセッションを1つ開いたままにしておきます。新しいログインが成功したと確認できるまでは閉じません。これが締め出し(ロックアウト)を防ぐいちばん確実な方法です。

パーミッションが緩いと鍵は黙って無視される

sshdは、サーバー側の ~/.ssh や authorized_keys を他のユーザーが書き換えられる状態だと、鍵を使わずにパスワード認証に切り替えます。画面にはエラーが出ないので、原因に気づきにくい箇所です。正しい値は ~/.ssh が700、~/.ssh/authorized_keys が600です。ホームディレクトリ自体がグループやその他のユーザーから書き込める状態でも、鍵は拒否されます。クライアント側の秘密鍵も600にしておく必要があります。緩いと ssh コマンドが鍵の読み込みを拒否します。

設定を誤ったときの戻し方

sshd_configは、編集前に必ずバックアップを取ります。問題が起きたら、開いたままのセッションでバックアップを書き戻し、設定を再読み込みすれば元に戻ります。すべてのセッションを失った場合は、SSH以外の経路で入るしかありません。VPSやクラウドなら事業者のWebコンソール、物理マシンなら直接接続したキーボードとモニターです。その経路があるかどうかも、作業前に確認しておきます。

手順1:手元のPCで鍵ペアを作る

クライアントで次のコマンドを実行します。-t ed25519 は鍵の種類の指定です。Ed25519は短くて安全性の高い方式で、現在のOpenSSHで広く使われています。-C は鍵に付けるコメントで、どの端末の鍵かを見分けるのに使います。

ssh-keygen -t ed25519 -C 'my-laptop'

保存先を聞かれたら、Enterで既定の ~/.ssh/id_ed25519 にします。同じ名前のファイルがすでにあると、上書きするか聞かれます。上書きすると古い鍵で入れていたサーバーに入れなくなるので、その場合は「n」と答えて別名を指定します。続いてパスフレーズを2回入力します。パスフレーズは秘密鍵を暗号化するための合言葉です。空にもできますが、秘密鍵のファイルが盗まれたときにそのまま使われてしまいます。設定しておくことを勧めます。

完了すると、秘密鍵 id_ed25519 と公開鍵 id_ed25519.pub ができます。サーバーに渡すのは .pub の方だけです。

手順2:ssh-copy-idで公開鍵をサーバーに登録する

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server

user と server は、自分のユーザー名とサーバーのホスト名またはIPアドレスに置き換えます。いつものパスワードを入力すると、公開鍵がサーバーの ~/.ssh/authorized_keys の末尾に追記されます。~/.ssh がなければ適切なパーミッションで作られます。この段階ではパスワード認証が必要なので、パスワード認証を無効にするのは最後にします。

ssh-copy-id がない環境では、id_ed25519.pub の中身をサーバーの ~/.ssh/authorized_keys に1行で追記します。そのうえで、前の節のパーミッションをサーバー上で設定します。

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

手順3:鍵でログインできることを確かめる

新しいターミナルを開き、パスワード認証を使わない指定でログインします。

ssh -o PasswordAuthentication=no user@server

「Enter passphrase for key」と聞かれたら、手順1のパスフレーズを入力します。「user@server's password:」と聞かれた場合は、鍵が使われていません。この指定ではパスワード認証を使わないので、鍵が通らなければ「Permission denied」で終わります。そうなったら、サーバー側のパーミッションをまず見直します。サーバーのログに「Authentication refused: bad ownership or modes」と出ていれば、パーミッションが原因です。ログは journalctl -u ssh(Red Hat系やArch Linuxでは -u sshd)で確認できます。クライアントで ssh -v を付けて実行すると、どの鍵を試したかも表示されます。

鍵でログインできたことを確認できるまで、次の手順には進みません。

手順4:sshd_configでパスワード認証を無効にする

ここから先はサーバーでの作業です。まず設定ファイルのバックアップを取ります。

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

次に、既存の設定がどこにあるかを調べます。

sudo grep -ri passwordauthentication /etc/ssh/sshd_config /etc/ssh/sshd_config.d/

sshdは、同じ項目が複数回書かれていると最初に読んだ値を使います。Ubuntu・Debian・Fedoraなど多くのディストリビューションでは、sshd_configの冒頭に Include /etc/ssh/sshd_config.d/*.conf があります。この場合、sshd_config.d内のファイルが本体より先に読まれ、ファイル名の順に処理されます。クラウドのイメージでは、ここに PasswordAuthentication yes を書いたファイルが置かれていることがあります。Includeがある環境では、先に読まれるよう /etc/ssh/sshd_config.d/00-disable-password.conf のような名前でファイルを作ります。Includeがなければ、sshd_config本体を編集します。書く内容は次の2行です。

PasswordAuthentication no
KbdInteractiveAuthentication no

2行目は、キーボード対話式認証の経路からパスワードを入力させないための設定です。古いOpenSSHでは同じ役割の項目名が ChallengeResponseAuthentication です。

保存したら、文法を確認してから設定を再読み込みします。sshd -t は何も表示しなければ正常です。エラーが出たら再読み込みせずに修正します。

sudo sshd -t
sudo systemctl reload ssh

サービス名は、DebianやUbuntuでは ssh、Red Hat系やArch Linuxでは sshd です。

無効化が効いたかを確認し、だめなら戻す

設定上の値は sudo sshd -T | grep -i passwordauthentication で確認できます。「passwordauthentication no」と表示されれば反映されています。続けて、新しいターミナルから2つ試します。1つは手順3と同じ鍵でのログインで、成功することを確かめます。もう1つは ssh -o PubkeyAuthentication=no user@server で、鍵を使わない接続です。「Permission denied (publickey)」で拒否されれば完了です。

鍵でも入れなくなった場合は、開いたままのセッションで元に戻します。作成したdrop-inファイルを削除するか、sudo cp /etc/ssh/sshd_config.bak /etc/ssh/sshd_config で書き戻します。そのあと sshd -t と再読み込みを実行すれば、パスワードでログインできる状態に戻ります。

設定を終えたあとにやること

これで、鍵を持つ端末からしかログインできなくなりました。秘密鍵の管理がそのまま安全性を左右します。複数の端末から接続する場合は、端末ごとに別の鍵を作って登録します。端末を紛失したら、その鍵の行を authorized_keys から削除すれば、その端末だけを締め出せます。パスフレーズの入力が手間なら、ssh-agent に鍵を登録すると入力はセッション中の1回で済みます。さらに締めたい場合は、次に PermitRootLogin を見直し、rootでの直接ログインを禁止するかどうかを決めます。