📘

Linux サーバー:SSH 設定(2024年7月更新)

2021/02/28に公開

何度繰り返すことになっても、必ずあなたを守ってみせる。 ── 暁美ほむら

⚠ 足りない設定や推奨設定あったらコメントで教えてください。設定用スクリプトに追記します。みんなで楽しましょう。

SSH とは

Secure SHell の略。サーバーのシェルにリモートから安全にアクセスするための仕組み。安全にとは、

  1. 鍵を持たない第三者はアクセスできない
  2. 第三者は通信を盗み見ることができない

ということを意味する。scp コマンドや sftp コマンドを用いて SSH 接続を介した安全なファイル送信も可能であり、リモート作業で非常に重宝する。

環境

  • サーバー:Ubuntu 20.04 LTS
  • クライアント:なんか適当なシェル

方針

  • ED25519公開鍵暗号を用いた SSH 通信
  • SSH のポートを変更してファイアウォールの設定もする
  • ファイアウォールの設定方針は
    • デフォルトですべての incoming を deny
    • SSH で用いるポートのみ allow

自動化スクリプトも用意するが、そこまでやるくらいなら Ansible とか検討したほうがいいかもしれない。

表記

どのシェルでの作業か自明な場合は

$ echo 'Hello, World!'

のように表記する。クライアントかサーバーかわかりにくいところでは

client$ echo 'Hello'
server$ echo 'World'

のように先頭にどちらでの作業かを示す。また root ユーザーでの作業は $ の代わりに '#' を用いて

server# 'Hello, World!'

のように示す。

クライアント側

0. キーの生成

ED25519 は RSA よりも強固で高速らしいので、新たに設定するならば ED25519 を使うほうがよいだろう。鍵の生成は以下のコマンドで行う。

$ ssh-keygen -t ed25519

鍵が作成される場所は ~/.ssh。名前はデフォルトでは

  • 秘密鍵:~/.ssh/id_ed25519
  • 公開鍵:~/.ssh/id_ed25519.pub

である。秘密鍵は絶対に誰にも見せてはいけない。見られたら設定し直しである。

Linux に慣れていない人に伝えておくと、~/はホームディレクトリ($ cdコマンドに何も引数を与えずに実行したときに移動するディレクトリ)を指す省略記号である。.ssh のようにドット記号から始まる名前は隠しファイル/ディレクトリなので通常の $ ls では表示されず、$ ls -a のようにオプションをつけて実行することで表示される。隠しディレクトリにも通常のディレクトリと同じように $ cd コマンドで移動できる。

$ cd
$ ls -a
.  ..  .bashrc  .config  .ssh
$ cd .ssh
$ ls
authorized_keys  config  id_ed25519  id_ed25519.pub  known_hosts

パスフレーズ

パスフレーズを設定しておくと、仮に管理用 PC が悪意ある第三者の手によって陥落しても、パスフレーズが特定されるまでの間は SSH の不正利用を防ぐことができる(サーバー側を守ることができる)。

そもそも管理用 PC の管理者アカウントを他人に触らせるという状況は考えづらいので、必要ないと思えば利便性の観点から設定しなくてもよい。

パスフレーズは、設定するならば、決して他に使用したことがないパスフレーズを使用すること

なぜならばパスフレーズがセキュリティとして機能する段階では、管理用 PC 自体が陥落しているので、そこから PC 内外のメモ書きやクラウドキーチェーンなどを辿って使い回しのパスワードは真っ先に試されるからである。

1. SSH サーバー初期状態でのアクセス方法

サーバー側で SSH サーバーが起動されており、何も設定が変更されていなければ

$ ssh username@hostname
# あるいは直接 IP アドレスを指定して
$ ssh username@192.168.XXX.XXX

のようにユーザー名とホスト名(または IP アドレス)を指定してパスワードログインが可能である。

2. SSH サーバー設定完了後でのアクセス方法

~/.ssh/configを作成して以下のように編集する。

