OpenSSH
OpenSSH (OpenBSD Secure Shell) は、Secure Shell (SSH) プロトコルを用いてコンピュータネットワーク経由の暗号化された通信セッションを提供するコンピュータプログラム群です。OpenSSH は、SSH Communications Security によって提供されているプロプライエタリな Secure Shell ソフトウェアスイートのオープンソースな代替として作成されました。OpenSSH は OpenBSD プロジェクトの一部として開発されており、Theo de Raadt によって率いられています。
OpenSSH は、似た名前の OpenSSL と混同されることがあります。しかし、これらのプロジェクトの目的は異なり、開発チームも異なります。この似たような名前は、ただ単にゴールが似ているからです。
目次
- 1 インストール
- 2 クライアントの使用
- 3 サーバの使用
- 4 他の SSH クライアントとサーバー
- 5 ヒントとテクニック
- 6 ソケットアクティベーションで SSH ポート番号を変更する (sshd.socket)
- 7 トラブルシューティング
- 7.1 チェックリスト
- 7.2 接続が拒否されるまたはタイムアウトする
- 7.3 "[your shell]: No such file or directory" / ssh_exchange_identification 問題
- 7.4 "Terminal unknown" や "Error opening terminal" エラーメッセージ
- 7.5 Connection closed by x.x.x.x [preauth]
- 7.6 サブシステム要求の失敗
- 7.7 OpenSSH 7.0 によって id_dsa が拒否される
- 7.8 OpenSSH 7.0 で No matching key exchange method found
- 7.9 SSH から切断したときに tmux/screen セッションが終了する
- 7.10 SSH セッションが応答しなくなる
- 7.11 Broken pipe
- 7.12 再起動後デーモンの起動が遅い
- 7.13 応答のない SSH 接続を終了する
- 7.14 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
- 7.15 適切な terminfo エントリがないリモートに接続する場合
- 8 参照
インストール
クライアントの使用
サーバに接続するには、以下を実行してください:
$ ssh -p ポート ユーザ@サーバアドレス
サーバが公開鍵認証のみを許可している場合、SSH 鍵 の記事に従ってください。
設定
クライアントは、共通のオプションとホストを保存するように設定することができます。全プションは、グローバルに、あるいは特定のホストに制限して宣言することができます。例えば:
~/.ssh/config
# global options User user # 特定のホストのオプション Host myserver Hostname server-address Port port
このような設定を用いると、以下の2つのコマンドは等価になります:
$ ssh -p port user@server-address $ ssh myserver
詳細は ssh_config(5) を見てください。
一部のオプションには等価なコマンドラインスイッチがありません。しかし、コマンドラインで -o
フラグを用いることで設定オプションを指定できます。例えば: -oKexAlgorithms=+diffie-hellman-group1-sha1
。
サーバの使用
sshd
は OpenSSH サーバのデーモンです。/etc/ssh/sshd_config
で設定され、sshd.service
によって管理されます。設定を変更する際は、サーバを再起動する前に sshd
をテストモードで使用し、サーバが正しく起動できることを確認してください。設定が有効である場合は、何も出力されません。
# sshd -t
設定
一部のユーザにのみアクセスを許可するには、以下の行を追加してください:
AllowUsers user1 user2
一部のグループにのみアクセスを許可するには:
AllowGroups group1 group2
素晴らしいウェルカムメッセージを (/etc/issue
ファイルなどから) 追加するには、Banner
オプションを設定してください:
Banner /etc/issue
公開ホスト鍵と秘密ホスト鍵は、sshdgenkeys サービスによって /etc/ssh
内に自動で生成されます。sshd_config
内の HostKeyAlgorithms
オプションによって一部の署名アルゴリズムしか許可されていない場合でも、鍵が存在しない場合、再生成されます。dsa、rsa、ecdsa、そして ed25519 アルゴリズムに基づく4組の鍵のペアが提供されます。sshd に特定の鍵を使用させるには、以下のオプションを指定してください:
HostKey /etc/ssh/ssh_host_rsa_key
サーバを WAN に公開するつもりであるならば、以下のようにデフォルトのポートを 22 から別のランダムで大きい値に変更することが推奨されます:
Port 39901
デーモンの管理
sshd.service
を起動/有効化してください。これは、SSH デーモンを永続的にアクティブ状態に保ち、各着信接続に対してフォークします。[1]
保護
SSH によるリモートログインを許可することは管理業務においては良いことですが、サーバーのセキュリティに脅威を及ぼすことにもなりえます。総当り攻撃の標的になりやすいので、SSH のアクセスは制限して、第三者がサーバーにアクセスできないようにする必要があります。
ssh-audit は、サーバとクライアントの設定の自動化された解析を提供します。このトピックに関して、いくつか他のガイドやツールが利用できます。例えば:
公開鍵認証を強制する
デフォルトでは、クライアントが公開鍵で認証できない場合、SSH サーバはパスワード認証にフォールバックします。なので、悪意のあるユーザがパスワードのブルートフォースによるアクセスを試みることができてしまいます。このような攻撃から保護する最も効果的な方法の1つが、パスワードログインを完全に無効化し、SSH 鍵の使用を強制することです。これは、デーモンの設定ファイルで以下のオプションを設定することで可能です:
/etc/ssh/sshd_config
PasswordAuthentication no AuthenticationMethods publickey
二要素認証と公開鍵
SSH は、認証に複数の方法を要求するようにセットアップすることができます。AuthenticationMethods
オプションを使うことで、どの認証方法を要求するかを指定することができます。これにより、公開鍵と2要素認証を使用することができます。
認証プロバイダ
Google Authenticator をセットアップする方法については Google Authenticator を参照してください。
Duo の場合、pam_duo.so
モジュールを提供する duo_unixAUR をインストールしてください。必須の Duo 認証情報 (Integration Key、Secret Key、API Hostname) をセットアップする方法に関するインストラクションは Duo Unix ドキュメント を参照してください。
PAM セットアップ
PAM を OpenSSH で使用するには、以下のファイルを編集してください:
/etc/ssh/sshd_config
KbdInteractiveAuthentication yes AuthenticationMethods publickey keyboard-interactive:pam
これで、PAM セットアップによって要求されるユーザ認証か公開鍵のどちらか一方でログインできます。
一方、PAM セットアップによって要求されるユーザ認証と公開鍵の両方でユーザを認証したい場合、AuthenticationMethods の分割にスペースではなくコンマを使用してください:
/etc/ssh/sshd_config
KbdInteractiveAuthentication yes AuthenticationMethods publickey,keyboard-interactive:pam
要求される公開鍵と pam 認証の両方を用いて、パスワード要件を無効化すると良いでしょう:
/etc/pam.d/sshd
auth required pam_securetty.so #disable remote root #Require google authenticator auth required pam_google_authenticator.so #But not password #auth include system-remote-login account include system-remote-login password include system-remote-login session include system-remote-login
ブルートフォース攻撃に対する保護
ブルートフォース (総当り) は単純なコンセプトです: 大量のランダムなユーザ名とパスワードの組み合わせを用いて、あるウェブページやサーバの SSH のようなログインプロンプトに絶え間なくログインを試みるのです。
iptables については ufw#ufw によるレート制限 や シンプルなステートフルファイアウォール#ブルートフォース攻撃 を参照してください。
あるいは、fail2ban や sshguard などの自動スクリプトを使うことで攻撃者をブロックすることでブルートフォース攻撃から身を守ることができます。
- 信頼された場所からの着信 SSH 接続のみを許可する。
- fail2ban か sshguard を使用して、パスワード認証に失敗しすぎた IP アドレスを自動的にブロックする。
- pam_shield を使用して、特定の時間内に多くのログイン試行を行った IP アドレスをブロックする。fail2ban や sshguard とは対照的に、このプログラムはアカウントへのログイン成功や失敗を考慮しません。
root ログインの制限
一般的に、root ユーザが制限無しで SSH を介してログインできることはバッドプラクティスだと考えられています。セキュリティを向上させるために SSH の root アクセスを制限する方法は2つあります。
拒否
Sudo を使うことで、root アカウントの認証を行うことなく、必要に応じて root 権限を選択的に付与することができます。このため SSH による root ログインを拒否して、攻撃者にパスワードに加えて (root でない) ユーザー名も推測させる必要を生じさせることで、ブルートフォース攻撃を困難にすることが可能です。
デーモンの設定ファイルで "Authentication" セクションを編集することで SSH からの root ユーザのログインを拒否するように設定できます。PermitRootLogin
を no
に設定してください:
/etc/ssh/sshd_config
PermitRootLogin no
次に、SSH デーモンを再起動してください。
これで、SSH を使って root でログインすることはできなくなります。ただし、通常ユーザーでログインしてから su や sudo を使ってシステム管理を行うことは依然として可能です。
制限
自動的な作業の中にも、リモートによるフルシステムバックアップなど、root 権限を必要とするものがあります。セキュアな方法で root を許可したい場合、SSH による root ログインを無効化する代わりに、特定のコマンドだけ root ログインを許可することができます。~root/.ssh/authorized_keys
を編集して、以下のように特定のキーの前にコマンドを記述します:
command="/usr/lib/rsync/rrsync -ro /" ssh-rsa …
上記の設定で、特定の鍵を使ってログインした場合はクォートで囲ったコマンドを実行できるようになります。
ログイン時に root ユーザーの名前を出すことで攻撃する対象が増えてしまうことに対しては sshd_config
に以下を追加することで埋め合わせができます:
PermitRootLogin forced-commands-only
上記の設定は root が SSH で実行できるコマンドを制限するだけでなく、パスワードの使用も無効化して、root アカウントでは強制的に公開鍵認証を使うようになります。
root で使えるコマンドは制限しないで公開鍵認証の強制だけをするという手もあり、それでもブルートフォース攻撃はほぼ不可能です。その場合、以下を設定:
PermitRootLogin prohibit-password
authorized_keys ファイルの探索
何らかの理由により、疑いのあるユーザに既存の鍵を追加/変更させるべきではないと思われる場合、そのファイルを操作できないようにできます。
サーバでは、ユーザの authorized_keys
ファイルを読み取り専用にして、他の全パーミッションを禁止してください:
$ chmod 400 ~/.ssh/authorized_keys
ユーザがパーミッションを元に戻せないようにするには、authorized_keys
ファイルに変更不可のビットを設定してください。ユーザが ~/.ssh
ディレクトリをリネームして新しい ~/.ssh
ディレクトリと authorized_keys
ファイルを作成できないようにするには、~/.ssh
ディレクトリにも変更不可のビットを設定してください。鍵を追加/削除するには、authorized_keys
から変更不可のビットを削除して一時的に書き込み可能にする必要があります。
他の SSH クライアントとサーバー
OpenSSH の他にも、多数の SSH クライアント とサーバーが存在します。
Dropbear
Dropbear は SSH-2 クライアント・サーバーです。dropbear は公式リポジトリから利用可能です。
コマンドラインの ssh クライアントは dbclient という名前が付けられています。
Mosh
Mosh の ウェブサイト より:
ローミングが可能で、断続的な接続もサポートしているリモート端末アプリケーションです。ユーザーのキーストロークのローカルエコーと行編集を提供します。Mosh は SSH の代替です。SSH よりも強固でレスポンスが早く、特に Wi-Fi や携帯端末からの接続、長距離通信など通信速度が遅い場合に役に立ちます。
公式リポジトリから mosh をインストールするか AUR にある最新版 mosh-gitAUR を使って下さい。
Mosh にはドキュメントに記載されていないコマンドラインオプション --predict=experimental
が存在し、よりアグレッシブにローカルのキーストロークのエコーを生成します。キーボード入力が遅延なく確認できるのに興味があるのであればこの prediction モードを使ってみて下さい。
ヒントとテクニック
暗号化 SOCKS トンネル
暗号化トンネルは信頼ができない様々なワイヤレス接続を使用するノートパソコンなどで非常に有用です。必要なのは SSH サーバーが安全な場所 (家や仕事場など) で動作していることだけです。DynDNS などのダイナミック DNS サービスを利用すれば IP アドレスを覚える必要もありません。
手順 1: 接続の開始
以下のコマンドを実行することで接続を開始できます:
$ ssh -TND 4711 user@host
user
はユーザー名に host
は SSH サーバーが動作しているホストに置き換えてください。パスワードを入力すると接続が行われます。N
フラグはインタラクティブプロンプトを無効化し、D
フラグは listen するローカルポートを指定します (ポート番号は何でもかまいません)。T
フラグは疑似 tty アロケーションを無効化します。
verbose (-v
) フラグを追加することで、接続が成功していることを出力から確認することができます。
手順 2: ブラウザ (やその他のプログラム) の設定
新しく作成した socks トンネルを使用するようにウェブブラウザ (や他のプログラム) を設定しないと上記の作業は無意味です。最新バージョンの SSH は SOCKS4 と SOCKS5 に対応しているため、どちらかを使うことができます。
- Firefox の場合: 編集 → 設定 → 詳細 → ネットワーク → 接続 → 接続設定:
- ラジオボタンの手動でプロキシを設定するにチェックを入れて、SOCKS ホストテキストフィールドに
localhost
と、次のテキストフィールドにポート番号を入力してください (上の例では4711
)。
Firefox はデフォルトでは DNS リクエストの作成に socks トンネルを使用しません。以下の手順で設定することでプライバシーを守ることができます:
- Firefox のロケーションバーに about:config と入力。
- network.proxy.socks_remote_dns を検索。
- 値を true に設定。
- ブラウザを再起動。
- Chromium の場合: 環境変数やコマンドラインオプションで SOCKS の設定ができます。以下の関数を
.bashrc
に追加することを推奨します:
function secure_chromium { port=4711 export SOCKS_SERVER=localhost:$port export SOCKS_VERSION=5 chromium & exit }
もしくは:
function secure_chromium { port=4711 chromium --proxy-server="socks://localhost:$port" & exit }
ターミナルから以下のように実行してください:
$ secure_chromium
X11 フォワーディング
X11 フォワーディングはリモートシステムで X11 プログラムを動作させて、グラフィカルインターフェイスをローカルのクライアントマシンで表示させるメカニズムです。X11 フォワーディングではリモートホストに X11 システムを完全にインストールさせる必要はなく、xauth をインストールするだけで十分です。xauth は、、X11 セッションの認証を行うために必要なサーバーとクライアントによって使用される Xauthority
の設定を管理するユーティリティです (ソース)。
セットアップ
リモート側
- 公式リポジトリから xorg-xauth と xorg-xhost をインストール
/etc/ssh/sshd_config
内:AllowTcpForwarding
とX11UseLocalhost
オプションを yes に設定し、X11DisplayOffset
を 10 に設定 (何も変更を加えてなければこの値がデフォルトになっています、man sshd_config
を参照)X11Forwarding
を yes に設定
- sshd デーモンを再起動
クライアント側
クライアント側では、接続するたびにコマンドラインで -X
スイッチを指定して ForwardX11
オプションを有効にするか、openSSH クライアントの設定ファイルで ForwardX11
を yes に設定してください。
使用方法
通常通りリモートマシンにログインします、クライアント側の設定ファイルで ForwardX11 を有効にしていない場合は -X
スイッチを指定します:
$ ssh -X user@host
グラフィカルなアプリケーションを実行しようとするとエラーが表示される場合、代わりに ForwardX11Trusted を試してみて下さい:
$ ssh -Y user@host
リモートサーバーで X プログラムが起動できるようになったら、出力がローカルセッションに転送されます:
$ xclock
"Cannot open display" エラーが表示される場合、root 以外のユーザーで以下のコマンドを実行してみてください:
$ xhost +
上記のコマンドは全てのユーザーに X11 アプリケーションの転送を許可します。特定のホストだけに転送を制限するには:
$ xhost +hostname
hostname は転送先のホストの名前に置き換えてください。詳しくは man xhost
を参照。
特定のアプリケーションではローカルマシンでインスタンスが動作しているかチェックが実行されます。例えば Firefox は以下の起動パラメータを使用してローカルマシンでリモートインスタンスを起動する必要があります:
$ firefox --no-remote
接続時に "X11 forwarding request failed on channel 0" と表示される場合 (サーバーの /var/log/errors.log
に "Failed to allocate internet-domain X11 display socket" と出力される場合)、xorg-xauth パッケージがインストールされていることを確認してください。上手く機能しない場合、以下の設定を試してみてください:
- サーバーの
sshd_config
でAddressFamily any
オプションを有効にする。 - サーバーの
sshd_config
でAddressFamily
オプションを inet に設定する。
IPv4 で Ubuntu クライアントを使っている場合は inet に設定することで問題が解決します。
SSH サーバーの他のユーザーで X アプリケーションを実行するには SSH でログインしているユーザーの xauth list
の認証行を xauth add
する必要があります。
他のポートのフォワーディング
SSH は X11 をサポートしているだけでなく、TCP 接続のセキュアなトンネル化に使用することもできます。ローカルフォワーディングとリモートフォワーディングの両方が使えます。
ローカルフォワーディングはローカルマシンのポートを開いて、リモートホストに接続が転送されます。転送先をリモートホストと同じにすることで、同一マシンでセキュアな VNC 接続などができます。ローカルフォワーディングは -L
スイッチで利用することができ <tunnel port>:<destination address>:<destination port>
という形式で転送先を指定します:
$ ssh -L 1000:mail.google.com:25 192.168.0.100
上記のコマンドは SSH で 192.168.0.100 にログインしてシェルを開きます。そしてローカルマシンの TCP ポート 1000 から mail.google.com のポート 25 にトンネルが作成されます。接続が確立すると localhost:1000 への通信は Gmail の SMTP ポートに接続されます。Google から見ると、192.168.0.100 から接続が来ているように見えます (必ずしも接続と一緒にデータが運ばれるとは限りません)。データはローカルマシンと 192.168.0.100 の間は安全に運ばれます。
同じように以下のコマンドは localhost:2000 に接続することができ、リモートホストのポート 6001 に透過的に送信されます:
$ ssh -L 2000:192.168.0.100:6001 192.168.0.100
前者の例はセキュリティ上問題がある (tightvncAUR パッケージに含まれている) vncserver ユーティリティによる VNC 接続などで有用です。
リモートフォワーディングは SSH トンネルとローカルマシンを通してリモートホストから任意のホストに接続できるようにします。ローカルフォワーディングとは逆の機能であり、ファイアウォールによってリモートホストの接続が限られている場合などに有用です。リモートフォワーディングは -R
スイッチで使うことができ <tunnel port>:<destination address>:<destination port>
という形式で転送先を指定します:
$ ssh -R 3000:irc.freenode.net:6667 192.168.0.200
上記のコマンドは 192.168.0.200 にシェルを立ち上げて、192.168.0.200 からローカルホストの3000番ポートへの接続をトンネルを通して送信し、それから irc.freenode.net のポート 6667 に転送します。ポート 6667 がブロックされている場合でもリモートホストから IRC プログラムを利用することができるようになります。
ローカルフォワーディングとリモートフォワーディングはどちらもセキュアなゲートウェイとして使用することができます。<tunnel address>:<tunnel port>:<destination address>:<destination port>
のようにバインドアドレスをつかうことで、SSH や SSH デーモンを動かしていなくても他のコンピュータが SSH トンネルを利用することが可能です。<tunnel address>
はトンネルの入り口となるマシンのアドレスです: localhost
, *
(あるいは空)。特定のアドレス経由の接続、ループバックインターフェイス経由の接続、全てのインターフェイス経由の接続を許可します。デフォルトでは、フォワーディングはトンネルの入り口のマシンからの接続だけに制限されており <tunnel address>
は localhost
に設定されています。ローカルフォワーディングは特に設定が必要ありませんが、リモートフォワーディングはリモートサーバーの SSH デーモンの設定によって制限を受けます。詳しくは sshd_config(5)
の GatewayPorts
オプションを見てください。
踏み台ホスト
場合によっては、接続先の SSH デーモンに直接接続できず、踏み台サーバー (ジャンプサーバー) を使わざるを得ないことがあります。ふたつ以上の SSH トンネルを接続して、それぞれのサーバーに対してローカルの鍵で認証します。SSH エージェントの転送 (-A
) と疑似端末の割当 (-t
) を使って以下のようにローカルの鍵を転送します:
$ ssh -A -t -l user1 bastion1 \ ssh -A -t -l user2 intermediate2 \ ssh -A -t -l user3 target
以下のように -J
フラグを使用することもできます:
$ ssh -J user1@bastion1,user2@intermediate2 user3@target
-J
ディレクティブで指定するホストはカンマで区切り、指定された順番で接続されます。user...@
の部分は必須ではありません。-J
で指定したホストは ssh の設定ファイルを使うため、必要であればホスト毎にオプションを設定することが可能です。
マルチプレクス
SSH デーモンは通常はポート 22 番をリッスンします。しかし、公共のインターネット・ホットスポットでは HTTP/HTTPS のポート(80 と 443)以外のトラフィックをブロックしていることが一般的です。そのため SSH 接続がブロックされてしまいます。すぐできる解決策として、許可されているポートで sshd を起動するという方法があります:
/etc/ssh/sshd_config
Port 22 Port 443
しかしポート 443 番は HTTPS を提供する Web サーバにすでに使われていることが多いです。その場合は sslh のようなマルチプレクサを使います。これは指定ポートをリッスンし、そこに来るパケットを複数のサービスに賢く振り分けることができます。
SSH の高速化
接続をグローバル及び特定のホストに対して高速化させることのできるクライアント設定がいくつかあります。これらのオプションに関する完全な説明は ssh_config(5) を参照してください。
- より高速な暗号を使う: AESNI 命令セットを持つ最近の CPU では、
aes128-gcm@openssh.com
やaes256-gcm@openssh.com
を使うことで openssh のデフォルトの優先暗号 (通常chacha20-poly1305@openssh.com
) よりも劇的に優れたパフォーマンスを得られるはずです。暗号は-c
フラグで選択できます。永続的に設定するには、~/.ssh/config
内にCiphers
オプションを追加し、暗号を優先順に並べて設定してください (例:Ciphers aes128-gcm@openssh.com,aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
)。
- 圧縮を有効化あるいは無効化する: 圧縮は低速な接続において速度を向上させることができます。
Compression yes
オプションか-C
フラグで有効化できます。しかし、使用される圧縮アルゴリズムは比較的低速な gzip(1) であり、高速なネットワークにおいてはボトルネックになってしまいます。接続を高速化させるために、ローカルネットワークや高速なネットワークにおいてはCompression no
を使うべきでしょう。
- 接続共有: 以下のオプションを使用することで、同一ホストに対するセッションをすべて単一の接続に共有させることができます:
ControlMaster auto ControlPersist yes ControlPath ~/.ssh/sockets/socket-%r@%h:%p
~/.ssh/sockets
の部分は、他のユーザによって書き込むことができないのであれば、どのディレクトリでも構いません。
ControlPersist
は、初回のクライアント接続が閉じられてから、新しいクライアントのためにマスターがどれくらいバックグラウンドで待機すべきかを指定します。利用可能な値は:no
: 最後のクライアントが切断されたらすぐに接続を閉じます。- 秒単位の時間
yes
: 永遠に待ちます。接続は自動的に閉じられなくなります。
AddressFamily inet
オプションか-4
フラグを使用して IPv6 ルックアップをバイパスすることで、ログインに要する時間を短くすることができます。
- 最後に、SSH を SFTP や SCP のために使用するつもりであれば、High Performance SSH/SCP は、SSH バッファサイズを動的に大きくすることで、スループットを劇的に増やすことができます。この改善のパッチが施されたバージョンの OpenSSH である openssh-hpn-gitAUR パッケージをインストールしてください。
SSHFS でリモートファイルシステムをマウントする
sshfs を使って (SSH でアクセスした) リモートのファイルシステムをローカルフォルダにマウントする方法は Sshfs の記事を参照してください。マウントしたファイルは様々なツールであらゆる操作することができます (コピー、名前の変更、vim で編集など)。基本的に shfs よりも sshfs を使用することを推奨します。sshfs は shfs の新しいバージョンであり、元の shfs は2004年から更新されていません。
Keep alive
一定時間操作がないと ssh セッションは自動的にログアウトします。接続を維持するには以下をクライアントの ~/.ssh/config
か /etc/ssh/ssh_config
に追加してください:
ServerAliveInterval 120
これで120秒ごとに "keep alive" シグナルがサーバーに送信されます。ServerAliveCountMax
や TCPKeepAlive
オプションも参照してください。
反対に、外部からの接続を維持するには、次をサーバーの /etc/ssh/sshd_config
に設定します (数字は0より大きく):
ClientAliveInterval 120
systemd で SSH トンネルを自動的に再起動
systemd を使ってブート時/ログイン時に SSH 接続を自動的に開始して、接続が失敗した時に再起動させることができます。SSH トンネルの管理に役立つツールとなります。
以下のサービスでは、ssh の設定に保存された接続設定を使って、ログイン時に SSH トンネルを開始します。接続が何らかの理由で閉じられた場合、10秒待機してから再起動します:
~/.config/systemd/user/tunnel.service
[Unit] Description=SSH tunnel to myserver [Service] Type=simple Restart=always RestartSec=10 ExecStart=/usr/bin/ssh -F %h/.ssh/config -N myserver
上記のユーザーサービスを有効化して起動してください。トンネルがタイムアウトするのを防ぐ方法は #Keep alive を見て下さい。起動時にトンネルを開始したい場合、ユニットをシステムサービスとして書きなおして下さい。
Autossh - SSH セッションとトンネルの自動再起動
ネットワークの状態が悪かったりしてクライアントが切断してしまい、セッションやトンネルの接続を維持できない場合、autossh を使って自動的にセッションとトンネルを再起動できます。
使用例:
$ autossh -M 0 -o "ServerAliveInterval 45" -o "ServerAliveCountMax 2" username@example.com
sshfs を組み合わせる:
$ sshfs -o reconnect,compression=yes,transform_symlinks,ServerAliveInterval=45,ServerAliveCountMax=2,ssh_command='autossh -M 0' username@example.com: /mnt/example
プロキシ設定で設定した SOCKS プロクシを使って接続:
$ autossh -M 0 -o "ServerAliveInterval 45" -o "ServerAliveCountMax 2" -NCD 8080 username@example.com
-f
オプションで autossh をバックグラウンドプロセスとして実行することができます。ただし対話式でパスフレーズを入力することができなくなります。
セッション中に exit
と入力したり autossh プロセスに SIGTERM, SIGINT, SIGKILL 信号が送られるとセッションは終了します。
systemd を使ってブート時に自動的に autossh を起動する
autossh を自動的に起動したい場合、以下の systemd ユニットファイルを作成します:
/etc/systemd/system/autossh.service
[Unit] Description=AutoSSH service for port 2222 After=network.target [Service] Environment="AUTOSSH_GATETIME=0" ExecStart=/usr/bin/autossh -M 0 -NL 2222:localhost:2222 -o TCPKeepAlive=yes foo@bar.com [Install] WantedBy=multi-user.target
AUTOSSH_GATETIME=0
は接続が成功して ssh が立ち上がったと autossh が認識する秒数です。0に設定すると autossh は ssh の最初の起動失敗を無視します。起動時に autossh を実行する場合は設定するべきです。他の環境変数は man ページを見てください。必要であればもっと複雑に設定することもできますが、AUTOSSH_GATETIME=0
を含む -f
は systemd では機能しません。
以下のように ControlMaster を無効化する必要もあるかもしれません:
ExecStart=/usr/bin/autossh -M 0 -o ControlMaster=no -NL 2222:localhost:2222 -o TCPKeepAlive=yes foo@bar.com
ソケットアクティベーションで SSH ポート番号を変更する (sshd.socket)
次の内容で /etc/systemd/system/sshd.socket.d/port.conf
ファイルを作成:
[Socket] # Disable default port ListenStream= # Set new port ListenStream=12345
リロードすれば systemd は自動的に新しいポートを開きます:
systemctl daemon-reload
トラブルシューティング
チェックリスト
以下は最初に行うべきトラブルシューティングのチェックリストです。何かする前に以下の問題をチェックすることを推奨します。
- 設定ディレクトリ
~/.ssh
の中身がユーザーからアクセスできることを確認する (クライアントとサーバーの両方で確認してください):$ chmod 700 ~/.ssh $ chmod 600 ~/.ssh/* $ chown -R $USER ~/.ssh
- クライアントの公開鍵 (例:
id_rsa.pub
) がサーバーの~/.ssh/authorized_keys
に存在することを確認する。 - サーバーの設定で
AllowUsers
やAllowGroups
によって SSH によるアクセスが制限されていることを確認する。 - ユーザーにパスワードが設定されていることを確認する。新しいユーザーにはパスワードが設定されていない可能性があります。
sshd
を再起動してクライアントとサーバーの両方で一度ログアウトをしてみる。
接続が拒否されるまたはタイムアウトする
ルーターがポートフォワーディングをしていないか?
NAT モードやルーターを使っている場合、ルーターが ssh 接続を転送していないか確認してください。サーバーの内部 IP アドレスは $ ip addr
で確認することができるので、SSH ポートの TCP をその IP に転送するようにルーターを設定してください。portforward.com も役に立ちます。
SSH が動作しているか?
$ ss -tnlp
上記のコマンドで SSH ポートが開いていると表示されない場合、SSH は動作していません。/var/log/messages
にエラーがないか確認してください。
接続をブロックするようなファイアウォールのルールが存在しないか?
iptables によってポート 22
の接続がブロックされている可能性があります。次のコマンドで確認してください:
# iptables -nvL
INPUT
チェインのパケットを拒否するようなルールがないか見て下さい。そして、必要であれば、次のようなコマンドでポートのブロックを解除します:
# iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT
ファイアウォールの設定に関する詳細はファイアウォールを見て下さい。
トラフィックがコンピュータにまで到達しているか?
以下のように問題のコンピュータのトラフィックを収集してみてください:
# tcpdump -lnn -i any port ssh and tcp-syn
上記は基本的な情報を表示します。トラフィックが表示されるまで待ってください。その後、接続を試行してみてください。何も出力がされない場合、コンピュータの外側にある何かがトラフィックを妨害しています (例: ハードウェアファイアウォールや NAT ルーターなど)。
ISP またはサードパーティによってデフォルトのポートがブロックされてないか?
ときどき ISP (インターネット・サービス・プロバイダ) が SSH のデフォルトポート (22番) をブロックしている場合があります。この場合はあなたが何をしても (ポートを開ける、スタックを強化する、フラッドアタックを防御する、など) 無意味になります。ブロックされているかどうか確認するために、全てのインターフェイス (0.0.0.0) をリッスンする sshd を立ち上げ、リモートから接続してみます。
このようなエラーメッセージが出る場合:
ssh: connect to host www.inet.hr port 22: Connection refused
これはそのポートが ISP にブロックされていないが、そのポートでサーバーの SSH が起動していないことを意味します (security through obscurity を参照)。
しかし、次のようなエラーメッセージが出る場合:
ssh: connect to host 111.222.333.444 port 22: Operation timed out
これは何かがポート 22 での TCP トラフィックを拒否 (reject) していることを意味します。そのポートはあなたのサーバー上のファイアーウォールか第三者 (ISP など) のどちらかによってステルスされています。自分のサーバーでファイアーウォールが起動していないことが確かなら、また、ルーターやスイッチの中でグレムリンが育っていないことが確かなら、ISP がトラフィックをブロックしています。
ダブルチェックのために、サーバ上で Wireshark を起動してポート 22 でのトラフィックをリッスンしてみましょう。Wireshark はレイヤ 2 のパケット・スニファリング・ユーティリティであり、TCP/UDP はレイヤ 3 以上なので (IP ネットワークスタックを参照)、もしリモートから接続するときに何も受け取っていなければ、十中八九、第三者がブロックしています。
問題診断
tcpdump または wireshark-cli パッケージの Wireshark をインストールしてください。
tcpdump の場合:
# tcpdump -ni interface "port 22"
Wireshark の場合:
$ tshark -f "tcp port 22" -i interface
interface
は WAN 接続に使っているネットワークインターフェイスに置き換えてください (確認したいときは ip a
を実行)。リモートで接続を試行してもパケットが全く受け取れない場合、ISP によってポート 22 のトラフィックがブロックされている可能性があります。
解決方法
解決方法は、単に ISP がブロックしていない他のポートを使うことです。/etc/ssh/sshd_config
を編集して他のポートを使うようにしましょう。例えば次を追加します:
Port 22 Port 1234
そしてこのファイル中の他の Port 設定をコメントアウトします。「Port 22」をコメントにして「Port 1234」を追加するだけでは、sshd がポート 1234 しかリッスンしなくなるので、この問題は解決しません。この 2 行どちらも使用し、sshd が両方のポートをリッスンするようにします。
あとは systemctl restart sshd.service
で sshd を起動するだけです。そして ssh クライアントでも同じポートに変更します。
Read from socket failed: connection reset by peer
最近の openssh のバージョンでは、楕円曲線暗号関連のバグのせいで、上記のエラーメッセージで接続が失敗することがあります。その場合 ~/.ssh/config
に次の行を追加してください:
HostKeyAlgorithms ssh-rsa-cert-v01@openssh.com,ssh-dss-cert-v01@openssh.com,ssh-rsa-cert-v00@openssh.com,ssh-dss-cert-v00@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,ssh-dss
openssh 5.9 では、上記の修正方法は働きません。代わりに、~/.ssh/config
に以下の行を記述してください:
Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc MACs hmac-md5,hmac-sha1,hmac-ripemd160
openssh バグフォーラムの 議論 も参照。
"[your shell]: No such file or directory" / ssh_exchange_identification 問題
シェルのバイナリに $PATH
が通っている場合でも、特定の SSH クライアントは $SHELL
に絶対パスを設定する必要があります (パスは whereis -b [your shell]
で確認できます)。
"Terminal unknown" や "Error opening terminal" エラーメッセージ
ssh でログイン時に "Terminal unknown" のようなエラーが表示されることがあります。これはサーバーがターミナルを認識できていないことを意味します。また、nano などの ncurses アプリケーションを実行すると "Error opening terminal" というメッセージが表示されます。
クライアントで使用しているターミナルの terminfo ファイルをサーバーにインストールするのが正しい解決方法です。これによってサーバーのコンソールプログラムがターミナルを正しく扱えるようになります。infocmp
を実行してからパッケージを確認することで terminfo に関する情報を取得できます。
通常の方法でファイルをインストールできない場合、サーバーのホームディレクトリに terminfo をコピーしてください:
$ ssh myserver mkdir -p ~/.terminfo/${TERM:0:1} $ scp /usr/share/terminfo/${TERM:0:1}/$TERM myserver:~/.terminfo/${TERM:0:1}/
サーバーから一度ログアウトしてからログインしなおすと問題が解決しているはずです。
$TERM 変数を設定する解決策
.bash_profile
などで TERM=xterm
と設定することでもエラーが表示されなくなり、ncurses アプリケーションが動作するようになります。ただし副作用があり、ターミナルの制御シーケンスが xterm と異なっている場合グラフィックがおかしくなります。
Connection closed by x.x.x.x [preauth]
sshd のログでこのエラーが確認できる場合、HostKey が正しく設定されているか確認してください:
HostKey /etc/ssh/ssh_host_rsa_key
サブシステム要求の失敗
OpenSSH 8.8 以降、scp はデータ転送のデフォルトプロトコルとして SFTP を使用し、sftp
というサブシステムを要求するようになりました。もし scp を冗長モード scp -v
で実行すれば、クライアントがどのサブシステムを使っているかがわかります (例: Sending subsystem: <subsystem-name>
)。subsystem request failed on channel 0
のようなエラーは、サーバのサブシステムを設定することで修正できるかもしれません。{man|5|sshd_config|Subsystem}} のように設定します。サーバの設定は、以下の例のようにする必要があります。
/etc/ssh/sshd_config
... Subsystem subsystem-name /path/to/subsystem-executable ...
OpenSSH 7.0 によって id_dsa が拒否される
OpenSSH 7.0 ではセキュリティ上の理由から ssh-dss が無効になっています。どうしても有効にする必要がある場合、クライアントの設定オプションを使用してください ([2] には書かれていない方法です):
PubkeyAcceptedKeyTypes +ssh-dss
OpenSSH 7.0 で No matching key exchange method found
OpenSSH 7.0 では Logjam 攻撃からの(理論上の)脆弱性を理由に diffie-hellman-group1-sha1 鍵アルゴリズムが無効になっています (http://www.openssh.com/legacy.html を参照)。特定のホストで鍵アルゴリズムが必要な場合、ssh は以下のようなエラーメッセージを吐きます:
Unable to negotiate with 127.0.0.1: no matching key exchange method found. Their offer: diffie-hellman-group1-sha1
古いアルゴリズムを使用しないようにサーバーをアップグレード・設定することで上記のエラーは解決します。サーバー側の設定を変更できない場合、クライアント設定で KexAlgorithms +diffie-hellman-group1-sha1
オプションを使うことでアルゴリズムを有効化できます。
SSH から切断したときに tmux/screen セッションが終了する
セッションの最後にプロセスが終了する場合、ソケットアクティベーションを使っているために SSH セッションプロセスが終了したときに systemd によって終了されている可能性があります。ssh.socket
の代わりに ssh.service
を使用してソケットアクティベーションを使わないことで解決できます。もしくは ssh@.service
の Service セクションで KillMode=process
を設定してください。
KillMode=process
は古典的な ssh.service
でも役に立つことがあります。サーバーが停止したり再起動したときに SSH セッションプロセスや screen や tmux のプロセスが終了されることを防ぐことができます。
SSH セッションが応答しなくなる
SSH は Software flow control XON
と XOFF
に反応します。Ctrl+s
を押すとフリーズ/ハングアップ/応答停止します。セッションを再開するには Ctrl+q
を使ってください。
Broken pipe
接続を作ろうとして packet_write_wait
のレスポンスが Broken pipe
になった場合、デバッグモードで接続を再試行し、出力がエラーで終わるかどうか確認する必要があります。
debug3: send packet: type 1 packet_write_wait: Connection to A.B.C.D port 22: Broken pipe
上記の send packet
の行は、応答パケットが受信されなかったことを示しています。つまり、これは QoS の問題であることがわかります。パケットを落とす可能性を減らすには、IPQoS
を設定してください。
/etc/ssh/ssh_config
Host * IPQoS reliability
サービスタイプは reliability
(0x04
) で、0x00
や throughput
(0x08
) と同様に問題を解決できるはずです。
再起動後デーモンの起動が遅い
特にヘッドレスサーバや仮想化サーバにおいて、再起動後のデーモン起動時間が過度に長くなる場合(例:デーモンが接続を受け付け始めるまでに数分かかる)、エントロピー不足が原因かもしれません [3] これは、あなたのシステムに適した Rng-tools や Haveged をインストールすれば改善することが可能です。ただし、それぞれのパッケージの wiki ページで説明されている、関連するセキュリティ上の影響に注意してください。
応答のない SSH 接続を終了する
クライアントセッションが応答しなくなり、実行中のプログラム (例えば シェル) に終了を指示しても終了しない場合、 Enter
, ~
, .
をこの順番で次々に押すと、セッションを終了させることが可能です。
この ~
は疑似端末エスケープ文字で (ssh(1) § ESCAPE CHARACTERS 参照) 終了させるクライアントセッションに応じて複数回付加することが可能です。例えば、A から B へ接続し、B から C へ接続したときに B から C へのセッションがフリーズした場合、 Enter
を押して ~.
と入力すれば、B の作業セッションを残して終了させることができます。
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
クライアントが ssh サーバの鍵が変更されたことを警告した場合、新しく提供された鍵が 本当にサーバオペレータのものであるかどうかを確認する必要があります。そして known_hosts
ファイルから古い鍵を ssh-keygen -R $SSH_HOST
で削除し、新しい鍵を新しいサーバであるかのように受け入れてください。
適切な terminfo エントリがないリモートに接続する場合
端末の terminfo エントリがないホストに接続する場合、例えば、terminfo エントリが ncurses に同梱されていない端末エミュレータを使用する場合などです。(例えば kitty や rxvt-unicode) terminfo データベースが制限されているホスト (例えば OpenWrt が動いているシステム) に接続する場合、terminfo(5) に依存しているソフトウェアで様々な問題が発生する可能性があります。
適切な解決策は、適切な terminfo エントリをホストに設置することです。それが不可能な場合は、リモートホストがサポートし、かつ端末と互換性のある値を TERM
に設定することで解決できます。
OpenSSH 8.7 以降、カスタムの TERM
環境変数をリモートホストに渡すには、 簡単な設定スニペットを使います。
~/.ssh/config
Host example.com SendEnv TERM=xterm-256color
参照
- Defending against brute force ssh attacks
- IBM developerWorks の OpenSSH キー (鍵) の管理: 第 1 回 と 第 2 回
- Secure Secure Shell