LinuxやBSDなどのサーバを扱う上で欠かせないSSH(Secure SHell)ですが、鍵の管理や認証まわりで多くのトラブルが起こり得ます。SSHは、サーバのオペレーションだけではなく、Gitなどの開発ツールでの利用、ソースコードのデプロイなどでも欠かせないプロトコルとなっており、利用する機会は非常に増えています。
第2回では、読者の多くは公開鍵認証方式でのSSHを利用していると想定して、パスワード認証方式ではなく、公開鍵認証方式を使ったSSHでよくあるトラブルとその対策について触れていきます(SSHの接続元をクライアント、接続先をサーバとして解説します) 。
トラシュー事例1
「ログインできない場合にありがちなこと」
SSHでサーバにログインしようとしたとき、「 Permission denied, please try again.」の表示や、公開鍵認証なのにパスワードを求められてログインできないという事象は、多くの方が遭遇したことがあるかと思います。この場合によくある代表的な原因をいくつか挙げます。
鍵のミスマッチ
sshコマンドの「-i」オプションで指定した秘密鍵、もしくはssh-add コマンドでssh-agentに登録した秘密鍵(ssh-agentの詳細については後述しています)と、サーバに設置してある公開鍵が一致しない場合はログインすることはできません。正しい鍵を用意して置き換えることができれば対処は簡単ですが、鍵を比較しないと正しいかどうか判断できません。どの鍵と一致しているかどうかを確認する方法の1つとして、それぞれの鍵のフィンガープリントを比較する方法があります。
フィンガープリントとは、公開鍵からハッシュ関数を使って作られるユニークな文字列です。ssh-keygenコマンドの「-l」オプションを使って、鍵のフィンガープリントを表示できます。
$ ssh-keygen -l -f key_file
2048 e6:ef:57:c6:20:b6:40:1c:44:cd:cb:8f:fc:90:40:5d username@hostname (RSA)
このコマンドは、複数の鍵を管理している場合などで、キーペアを確認することに役立ちます。万が一、公開鍵を紛失した場合や、何の公開鍵かがわからなくなってしまった場合は、秘密鍵から公開鍵を生成することもできます。ssh-keygenコマンドの「-y」オプションを使うことで、秘密鍵をもとに公開鍵を表示できます(パスフレーズが設定されている場合はパスフレーズの入力が必要です) 。
$ ssh-keygen -y -f secret_key_file
ssh-rsa AAAAB3NzaC1yc2……
パーミッションの問題
手元の秘密鍵のパーミッションに問題があるとログインできません。たとえば、秘密鍵ファイルのパーミッションが0644の場合、sshコマンド実行時に図1 のようなエラーが発生します。
図1 秘密鍵ファイルのパーミッションに問題がある場合
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '.ssh/id_rsa' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
bad permissions: ignore key: .ssh/id_rsa
Permission denied (publickey).
秘密鍵のファイルは、所有者のみが読み書きできるように、パーミッションを0600に設定しておくことが必要です。
シェルのスタートアップファイルの問題
ログイン時に読み込まれるシェルのスタートアップファイル($HOME/.bashrcなど)の記述に問題があるとSSHやSCP(Secure Copy)ができない場合があります。
たとえば、接続先のサーバの$HOME/.bashrcの先頭に別のシェル(たとえば/bin/zshなど)を起動するように設定していると、SCPができなくなったり、SSH越しのコマンド実行ができなくなります。SSHでログインはできるけどSCPができないといった場合には、スタートアップファイルを疑ってみましょう。
ホスト認証用の公開鍵とknown_hostsの不一致
初めてサーバに接続すると、サーバが持つホスト認証用の公開鍵がクライアント側の$HOME/.ssh/known_hostsへ記録されます。known_hostsに公開鍵が記録されたままで、サーバが持つ公開鍵が何らかの原因で変わった場合、ssh接続時にエラーが発生します(図2 ) 。
図2 ホスト認証用公開鍵とknown_hostsの不一致時に起きるエラー
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
1a:91:35:c3:58:2a:21:cb:de:72:02:51:37:2d:83:64.
Please contact your system administrator.
Add correct host key in /home/isao.shimizu/.ssh/known_hosts to get rid of this message.
Offending RSA key in /home/isao.shimizu/.ssh/known_hosts:9
RSA host key for 192.168.0.10 has changed and you have requested strict checking.
Host key verification failed.
サーバ側のホストキーの再生成や、OSの再インストール時などによって遭遇することが多くありますが、そうではない場合は、意図せずなりすまされた別のサーバに接続しようとしている可能性もあるので注意が必要です。前者の場合は、とくに問題はないため、known_hostsファイルから使わなくなった公開鍵を削除することで解決します。削除はssh-keygenコマンドの「-R」オプションを使います。
$ ssh-keygen -R ホスト名
/home/isao.shimizu/.ssh/known_hosts updated.
Original contents retained as /home/isao.shimizu/.ssh/known_hosts.old
ここまでは、とくに基本的なログインに関わるトラブルと対策について解説してきました。
しかし、SSH をより深く使っていくとさらなるトラブルに遭遇するかもしれません。次は少し上級者向けのトラブル事例と対策について解説していきます。
トラシュー事例2
「SSHのログインが遅い」
sshコマンド実行後、ログインに何秒も待たされる場合があります。これはサーバ側のsshd_configの設定が原因というケースが多くあります。よくある原因としてはホストの名前解決によるもので、sshdにはUseDNSという設定値があり、デフォルトでyesが設定されています[1] 。
この設定が有効の場合、sshでクライアントからサーバへ接続する際に、接続元のクライアントのIPアドレスから逆引きしてホスト名を取得し、さらに正引きしてIPアドレスに戻して、もとのIPアドレスと同じになるかどうかをsshdがチェックします。サーバ側のDNSの設定(たとえば、resolv.conf、hosts、nsswitch.confなど)が影響して、この名前解決がうまくいかずにタイムアウトを待ってしまう場合に、ログインが非常に遅くなってしまいます。こういった場合は、UseDNSをnoに設定して問題を回避できます。
もう1つ、ログインが遅くなる原因として、GSSAPIによる認証が有効になっていることが挙げられます。GSSAPIとは、Generic Security Standard Application Programming Interfaceの略で、RFC 2743で定義されている認証方式です。Kerberosと組み合わせて使われることが多く、シングルサインオンを目的として使われています。sshd_configにはGSSAPIAuthenticationという設定が用意されており、デフォルトはnoとなっているため、影響はほとんどないと思われますが、もしyesになっていてシングルサインオンなどでGSSAPIを使っていない場合はnoにして無効にしておきましょう。
トラシュー事例3
「エージェント転送がうまくいかない」
SSHにはエージェント転送という機能があり、正しく使いこなせると非常に便利で、SSHに関わる作業効率が向上するでしょう。しかし、正しく理解していないと使い方を間違っていたり、エージェントが機能していなかったりとトラブルが多く、便利な機能にもかかわらず結局使っていないといったことをよく見かけます。まずは、エージェント転送の基本的な動作について解説します。
エージェント転送について理解を深める前に、SSHにおける公開鍵認証の流れを見てみましょう(図3 ) 。
図3 SSHにおける公開鍵認証
公開鍵認証方式では、図3のように秘密鍵と公開鍵を使って認証が行われます。この例では、クライアントから1台のサーバへ接続するような非常にシンプルな構成ですが、実際の運用ではサーバからさらに別のサーバへログインすることが多くあります。いわゆる「踏み台」となるサーバを経由して、その先にある複数のサーバへログインするというケースです。この場合に秘密鍵の置き場所はどのようにすべきでしょうか。
秘密鍵を踏み台となるサーバに設置することで、さらに先のサーバへログインすることはできますが、秘密鍵をサーバに置くことはお勧めできません。万が一、秘密鍵が置いてあるサーバに誰でもアクセスできてしまうことになってしまい、秘密鍵を奪われた場合、あらゆるサーバへログインすることが可能になるかもしれません。秘密鍵は特別な理由がない限りは手元に置きましょう。
それでは、手元にしかない秘密鍵を使って、さらに先のサーバへログインするにはどうしたらよいでしょうか。ここで、エージェント転送と呼ばれる機能を使います。エージェント転送を使うことで、クライアントから踏み台となるサーバへログインし、そこからさらに別のサーバへログインすることができます。
エージェント転送を使うには、ssh-agentコマンドを利用します。ssh-agentを起動すると、鍵の転送用にUNIXドメインソケットを作成し、SSH_AUTH_SOCK環境変数にソケットのパスを設定します。
次に、ssh-addコマンドによって秘密鍵をエージェントに追加します。この状態で、sshコマンドを使ってサーバへログインしようとすると、sshはSSH_AUTH_SOCK環境変数の有無をチェックして、エージェントに登録されている秘密鍵を使って認証します。このとき、sshコマンドでエージェント転送オプションである「-A」オプションを利用しておくと、接続先のサーバでUNIXドメインソケットを作成し、SSH_AUTH_SOCK環境変数をログイン後のシェルに設定します。こうすることで接続先のサーバでも手元で起動したssh-agentに保存された秘密鍵を利用することが可能になります(図4 ) 。
図4 SSHエージェント転送の例
エージェント転送が期待した動作をしない場合は、まずはエージェントが正しく起動しているか、そしてエージェントに保存されている鍵が何かを確認してみましょう。ssh-addコマンドの「-l」オプションを使うことで、保存されているすべての鍵のフィンガープリントを確認できます。
まとめ
SSHにかかわるコマンドやオプション、設定ファイル、設定項目は決して少なくありませんが、非常によく起きるトラブルに対して押さえておくべき基本的な点について解説しました。もし、すでにSSHに関して何か問題を抱えている場合は、今回の内容が少しでも問題の解決につながれば幸いです。
LinuxやBSDでSSHを使う場合、OpenSSHを使うことがほとんどだと思いますが、OpenSSHは現在もバージョンアップが続けられており、バグフィックスや新機能が多く追加されています。バグによってトラブルが起きている場合はバージョンアップによって解決するかもしれません。新たなトラブルが起きた場合は、sshやsshdのデバッグログを出力して動作を確認することや、manコマンドを活用して各種設定項目の詳細について理解を深めることで解決につながることでしょう。
.ssh/configを有効活用しよう
sshコマンドを使う際、毎回同じオプションやユーザ名、IPアドレスなどを入力するのは無駄が多かったり、オプションの設定漏れなどのトラブルが起きやすいため、$HOME/.ssh/configというsshコマンドで共通に使われる設定ファイルを用意しましょう。設定ファイルには、接続するすべてのサーバ共通のオプション、サーバごとのオプションを定義できます。ここではとくに便利だと思われる設定について触れていきます。
まずは、ForwardAgent。yesを設定すると「-A」オプションと同等で、エージェント転送が有効になります。次にHost。ホスト単位の設定で、ここで定義した名前はsshのホスト名の代替として使うことができます。
たとえば、Host server01と設定した場合は、ssh server01でログインできます。それ以降の行に書いた設定はホストに関する設定となり、次のHostの定義が出現するまで続きます。HostNameはホストのIPアドレスかホスト名を記述、Portはポート番号、Userはログインユーザ名、IdentityFileは秘密鍵ファイルのパスになります。
さらにポートフォワードの設定もできます。LocalForwardで接続先サーバのポートをローカルに転送することができます。DynamicForwardを設定すると接続先のサーバをSOCKS Proxyとして使うことができます。
.ssh/configで設定できる個別の項目について、詳細はここでは詳しく述べませんが、このほかにもいろいろと便利な設定ができます。詳細についてはman ssh_configで確認してみましょう。
第1特集
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識
第2特集
「知りたい」「 使いたい」「 発信したい」をかなえる
OSSソースコードリーディングのススメ
特別企画
企業のシステムを支えるOSとエコシステムの全貌
[特別企画]Red Hat Enterprise Linux 9最新ガイド
短期連載
今さら聞けないSSH
[前編]リモートログインとコマンドの実行
短期連載
MySQLで学ぶ文字コード
[最終回]文字コードのハマりどころTips集
短期連載
新生「Ansible」徹底解説
[4]Playbookの実行環境(基礎編)