~/.ssh/config
Host sandbox
    HostName    192.168.XXX.XXX
    Port        2222
    IdentityFile    ~/.ssh/id_ed25519
    User        username
  • Host
    • アクセス先につける名前
  • Hostname
    • アクセス先のサーバーのホスト名または IP アドレス
  • Port
    • SSH ポートの番号(デフォルト 22 )
  • IdentityFile
    • SSH 接続に用いる鍵
    • ssh-keygen で生成したやつ
  • User
    • アクセス先のサーバーのアクセスしたいユーザー名

設定ファイルを作成しておくことで、Host に設定した名前でアクセスできる。

$ ssh sandbox

Linux サーバー側

締め出し上等。

0. SSH サーバーのインストールと root での作業(必要な場合)

SSH サーバーのインストール方法はディストリビューションによるので各自検索すること。サーバー用 OS なら大抵最初から入っている。Ubuntu 20.04 LTS Desktop では

server$ sudo apt -y install openssh-server

でインストールできる。SSH サーバーの状態はサーバーのコンソールから

server$ systemctl status ssh
● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2021-02-26 23:25:24 JST; 1h 51min ago

のように確認できる。止まっていたら $ systemctl restart ssh なり、サーバーを再起動するなりすればよい。SSH サーバーが起動した時点で、クライアント側からは

client$ ssh username@hostname
# あるいは直接 IP アドレスを指定して
client$ ssh username@192.168.XXX.XXX

のようにユーザー名とホスト名(または IP アドレス)を指定すれば SSH にパスワードログインが可能である。

Ubuntu はデフォルトでは root ユーザーのパスワードが設定されておらず、その状態で SSH で root ログインすることはできない。一度他のユーザーでアクセスして $ sudo -s で昇格すれば一時的に root ユーザーで作業できる。

$ sudo passwd root で root にパスワードを設定すれば root にログインして作業することも可能である。

1. SSH 公開鍵のサーバーへの登録

SSH サーバーの設定を変更する前に公開鍵の準備をしておかないと締め出される

SSH サーバー側に .ssh ディレクトリがないと、このあとの scp コマンドが失敗するので、先に確認しておく。

client$ ssh username@hostname
server$ ls -a

# もし .ssh がなければ
server$ mkdir .ssh

server$ exit

クライアント側から scp コマンドでサーバーに公開鍵を移動し、サーバー側の authorized_keys に追加する。authorized_keys のファイルは権限を chmod 600 で設定する。

client$ scp ~/.ssh/id_ed25519.pub username@hostname:~/.ssh/register_key
client$ ssh username@hostname
server$ cd .ssh
server$ cat register_key >> authorized_keys
server$ chmod 600 authorized_keys
server$ rm register_key

2. SSH サーバーの設定

SSH サーバーの設定ファイルは /etc/ssh/sshd_config である。手で編集するのが面倒くさい人はあとで示す ssh_setting.sh スクリプトファイルを送って root ユーザーで実行すればよい。

設定ファイルは SSH デーモンを再起動すると反映されるが、まだやっちゃダメもしもポート番号を編集していたらファイアウォールにそのポートへのアクセスを弾かれて締め出されるファイアウォールの設定を変更し、反映したあとで SSH デーモンを再起動する

client$ scp ssh_setting.sh username@hostname:~/ssh_setting.sh
server$ sudo -s
server# ./ssh_setting.sh
Port 22
PermitRootLogin no
PasswordAuthentication no
ChallengeResponseAuthentication no
PermitEmptyPasswords no
SyslogFacility AUTHPRIV
LogLevel VERBOSE

手で編集する場合は $ sudo vi /etc/ssh/sshd_config などで頑張る。

ポートは SSH_PORT_NUMBER に好きなものを設定してよいが、0 〜 1023 番ポートはウェルノウンポートとして予約されているので 1024 〜 65535 番を指定する。

スクリプトは一度実行されると、もとの /etc/ssh/sshd_config/etc/ssh/sshd_config.bk としてバックアップする。/etc/ssh/sshd_config.bk が存在する場合は設定が行われたとみなして何もせずに終了する。

