Hadoop運用 >
目次 †
基本 †
on Windows †
最新のWindows10にはすでにOpenSSHが含まれています。以下のようにSSHエージェントサービスを起動させ、公開鍵認証を使用するとよいでしょう。
PowerShell? を管理者として起動し、以下のコマンドでSSHエージェントサービスを常時起動させるようにします。
PS C:\> Set-Service ssh-agent -StartupType Automatic
PS C:\> Start-Service ssh-agent
PS C:\> Get-Service ssh-agent
これ以降はLinux環境の場合と同様に設定します。
秘密鍵を登録します。鍵へのパスを明示しない場合のデフォルトは、%USERPROFILE%\.ssh 以下にあるそれぞれの鍵です。
PS C:\> ssh-add C:\path\to\private.key
...
PS C:\> ssh-add -l
設定ファイルのデフォルトも %USERPROFILE%\.ssh\config になりますので適宜設定します。
Host dev.example.com
HostName dev.example.com
User alice
IdentityFile C:\Users\alice\.ssh\id_rsa
ForwardAgent yes
ssh コマンドで接続します。
SSH-CA認証 †
概要 †
SSH-CA認証は、認証局(CA)によって署名された公開鍵証明書によってホストとユーザ(クライアント)を相互に認証する仕組です。
これは、ver. 5.4 より追加された機能です。ただし、証明書のフォーマットがX.509のものと異なる点には注意が必要です。
CentOS6.7のOpenSSHは5.3p1ですが、SSH-CA認証を利用可能です。
各ノードには、認証局の公開鍵そのもの(自己署名証明書ではない)と自ホストの公開鍵証明書を配備すればよく、運用がシンプルになります。従来の公開鍵認証のように各ユーザの公開鍵をノードに配布する必要はありません。
設定によりますが、SSH-CA認証を満たさない場合にはこれまでの認証方法*1 にフォールバックできますので、ユーザ認証のみに用いることなども可能です。
SSH認証局の準備 †
認証局のキーペアを生成し、漏洩しないように厳重に守ります。
$ ssh-keygen -t rsa -b 4096 -C 'grid-ssh-ca' -f grid_ssh_ca
$ sudo chown root:root grid_ssh_ca
$ sudo chmod 400 grid_ssh_ca
公開鍵は必要なノードに配布します。
ユーザの認証 †
サーバ側の設定 †
フィンガープリントを確認の上、認証局の公開鍵ファイルを適切な場所に配備します。複数の認証局を登録する場合には、一行に一公開鍵を追加します(#で始まるコメント行や空行は無視されます)。
/etc/ssh/sshd_config : サーバの設定に以下のような内容を追加し、サービスを再起動して反映させます。
TrustedUserCAKeys / etc/ ssl/ certs/ grid_ssh_ca.pub
クライアント側の設定 †
自らの公開鍵を認証局に送付し、署名を依頼します。
認証局は以下の要領で署名し、証明書(以下の例では、id_rsa-cert .pub になります)をユーザに返送します。
$ sudo ssh-keygen -s /path/to/grid_ssh_ca -I alice-certificate -n alice -V +365d id_rsa.pub
署名時には -O オプションでユーザに許可される機能を制限することができます。設定できるオプションは以下のとおりです。
-O option
Specify a certificate option when signing a key. This option may
be specified multiple times. Please see the CERTIFICATES section
for details. The options that are valid for user certificates
are:
clear Clear all enabled permissions. This is useful for clear‐
ing the default set of permissions so permissions may be
added individually.
force-command=command
Forces the execution of command instead of any shell or
command specified by the user when the certificate is
used for authentication.
no-agent-forwarding
Disable ssh-agent(1) forwarding (permitted by default).
no-port-forwarding
Disable port forwarding (permitted by default).
no-pty Disable PTY allocation (permitted by default).
no-user-rc
Disable execution of ~/.ssh/rc by sshd(8) (permitted by
default).
no-x11-forwarding
Disable X11 forwarding (permitted by default).
permit-agent-forwarding
Allows ssh-agent(1) forwarding.
permit-port-forwarding
Allows port forwarding.
permit-pty
Allows PTY allocation.
permit-user-rc
Allows execution of ~/.ssh/rc by sshd(8).
permit-x11-forwarding
Allows X11 forwarding.
source-address=address_list
Restrict the source addresses from which the certificate
is considered valid. The address_list is a comma-sepa‐
rated list of one or more address/netmask pairs in CIDR
format.
At present, no options are valid for host keys.
ユーザは、証明書を自らの秘密鍵と同じ場所に配備します(削除する必要もありませんが元の公開鍵は不要です)。
通常通り、SSHログインします。以下のようにエージェントにキーを追加しておくと(同時に証明書も追加され)、以降のパスフレーズ入力を省略できます。Pageant は、まだ証明書の追加に対応していないようです。他のキーチェーンツールもまだ未対応な場合が多いようです。
$ ssh-add
Enter passphrase for /home/alice/.ssh/id_rsa:
Identity added: /home/alice/.ssh/id_rsa (/home/alice/.ssh/id_rsa)
Certificate added: /home/alice/.ssh/id_rsa-cert.pub (alice-certificate)
$ ssh-add -l
4096 e8:dd:a4:72:65:b0:4a:de:07:73:b1:e4:76:33:40:c6 /home/alice/.ssh/id_rsa (RSA)
4096 e8:dd:a4:72:65:b0:4a:de:07:73:b1:e4:76:33:40:c6 /home/alice/.ssh/id_rsa (RSA-CERT)
ホストの認証 †
サーバ側の設定 †
サーバ管理者はホストの各公開鍵を認証局に送付し、署名を依頼します。
認証局は以下の要領で署名し、各証明書をサーバ管理者に返送します。-h オプションがホスト証明書の指定です。
$ sudo ssh-keygen -s /path/to/grid_ssh_ca -I adm00.grid.example.com -h -n adm00.grid.example.com -V +1825d ssh_host_dsa_key.pub
$ sudo ssh-keygen -s /path/to/grid_ssh_ca -I adm00.grid.example.com -h -n adm00.grid.example.com -V +1825d ssh_host_rsa_key.pub
証明書の内容は、-L オプションで確認できます。
$ ssh-keygen -L -f /etc/ssh/ssh_host_dsa_key-cert.pub
/etc/sshd_config : 証明書の設定を追加し、反映のためサービスを再起動させます。
HostCertificate / etc/ ssh/ ssh_host_rsa_key-cert.pub
HostCertificate / etc/ ssh/ ssh_host_dsa_key-cert.pub
クライアント側の設定 †
~/.ssh/known_hosts に、@cert-authority マーカーから始めて、スペース区切りでホストパターンとCAの公開鍵を追加します。
@ cert-authority * .grid.example.com ssh-rsa AAAAB3 ...
同様に失効鍵を追加する場合には、@revoked マーカーから始めて、スペース区切りでホストパターン(指定なしでもよい)とそのホストの公開鍵を追加します。クライアント側ではKRLファイルによる設定はないようです。
@ revoked * ssh-rsa AAAAB3 ...
鍵失効リスト(KRL, Key Revocation List)の管理 †
悪意のあるユーザや鍵の漏洩が発覚した場合には、直ちにそれらの鍵を失効させる必要があります。そのためには運用のはじめから鍵失効リストをきちんとサーバに設定していなければいけません。
OpenSSHのKRLはSSL証明書のCRLと似た仕組をしており、失効した公開鍵情報を格納するファイルです。ファイルフォーマットには、失効公開鍵を一行につき一つずつそのままリストしていくテキスト形式と、ssh-keygen コマンドによって生成、更新するバイナリ形式の二種類があります。
一方、ホストの鍵失効リストファイルは存在せず(作成しても設定する場所がなく)、上記の通りユーザが ~/.ssh/known_hosts ファイルに任意に追加して管理します。
KRLの作成と管理
テキスト形式のKRLファイルを用いる場合には、失効公開鍵を一行につき一つずつそのままリストします。
ssh-keygenコマンドを用いる場合には、以下の要領でKRLファイルを用意します。
失効させる公開鍵を与えて、KRLファイルを新規作成します。他にも、対象公開鍵のシリアルナンバ、キーIDやハッシュ値を与えてファイルを作成、更新する方法があります。
$ ssh-keygen -k -f grid_ssh_ca.krl eve-id_rsa.pub
Revoking from eve-id_rsa.pub
既存のKRLファイルに失効公開鍵を追加する場合には、-u オプションを追加します。
$ ssh-keygen -k -f grid_ssh_ca.krl -u mallory-id_rsa.pub
Revoking from mallory-id_rsa.pub
/etc/ssh/sshd_config : サーバにKRLを設定し、サービスを再起動させ反映します。
RevokedKeys / path/ to/ grid_ssh_ca.krl
多段SSH(SSH in SSH) †
PuTTY の場合は、こちら 。
~/.ssh/config : ProxyCommand? を利用することにより、n個のゲートウェイを経由したSSHログインを簡単に行うことができます。以下の例では、gateway.wonderland .example.com と geteway.neverland .example.com を順に経由して、*.neverland .example.com にマッチするホストにログインできます。最近のOpenSSHで -W オプションが利用できる場合には netcat ではなく、そちらを利用するとよいでしょう。
Host gw.wonderland
HostName gateway.wonderland.example.com
Host gateway.wonderland.example.com
ProxyCommand none
Host * .wonderland.example.com
ProxyCommand ssh -qax gw.wonderland nc % h % p
# OpenSSH 5.4 or later
#ProxyCommand ssh -q -W %h:%p gw.wonderland
Host gw.neverland
HostName gateway.neverland.example.com
ProxyCommand ssh -qax gw.wonderland nc % h % p
#ProxyCommand ssh -q -W %h:%p gw.wonderland
Host gateway.neverland.example.com
ProxyCommand ssh -qax gw.wonderland nc % h % p
#ProxyCommand ssh -q -W %h:%p gw.wonderland
Host * .neverland.example.com
ProxyCommand ssh -qax gw.neverland nc % h % p
#ProxyCommand ssh -q -W %h:%p gw.neverland
一つのHostに複数のパターンを定義する場合には、空白で区切ります。
引数に与えられたホスト文字列がマッチするHostセクションの設定項目が順に適用されます。ただし、先に見つかった設定項目が優先され、それ以降の同じキーワードの設定値で上書きされることはありません。例えば、ホスト文字列 "gateway.wonderland.example.com" は、文字列そのもののセクションにある "ProxyCommand? none " が適用され、次に "*.wonderland.example.com" セクションにマッチしますが、ProxyCommand? はすでに適用済みのため、"ProxyCommand? ssh -qax gw.wonderland nc %h %p " によって上書きされることはありません。
なお、ProxyCommand? のsshコマンドには -ax オプションを付加してエージェントおよびX転送を無効にし、よりセキュアにします。また -W オプションを利用する場合にはそのオプションに ClearAllForwardings? が暗黙に含まれており、sshのあらゆるフォワーディング機能が無効にされます。
ログインしてみます。
$ ssh gateway.wonderland.example.com
...
alice@gateway:~$ exit
$ ssh hare.wonderland.example.com
...
alice@hare:~$ exit
$ ssh gateway.neverland.example.com
...
alice@gateway:~$ exit
$ ssh roger.neverland.example.com
...
alice@roger:~$ exit
複数セッションによる接続の共有 †
上記の設定のまま、ゲートウェイを経由したセッションをいくつも開始すると、その都度ゲートウェイへの接続が確立されてしまい、リソースの無駄となります。それを回避するために複数セッションで同じ接続を共有するための設定を行います。
~/.ssh/config の以下のHostセクションを追加します。
Host * .example.com
# Not supported by Cygwin and MinGW
ControlPath ~/ .ssh/ mux-% r@% h-% p
ControlMaster auto
# OpenSSH 5.6 or later
#ControlPersist yes
ControlPersist 10
最初のセッションを開始し(接続が確立され)、ターミナルはそのままにします。
alice@ops:~$ ssh roger.neverland.example.com
...
alice@roger:~$
別のセッションを開始します。最初に確立された接続が共有されますので、レスポンスが速いのが分かります。ログアウト時のメッセージから接続が共有されていたことが分かります。
alice@ops:~$ ssh roger.neverland.example.com
...
alice@roger:~$ exit
logout
Shared connection to roger.neverland.example.com closed.
最後のセッションがクローズしてから10秒後にすべての接続がクローズされます。
alice@roger:~$ exit
logout
Shared connection to roger.neverland.example.com closed.
alice@ops:~$ ps -fe | grep .ssh/mux- | wc -l
4
... (10秒以上経過)
alice@ops:~$ ps -fe | grep .ssh/mux- | wc -l
1
Cygwin や MSYS 環境では不可 †
ただし、Cygwin やMSYS 環境では、Unixドメインソケット経由によるファイルディスクリプタの送信ができないため、接続の共有は動作しません。
PuTTY 0.64以降では可能ですので、Windows環境ではPuTTY を利用するとよいでしょう。
Cygwin環境では通常の接続にフォールバックします。
$ ssh -V
OpenSSH_7.1p1, OpenSSL 1.0.2e 3 Dec 2015
$ ssh roger.neverland.example.com
mux_client_request_session: read from master failed: Connection reset by peer
ControlSocket /home/alice/.ssh/mux-alice@roger.neverland.example.com-22 already exists, disabling multiplexing
...
MSYS環境ではエラーとなります。
$ ssh -V
OpenSSH_5.4p1, OpenSSL 1.0.0 29 Mar 2010
$ ssh roger.neverland.example.com
mm_send_fd: file descriptor passing not supported
mux_client_request_session: send fds failed
その他 †
タイムゾーン †
あるホストで
タイムゾーンは JST と表示されますが、オフセットは +0000 (+0900が正しい)となっています。Pythonのタイムゾーンの扱いが原因のようです。
$ ssh remote.example.com date
2018年 4月 11日 水曜日 18:51:47 JST
$ ssh -t remote.example.com python
Python 2.7.14 (default, Sep 23 2017, 22:06:14)
[GCC 7.2.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import logging
>>> import sys
>>> logging.basicConfig(
... stream=sys.stdout,
... level=logging.INFO,
... format='%(asctime)s %(name)s %(levelname)s %(message)s',
... datefmt='%Y-%m-%d %H:%M:%S %Z %z'
... )
>>> logging.getLogger(__name__).info('test')
2018-04-11 18:52:30 JST +0000 __main__ INFO test
$ ssh remote.example.com
...
$ python
Python 2.7.14 (default, Sep 23 2017, 22:06:14)
[GCC 7.2.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import logging
>>> import sys
>>> logging.basicConfig(
... stream=sys.stdout,
... level=logging.INFO,
... format='%(asctime)s %(name)s %(levelname)s %(message)s',
... datefmt='%Y-%m-%d %H:%M:%S %Z %z'
... )
>>> logging.getLogger(__name__).info('test')
2018-04-11 18:59:38 JST +0000 __main__ INFO test
logging.Formatter.converter を設定すると、UTC(GMT)ゾーンの時刻が取得できますが、タイムゾーンは不正なままです。
$ ssh -t remote.example.com python
Python 2.7.14 (default, Sep 23 2017, 22:06:14)
[GCC 7.2.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import logging
>>> import sys
>>> import time
>>> logging.Formatter.converter = time.localtime
>>> logging.basicConfig(
... stream=sys.stdout,
... level=logging.INFO,
... format='%(asctime)s %(name)s %(levelname)s %(message)s',
... datefmt='%Y-%m-%d %H:%M:%S %Z %z'
... )
>>> logging.getLogger(__name__).info('test')
2018-04-11 22:10:03 JST +0000 __main__ INFO test
$ ssh -t remote.example.com python
Python 2.7.14 (default, Sep 23 2017, 22:06:14)
[GCC 7.2.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import logging
>>> import sys
>>> import time
>>> logging.Formatter.converter = time.gmtime
>>> logging.basicConfig(
... stream=sys.stdout,
... level=logging.INFO,
... format='%(asctime)s %(name)s %(levelname)s %(message)s',
... datefmt='%Y-%m-%d %H:%M:%S %Z %z'
... )
>>> logging.getLogger(__name__).info('test')
2018-04-11 13:11:24 JST +0000 __main__ INFO test
Dockerの場合
$ docker run --rm python:2-slim-jessie date
Wed Apr 11 10:19:10 UTC 2018
$ docker run -it --rm python:2-slim-jessie python
Python 2.7.14 (default, Mar 14 2018, 18:25:11)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import logging
>>> import sys
>>> logging.basicConfig(
... stream=sys.stdout,
... level=logging.INFO,
... format='%(asctime)s %(name)s %(levelname)s %(message)s',
... datefmt='%Y-%m-%d %H:%M:%S %Z %z'
... )
>>> logging.getLogger(__name__).info('test')
2018-04-11 10:19:50 UTC +0000 __main__ INFO test
リソース †