Hadoop運用 >

目次

SSH-CA認証

概要

  • SSH-CA認証は、認証局(CA)によって署名された公開鍵証明書によってホストとユーザ(クライアント)を相互に認証する仕組です。
  • これは、ver. 5.4より追加された機能です。ただし、証明書のフォーマットがX.509のものと異なる点には注意が必要です。
    • centos.pngCentOS6.7のOpenSSHは5.3p1ですが、SSH-CA認証を利用可能です。
  • 各ノードには、認証局の公開鍵そのもの(自己署名証明書ではない)と自ホストの公開鍵証明書を配備すればよく、運用がシンプルになります。従来の公開鍵認証のように各ユーザの公開鍵をノードに配布する必要はありません。
  • 設定によりますが、SSH-CA認証を満たさない場合にはこれまでの認証方法*1にフォールバックできますので、ユーザ認証のみに用いることなども可能です。

SSH認証局の準備

  1. 認証局のキーペアを生成し、漏洩しないように厳重に守ります。
    $ 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
  2. 公開鍵は必要なノードに配布します。

ユーザの認証

サーバ側の設定

  1. フィンガープリントを確認の上、認証局の公開鍵ファイルを適切な場所に配備します。複数の認証局を登録する場合には、一行に一公開鍵を追加します(#で始まるコメント行や空行は無視されます)。
  2. /etc/ssh/sshd_config: サーバの設定に以下のような内容を追加し、サービスを再起動して反映させます。
    1. TrustedUserCAKeys /etc/ssl/certs/grid_ssh_ca.pub

クライアント側の設定

  1. 自らの公開鍵を認証局に送付し、署名を依頼します。
  2. 認証局は以下の要領で署名し、証明書(以下の例では、id_rsa-cert.pub になります)をユーザに返送します。
    $ sudo ssh-keygen -s /path/to/grid_ssh_ca -I alice-certificate -n alice -V +365d id_rsa.pub
    1. info.png署名時には -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.
  3. ユーザは、証明書を自らの秘密鍵と同じ場所に配備します(削除する必要もありませんが元の公開鍵は不要です)。
  4. 通常通り、SSHログインします。以下のようにエージェントにキーを追加しておくと(同時に証明書も追加され)、以降のパスフレーズ入力を省略できます。note.pngPageantは、まだ証明書の追加に対応していないようです。他のキーチェーンツールもまだ未対応な場合が多いようです。
    $ 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)

ホストの認証

サーバ側の設定

  1. サーバ管理者はホストの各公開鍵を認証局に送付し、署名を依頼します。
  2. 認証局は以下の要領で署名し、各証明書をサーバ管理者に返送します。-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
    1. 証明書の内容は、-L オプションで確認できます。
      $ ssh-keygen -L -f /etc/ssh/ssh_host_dsa_key-cert.pub
  3. /etc/sshd_config: 証明書の設定を追加し、反映のためサービスを再起動させます。
    1. HostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub
    2. HostCertificate /etc/ssh/ssh_host_dsa_key-cert.pub

クライアント側の設定

  1. ~/.ssh/known_hosts に、@cert-authority マーカーから始めて、スペース区切りでホストパターンとCAの公開鍵を追加します。
    1. @cert-authority *.grid.example.com ssh-rsa AAAAB3 ...
  2. 同様に失効鍵を追加する場合には、@revoked マーカーから始めて、スペース区切りでホストパターン(指定なしでもよい)とそのホストの公開鍵を追加します。クライアント側ではKRLファイルによる設定はないようです。
    1. @revoked * ssh-rsa AAAAB3 ...

鍵失効リスト(KRL, Key Revocation List)の管理

  • 悪意のあるユーザや鍵の漏洩が発覚した場合には、直ちにそれらの鍵を失効させる必要があります。そのためには運用のはじめから鍵失効リストをきちんとサーバに設定していなければいけません。
  • OpenSSHのKRLはSSL証明書のCRLと似た仕組をしており、失効した公開鍵情報を格納するファイルです。ファイルフォーマットには、失効公開鍵を一行につき一つずつそのままリストしていくテキスト形式と、ssh-keygen コマンドによって生成、更新するバイナリ形式の二種類があります。
  • 一方、ホストの鍵失効リストファイルは存在せず(作成しても設定する場所がなく)、上記の通りユーザが ~/.ssh/known_hosts ファイルに任意に追加して管理します。
  1. KRLの作成と管理
    1. テキスト形式のKRLファイルを用いる場合には、失効公開鍵を一行につき一つずつそのままリストします。
    2. ssh-keygenコマンドを用いる場合には、以下の要領でKRLファイルを用意します。
      1. 失効させる公開鍵を与えて、KRLファイルを新規作成します。他にも、対象公開鍵のシリアルナンバ、キーIDやハッシュ値を与えてファイルを作成、更新する方法があります。
        $ ssh-keygen -k -f grid_ssh_ca.krl eve-id_rsa.pub
        Revoking from eve-id_rsa.pub
      2. 既存のKRLファイルに失効公開鍵を追加する場合には、-u オプションを追加します。
        $ ssh-keygen -k -f grid_ssh_ca.krl -u mallory-id_rsa.pub
        Revoking from mallory-id_rsa.pub
  2. /etc/ssh/sshd_config: サーバにKRLを設定し、サービスを再起動させ反映します。
    1. RevokedKeys /path/to/grid_ssh_ca.krl