ssh_setting.sh
#!/bin/bash

# SSH の設定
SSH_CONFIG="/etc/ssh/sshd_config"
SSH_CONFIG_BACKUP="/etc/ssh/sshd_config.bk"

SSH_PORT_NUMBER="22"

function change_setting () {
  TARGET=$1
  KEYWORD=$2
  VALUE=$3

  EXIST=$(grep "^${KEYWORD}" ${TARGET} || true)
  EXIST_COMMENT=$(grep "^#${KEYWORD}" ${TARGET} || true)

  if [ "${EXIST}" != "" ]; then
    sed -i '/^'${KEYWORD}'/c '${KEYWORD}' '${VALUE}'' ${TARGET}
  elif [ "${EXIST_COMMENT}" != "" ]; then
    sed -i '/^#'${KEYWORD}'/c '${KEYWORD}' '${VALUE}'' ${TARGET} 
  else
    echo -e "${KEYWORD} ${VALUE}" >> ${TARGET}
  fi
}

if [ -f ${SSH_CONFIG_BACKUP} ]; then
  echo "SSH setting is already done."
else
  echo "Modify ${SSH_CONFIG}"

  cp -i ${SSH_CONFIG} ${SSH_CONFIG_BACKUP}
  
  # Port
  change_setting ${SSH_CONFIG} Port ${SSH_PORT_NUMBER}
  grep "^Port" ${SSH_CONFIG}

  # PermitRootLogin
  change_setting ${SSH_CONFIG} PermitRootLogin no
  grep "^PermitRootLogin" ${SSH_CONFIG}

  # PasswordAuthentication
  change_setting ${SSH_CONFIG} PasswordAuthentication no
  grep "^PasswordAuthentication" ${SSH_CONFIG}

  # UsePAM
  change_setting ${SSH_CONFIG} UsePAM no
  grep "^UsePAM" ${SSH_CONFIG}

  # PubkeyAuthentication
  change_setting ${SSH_CONFIG} PubkeyAuthentication yes
  grep "^PubkeyAuthentication" ${SSH_CONFIG}

  # ChallengeResponseAuthentication
  change_setting ${SSH_CONFIG} ChallengeResponseAuthentication no
  grep "^ChallengeResponseAuthentication" ${SSH_CONFIG}

  # PermitEmptyPasswords
  change_setting ${SSH_CONFIG} PermitEmptyPasswords no
  grep "^PermitEmptyPasswords" ${SSH_CONFIG}

  # SyslogFacility
  change_setting ${SSH_CONFIG} SyslogFacility AUTHPRIV
  grep "^SyslogFacility" ${SSH_CONFIG}

  # LogLevel
  change_setting ${SSH_CONFIG} LogLevel VERBOSE
  grep "^LogLevel" ${SSH_CONFIG}

  # TCP Port Forwarding
  #change_setting ${SSH_CONFIG} AllowTcpForwarding no
  #grep "^AllowTcpForwarding" ${SSH_CONFIG}

  # AllowStreamLocalForwarding
  #change_setting ${SSH_CONFIG} AllowStreamLocalForwarding no
  #grep "^AllowStreamLocalForwarding" ${SSH_CONFIG}

  # GatewayPorts
  #change_setting ${SSH_CONFIG} GatewayPorts no
  #grep "^GatewayPorts" ${SSH_CONFIG}

  # PermitTunnel
  #change_setting ${SSH_CONFIG} PermitTunnel no
  #grep "^PermitTunnel" ${SSH_CONFIG}
fi

以下、設定項目の解説である。

  • Port
    • SSH のポート番号(デフォルトは 22 )
  • PermitRootLogin no
    • SSH 経由で root ユーザーへのログインを許可しない
  • PasswordAuthentication no
    • パスワードでのログインを許可しない
  • ChallengeResponseAuthentication no
    • パスワードに毛が生えた程度のチャレンジレスポンス認証でのログインは許可しない
  • PermitEmptyPasswords
    • パスワード未設定でも空パスワードでのログインは許可しない
  • SyslogFacility
  • LogLevel

