ホームページ  >  記事  >  運用・保守  >  SSH および SSH サービスの基本的な概要 (トンネルの内容を含む)

SSH および SSH サービスの基本的な概要 (トンネルの内容を含む)

零下一度
零下一度オリジナル
2017-07-21 13:47:225684ブラウズ

1.1 非対称暗号化の基礎知識

対称暗号化: 暗号化と復号化は、復号化時に暗号化と一致するパスワードが提供される限り、同じアルゴリズムを使用します。 たとえば、QQ ログイン パスワード、銀行カード パスワード、パスワードが正しいことを確認してください。

非対称暗号化: 公開キーと秘密キーを使用して暗号化および復号化します。公開鍵で暗号化されたコンテンツは秘密鍵を使用して復号化でき、秘密鍵で暗号化されたコンテンツは公開鍵を使用して復号化できます。一般に、公開キー暗号化と秘密キー復号化が使用されますが、これが絶対に当てはまるわけではありません。たとえば、CA が証明書に署名する場合、暗号化には独自の秘密キーが使用されます。次に紹介する SSH サービスでは、常に公開鍵を配布することが推奨されていますが、秘密鍵を配布することもできます。

つまり、A が (秘密鍵 A、公開鍵 A) を生成し、B が (秘密鍵 B、公開鍵 B) を生成すると、A と B の間の非対称暗号化セッションの状況は次のようになります。

(1) .A は次のようになります。彼の公開鍵 A を B に送信します。B は公開鍵 A を使用してデータを暗号化し、暗号化されたデータを A に送信します。A は秘密鍵 A を使用してデータを復号化します。

(2).A は公開鍵 A を B に配布し、自身の秘密鍵 A を使用してデータを暗号化し、B は公開鍵 A を使用してデータを復号します。

(3).B は公開鍵 B を A に配布します。A は公開鍵 B を使用してデータを暗号化し、暗号化されたデータを B に送信します。B は独自の秘密鍵 B を使用してデータを復号します。

(4).B は公開鍵 B を A に配布し、自分の秘密鍵 B を使用してデータを暗号化し、A は公開鍵 B を使用してデータを復号します。

理論的には 4 つのシナリオをサポートしていますが、SSH の認証フェーズでは、SSH は公開鍵を保持するサーバーと秘密鍵を保持するクライアントのみをサポートします したがって、方法は 2 つだけです: クライアントが鍵を生成するはい、公開キーをサーバーに配布します。サーバーはキーのペアを生成し、秘密キーをクライアントに配布します。ただし、セキュリティと利便性のため、通常はクライアントがキー ペアを生成し、公開キーを配布します。これら 2 つの配布方法の例を以下に示します。

1.2 SSH の概要

(1). SSH は、トランスポート層とアプリケーション層のセキュリティ プロトコルであり、2 つの当事者間のセッションを暗号化することによってのみ接続のセキュリティを確保できます。 ssh 接続が成功すると、クライアントとサーバー間のセッションが確立され、セッションは暗号化され、その後のクライアントとサーバー間のすべての通信はセッションを通じて送信されます。

(2). SSH サービスのデーモン プロセスは sshd であり、デフォルトではポート 22 でリッスンします。

(3). ssh コマンド、scp、sftp、ssh-copy-id、その他のコマンドを含むすべての ssh クライアント ツールは、タスクを完了するために ssh 接続を使用します。つまり、これらはすべてサーバーのポート 22 に接続されますが、接続後、実行される関連コマンドが変換されてリモート ホストに送信され、リモート ホストで実行されます。

(4).ssh クライアント コマンド (ssh、scp、sftp など) は、グローバル設定ファイル /etc/ssh/ssh_config とユーザー設定ファイル ~/.ssh/config の 2 つの設定ファイルを読み取ります。実際、構成オプションはコマンドラインで渡すこともできます。有効になる優先順位は次のとおりです。コマンド ライン設定オプション > ~/.ssh/config > /etc/ssh/ssh_config。

(5).ssh には、ホスト検証とユーザー認証という 2 つの検証が含まれます。ホストの検証とホストでのユーザーの検証を通じて、ユーザーの ID を一意に決定できます。ホスト上には多数のユーザーが存在する可能性があるため、各ホストの認証は 1 回だけで済みますが、ホスト上の各ユーザーは個別に認証する必要があります。

(6).ssh は複数の認証をサポートしています。最も一般的に使用されるものは、パスワード認証メカニズムと公開キー認証メカニズムです。一部のシナリオでは、2 つのマシン間の相互信頼を確立するために公開キー認証メカニズムがほぼ必要です。上記 2 つの認証メカニズムは一般的に使用されますが、認証時のデフォルトの順序は、gssapi-with-mic、hostbased、publickey、keyboard-interactive、password です。ホスト認証メカニズム hostbased はホスト認証ではないことに注意してください。ホスト認証はほとんど使用されないため (読み込まれる認証ファイルは /etc/hosts.equiv または /etc/shosts.equiv)、ネットワーク上ではほとんど見られません。 。一般に、ssh 構成ファイル (sshd 構成ファイルではないことに注意) でコマンド PreferredAuthentications を使用して認証順序を変更することは、検証の効率を向上させる方法と見なされます。

(7). SSH クライアントには、ポート転送 (トンネル モード)、プロキシ認証、接続共有 (接続多重化) など、実際には多くの強力な機能があります。

(8). ssh サーバー設定ファイルは /etc/ssh/sshd_config であり、クライアントのグローバル設定ファイル /etc/ssh/ssh_config とは区別されることに注意してください。

(9). 最も重要な点は、sshがログインするときに、擬似端末の割り当てを要求することです。ただし、sudo などの一部の認証プログラムでは、この種の端末割り当てが禁止されており、ssh 接続が失敗する可能性があります。たとえば、ssh を使用して sudo コマンドを実行する場合、sudo は端末を ssh に割り当てる必要があるかどうかを検証します。


1.3 SSH認証プロセス分析

クライアントA(172.16.10.5)からサーバーB(172.16.10.6)に接続する場合、RSA非対称暗号化に基づくホスト検証とユーザー認証の2つのプロセスが含まれます。アルゴリズムを例として挙げます。

[root@xuexi ~]# ssh 172.16.10.6

sshd サービス プログラムは最初にサーバー B で開始されます。つまり、ssh サービスが有効になり、ポート 22 が開きます (デフォルト)。


1.3.1 ホスト検証プロセス

クライアント A が B に接続したい場合、最初にホスト検証プロセスを実行します。つまり、ホスト B が以前に接続したかどうかを判断します。

確認方法は、~/.ssh/known_hosts ファイルと /etc/ssh/known_hosts ファイルを読み取り、172.16.10.6 のホスト情報を検索します (ホスト情報はホストキーと呼ばれます)。はホスト ID を表します)。アドレスに対応するホスト キーが見つからない場合は、ホスト B から送信されたホスト キーを保存するかどうかを確認します。アドレスのホスト キーが見つかった場合は、ホスト キーとホスト B から送信されたホスト キーを比較します。まったく同じである場合は、ホスト A が以前にホスト B のホスト キーを保存していることを意味します。まったく同じでない場合は、再度保存する必要はなく、次のプロセスである認証に直接進みます。ホスト B が現在使用しているホスト キーを保存するかどうかを確認するプロンプトが表示されます。

ホストキーを保存するかどうかを尋ねるプロセスは次のとおりです:

[root@xuexi ~]# ssh 172.16.10.6 The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established.RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf.
Are you sure you want to continue connecting (yes/no)? yes

または、Windowsでグラフィカルインターフェイスsshクライアントツールを使用する場合:

認証プロセスを説明する前に、次のことを見てみましょう。 known_hosts ファイルの形式を見てください。 ~/.ssh/known_hosts を例として取り上げます。

