こんにちは。日本仮想化技術株式会社の水野です。
今回は、サーバーのリモート操作に使われている「SSH」について、運用において注意すべきセキュリティ面にフォーカスしてお話してみたいと思います。
SSHってなに?
サーバールームやデータセンターに置かれているサーバーは、PCとは違って直接触れることができません。いえ、もちろんデータセンターに入館してコンソールを繋げば可能ではあるのですが、よほどの緊急時を除けば、オフィスのPCからリモート接続で操作するのが基本ですよね。特にクラウド上の仮想サーバーは、どう頑張っても物理的に触れないため、リモート操作がマストです。
そして、サーバーのリモート操作に使われているのが「Secure Shell」、通称「SSH」です。その中でもオープンソースな実装である「OpenSSH」を使うケースが多いでしょう。OpenSSHクライアントは、Linuxはもちろん、macOSやWindowsにも標準で用意されているため、別途ツールをインストールする必要もなく便利です*1。
なおSSHの概要については、2023年11月29日のニフクラエンジニアミートアップ「サーバー管理者のためのセキュリティ超入門」の宮原さんのセッションのスライドも参照してください。
やっておきたいSSHのセキュリティ対策
一般的なSSHサーバーというのはその用途的に、以下のような問題を抱えています。
- 外部からの接続を受け付ける必要がある
- 認証に成功するとサーバーのシェルを取れる
誰がどう見ても、認証を突破されるとヤバいですよね。
後述するように、SSHを使わずに済むのであれば、それに越したことはありません。 ですが、そうもいかないのが現実でしょう。
そこでSSHを運用するにあたって、やっておくべきセキュリティ対策を紹介します。少なくとも、パスワード認証が可能な状態のSSHサーバーを、インターネットに対して晒すようなことは、絶対にしないでください。
公開鍵認証
まず最初にやっておきたいのが、パスワード認証を禁止し、公開鍵認証に切替えることです。公開鍵認証は、パスワードのように総当たりによる突破が現実的に困難です。先日のセミナーのおさらいになりますが、鍵ペアの作成は、ssh-keygen
コマンドで行います。
$ ssh-keygen -t 鍵タイプ -C 鍵コメント
鍵タイプは、セキュリティ的にはed25519
がお勧めです。ですがまだ、この形式に対応していないアプリやWebサービスも存在するため、互換性を重視するのであればrsa
を選択するとよいでしょう。ただしrsaの場合は-b
オプションで長めのビット長を指定しておいた方がよいかもしれません。現在のデフォルトは3072ビットです。
作成した公開鍵は、ログインするサーバーの~/.ssh/authorized_keys
というファイルに登録する必要があります。このファイルをテキストエディタで直接編集してもよいのですが、もしも誤編集してしまうと、サーバーにログインできなくなってしまうかもしれません。そこで専用のコマンドである、ssh-copy-id
を使うとよいでしょう。
$ ssh-copy-id -i 公開鍵ファイル サーバーのアドレス
ですがこの方法で公開鍵をサーバーに登録するには、登録対象の鍵以外の方法でログインできる必要があります。そのためパスワード認証が禁止されており、鍵がひとつも登録されていないサーバーには、ユーザー自信で鍵を登録することができません。現実的には、サーバー管理者に鍵を登録してもらうことになるでしょう。
サーバー管理者向けの鍵登録コマンドが、ssh-import-id
です。これはログイン対象のサーバー上で実行するコマンドで、GitHubやLaunchpadから指定したユーザーの公開鍵を取得して登録します。例えばGitHubのuser1
ユーザーの公開鍵を登録するには、以下のように実行します。
$ ssh-import-id gh:user1
どちらのコマンドも、詳しい使い方はmanページを参照してください。
なおUbuntuサーバーでは、GitHubからの公開鍵のインポートと、パスワードによるSSHログインの禁止は、インストーラーで設定することができます。SSHによるログインを許可する予定があるのであれば、可能な限りOSのインストール時に行うことをお勧めします。
スマホアプリを使った2要素認証
公開鍵による認証は、それだけで十分な効果があるのですが、実運用においては問題もあります。やはり大きいのは、公開鍵認証に馴染みのないユーザーに、鍵の作成や安全な管理を求めるのが難しいという点です。新人さんに「サーバーにアカウントを作るので、手順書に従って公開鍵を作って送ってください」とお願いしても、なかなか理解してもらえなかったり、秘密鍵を速攻で紛失する人が出たり、なぜか秘密鍵が送られてきたり、といった経験をした情シス担当の方もいるのではないでしょうか。
UbuntuのSSHサーバーでは、パスワードとTOTPによる2要素認証が使えます。スマホアプリを使ったワンタイムパスワードは、Webサービスでもお馴染みですから、IT初心者でもとっつきやすいのではないでしょうか。ここではUbuntu 22.04での設定例を紹介します。なおパスワードとの2要素認証となるため、パスワード認証を禁止している場合は、許可するようサーバーの設定を変更しておいてください。
まずサーバーにlibpam-google-authenticator
パッケージをインストールします。
$ sudo apt install -y libpam-google-authenticator
続いて/etc/pam.d/sshd
をテキストエディタで開き、@include common-auth
の直下にauth required pam_google_authenticator.so
という行を追加してください。
@include common-auth auth required pam_google_authenticator.so # ← この行を追加
SSHサーバーの設定も変更します。/etc/ssh/sshd_config
をテキストエディタで開き、62行目付近のKbdInteractiveAuthentication no
の、no
をyes
に書き換えます。またその下にChallengeResponseAuthentication yes
という行を追加します。また86行目付近にUsePAM yes
と書かれた行があることを確認します。デフォルトでこの設定となっているはずですので、ここは通常変更する必要はありません。
(...略...) KbdInteractiveAuthentication yes # ← この行をnoからyesに変更 ChallengeResponseAuthentication yes # ← この行を追加 (...略...) UsePAM yes # ← この行があることを確認する
最後に、SSHサーバーを再起動します。
$ sudo systemctl restart sshd.service
続いて、ログインするユーザーごとの設定を行います。この操作は管理者権限ではなく、各ユーザーの権限で行ってください。google-authenticator
コマンドをすると、認証コードを時刻ベースにするかどうか訊かれますので、y
と入力してEnterキーを押します。するとターミナルにQRコードが表示されますので、これをスマホのトークンアプリに読み込ませてください。その後、確認のため認証コードの入力を促されますので、スマホの画面に表示された6桁の数字を入力してください。
その後、いくつかの質問が表示されますが、基本はすべてy
を選択して構いません。以後はSSH接続を行うと、パスワードの入力を求められた後に、認証コードの入力を求められるようになります。なお本設定は公開鍵認証と併用することができ、公開鍵を登録している場合は、2要素認証をバイパスしてログイン可能です。
ファイアウォールによる防御
ファイアウォールによる防御も有効です。SSHが使っているポートをパブリックに解放してしまうのではあまり意味がありませんが、利用するユーザーの接続元を限定できるのであれば、ソースIPアドレスで制限をかけてもよいでしょう。ですがリモートワークが一般的となった現在、ソースIPアドレスによる制限は難しいこともあるでしょう。そこで便利なのが、Ubuntuのファイアウォールにあるlimit
機能です。ufw
パッケージをインストールした後に、以下のようにルールを投入します。
$ sudo apt install -y ufw $ sudo ufw enable $ sudo ufw limit ssh
limitは同一のIPアドレスからの接続を、30秒間に5回まで許可し、6回目以降をブロックします。つまりこの設定で、SSHサーバーに対するブルートフォース攻撃をある程度防御することが可能になります。どうしてもパスワード認証を使わなければならない場合などは、力技で認証を突破されないためにも、こうした防御策を検討してください。なおファイアウォールについて詳しくは、UFWのマニュアルを参照してください。
SSHサーバーのポート変更
SSHサーバーに対する攻撃は、22番ポートを対象に行われます。そこでサーバーが待ち受けるポートを変更すれば、大部分の攻撃を回避できます。なお変更の際には、2022
や8022
といった「SSHだと推測されやすい」ポートは避け、完全にランダムなポートを採用することが重要です。
待ち受けるポート番号は、/etc/ssh/sshd_config
で設定されています。このファイルを編集し、Port
行に書かれている番号を変更してください。また他のサーバーとのバッティングを避けるため、/etc/services
に記載されていない番号を選ぶようにするとよいでしょう。
Port 22 ↓ Port 27365 # ← 22番ポートから推測しにくいポート番号に変更する
ポートを変更したら、SSHサーバーを再起動してください。
なおUbuntu 22.10以降のバージョンでは、SSHサーバー自身が接続を待ち受けるのではなく、systemdのSocket-Based Activation経由でサーバーが起動されるように変更されました。そのため待ち受けポートはsshd_config
ではなく、systemdのUnitファイルで設定する必要があります。以下のコマンドを実行すると、設定を上書きするためのテキストエディタが開きます。
$ sudo systemctl edit ssh.socket
例えばポートを27365番に変更するには、以下のように記述します。ListenStream=
という値が空の行を挟むことで、22番ポートで待ち受ける設定をクリアしているため、この行は省略しないでください。
[Socket] ListenStream= ListenStream=27365
ファイルを保存したら、ssh.socket
を再起動してください。
$ sudo systemctl restart ssh.socket
そもそもSSHを使わないという選択肢
ニフクラをはじめとする多くのクラウドサービスでは、Webからサーバーのコンソールを使うことができます。この方法であれば、セキュリティホールになりうるSSHサーバーをインターネットに公開する必要はありません。また現在では、IaCによるサーバー構築の自動化や、immutable infrastructureの概念も一般的になってきており、そもそもサーバーに直接ログインするのがアンチパターンという考え方もあります。
セキュリティ対策を考えるのは当然なのですが、その前段階として、本当に古典的なSSHの口をインターネットに公開する必要があるのか、もう一度考えてみてもよいのではないでしょうか。
*1:意外と知らない人もいるようですが、最近のWindowsはデフォルトでPowerShellからssh.exeを実行することができます。