3. ファイアウォール設定

Ubuntu ではファイアウォールに UFW(Uncomplicated FireWall)が使われる。他のファイアウォールを使う場合は各自で同様の設定を行うこと。

以下のコマンドでファイアウォールの現在の状態を確認できる

$ sudo ufw status
状態: アクティブ or 非アクティブ

アクティブだったら一旦ファイアウォールを無効化しておく。

$ sudo ufw disable
ファイアウォールを無効にし、システム起動時にも無効にします

デフォルトですべてのポートへの incoming を deny し、SSH に使うポートでの通信は許可する。ポートは自分で設定したものを選ぶこと

$ sudo ufw default deny
$ sudo ufw allow 22

ポート番号をミスってもあとで削除できるのでとりあえずは無視して、自分が設定したポートを確実に開けておく

設定できたら、SSH デーモンを再起動して、ファイアウォールを起動する。

$ sudo systemctl restart ssh
$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
ファイアウォールはアクティブかつシステムの起動時に有効化されます。

設定をミスっていたらこの時点で SSH 接続が切れて締め出しを食らう。一度接続を切ってみて、『SSH サーバー設定完了後でのアクセス方法』で解説した、公開鍵でのアクセスを試みる。

client$ ssh sandbox

接続できればよし。できなければ何かが間違っているので、あなたの戦場はここじゃない。少しあとに書く『締め出された人へ』で状態を復旧したあとに『クライアント側 0. キーの生成』あたりからやり直す。

繰り返す。私は何度でも繰り返す。同じ時間を何度も巡り、たった一つの出口を探る。あなたを、絶望の運命から救い出す道を ── 暁美ほむら

公開鍵で接続できたらファイアウォールの状態を確認する。

$ sudo ufw status verbose
状態: アクティブ

To                         Action      From
--                         ------      ----
22                         ALLOW       Anywhere
22 (v6)                    ALLOW       Anywhere (v6)

もしもミスった設定を追加していたら、以下の手順で削除する。

$ sudo ufw status numbered
状態: アクティブ

     To                         Action      From
     --                         ------      ----
[ 1] 22                         ALLOW IN    Anywhere
[ 2] 2822                       ALLOW IN    Anywhere
[ 3] 22 (v6)                    ALLOW IN    Anywhere (v6)
[ 4] 2822 (v6)                  ALLOW IN    Anywhere (v6)
$ sudo ufw delete 2

一度 delete すると数字が詰められるので、複数削除する場合は再度 $ sudo ufw status numbered で数字を確認する。

Web サーバー用途であれば、80番、443番は開けておく。この辺はお好みで。

$ sudo ufw allow 80
$ sudo ufw allow 443

設定が終わったらファイアウォールを再起動する。

$ sudo ufw reload

締め出された人へ

🎉 🎊 おめでとうございます。あなたは人生の貴重な時間を完全に無駄にしました。🎊 🎉

締め出されたらサーバーのコンソールに別ルート(サーバーに直接繋がっているディスプレイとキーボードとか、VPS であればバーチャルコンソールなど)から直接アクセスして、/etc/ssh/sshd_config.bk/etc/ssh/sshd_config を上書きし、ファイアウォールの設定をすべて削除した後に無効化すれば元の設定に戻る。

$ sudo rm /etc/ssh/sshd_config
$ sudo cp /etc/ssh/sshd_config.bk /etc/ssh/sshd_config
$ sudo rm /etc/ssh/sshd_config.bk
$ sudo systemctl restart ssh
$ sudo ufw status numbered
$ sudo ufw delete 番号(全部削除するまで繰り返す)
$ sudo ufw disable

これで一応は再びパスワードで SSH 接続できる状態になる。何もわからないくらいしっちゃかめっちゃかになってしまったら、サーバー側の OS を再インストールする。セキュリティに関わる部分なので、どんな変更が加わっているかわからない状態でサーバーを運用するのは危険だからである。

Discussion