[root@xuexi ~]# cat ~/.ssh/known_hosts172.16.10.6 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC675dv1w+GDYViXxqlTspUHsQjargFPSnR9nEqCyUgm5/32jXAA3XTJ4LUGcDHBuQ3p3spW/eO5hAP9eeTv5HQzTSlykwsu9He9w3ee+TV0JjBFulfBR0weLE4ut0PurPMbthE7jIn7FVDoLqc6o64WvN8LXssPDr8WcwvARmwE7pYudmhnBIMPV/q8iLMKfquREbhdtGLzJRL9DrnO9NNKB/EeEC56GY2t76p9ThOB6ES6e/87co2HjswLGTWmPpiqY8K/LA0LbVvqRrQ05+vNoNIdEfk4MXRn/IhwAh6j46oGelMxeTaXYC+r2kVELV0EvYV/wMa8QHbFPSM6nLz

このファイルでは、各行にホストキーがあり、行の先頭はホストキーを検索する際のインデックスとなるホスト名と、ホスト名以降の内容となっています。ホストキー部分です。このファイルを例にとると、クライアント A がかつて 172.16.10.6 でホスト B に接続しようとして、ホスト B のホスト キーを保存したことを示します。次回ホスト B に接続するときに、ホスト キーを検索します。 10.6 によって送信されたホスト キーを比較します。一致する場合は、そのホスト キーが実際に 172.16.10.6 で使用されているということを意味します。一致しない場合は、172.16.10.6 が変更されたことを意味します。ホストキー、またはこのファイル内のホストキーが変更されました。 ホストキーが変更されました。

では、ホスト B が現在使用しているホストキーはどこに保存されているのでしょうか? /etc/ssh/ssh_host* ファイルでは、これらのファイルは、サーバー (ここではホスト B) の sshd サービス プログラムの起動時に再構築されます。 rsa アルゴリズムを例に挙げると、/etc/ssh/ssh_host_rsa_key および /etc/ssh/ssh_host_rsa_key.pub に保存されます。公開鍵ファイル /etc/ssh/ssh_host_rsa_key.pub にはホスト鍵が保存されます。

[root@xuexi ~]# cat /etc/ssh/ssh_host_rsa_key.pub   # 在主机B上查看ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC675dv1w+GDYViXxqlTspUHsQjargFPSnR9nEqCyUgm5/32jXAA3XTJ4LUGcDHBuQ3p3spW/eO5hAP9eeTv5HQzTSlykwsu9He9w3ee+TV0JjBFulfBR0weLE4ut0PurPMbthE7jIn7FVDoLqc6o64WvN8LXssPDr8WcwvARmwE7pYudmhnBIMPV/q8iLMKfquREbhdtGLzJRL9DrnO9NNKB/EeEC56GY2t76p9ThOB6ES6e/87co2HjswLGTWmPpiqY8K/LA0LbVvqRrQ05+vNoNIdEfk4MXRn/IhwAh6j46oGelMxeTaXYC+r2kVELV0EvYV/wMa8QHbFPSM6nLz

/etc/ssh/ssh_host_rsa_key.pub ファイルの内容が、ホスト以外の部分があることを除いて、~/.ssh/known_hosts のホストのホスト キー部分とまったく同じであることがわかりました。 ~/.ssh/known_hosts の key 部分 ホスト名を指定します。ホストを検索するときにインデックスが付けられます。

要約すると、 ホスト検証フェーズでは、サーバーが秘密鍵を保持し、クライアントはサーバーから公開鍵を保存します。これは、認証フェーズ中に誰がキーを保持するかということとは逆であることに注意してください。

実際には、ホストキーは長すぎて比較効率が低いため、sshはホストキーを直接比較しません。そのため、ssh はホスト キーをホスト キー フィンガープリントに変換し、両側のホスト キー フィンガープリントを比較します。フィンガープリントの形式は次のとおりです:

[root@xuexi ~]# ssh 172.16.10.6 The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established.RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf.
Are you sure you want to continue connecting (yes/no)? yes

ホスト鍵のフィンガープリントは ssh-kegen で計算できます。たとえば、次はホスト A (172.16.10.5) が保存したホスト キーのフィンガープリントと、ホスト B (172.16.10.6) が現在使用しているホスト キーのフィンガープリントです。それらは全く同じであることがわかります。

[root@xuexi ~]# ssh-keygen -l -f ~/.ssh/known_hosts2048 f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf 172.16.10.6 (RSA)

[root@xuexi ~]# ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key2048 f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf   (RSA)

実際、ssh は、ホスト キーをグラフィカル フィンガープリントに変換するホスト キーのファジー比較もサポートしています。このようにして、大きく異なるグラフ結果を簡単に比較できます。ファジー比較と呼ばれる理由は、ssh が非常に類似したグラフィック フィンガープリントを誤って判断する可能性があるためです。グラフィカル フィンガープリントは次のように生成されます。上記のコマンドに「-v」オプションを追加するだけで、詳細モードに入ります。

りー


1.3.2 身份验证过程

主机验证通过后,将进入身份验证阶段。SSH支持多种身份验证机制,它们的验证顺序如下:gssapi-with-mic,hostbased,publickey,keyboard-interactive,password,但常见的是密码认证机制(password)和公钥认证机制(public key)。当公钥认证机制未通过时,再进行密码认证机制的验证。这些认证顺序可以通过ssh配置文件(注意,不是sshd的配置文件)中的指令PreferredAuthentications改变。

如果使用公钥认证机制,客户端A需要将自己生成的公钥(~/.ssh/id_rsa.pub)发送到服务端B的~/.ssh/authorized_keys文件中。当进行公钥认证时,客户端将告诉服务端要使用哪个密钥对,并告诉服务端它已经访问过密钥对的私钥部分~/.ssh/id_rsa(不能直接提供给服务端匹配检测,因为私钥不能泄露),然后服务端将检测密钥对的公钥部分,判断该客户端是否允许通过认证。如果认证不通过,则进入下一个认证机制,以密码认证机制为例。

当使用密码认证时,将提示输入要连接的远程用户的密码,输入正确则验证通过。


1.3.3 验证通过

当主机验证和身份验证都通过后,分两种情况:直接登录或执行ssh命令行中给定某个命令。如:

[root@xuexi ~]# ssh 172.16.10.6 [root@xuexi ~]# ssh 172.16.10.6  'echo "haha"'

(1)。前の ssh コマンド ラインにはコマンド パラメータが含まれていません。つまり、リモート ホストのユーザー (ここでは root ユーザー) がリモート ホスト 172.16.10.6 にログインするために使用されます。 ssh 用の疑似ターミナルを割り当て、bash 環境に入ります。

(2)。後者の ssh コマンド ラインにはコマンド パラメーターがあり、リモート ホスト上で指定されたコマンド [echo "haha"] を実行することを意味します。 ssh コマンドライン上のリモートコマンドは、fork ssh-agent によって取得された子プロセスを通じて実行されます。コマンドが実行されると、子プロセスは消滅し、ssh も終了し、確立されたセッションと接続も閉じられます。 (ここでリモートコマンドの実行処理を明確に説明する必要があるのは、後ほど紹介するsshによるポートフォワーディングを実装する際の注意点を説明するためです)

実際には、ssh接続が成功する前に、ログインやコマンドの実行が行われます。コマンドラインでは、リモートで実行するコマンドを指定できます。これらのコマンドは、~/.ssh/rc または /etc/ssh/rc ファイルに配置されます。つまり、ssh 接続が確立された後に最初に実行することになります。両方のファイルのリモート ホストでこれらのコマンドを実行することです。


1.4 各種ファイルの配布

ホスト A が SSH クライアント、ホスト B が SSH サーバーであるホスト B に接続する例を取り上げます。