多段SSH(SSH in SSH)

info.pngPuTTYの場合は、こちら

  1. ~/.ssh/config: ProxyCommand?を利用することにより、n個のゲートウェイを経由したSSHログインを簡単に行うことができます。以下の例では、gateway.wonderland.example.com と geteway.neverland.example.com を順に経由して、*.neverland.example.com にマッチするホストにログインできます。最近のOpenSSHで -W オプションが利用できる場合には netcat ではなく、そちらを利用するとよいでしょう。
    1. Host gw.wonderland
    2.     HostName gateway.wonderland.example.com
    3.  
    4. Host gateway.wonderland.example.com
    5.     ProxyCommand none
    6.  
    7. Host *.wonderland.example.com
    8.     ProxyCommand ssh -qax gw.wonderland nc %h %p
    9.     # OpenSSH 5.4 or later
    10.     #ProxyCommand ssh -q -W %h:%p gw.wonderland
    11.  
    12. Host gw.neverland
    13.     HostName gateway.neverland.example.com
    14.     ProxyCommand ssh -qax gw.wonderland nc %h %p
    15.     #ProxyCommand ssh -q -W %h:%p gw.wonderland
    16.  
    17. Host gateway.neverland.example.com
    18.     ProxyCommand ssh -qax gw.wonderland nc %h %p
    19.     #ProxyCommand ssh -q -W %h:%p gw.wonderland
    20.  
    21. Host *.neverland.example.com
    22.     ProxyCommand ssh -qax gw.neverland nc %h %p
    23.     #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のあらゆるフォワーディング機能が無効にされます。
  2. ログインしてみます。
    $ 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

複数セッションによる接続の共有

  • 上記の設定のまま、ゲートウェイを経由したセッションをいくつも開始すると、その都度ゲートウェイへの接続が確立されてしまい、リソースの無駄となります。それを回避するために複数セッションで同じ接続を共有するための設定を行います。
  1. ~/.ssh/config の以下のHostセクションを追加します。
    1. Host *.example.com
    2.     # Not supported by Cygwin and MinGW
    3.     ControlPath ~/.ssh/mux-%r@%h-%p
    4.     ControlMaster auto
    5.     # OpenSSH 5.6 or later
    6.     #ControlPersist yes
    7.     ControlPersist 10
  2. 最初のセッションを開始し(接続が確立され)、ターミナルはそのままにします。
    alice@ops:~$ ssh roger.neverland.example.com
    ...
    alice@roger:~$
  3. 別のセッションを開始します。最初に確立された接続が共有されますので、レスポンスが速いのが分かります。ログアウト時のメッセージから接続が共有されていたことが分かります。
    alice@ops:~$ ssh roger.neverland.example.com
    ...
    alice@roger:~$ exit
    logout
    Shared connection to roger.neverland.example.com closed.
  4. 最後のセッションがクローズしてから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

star.pngBashOnUbuntuOnWindows環境では正常に動作

  • CygwinMSYS環境とは対照的に、全く問題なく動作します。

note.pngCygwin や MSYS 環境では不可

  • ただし、CygwinMSYS環境では、Unixドメインソケット経由によるファイルディスクリプタの送信ができないため、接続の共有は動作しません。
  • lightbulb.pngPuTTY 0.64以降では可能ですので、Windows環境ではPuTTYを利用するとよいでしょう。
  1. 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
    ...
  2. 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

その他

タイムゾーン

  • 環境によって不正になる場合があります。
  1. あるホストで
    1. タイムゾーンは 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
    2. 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
  2. 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

リソース


*1 ホストについては knownhost への動的追加、ユーザについてはあらかじめ authorized_keys に追加された公開鍵による認証やパスワード認証等

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2018-04-11 (水) 22:26:10 (40d)