Last Updated at $Date: 2016/09/06 21:55:08 $.
SSH (Secure SHell) は, rsh/rlogin/rcp などの r 系コマンドを代替するために作られたプログラムです. 広く知られているように, r 系コマンドは認証手段が非常に貧弱なので,クラッカーの格好の標的となってしまいます. 対して,SSH は,公開鍵暗号に基づく強力な認証手段と通信路の暗号化により, 信用できないネットワーク上で安全な通信を行う方法を提供します.
セキュリティ上の理由から, アクセスを受け付けるホストを制限しているメールサーバーやニュースサーバーが増えています. このようなサーバーに接続する場合,SSH の Port Forwarding の機能を利用することによって, 特定のホストから通信しているように見せかけることができます. また,SSH は Port Forwarding の対象となるネットワーク接続を暗号化しますから, 様々な通信を安全に行うことが出来るようにもなります.
この方法の具体例については, FAQ の『4.8 ftp や POP などのサービスを安全にするために ssh が使えますか?』が大変参考になります.
portfwd.el
を使うと,Emacs 上で動作するプログラムの通信を
Port Forwarding によって中継することができます.
ただし,SSH によって作成された Port Forwarding 用の通信経路は, クライアント上の全ての local process から接続することができます. したがって,「クライアント上の全ての local user が信用できる」という環境でなければ, Port Forwarding の利用はセキュリティ上の危険を増加させる可能性があります.
OpenSSH-5.4 より新しい SSH を使っている場合は,netcat mode を使う方法がもっとも簡単です.
~/.fetchmailrc
に,以下のように設定してください.
poll pop3-server protocol apop plugin "ssh -C ssh-server -W pop3-server:%p" username "username" password "password"
OpenSSH-5.4 より古い SSH を使っている場合,中継サーバで netcat 相当のコマンドが利用できれば,以下のように設定すると netcat mode と同様に動作します.
poll pop3-server protocol apop plugin "ssh -C ssh-server connect pop3-server %p" username "username" password "password"
この設定例では,netcat 相当のコマンドとして connect
コマンドを使っています.
Debian GNU/Linux では connect-proxy
という名前のパッケージになっていますから,中継サーバで apt-get install connect-proxy
とコマンドを実行してインストールしてください.中継サーバに netcat 相当のコマンドがインストールされていない場合は,個人のホームディレクトリ以下にインストールして適切に環境設定すれば,利用できるようになります.
OpenSSH-5.4 より古い SSH を使っていて,中継サーバで netcat 相当のコマンドも利用できない場合は,以下のように設定して Port Forwarding を使いましょう.
poll pop3-server via localhost port 8236 protocol apop preconnect 'ssh -f -P -C -L 8236:pop3-server:110 ssh-server sleep 15 </dev/null >/dev/null 2>&1' username "username" password "password"
しかし,Port Fowarding を使う方法では,指定されたポート 8236 番が何らかの理由で使用中の場合にうまく接続できません. そのため,2012年現在では netcat mode を使う方法が最もおすすめです.
普段使っているコマンドなのに,SSH 経由で実行すると, 以下のようにエラーメッセージが出力されて実行できないことがあります.
[client:~]% ssh server command Command not found
この問題は,ログイン先のホストでシェルが実行されているかどうかの違いに原因があります.
あるホストにログインして対話的に利用する場合には,
通常のシェル初期化ファイル(~/.bashrc
や ~/.cshrc
など)が読み込まれるので,環境変数 PATH は普段通りに設定されます.
しかし,この例のように単にコマンドを実行するだけの場合は,
ログイン先のホストではシェルは呼び出されないため,環境変数 PATH が設定されません.
その結果,普段使っているコマンドなのに見つからない,ということが起こります.
SSH はコマンド実行時の環境変数を ~/.ssh/environment
というファイルから読み込むようになっています.したがって,以下のコマンドを実行して,
~/.ssh/environment
を用意しておくと,
普段通りの環境変数が設定されるようになり,コマンドもきちんと見つかるようになります.
[client:~]% ssh server [server:~]% printenv | egrep '^PATH=' > ~/.ssh/environment
しかし,NFS などでホームディレクトリを共有している異種混在環境にログインする場合には,
上述のような静的な手法では対処できません.最近の OpenSSH であれば,
~/.ssh/rc
というファイルにシェルスクリプトを記述することができ,
そのような環境でも対応することができます.
以下は,私の使っている ~/.ssh/rc
です.
if [ -d /var/lib/dpkg ]; then PATH=/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games else PATH=/usr/local/bin/X11:/usr/local/bin:/usr/bin:/bin:/usr/ccs/bin:/usr/ucb fi if read proto cookie && [ -n "$DISPLAY" ]; then if [ `echo $DISPLAY | cut -c1-10` = 'localhost:' ]; then echo add unix:`echo $DISPLAY | cut -c11-` $proto $cookie else echo add $DISPLAY $proto $cookie fi | xauth -q - fi
なお,上記スクリプトの後半部分は,X11転送を処理するためのスクリプトです. 詳しくは sshd のマニュアルを参照してください.
OpenSSH には,単体で SOCKS 経由の通信を行う機能はありません. その代わりに,ProxyCommand というオプションを利用して, SOCKS サーバーに接続する外部プログラムを呼び出すことによって通信を行います.
ProxyCommand には,指定されたホスト・ポートに対する通信を標準入出力に
redirect するコマンドを指定します.そのようなコマンドとして,後藤さんによって作られた
connect
コマンドを利用することができます.具体的には,以下のように ~/.ssh/config
に設定します.
Host * ProxyCommand connect -S socks-server %h %p
connect
コマンドは,Debian GNU/Linux では connect-proxy
という名前のパッケージになっています.SSH を実行する端末で apt-get install connect-proxy
してインストールしてください.
防火壁の外から防火壁内のマシンにログインする場合,指定された中継サーバにログインしなければならない,という状況は珍しくありません.
OpenSSH-5.6 より新しい SSH を使っている場合は,netcat mode と複数セッションの共有を組み合わせて使う方法がもっともお勧めです.
以下のように ~/.ssh/config
に設定して下さい.
Host relay-server.example.net ProxyCommand none ControlMaster auto ControlPath ~/.ssh/mux-%r@%h:%p ControlPersist 10 Host *.example.net ProxyCommand ssh -C relay-server.example.net -W %h:%p
なお,中継サーバ上の SSH が OpenSSH-5.4 より古くても netcat mode は動作しますから,手元の端末で動作する SSH だけを更新すれば netcat mode が使えるようになります.どうしても OpenSSH-5.4 以後に更新できない場合,中継サーバで netcat 相当のコマンドが利用できれば,以下のように設定すると netcat mode と同様に動作します.
Host relay-server.example.net ProxyCommand none Host *.example.net ProxyCommand ssh -C relay-server.example.net connect %h %p
この設定例では,netcat 相当のコマンドとして connect
コマンドを使っています.
Debian GNU/Linux では connect-proxy
という名前のパッケージになっていますから,中継サーバで apt-get install connect-proxy
とコマンドを実行してインストールしてください.中継サーバに netcat 相当のコマンドがインストールされていない場合は,個人のホームディレクトリ以下にインストールして適切に環境設定すれば,利用できるようになります.
外部ネットワークと DMZ の間に防火壁,さらに DMZ と内部ネットワークの間にも防火壁というように防火壁が多段になっている場合,以下のように設定すると自動的に中継サーバを2つ経由して接続します.
# local-machine --> firset-relay-server --> second-relay-server --> target-machine と接続する Host first-relay-server.example.net ProxyCommand none Host second-relay-server.example.net ProxyCommand ssh -C first-relay-server.example.net -W %h:%p Host *.example.net ProxyCommand ssh -C second-relay-server.example.net -W %h:%p
逆に,防火壁の中から防火壁の外に通信する時に中継サーバを経由する必要がある場合は,以下のように設定してください.
Host *.example.net ProxyCommand none Host *.* ProxyCommand ssh -C relay-server.example.net -W %h:%p
防火壁外にいる時に,防火壁内の WWW サーバなどに対する透過的な接続が必要になってしまう場合があります. 防火壁内のマシンで root になることができれば,PPP over SSH などの様々な手法で VPN を構成してしまうことが可能ですが,防火壁内のマシンで root になれない場合には,そうはいきません. そのような場合には,
という構成で PPP over SSH の設定を行うことにより,防火壁内での権限なしに通信経路を設置することができます.
slirp は,モデムや telnet/rsh 経由の端末接続しか提供されていない環境で, PPP 接続を模擬するためのソフトウェアです. かつて,まだ PPP 接続を提供しているプロバイダが一般的ではなく, 大学の情報処理センターの端末接続しか利用できなかった場合にお世話になりましたが, こんなところで再び使うことになるとは….
なお,以下の説明は Debian での pppd の設定ファイルの配置に依存していますので, それ以外の環境では,それぞれ適当に置き換えてください.
% ssh-keygen -t dsa -N '' -f ~/.ssh/vpn_dsa
~/.ssh/authorized_keys
に追加します.
その上で,その公開鍵の行の先頭に以下のように指定して,
slirp 以外は実行できないようにしておきます.
command="slirp -P",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-dss ...
% ssh -C -x -t -e none -i ~/.ssh/vpn_dsa in.example.net slirp -P
/etc/ppp/peers/example
を用意します.
防火壁外のマシン用の IP アドレスとして 10.0.2.15, 防火壁内のマシン用の IP アドレスとして 10.0.2.2 を指定していますが, これは slirp のデフォルト設定に合わせています.詳しくは slirp(1) を参照してください.hide-password noauth pty "ssh -C -x -t -e none -i /home/vpn/.ssh/vpn_dsa in.example.net slirp -P" 10.0.2.15:10.0.2.2 #debug ipparam example nodefaultroute noipdefault
pon example
というコマンドで接続できるようになっているはずです.
接続してみて,10.0.2.2 が見えていることを確認してください.
/etc/ppp/ip-up.d/example
を用意します.
#!/bin/sh PATH=/bin:/usr/bin:/sbin:/usr/sbin if [ x"$6" = xexample ]; then route add -net `echo "$4"|sed 's,[0-9][0-9]*$,0,'` netmask 255.255.255.240 "$1" route add -net x.y.z.0 netmask 255.255.255.0 gw "$5" fi
/etc/resolv.conf
に指定されている DNS サーバに転送してくれます.
そのため,2通りの方法があります.
手元の防火壁外のマシンの /etc/resolv.conf
に以下のように指定すれば良いです.
nameserver 10.0.2.3
手元の防火壁外のマシンに dnsmasq
をインストールしましょう.その上で,以下の指定を /etc/dnsmasq.conf
に加えておきます.
server=/.example.net/10.0.2.3
このように設定して,防火壁内部のマシンに対する接続が必要になったら pon example, 不要になったら poff example として使います.
ここまでの方法は,接続が必要になると手動で接続を作らなければならない,という不便さがありました. これを解決するには,手元の防火壁外のマシンには pppd を常時実行しておき, 通信が発生すると自動的に接続を開始するように設定します.ただ,こちらの方法はかなり複雑になります.
/home/vpn/connect
を用意します.
#!/bin/sh PATH=/bin:/usr/bin:/sbin:/usr/sbin dir=/home/vpn file=${dir}/example-ondemand.pid case "$1" in start) echo "$$" > ${file} exec ssh -C -x -t -e none -i ${dir}/.ssh/vpn_dsa in.example.net slirp -P ;; stop) if [ -r ${file} ]; then pid=`cat ${file}` if [ ! -z ${pid} ]; then kill ${pid} fi rm ${file} fi ;; esac
/etc/ppp/peers/example-ondemand
を用意します.
hide-password noauth pty "su vpn /home/vpn/connect start" 10.0.2.15:10.0.2.2 #debug ipparam example-ondemand nodefaultroute noipdefault demand idle 90 holdoff 60
/etc/init.d/example-ondemand
を用意して,適当なタイミングで実行されるように /etc/rc3.d/
にリンクを作っておきます.
#!/bin/sh PATH=/bin:/usr/bin:/sbin:/usr/sbin PEER=example-ondemand INTERFACE=ppp0 start(){ pon ${PEER} for sec in 1 2 3; do if ( grep -q ${INTERFACE}: /proc/net/dev ); then break fi sleep ${sec} done route add -net 10.0.2.0 netmask 255.255.255.240 ${INTERFACE} route add -net x.y.z.0 netmask 255.255.255.0 gw 10.0.2.2 } stop(){ poff ${PEER} } case "$1" in start) start ;; stop) stop ;; restart|force-reload) stop start ;; esac
# /etc/init.d/example-ondemand start
/etc/ppp/ip-down.d/example-ondemand
を用意して,
明示的に子プロセスを停止するように工夫します.
#!/bin/sh if [ x"$6" = xexample-ondemand ]; then su vpn /home/vpn/connect stop fi
Hostbased 認証とは,登録済みのクライアントから接続があった場合は, そのクライアント上でのユーザー情報を信用して接続を許可する認証方式です. 多くのクライアントとサーバーが稼働しているサイトで適切に利用すると, ユーザーのパスワード入力の手間を減らすことができます. 設定手順は以下の通りです.
Hostbased 認証は,クライアントのホスト鍵,特に秘密鍵の秘匿性に基づいて認証を行っています. したがって,秘密鍵のファイル([client:~]# ssh-keygen -t rsa -N '' -f /etc/ssh/ssh_host_rsa_key
/etc/ssh/ssh_host_rsa_key
)の読み取り許可属性が適切に設定されていることを確認しておく必要があります.
[client:~]$ ls -l /etc/ssh/ssh_host_rsa_key -rw------- 1 root root 883 2002-01-01 12:00 /etc/ssh/ssh_host_rsa_key
/etc/ssh/ssh_config
に以下の設定を追加して,server に対して通信を行う時は
Hostbased 認証を利用するように指定します.
OpenSSH-3.8 以後は,以下の指定も加える必要があります.Host server HostbasedAuthentication yes
この指定は,対象となるホストが指定されていない部分に書く必要があります.EnableSSHKeysign yes
/etc/ssh/sshd_config
に以下の設定を追加して,Hostbased 認証を受け付けるように指定します.
以下は Hostbased 認証と直接の関係はありませんが,一般ユーザーによる Hostbased 認証の乱用を禁止するために設定しておくべき項目です.HostbasedAuthentication yes
IgnoreRhosts yes IgnoreUserKnownHosts yes RhostsAuthentication no RhostsRSAAuthentication no
[server:~]# ssh-keyscan -t rsa client,address >> /etc/ssh/ssh_known_hosts
[server:~]# echo client >> /etc/ssh/shosts.equiv
これで,クライアントからサーバーに対してパスワード入力を省略して ssh 経由でログインすることができるようになっているはず‥‥でしたが, 私は以下のような問題で苦労しました.
OpenSSH-3.4p1 はバグのために,Hostbased 認証が正常に機能しないようです. パッチを適用する必要があります.
ProxyCommand と Hostbased 認証は共存できません. ProxyCommand が設定されている状態で Hostbased 認証を行おうとすると,以下のようなエラーメッセージが表示されます.
% ssh server userauth_hostbased: cannot get local ipaddr/name
OpenSSH-3.6p1 以降は ProxyCommand の設定を無効化することができるらしいので, Hostbased 認証を行うホストを対象として ProxyCommand を無効化するという設定を試してみたのですが,手元では正しく動作しませんでした. したがって,現在のところ簡単な解決策はなく, ProxyCommand を利用するホストを小まめに指定するしかないようです.
Hostbased 認証には,クライアントの秘密鍵の情報が必要です.
通常,秘密鍵は root 以外は読み出せないようになっていますから,
OpenSSH は ssh-keysign
という setuid root
された補助プログラムを利用して認証を行っています.
補助プログラムが setuid root されていないと, 以下のようなエラーメッセージが表示されました.
% ssh server could not open any host key ssh_keysign: no reply key_sign failed
Debian の場合は,dpkg-recongfigure ssh というコマンドを実行して, "Do you want /usr/lib/ssh-keysign to be installed SUID root?" という質問に「Yes」と答えると,適切な実行許可属性に設定されます. それ以外の環境では,以下のコマンドで修正できるでしょう.
# chmod u+s /usr/lib/ssh-keysign
久しぶりに,OpenSSH ではなく商用の SSH がインストールされている環境を使うことになったのですが, 公開鍵認証によるログインがうまくいかずに,かなり悩みました.
ssh-keygen -x -f ~/.ssh/id_rsa.pub > ~/id_rsa.pub
~/.ssh/authorized_keys
に,全ての公開鍵を直接書き込みます.
公開鍵は全て個別に保存(例えば,~/.ssh2/id_rsa.pub
)し,
そのファイルの場所を ~/.ssh2/authorization
に書き込みます.
key id_rsa.pub
より詳しい情報は, 「OpenSSH と SSH の相互運用」や 「相互運用のための鍵管理」を参照してください.
クライアント側の設定に問題があると考えられる場合は, -v オプションを指定してクライアントを実行した結果を調べると原因が分かることがあります.
% ssh -v server
サーバー側の設定に問題があると考えられる場合は, -d -d -d オプションを指定してサーバーを実行した結果が有用です.
# /etc/init.d/ssh stop # /usr/sbin/sshd -d -d -d
[Top] / [SSH について]