サーバー、ホスト B:

  • /etc/ssh/sshd_config: SSH サービス プログラム sshd の構成ファイル。

  • /etc/ssh/ssh_host_* : サービスプログラム sshd の起動時に生成されるサーバー公開鍵ファイルと秘密鍵ファイル。 ssh_host_rsa_key や ssh_host_rsa_key.pub など。 B: .pub ファイルはホスト検証時のホスト キーであり、クライアントの ~/.ssh/known_hosts ファイルに書き込まれます。私: 中 秘密キー ファイルの厳密な要件は 600 です。そうでない場合、SSHD サービスの開始が拒否される可能性があります。

  • ~/.ssh/authorized_keys: 公開鍵認証メカニズムに基づいてクライアントからの公開鍵を保存します。公開キー認証メカニズムに基づいて認証する場合、サーバーはこのファイルを読み取ります。
  • クライアント、ホスト A:

  • /etc/ssh/ssh_config : クライアントのグローバル設定ファイル。

~/.ssh/config : クライアントのユーザー設定ファイルは、グローバル設定ファイルよりも高い優先度を持ちます。通常、このファイルはデフォルトでは存在しません。ファイルには権限が厳密に設定されています。グリッドは所有者に対してのみ読み取り/書き込み権限を持つ必要があり、他のユーザーへの書き込み権限は完全に拒否されます。

    ~/.ssh/known_hosts: ホスト検証中にサーバーホストのホストキーを保存するファイル。ファイルの内容は、サーバー側の ssh_host_rsa_key.pub ファイルから取得されます。
  • /etc/ssh/known_hosts: グローバルホストキー保存ファイル。この関数は ~/.ssh/known_hosts と同等です。
  • ~/.ssh/id_rsa : クライアントによって生成された秘密鍵。 ssh-keygen によって生成されます。このファイルは、他のユーザーがこのファイルの読み取り可能なアクセス許可を持っている場合、SSH:SSHがファイルを直接無視する必要があります。
  • ~/.ssh/id_rsa.pub : 秘密鍵 id_rsa のペアの公開鍵。権限には敏感ではありません。公開キー認証メカニズムを使用する場合、このファイルのコンテンツをサーバーの

  • :〜/.ssh/authorized_keysファイルにコピーする必要があります。 [〜/.ssh/rc]を保存します。 S/ETC/SSH/RC: 役割は ~/.ssh/rc と同等です。

  • 1.5 設定ファイルの簡単な紹介

    サーバー設定ファイル /etc/ssh/sshd_config とクライアント設定ファイル /etc/ssh/ssh_config (グローバル) または ~/.ssh/config (ユーザー) に分かれています)。
  • 虽然服务端和客户端配置文件默认已配置项虽然非常少非常简单,但它们可配置项非常多。sshd_config完整配置项参见金步国翻译的sshd_config中文手册,ssh_config也可以参考sshd_config的配置,它们大部分配置项所描述的内容是相同的。


    1.5.1 sshd_config

    简单介绍下该文件中比较常见的指令。

    [root@xuexi ~]#  /etc//.  # 监听的IP地址。. protocol version /etc//ssh_host_key      # SSH 1保存位置/etc// protocol version /etc//ssh_host_rsa_key  # SSH 2保存RSA位置/etc///etc//ssh_host_dsa_key  # SSH 2保存DSA位置/etc///var/run//var/log//

    一般来说,如非有特殊需求,只需修改下监听端口和UseDNS为no以加快主机验证阶段的速度即可。

    配置好后直接重启启动sshd服务即可。

    [root@xuexi ~]# service sshd restart


    1.5.2 ssh_config

    需要说明的是,客户端配置文件有很多配置项和服务端配置项名称相同,但它们一个是在连接时采取的配置(客户端配置文件),一个是sshd启动时开关性的设置(服务端配置文件)。例如,两配置文件都有GSSAPIAuthentication项,在客户端将其设置为no,表示连接时将直接跳过该身份验证机制,而在服务端设置为no则表示sshd启动时不开启GSSAPI身份验证的机制。即使客户端使用了GSSAPI认证机制,只要服务端没有开启,就绝对不可能认证通过。

    下面也简单介绍该文件。

    # Host *                              # Host指令是ssh_config中最重要的指令,只有ssh连接的目标主机名能匹配此处给定模式时,
                                          # 下面一系列配置项直到出现下一个Host指令才对此次连接生效
    #   ForwardAgent no
    #   ForwardX11 no
    #   RhostsRSAAuthentication no
    #   RSAAuthentication yes
    #   PasswordAuthentication yes     # 是否启用基于密码的身份认证机制
    #   HostbasedAuthentication no     # 是否启用基于主机的身份认证机制
    #   GSSAPIAuthentication no        # 是否启用基于GSSAPI的身份认证机制
    #   GSSAPIDelegateCredentials no
    #   GSSAPIKeyExchange no
    #   GSSAPITrustDNS no
    #   BatchMode no                   # 如果设置为"yes",将禁止passphrase/password询问。比较适用于在那些不需要询问提供密
                                       # 码的脚本或批处理任务任务中。默认为"no"。
    #   CheckHostIP yes
    #   AddressFamily any
    #   ConnectTimeout 0#   StrictHostKeyChecking ask        # 设置为"yes",ssh将从不自动添加host key到~/.ssh/known_hosts文件,
                                         # 且拒绝连接那些未知的主机(即未保存host key的主机或host key已改变的主机)。
                                         # 它将强制用户手动添加host key到~/.ssh/known_hosts中。
                                         # 设置为ask将询问是否保存到~/.ssh/known_hosts文件。
                                         # 设置为no将自动添加到~/.ssh/known_hosts文件。
    #   IdentityFile ~/.ssh/identity     # ssh v1版使用的私钥文件
    #   IdentityFile ~/.ssh/id_rsa       # ssh v2使用的rsa算法的私钥文件
    #   IdentityFile ~/.ssh/id_dsa       # ssh v2使用的dsa算法的私钥文件
    #   Port 22                          # 当命令行中不指定端口时,默认连接的远程主机上的端口
    #   Protocol 2,1#   Cipher 3des                      # 指定ssh v1版本中加密会话时使用的加密协议
    #   Ciphers aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc  # 指定ssh v1版本中加密会话时使用的加密协议
    #   MACs hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160
    #   EscapeChar ~#   Tunnel no
    #   TunnelDevice any:any
    #   PermitLocalCommand no    # 功能等价于~/.ssh/rc,表示是否允许ssh连接成功后在本地执行LocalCommand指令指定的命令。
    #   LocalCommand             # 指定连接成功后要在本地执行的命令列表,当PermitLocalCommand设置为no时将自动忽略该配置
                                 # %d表本地用户家目录,%h表示远程主机名,%l表示本地主机名,%n表示命令行上提供的主机名,
                                 # p%表示远程ssh端口,r%表示远程用户名,u%表示本地用户名。
    #   VisualHostKey no         # 是否开启主机验证阶段时host key的图形化指纹
    Host *GSSAPIAuthentication yes

     

    如非有特殊需求,ssh客户端配置文件一般只需修改下GSSAPIAuthentication的值为no来改善下用户验证的速度即可,另外在有非交互需求时,将StrictHostKeyChecking设置为no以让主机自动添加host key。


    1.6 ssh命令简单功能

    此处先介绍ssh命令的部分功能,其他包括端口转发的在后文相关内容中解释,关于连接复用的选项本文不做解释。

    语法:

    ssh [options] [user@]hostname [command]
     
    参数说明:-b bind_address :在本地主机上绑定用于ssh连接的地址,当系统有多个ip时才生效。-E log_file     :将debug日志写入到log_file中,而不是默认的标准错误输出stderr。-F configfile   :指定用户配置文件,默认为~/.ssh/config。-f              :请求ssh在工作在后台模式。该选项隐含了"-n"选项,所以标准输入将变为/dev/null。-i identity_file:指定公钥认证时要读取的私钥文件。默认为~/.ssh/id_rsa。-l login_name   :指定登录在远程机器上的用户名。也可以在全局配置文件中设置。-N              :显式指明ssh不执行远程命令。一般用于端口转发,见后文端口转发的示例分析。-n              :将/dev/null作为标准输入stdin,可以防止从标准输入中读取内容。ssh在后台运行时默认该项。-p port         :指定要连接远程主机上哪个端口,也可在全局配置文件中指定默认的连接端口。-q              :静默模式。大多数警告信息将不输出。-T              :禁止为ssh分配伪终端。-t              :强制分配伪终端,重复使用该选项"-tt"将进一步强制。-v              :详细模式,将输出debug消息,可用于调试。"-vvv"可更详细。-V              :显示版本号并退出。-o              :指定额外选项,选项非常多。
    user@hostname   :指定ssh以远程主机hostname上的用户user连接到的远程主机上,若省略user部分,则表示使用本地当前用户。
                    :如果在hostname上不存在user用户,则连接将失败(将不断进行身份验证)。
    command         :要在远程主机上执行的命令。指定该参数时,ssh的行为将不再是登录,而是执行命令,命令执行完毕时ssh连接就关闭。

    例如,以172.16.10.6主机上的longshuai用户登录172.16.10.6。

    [root@xuexi ~]# ssh longshuai@172.16.10.6The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established.ECDSA key fingerprint is 18:d1:28:1b:99:3b:db:20:c7:68:0a:f8:9e:43:e8:b4.Are you sure you want to continue connecting (yes/no)? yes       # 主机验证Warning: Permanently added '172.16.10.6' (ECDSA) to the list of known hosts.longshuai@172.16.10.6's password:                      # 用户验证Last login: Wed Jul  5 12:27:29 2017 from 172.16.10.6

    此时已经登录到了172.16.10.6主机上。

    [longshuai@xuexi ~]$ hostname -I172.16.10.6

    要退出ssh登录,使用logout命令或exit命令即可返回到原主机环境。

    使用ssh还可以实现主机跳转,即跳板功能。例如主机B能和A、C通信,但A、C之间不同通信,即A71fb34173e4ee87dab1f85dc1c283a44B71fb34173e4ee87dab1f85dc1c283a44Cfe36de6936ad20bec13da33b00c99495A的情形。如果要从A登陆到C,则可以借助B这个跳板登录到C。此处一个简单示例为:从172.16.10.5登录到172.16.10.6,再以此为基础登录到172.16.100.3上。

    [root@xuexi ~]# ssh 172.16.10.6The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established.RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf.
    Are you sure you want to continue connecting (yes/no)? yes
    Warning: Permanently added '172.16.10.6' (RSA) to the list of known hosts.
    Last login: Wed Jul  5 12:36:51 2017 from 172.16.10.6
    [root@xuexi ~]# ssh 172.16.10.3The authenticity of host '172.16.10.3 (172.16.10.3)' can't be established.ECDSA key fingerprint is 18:d1:28:1b:99:3b:db:20:c7:68:0a:f8:9e:43:e8:b4.
    Are you sure you want to continue connecting (yes/no)? yes
    Warning: Permanently added '172.16.10.3' (ECDSA) to the list of known hosts.
    root@172.16.10.3's password:Last login: Thu Jun 29 12:38:56 2017 from 172.16.10.6
    [root@xuexi ~]# hostname -I172.16.10.3 172.16.10.4

    同样,在退出时,也是一层一层退出的。

    [root@xuexi ~]# exit
    logout
    Connection to 172.16.10.3 closed.
    
    [root@xuexi ~]# hostname -I   
    172.16.10.6[root@xuexi ~]# exit
    logout
    Connection to 172.16.10.6 closed.

    注意,由于在借助172.16.10.6当跳板连接到172.16.10.3,所以172.16.10.3的host key是添加到172.16.10.3上而非172.16.10.5上的。

    如果在命令行给出了要执行的命令,默认ssh将工作在前台,如果同时给定了"-f"选项,则ssh工作在后台。但尽管是工作在后台,当远程执行的命令如果有消息返回时,将随时可能显示在本地。当远程命令执行完毕后,ssh连接也将立即关闭。

    [root@xuexi ~]# ssh 172.16.10.6 'sleep 5'     # 在前台睡眠5秒钟
    
    [root@xuexi ~]# ssh 172.16.10.6 -f 'sleep 5;echo over'   # 在后台睡眠5秒,睡眠完成后echo一段信息

    由于第二条命令是放在后台执行的,所以该ssh一建立完成ssh会话就立即返回本地bash环境,但当5秒之后,将在本地突然显示"over"。

    ssh执行远程命令默认允许从标准输入中读取数据然后传输到远程。可以使用"-n"选项,使得标准输入重定向为/dev/null。例如:

    [root@xuexi ~]# echo haha | ssh 172.16.10.6 'cat'haha
    
    [root@xuexi ~]# ssh 172.16.10.6 'cat' c63e8327f4334772e0cd0645af928a6f> ~/.ssh/authorized_keys"

    该命令首先建立ssh连接,并在远程执行"umask 077"临时修改远程的umask值,使得远程创建的目录权限为700,然后判断远程主机上是否有~/.ssh目录,如果没有则创建,最后从标准输入中读取本地公钥文件~/.ssh/id_rsa.pub的内容并将其追加到~/.ssh/authorized_keys文件中。

    如果将此命令改为如下命令,使用ssh的"-n"选项,并将追加重定向改为覆盖重定向符号。

    [root@xuexi ~]# cat ~/.ssh/id_rsa.pub | ssh -n 172.16.10.6 "umask 077; test -d ~/.ssh || mkdir ~/.ssh ; cat > ~/.ssh/authorized_keys"

    该命令的结果是清空远程主机172.16.10.6上的~/.ssh/authorized_keys文件,因为ssh的"-n"选项强行改变了ssh读取的标准输入为/dev/null。


    1.7 scp命令及过程分析

    scp是基于ssh的远程拷贝命令,也支持本地拷贝,甚至支持远程到远程的拷贝。

    scp由于基于ssh,所以其端口也是使用ssh的端口。其实,scp拷贝的实质是使用ssh连接到远程,并使用该连接来传输数据。下文有scp执行过程的分析。

    另外,scp还非常不占资源,不会提高多少系统负荷,在这一点上,rsync远不及它。虽然 rsync比scp会快一点,但rsync是增量拷贝,要判断每个文件是否修改过,在小文件众多的情况下,判断次数非常多,导致rsync效率较差,而scp基本不影响系统正常使用。

    scp每次都是全量拷贝,在某些情况下,肯定是不及rsync的。

    scp [-12BCpqrv] [-l limit] [-o ssh_option] [-P port] [[user@]host1:]src_file ... [[user@]host2:]dest_file
    
    选项说明:-1:使用ssh v1版本,这是默认使用协议版本-2:使用ssh v2版本-C:拷贝时先压缩,节省带宽-l limit:限制拷贝速度,Kbit/s,1Byte=8bit,所以"-l 800"表示的速率是100K/S-o ssh_option:指定ssh连接时的特殊选项,一般用不上。-P port:指定目标主机上ssh端口,大写的字母P,默认是22端口-p:拷贝时保持源文件的mtime,atime,owner,group,privileges-r:递归拷贝,用于拷贝目录。注意,scp拷贝遇到链接文件时,会拷贝链接的源文件内容填充到目标文件中(scp的本质就是填充而非拷贝)-v:输出详细信息,可以用来调试或查看scp的详细过程,分析scp的机制

    src_file是源位置,dest_file是目标位置,即将src_file复制到dest_file,其中src_file可以指定多个。由于源位置和目标位置都可以使用本地路径和远程路径,所以scp能实现本地拷贝到远程、本地拷贝到本地、远程拷贝到本地、远程拷贝到另一个远程。其中远程路径的指定格式为"user@hostname:/path",可以省略user,也可以省略":/path",省略":/path"时表示拷贝到目的用户的家目录下。

    注意:scp拷贝是强制覆盖型拷贝,当有重名文件时,不会进行任何询问。

    例如:

    (1).本地拷贝到本地:/etc/fstab-->/tmp/a.txt。

    [root@xuexi ~]# scp /etc/fstab /tmp/a.txt

    (2).本地到远程:/etc/fstab-->172.16.10.6:/tmp/a.txt。

    [root@xuexi ~]# scp /etc/fstab 172.16.10.6:/tmp
    fstab                                          100%  805     0.8KB/s   00:00

    (3).远程到本地:172.16.10.6:/etc/fstab-->/tmp/a.txt。

    [root@xuexi ~]# scp 172.16.10.6:/etc/fstab /tmp/a.txt
    fstab                                          100%  501     0.5KB/s   00:00

    (4).远程路径1到远程路径2:172.16.10.6:/etc/fstab-->/172.16.10.3:/tmp/a.txt。

    [root@xuexi ~]# scp 172.16.10.6:/etc/fstab 172.16.10.3:/tmp/a.txt
    fstab                                          100%  501     0.5KB/s   00:00   Connection to 172.16.10.6 closed.


    1.7.1 scp拷贝机制分析

    scp的拷贝实质是建立ssh连接,然后通过此连接来传输数据。如果是远程1拷贝到远程2,则是将scp命令转换后发送到远程1上执行,在远程1上建立和远程2的ssh连接,并通过此连接来传输数据。

    在远程复制到远程的过程中,例如在本地(172.16.10.5)执行scp命令将A主机(172.16.10.6)上的/tmp/copy.txt复制到B主机(172.16.10.3)上的/tmp目录下,如果使用-v选项查看调试信息的话,会发现它的步骤类似是这样的。

    # 以下是从结果中提取的过程
    # 首先输出本地要执行的命令
    Executing: /usr/bin/ssh -v -x -oClearAllForwardings yes -t -l root 172.16.10.6 scp -v /tmp/copy.txt root@172.16.10.3:/tmp
     
    # 从本地连接到A主机
    debug1: Connecting to 172.16.10.6 [172.16.10.6] port 22.
    debug1: Connection established.
     
    # 要求验证本地和A主机之间的连接
    debug1: Next authentication method: password
    root@172.16.10.6's password: # 将scp命令行修改后发送到A主机上
    debug1: Sending command: scp -v /tmp/copy.txt root@172.16.10.3:/tmp
     
    # 在A主机上执行scp命令
    Executing: program /usr/bin/ssh host 172.16.10.3, user root, command scp -v -t /tmp
     
    # 验证A主机和B主机之间的连接
    debug1: Next authentication method: password
    root@172.16.10.3's password: # 从A主机上拷贝源文件到最终的B主机上
    debug1: Sending command: scp -v -t /tmp
    Sending file modes: C0770 24 copy.txt
    Sink: C0770 24 copy.txt
    copy.txt                                                           100%   24     0.0KB/s  
     
    # 关闭本地主机和A主机的连接
    Connection to 172.16.10.6 closed.

    也就是说,远程主机A到远程主机B的复制,实际上是将scp命令行从本地传输到主机A上,由A自己去执行scp命令。也就是说,本地主机不会和主机B有任何交互行为,本地主机就像是一个代理执行者一样,只是帮助传送scp命令行以及帮助显示信息。

    其实从本地主机和主机A上的~/.ssh/know_hosts文件中可以看出,本地主机只是添加了主机A的host key,并没有添加主机B的host key,而在主机A上则添加了主机B的host key。

     

    1.8 基于公钥认证机制实现双机互信

    在身份验证阶段,由于默认情况下基于公钥认证的机制顺序优先于基于密码认证的机制,所以基于公钥认证身份,就可以免输入密码,即实现双机互信(实际上只是单方向的信任)。

    基于公钥认证机制的认证过程在前文已经详细说明过了,如还不清楚,请跳回上文。


    1.8.1 实现步骤

    以下是实现基于公钥认证的实现步骤:

    (1).在客户端使用ssh-keygen生成密钥对,存放路径按照配置文件的指示,默认是在~/.ssh/目录下。

    [root@xuexi ~]# ssh-keygen -t rsa    # -t参数指定算法,可以是rsa或dsa
    Generating public/private rsa key pair.
    Enter file in which to save the key (/root/.ssh/id_rsa):  # 询问私钥保存路径
    Enter passphrase (empty for no passphrase):               # 询问是否加密私钥文件
    Enter same passphrase again:            
    Your identification has been saved in /root/.ssh/id_rsa. 
    Your public key has been saved in /root/.ssh/id_rsa.pub.

    如果不想被询问,则可以使用下面一条命令完成:"-f"指定私钥文件,"-P"指定passphrase,或者"-N"也一样。

    [root@xuexi ~]# ssh-keygen -t rsa -f ~/.ssh/id_rsa -P ''   # 指定加密私钥文件的密码为空密码,即不加密
    
    [root@xuexi ~]# ssh-keygen -t rsa -f ~/.ssh/id_rsa -N ''   # 同上

    查看~/.ssh/目录下私钥的权限。私钥文件有严格的权限要求,当私钥文件的非所有者有可读权限时,将直接忽略该私钥文件导致公钥认证失败。

    [root@xuexi ~]# ls -l ~/.sshtotal 12-rw------- 1 root root 1671 Jun 29 00:18 id_rsa      # 私钥权限必须600,属主为自己-rw-r--r-- 1 root root  406 Jun 29 00:18 id_rsa.pub-rw-r--r-- 1 root root  393 Jun 29 05:56 known_hosts

    (2).将上面生成的公钥使用ssh-copy-id分发(即复制)到远程待信任主机上。

    ssh-copy-id用法很简单,只需指定待信任主机及目标用户即可。如果生成的公钥文件,路径不是~/.ssh/id_rsa.pub,则使用"-i"选项指定要分发的公钥。

    ssh-copy-id [-i [identity_file]] [user@]machine

    例如,将公钥分发到172.16.10.6上的root用户家目录下:

    [root@xuexi ~]# ssh-copy-id 172.16.10.6

    ssh-copy-id唯一需要注意的是,如果ssh服务端的端口不是22,则需要给ssh-copy-id传递端口号,传递方式为"-p port_num [user@]hostname",例如"-p 22222 root@172.16.10.6"。之所以要如此传递,见下面摘自ssh-copy-id中公钥分发的命令部分。

    { eval "$GET_ID" ; } | ssh $1 "umask 077; test -d ~/.ssh || mkdir ~/.ssh ; cat >> ~/.ssh/authorized_keys && (test -x /sbin/restorecon && /sbin/restorecon ~/.ssh ~/.ssh/authorized_keys >/dev/null 2>&1 || true)" || exit 1

    其中"{ eval "$GET_ID" ; }"可理解为待分发的本地公钥内容,"(test -x /sbin/restorecon && /sbin/restorecon ~/.ssh ~/.ssh/authorized_keys >/dev/null 2>&1 || true)"和selinux有关,不用管,所以上述命令简化为:

    cat ~/.ssh/id_rsa.pub | ssh $1 "umask 077; test -d ~/.ssh || mkdir ~/.ssh ; cat >> ~/.ssh/authorized_keys || exit 1

    可见,ssh-copy-id的所有参数都是存储在位置变量$1中传递给ssh,所以应该将ssh的端口选项"-p port_num"和user@hostname放在一起传递。

    通过分析上面的命令,也即知道了ssh-copy-id的作用:在目标主机的指定用户的家目录下,检测是否有~/.ssh目录,如果没有,则以700权限创建该目录,然后将本地的公钥追加到目标主机指定用户家目录下的~/.ssh/authorized_keys文件中。


    1.8.2 一键shell脚本

    就这样简单的两步就实现了基于公钥的身份认证。当然,不使用ssh-copy-id,也一样能实现上述过程。更便捷地,可以写一个简单的脚本,简化上述两个步骤为1个步骤。

    #!/bin/bash
     
    ###########################################################
    #  description: public key authentication in one step     #
    #  author     : 骏马金龙                                   #
    #  blog       : http://www.cnblogs.com/f-ck-need-u/       ############################################################
     
    privkey="$HOME/.ssh/id_rsa"publickey="$HOME/.ssh/id_rsa.pub"
     # Usage helpif [ $# -ne 1 ];then
       echo "Usage:$0 [user@]hostname"
       exit 1fi
     # test private/publick key exist or not, and the privilege 600 or notif [ -f "$privkey" -a -f "$publickey" ];then
       privkey_priv=`stat -c %a $privkey`   if [ "$privkey_priv" -ne 600 ];then   echo "The privilege of private key ~/.ssh/id_rsa is not 600, exit now."   exit 1
       fielse
       echo "private/public key is not exist, it will create it"
       ssh-keygen -t rsa -f $privkey -N ''
       echo "keys created over, it located on $HOME/.ssh/"fi
     ssh-copy-id "-o StrictHostKeyChecking=no $1"
     if [ $? -eq 0 ];then
       echo -e "\e[31m publickey copy over \e[0m"else
       echo "ssh can't to the remote host"
       exit 1fi

    该脚本将检查本地密钥对~/.ssh/{id_rsa,id_rsa.pub}文件是否存在,还检查私钥文件的权限是否为600。如果缺少某个文件,将自动新创建密钥对文件,最后分发公钥到目标主机。


    1.8.3 公钥认证之——服务端分发私钥

    对于基于公钥认证的身份验证机制,除了上面客户端分发公钥到服务端的方法,还可以通过分发服务端私钥到客户端来实现。

    先理清下公钥认证的原理:客户端要连接服务端,并告诉服务端要使用那对密钥对,然后客户端访问自己的私钥,服务端检测对应的公钥来决定该公钥所对应的客户端是否允许连接。所以对于基于公钥认证的身份验证,无论是客户端分发公钥,还是服务端分发私钥,最终客户端保存的一定是私钥,服务端保存的一定是公钥。

    那么服务端分发私钥实现公钥认证是如何实现的呢?步骤如下:假如客户端为172.16.10.5,服务端为172.16.10.6。

    (1).在服务端使用ssh-keygen生成密钥对。

    [root@xuexi ~]# ssh-keygen -f ~/.ssh/id_rsa -P ''

    (2).将上面生成的公钥追加到自己的authorized_keys文件中。

    [root@xuexi ~]# ssh-copy-id 172.16.10.6

    (3).将私钥拷贝到客户端,且路径和文件名为~/.ssh/id_rsa。

    [root@xuexi ~]# scp -p ~/.ssh/id_rsa* 172.16.10.5:/root/.ssh/

    注意,第三步中也拷贝了公钥,原因是客户端连接服务端时会比较自己的公钥和私钥是否配对,如果不配对将直接忽略公钥认证机制,所以会要求输入密码。可以将客户端的公钥删除掉,或者将服务端生成的公钥覆盖到客户端的公钥上,都能完成公钥认证。

    虽说,服务端分发私钥的方式很少用,但通过上面的步骤,想必对ssh基于公钥认证的身份验证过程有了更深入的理解。


    1.9 expect实现ssh/scp完全非交互(批量)

    expect工具可以在程序发出交互式询问时按条件传递所需的字符串,例如询问yes/no自动传递y或yes,询问密码时自动传递指定的密码等,这样就能让脚本完全实现非交互。

    显然,ssh等客户端命令基于密码认证时总是会询问密码,即使是基于公钥认证,在建立公钥认证时也要询问一次密码。另外,在ssh主机验证时还会询问是否保存host key。这一切都可以通过expect自动回答。

    关于expect工具,它使用的是tcl语言,虽说应用在expect上的tcl语言并非太复杂,但这并非本文内容,如有兴趣,可网上搜索或直接阅读man文档。

    首先安装expect工具。

    [root@xuexi ~]# yum -y install expect


    1.9.1 scp自动应答脚本

    以下是scp自动问答的脚本。

    [root@xuexi ~]# !/usr/bin/ { send

    用法:autoscp.exp [user@]hostname src_file dest_file [password]

    该自动回答脚本可以自动完成主机验证和密码认证,即使已经是实现公钥认证的机器也没问题,因为公钥认证机制默认优先于密码认证,且此脚本的password项是可选的,当然,在没有实现公钥认证的情况下,password是必须项,否则expect实现非交互的目的就失去意义了。

    以下是几个示例:

    [root@xuexi ~]# ./autoscp.exp 172.16.10.6 /etc/fstab /tmp 123456spawn scp /etc/fstab 172.16.10.6:/tmp
    The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established.RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf.
    Are you sure you want to continue connecting (yes/no)? yes          # 主机验证时询问是否保存host key,自动回答yes
    Warning: Permanently added '172.16.10.6' (RSA) to the list of known hosts.
    root@172.16.10.6's password:                                        # 密码认证过程,自动回答指定的密码"123456"fstab                                                        100%  805     0.8KB/s   00:00

    也可以指定完成的用户名和主机名。

    [root@xuexi ~]# ./autoscp.exp root@172.16.10.6 /etc/fstab /tmp 123456spawn scp /etc/fstab root@172.16.10.6:/tmp
    root@172.16.10.6's password:         fstab                                 100%  805     0.8KB/s   00:00


    1.9.2 ssh-copy-id自动应答脚本

    以下是在建立公钥认证机制时,ssh-copy-id拷贝公钥到服务端的自动应答脚本。

    [root@xuexi ~]# cat /tmp/autocopy.exp
    #!/usr/bin/expect
    
    ###########################################################
    #  description: scp without interactive                   #
    #  author     : 骏马金龙                                   #
    #  blog       : http://www.cnblogs.com/f-ck-need-u/       ############################################################
    
    set timeout 10set user_hostname [lindex $argv 0]
    set password [lindex $argv 1]
    spawn ssh-copy-id $user_hostname
        expect {"(yes/no)?"{
                send "yes\n"expect "*assword:" { send "$password\n"}
            }"*assword:"{
                send "$password\n"}
        }
    expect eof

    用法:autocopy.exp [user@]hostname password

    以下是一个示例,

    [root@xuexi ~]# /tmp/autocopy.exp root@172.16.10.6 123456spawn ssh-copy-id root@172.16.10.6The authenticity of host '172.16.10.6 (172.16.10.6)' can't be established.RSA key fingerprint is f3:f8:e2:33:b4:b1:92:0d:5b:95:3b:97:d9:3a:f0:cf.
    Are you sure you want to continue connecting (yes/no)? yes      # 主机认证时,自动应答yes
    Warning: Permanently added '172.16.10.6' (RSA) to the list of known hosts.
    root@172.16.10.6's password:                                    # 密码认证时自动输入密码"123456"Now try logging into the machine, with "ssh 'root@172.16.10.6'", and check in:
     
      .ssh/authorized_keys
     
    to make sure we haven't added extra keys that you weren't expecting.

    如果要实现批量非交互,则可以写一个shell脚本调用该expect脚本。例如:

    [root@xuexi ~]# cat /tmp/sci.sh#!/bin/bash
    
    ###########################################################
    #  description: scp without interactive                   #
    #  author     : 骏马金龙                                   #
    #  blog       : http://www.cnblogs.com/f-ck-need-u/       ############################################################ 
    passwd=123456               # 指定要传递的密码为123456
    user_host=`awk '{print $3}' ~/.ssh/id_rsa.pub`   # 此变量用于判断远程主机中是否已添加本机信息成功 
    for i in $@  
    do/tmp/autocopy.exp $i $passwd >&/dev/nullssh $i "grep "$user_host" ~/.ssh/authorized_keys" >&/dev/null  # 判断是否添加本机信息成功if [ $? -eq 0 ];thenecho "$i is ok"elseecho "$i is not ok"fidone

    用法:/tmp/sci.sh [user@]hostname

    其中hostname部分可以使用花括号展开方式枚举。但有个bug,最好ssh-copy-id的目标不要是脚本所在的本机,可能会强制输入本机密码,但批量脚本autocopy.exp则没有此bug。

    例如:

    [root@xuexi tmp]# /tmp/sci.sh 172.16.10.3 172.16.10.6172.16.10.3 is ok172.16.10.6 is ok
    [root@xuexi tmp]# /tmp/sci.sh 172.16.10.{3,6}172.16.10.3 is ok172.16.10.6 is ok
    [root@xuexi tmp]# /tmp/sci.sh root@172.16.10.3 172.16.10.6root@172.16.10.3 is ok172.16.10.6 is ok


    1.10 ssh连接速度慢的几个原因和解决办法

    ssh连接包括两个阶段:主机验证阶段和身份验证阶段。这两个阶段都可能导致连接速度慢。

    具体是哪个阶段的速度慢,完全可以通过肉眼看出来:

    (1).卡着很久才提示保存host key肯定是主机验证过程慢。

    (2).主机验证完成后卡着很久才提示输入密码,肯定是身份验证过程慢。

    其中主机验证过程慢的原因,可能是网络连接慢、DNS解析慢等原因。网络连接慢,ssh对此毫无办法,而DNS解析慢,ssh是可以解决的,解决方法是将ssh服务端的配置文件中UseDNS设置为no(默认为yes)。

    而身份验证慢的原因,则考虑ssh的身份验证顺序:gssapi,host-based,publickey,keyboard-interactive,password。其中gssapi认证顺序是比较慢的,所以解决方法一是在ssh客户端配置文件中将GSSAPI认证机制给关掉,解决方法二是在ssh客户端配置文件中使用PreferredAuthentications指令修改身份验证顺序。

    方法一修改:GSSAPIAuthentication yes

    方法二修改:PreferredAuthentications publickey,password,gssapi,host-based,keyboard-interactive

    如果感受不到哪个阶段导致速度变慢,可以使用ssh或scp等客户端工具的"-vvv"选项进行调试,看看是卡在哪个地方,不过,想看懂"-vvv"的过程,还是比较考验耐心的。


    1.11 SSH安全隧道(ssh跃墙之术)


    1.11.1 ssh安全隧道(一):本地端口转发

    如下图,假如host3和host1、host2都同互相通信,但是host1和host2之间不能通信,如何从host1连接上host2?

    对于实现ssh连接来说,实现方式很简单,从host1 ssh到host3,再ssh到host2,也就是将host3作为跳板的方式。但是如果不是ssh,而是http的80端口呢?如何让host1能访问host2的80端口?

    ssh支持本地端口转发,语法格式为:

    ssh -L [bind_addr:]local_port:remote:remote_port middle_host

    以上图为例,实现方式是在host1上执行:

    [root@xuexi ~]# ssh -g -L 2222:host2:80 host3

    其中"-L"选项表示本地端口转发,其工作方式为:在本地指定一个由ssh监听的转发端口(2222),将远程主机的端口(host2:80)映射为本地端口(2222),当有主机连接本地映射端口(2222)时,本地ssh就将此端口的数据包转发给中间主机(host3),然后host3再与远程主机的端口(host2:80)通信。

    现在就可以通过访问host1的2222端口来达到访问host2:80的目的了。例如:

    再来解释下"-g"选项,指定该选项表示允许外界主机连接本地转发端口(2222),如果不指定"-g",则host4将无法通过访问host1:2222达到访问host2:80的目的。甚至,host1自身也不能使用172.16.10.5:2222,而只能使用localhost:2222或127.0.0.1:2222这样的方式达到访问host2:80的目的,之所以如此,是因为本地转发端口默认绑定在回环地址上。可以使用bind_addr来改变转发端口的绑定地址,例如:

    [root@xuexi ~]# ssh -L 172.16.10.5:2222:host2:80 host3

    这样,host1自身就能通过访问172.16.10.5:2222的方式达到访问host2:80的目的。

    一般来说,使用转发端口,都建议同时使用"-g"选项,否则将只有自身能访问转发端口。

    再来分析下转发端口通信的过程。

    当host4发起172.16.10.5:2222的连接时(即步骤①),数据包的目标地址和端口为"172.16.10.5:2222"。由于host1上ssh已经监听了2222端口,并且知道该端口映射自哪台主机哪个端口,所以将会把该数据包目标地址和端口替换为"172.16.10.3:80",并将此数据包通过转发给host3。当host3收到该数据包时,发现是host1转发过来请求访问host2:80的数据包,所以host3将代为访问host2的80端口。

    所以,host1和host3之间的通信方式是SSH协议,这段连接是安全加密的,因此称为"安全隧道",而host3和host2之间通信协议则是HTTP而不是ssh。

    现在再来考虑下,通过本地端口转发的方式如何实现ssh跳板的功能呢?仍以上图为例:

    [root@xuexi ~]# ssh -g -L 22333:host2:22 host3

    这样只需使用ssh连上host1的22333端口就等于连接了host2的22端口。

    甚至还可以在host3上执行:

    [root@xuexi ~]# ssh -L 172.16.10.5:22333:host2:80 host2
    
    或:
    
    [root@xuexi ~]# ssh -L 172.16.10.5:22333:host2:80 host3

    这样在host3就开启了一个转发端口22333供host1连接。当host1连接到host3:22333时,host3将转发给host2或host3自身,再由host2或host3自身与host2通信。

    最后,关于端口转发有一个需要注意的问题:ssh命令中带有要执行的命令。考虑了下面的三条在host1上执行的命令的区别。

    [root@xuexi ~]# ssh -g -L 22333:host2:22 host3
    
    [root@xuexi ~]# ssh -g -L 22333:host2:22 host3 "ifconfig"[root@xuexi ~]# ssh -g -L 22333:host2:22 host3 "sleep 10"

    第一条命令开启了本地端口转发,且是以登录到host3的方式开启的,所以执行完该命令后,将跳到host3主机上,当退出host3时,端口转发功能将被关闭。另外,host1上之所以要开启端口转发,目的是为了与host2进行通信,而不是跳到host3上,所以应该在ssh命令行上加上"-f"选项让ssh在本机host1上以后台方式提供端口转发功能,而不是跳到host3上来提供端口转发功能。

    第二条命令在开启本地转发的时候还指定了要在host3上执行"ifconfig"命令,但是ssh的工作机制是远程命令执行完毕的那一刻,ssh关闭连接,所以此命令开启的本地端口转发功能有效期只有执行ifconfig命令的一瞬间。

    第三条命令和第二条命令类似,只不过指定的是睡眠10秒命令,所以此命令开启的本地转发功能有效期只有10秒。

    结合上面的分析,开启端口转发功能时,建议让ssh以后台方式提供端口转发功能,且明确指示不要执行任何ssh命令行上的远程命令。即最佳开启方式为:

    [root@xuexi ~]# ssh -f -N -g -L 22333:host2:22 host3


    1.11.2 ssh安全隧道(二):远程端口转发

    ssh除了支持本地端口转发,还支持远程端口转发。顾名思义,远程端口转发表示的是将远程端口的数据转发到本地。

    如下图:假如host3是内网主机,它能和host2互相通信,也能和host1通信,但反过来,host1不能和host3通信。这时要让host1访问host3或host2就没办法通过本地端口转发了,因为要在host1上开启本地端口转发,必须要和host3通信请求建立隧道。

    可以通过在host3上发起远程端口转发来实现,因为host3能和host1通信,host3可以请求在host1和host3之间建立隧道。

    语法如下:

    ssh -R [bind_addr:]remote1_port:host:port remote1

    以上图为例,实现方式是在host3上执行:

    [root@xuexi ~]# ssh -R 22333:host2:80 host1

    这表示host3请求host1上的sshd服务,在host1上建立一个套接字监听22333端口,它是host2端口的映射,当有主机连接host1:22333时,此连接中的数据全部都通过host1和host3之间的安全隧道转发给host3,再由host3向host2的80端口发起访问。由于host3请求开启的转发端口是在远程主机host1上的,所以称为"远程端口转发"。

    再考虑下面这条命令所开启的远程转发端口,它是在host3上执行的。

    [root@xuexi ~]# ssh -R 22333:host3:80 host1

    该命令将自身的host3:80映射到host1:22333上,这也能让host1和host2、host3通信,因为隧道是建立在host1:2233371fb34173e4ee87dab1f85dc1c283a44host3:80上的。

    但是,远程端口转发和本地端口转发最大的一个区别是,远程转发端口是由host1上的sshd服务控制的,默认配置情况下,sshd服务只允许本地开启的远程转发端口(22333)绑定在环回地址(127.0.0.1),即使显式指定了bind_addr也无法覆盖。例如:

    [root@xuexi ~]# ssh -R *:22333:host2:80 host1
    
    [root@xuexi ~]# netstat -tnlp
    Active Internet connections (only servers)
    Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name  
    tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN      8405/sshd          
    tcp        0      0 127.0.0.1:25                0.0.0.0:*                   LISTEN      1422/master        
    tcp        0      0 127.0.0.1:22333             0.0.0.0:*                   LISTEN      8407/sshd          
    tcp        0      0 :::22                       :::*                        LISTEN      8405/sshd          
    tcp        0      0 ::1:25                      :::*                        LISTEN      1422/master         
    tcp        0      0 ::1:22333                   :::*                        LISTEN      8407/sshd

    要允许本地的远程转发端口绑定在非环回地址上,需要在host1的sshd配置文件中启用"GatewayPorts"项它的默认值为no。启动该选项后,不给定bind_addr或bind_addr设置为"*"都表示绑定在所有地址上。如下:

    [root@xuexi ~]# ssh -g -R *:22333:host2:80 host1
    
    [root@xuexi ~]# netstat -tnlp
    Active Internet connections (only servers)
    Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name  
    tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN      8466/sshd          
    tcp        0      0 127.0.0.1:25                0.0.0.0:*                   LISTEN      1422/master        
    tcp        0      0 0.0.0.0:22333               0.0.0.0:*                   LISTEN      8468/sshd           
    tcp        0      0 :::22                       :::*                        LISTEN      8466/sshd          
    tcp        0      0 ::1:25                      :::*                        LISTEN      1422/master        
    tcp        0      0 :::22333                    :::*                        LISTEN      8468/sshd

    和前面的本地转发端口一样,建议的几个选项是:"-g"、"-f"、"-N"。即推荐的命令写法是:

    [root@xuexi ~]# ssh -fgN -R 22333:host2:80 host1

    现在,就可以通过访问host1:22333达到访问host2:80的目的了。如下图所示。

     

    1.11.3 ssh安全隧道(三):动态端口转发(SOCKS代理)

    无论是本地端口转发还是远程端口转发,都是将某固定主机及其端口映射到本地或远程转发端口上,例如将host2:80映射到host1:2222。也就是说,本地或远程转发端口和目标端口所代表的应用层协议是一对一的关系,2222端口必须对应的是http的80端口,使用浏览器向host1:2222端口发起http请求当然没问题,但是使用ssh工具向host1:2222发起连接将会被拒绝,因为host2上http服务只能解析http请求,不能解析ssh连接请求。

    ssh支持动态端口转发,由ssh来判断发起请求的工具使用的是什么应用层协议,然后根据判断出的协议结果决定目标端口。

    以下图为例进行说明,host1处在办公内网,能和host3互相通信,但它无法直接和互联网和host2通信,而host3则可以和host2以及互联网通信。

    要让host1访问互联网,又能和host2的22端口即ssh服务通信,显然在host1上仅设置一个本地端口转发是不够的,虽然可以设置多个本地转发端口分别映射不同的端口,但这显然比较笨重和麻烦。使用动态端口转发即可。

    语法格式为:

    ssh -D [bind_addr:]port remote

    以上图为例,在host1上执行:

    [root@xuexi ~]# ssh -Nfg -D 2222 host3

    上記のコマンドを実行すると、host1 はローカルで SOCKS4 または SOCKS5 サービスを開始し、ポート 2222 をリッスンします。 クライアント プログラム ツール (使用されるアプリケーション層プロトコルの種類を意味します) が独自のプロキシを host1:2222 に設定している限り、プログラムによって生成されたすべてのデータは host1:2222 に転送され、その後 host1:2222 にデータが転送されます。はトンネルを通じて host3 に転送され、最終的に host3 はインターネット、または host2 のクライアント ツールに対応するアプリケーション層プロトコルのポートと通信します。

    実際、host4 が IE ブラウザをクライアント ツールとして使用し、IE ブラウザのプロキシを host1:2222 に設定すると、IE ブラウザによって開始されたリクエストは http プロトコルを使用するため (他の可能性はここでは考慮されません)。 )、IE ブラウザーによって生成されたデータは host1:2222 に転送され、次に host1:2222 がそれをトンネル経由で host3 に転送します。Host3 はインターネットに接続できるため、host4 はネットワーク機能を実現します。設定は以下のとおりです:

    別の例では、host4 上の QQ クライアントもプロキシを設定できます。このようにして、QQ によって生成されたデータは host1:2222 経由で転送され、host1:2222 は QQ データを host3 に転送します。Host3 はこれらのデータで使用されるプロトコルが oicq であることを認識しているため、host3 は Tencent の QQ サーバーに接続します ( oicq サービス ポートに対応します)。一部のクライアント ツールは、プロキシ タイプを明確に示す必要があります。

    ローカルおよびリモートのポート転送と同様に、推奨されるオプションは「-f」、「-N」、および「-g」です。

    ssh 動的ポート転送は ssh クライアントの機能であるため、ssh コマンドを使用せずに、SecurtCRT や putty などの ssh クライアント ツールを使用してプロキシ インターネット アクセスを実現できます。たとえば、ローカル ホストがインターネットにアクセスできないが、172.16.10.6 の SSH サービスと通信でき、172.16.10.6 がインターネットにアクセスできる場合、まず SecurtCRT を使用してローカル ホスト上の 172.16.10.6 に接続できます。次に、対応するセッション オプションで次の設定を行い、ローカル ホストもインターネットにアクセスできるようにします。 (

    注: FQ できるとは言っていません。善良な市民は FQ しないでください!!!

    )

    次に、SecurtCRT で指定された 8888 動的転送ポートが監視されているかどうかをローカル ホストで確認します。

    これで、このマシンのすべてのデータ パケットは、SecurtCRT に接続された 172.16.10.6 を通じて外の世界に流れます。

以上がSSH および SSH サービスの基本的な概要 (トンネルの内容を含む)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。