本人对SSH啊,网络编程之类的东东几乎不懂(唯一的知识大概就来源于CSAPP里面的那几章)。因此,以下内容仅供参考……

1. 配置 Server 与 Client



首先,当然是在远程服务器上安装SSH server,在本地机器上安装SSH client啦(一般两个都会安装)!不过,由于大多数的Linux发行版本都预安装了openssh-client/server. 因此,大家会忽视这一点。

另外,SSH的server默认使用系统的端口号22。记得要查看这个端口号在防火墙中是否打开,如果不是的话,是无法使用的(当然,这个端口也可以通过参数-p改变)。

2. 通过系统账户密码建立SSH连接



在本地机器上,打开终端,输入:

➜  ~ ssh username@hostname

其中的hostname可以是remote的主机名也可以是remote的IP地址。例如:

➜  ~ ssh magodo@magodo-F83VF

如果是第一次连接,会弹出类似如下的信息:

The authenticity of host 'magodo-f83vf (127.0.1.1)' can't be established.
ECDSA key fingerprint is 02:99:4d:27:31:83:96:ca:5e:6c:5e:e8:6f:d6:a9:a0.
Are you sure you want to continue connecting (yes/no)? 

这是SSH的一个特性:主机验证. 在确定之后,该主机及其相应验证信息会以HostKey的形式被被保存于~/.ssh/known_host文件中。这样的好处是,当有人尝试欺骗你去登录它们的主机的时候,你会得到如下警告:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
The RSA host key for arvo.suso.org has changed,
and the key for the according IP address 216.9.137.122
is unchanged. This could either mean that
DNS SPOOFING is happening or the IP address for the host
and its host key have changed at the same time.
Offending key for IP in /home/suso/.ssh/known_hosts:10
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    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 the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
96:92:62:15:90:ec:40:12:47:08:00:b8:f8:4b:df:5b.
Please contact your system administrator.
Add correct host key in /home/suso/.ssh/known_hosts to get rid of this message.
Offending key in /home/suso/.ssh/known_hosts:53
RSA host key for arvo.suso.org has changed and you have requested strict
checking.
Host key verification failed.

不过,这也有可能是因为远程主机的host key更新了(例如:IP地址被更改了)。

之后,你需要输入你在远程主机的系统帐号的密码即可登录。

整个过程如下:

  1. (在第一次链接的时候) 远程SSH server收到用户的登录请求,把自己的公钥发给用户。保存于/etc/ssh/known_hosts 或者 ~/.ssh/known_hosts
  2. 用户使用远程server的公钥,将登录密码加密后,发送给远程server;
  3. 远程主机用自己的私钥,解密登录密码,如果密码正确就同意用户登录。

3. 通过SSH key来建立SSH连接



通过key来建立SSH连接的好处在于(我的理解):

创建密钥的命令为(这里使用dsa加密):

➜  .ssh ssh-keygen -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/home/magodo/.ssh/id_dsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/magodo/.ssh/id_dsa.
Your public key has been saved in /home/magodo/.ssh/id_dsa.pub.
The key fingerprint is:
14:9f:60:fb:72:13:2f:f2:97:06:67:cd:9c:1e:2d:b7 magodo@magodo-F83VF
The key's randomart image is:
+--[ DSA 1024]----+
|        +        |
|       . = .     |
|        o +      |
|       . . o + o |
|        S * + B o|
|         = * o +.|
|          . + .E |
|           o     |
|                 |
+-----------------+

这里会在~/.ssh下生成两个文件:private key && public key:

整个过程如下:

  1. 远程server发送一段随机的字符串给本地机器;
  2. 本地用户验证本地机器的private key的passphrase;
  3. 本地用户利用验证后的private key将这段收到的随机字符串机密后发回给远程server
  4. 远程server上使用保存于~/.ssh/authorized_keys中的本地机器的public key对收到的机密信息解密,如果是与之前的随机字符串相同,则验证通过。

3.1 public key的安装时的可能问题

虽然没有遇到过,但是还是记一笔。据说,当你将public key拷贝到远程机器的~/.ssh/authorized_keys上之后,按道理只要输入passphrase即可。可是,如果失败了的话,有可能是因为authorized_keys 或者 .ssh的权限不够restrict。你可以将authorized_keys 设置成600 以及将 .ssh设置成700.

4. ssh-agent



ssh-agent是在本地X windows启动之前启动的进程,所有的X windows程序都与其有连接,包括terminal(例如Gnome terminal, xterm……)。这意味着,在你使用ssh-agent启动X windows之后,你可以使用ssh-add来将你的passphrase加入到agent里面。这样,以后的连接都不再需要输入验证信息,agent会自动地完成验证。

大部分的发行版都会自动运行ssh-agent.

5. X11 会话转发



SSH允许用户在一台安装有X windows应用程序的远程服务器上运行X windows application, 并且将这些GUI的输出显示在本地机器上。

前提条件是:

  1. 本地机器装有X Window Server. 大多是类Unix发行版的窗口管理系统(windowing system)都是X Window, 因此自带了该服务。而对于Windows系统的PC, 则可以安装类似Xming的X Window Server.
  2. 在本地机器建立SSH连接的时候加上-X参数:

         ssh -X username@host
    

如果以上方法无效的话,很可能是因为远程主机上的SSH daemon的相应配置没有开启,检查下/etc/ssh/sshd_config文件是否设置了以下选项:

X11Forwarding yes
X11DisplayOffset 10
X11UserLocalhost yes

(更改后需要执行:$ service ssh restart使之生效)

注意:对于一些比较新的X Window应用和X Window Server,需要使用-Y选项。。

6. TCP端口转发



就像X11会话转发一样,SSH也支持其他TCP应用层的端口转发。

6.1 本地端口转发

假设host1是本地主机,host2是目标主机,由于某些原因无法直接建立连接。然而,它们可以同时与远程主机host3连接。因此,可以host1可以通过host3与host2间接建立连接。这种技术称为“隧道(tunnel)”。

我们在host1上执行下面指令:

$ ssh -L 8080:host2:21 host3

命令中-L分别有三个参数:

这条指令的结果是,SSH绑定本地端口8080监听信息 ,然后指定host3将host1的所有数据转发到host2的端口21上;并将host2的response原路返回到host1的端口8080上(假如host2运行ftp,其默认端口即为21)。如下图所示:

Local port forwarding

(注意:host2与host3的通信并不一定是安全的,以虚线表示)

这样,我们就可以在这个ssh连接上进行host1与host2的通信:

$ ftp localhost:8080

这种方式可以绕过一些仅允许本机(host2)执行的应用的限制。

其他的一些用例有:

case 1:

$ ssh -L 5900:localhost:5900 host3

它表示将本地主机的5900端口绑定目标主机host3的5900端口(这里的localhost指的是host3,因为”目标主机”在本地端口转发中是是相对与远程主机而言的)。

case 2:

$ ssh -L 8080:host2:22 host3

(注意:目标主机的端口为22,是SSH server的默认端口)

这时,只要ssh登录本机的8080端口,就相当与登录host2了:

$ ssh -p 8080 localhost

6.2 远程端口转发

还是接着上面的例子,host1与host2无法通信。但是,现在出现了不同的情况: host3与host1的连接是单向的,只能由host3连接host1,但是反过来就不行(例如host3处于内网或有防火墙,又或者host3只有ssh客户端,没有sshd)。这时,本地端口转发就不能用了。

解决办法是,既然host3可以连host1,那么就从host3上建立host1的SSH连接。我们在host3上执行下面的指令:

$ ssh -R 8080:host2:21 host1

远程端口转发和本地端口转发,在指令参数上区别主要有:

  1. 第一个参数“端口”现在指的是远程主机的端口(这里是host1)。结果就是,可以在本地(host3)设置远程机器上(host1)用于监听信息的端口号。如下图所示:
  2. 目标主机现在是相对于本地主机而言的

Remote port forwarding

(注意:host2与host3的通信并不一定是安全的,以虚线表示)

然后,你可以经由此ssh连接进行host1与host2的通信,或者直接在host1上操作:

$ ftp localhost:8080

这种远程端口转发也可以用于有防火墙或内网保护的情况,例如有host1和host2,host2有防火墙保护,host2可以连接host1而host1不可易反过来连接host2. 此时,可以在host2上执行:

$ ssh -R 8080:localhost:22 host1

这会使host2连接到host1并且使host1监听端口8080(注意:这里的localhost指的是host2,因为”目标主机”在远程端口转发中是是相对本地主机而言的). 这时,只要在host1上执行(假设上条指令的连接在这时还存在):

$ ssh -p 8080 localhost

就可以反向连接到host2. 注意,由于这里的localhost不再是host1,如果之前在known_host中有过记录的话,这里可能会跳出上面提到过的警告。

这里还有个技巧是,为了防止host1在host2执行完远程端口转发之后关机或重启了,那么之前建立的SSH就断了,一个技巧是,在host2上执行个类似以下的循环:

while true ; do ssh -R 8080:localhost:22 host1 ; sleep 60 ; done

7. SOCKS5 代理(动态转发)



有的时候,需要转发至目标主机的端口是未知的,可能是由应用的协议来决定。SSH允许你为本机上的某个端口动态地绑定使用SSH通道进行至某主机的通信,例如下面的指令:

$ ssh -D 9999 username@remotehost.net

任何支持SOCKS5协议的应用都可以通过SSH来传输并且动态地转发给任何host. 例如,一个web浏览器通过此类设置(一般在preference下的connection设置中),可以将用户输入的URL通过SSH隧道发送。

8. 远程执行命令



$ ssh username@remotehost.net command

9. SCP



SCP允许两台主机通过SSH连接来进行文件的传输。

case 1: 将本地的一个文件copy至远程主机

$ scp report.doc username@remote.host.net:

注意,最后的”:”后面可以加路径或者文件名。如果没有文件名,则远程主机的文件名与本地文件同名;如果没有路径,则copy至用户HOME目录。

case 2: 将远程主机的文件copy回本地

$ scp username@remote.host.net:report.doc report.doc

case 3: copy 目录

$ scp -r mail username@remote.host.net:

case 4: 保持文件的timestamps,如果可能的话也保留user,group以及权限

$ scp -rp mail username@remote.host.net:

10. 保持SSH连接



有时候,由于某种原因,可能是防火墙的设置(连接只能存在15分钟之类的)或者别的,你的SSH连接总是会断。

好在最近的OpenSSH,有个fix很好的解决了这个问题。只要把下面几行放到 ~/.ssh/config中:

Host * Protocol 2 TCPKeepAlive yes ServerAliveInterval 60

其中的TCPKeepAlive会使client每隔ServerAliveInterval秒向server发一个信息告诉server自己还连着。这样子可以使一些防火墙的设置失效。

11. 结束SSH连接



以下指令可以结束SSH连接:

$ exit
$ logout
$ (ctrl-d)

这些指令会使远程主机的SSH server终止。

此外,SSH还提供了一些所谓的命令行转义序列,它们提供了例如 终止连接,创建端口转发等。

其中,关机指令为: ~.

这个指令会从client端断开SSH连接。

99. Tips

99.1 私钥正确却无法连接

最近遇到一个奇怪的问题,我从A机器去连B机器,B机器的authorized_keys中有A机器的公钥,A机器的私钥和公钥文件都存在且正确,但是就是连接失败。

在B机器的/var/log/secure文件中的log如下:

...
error: RSA_public_decrypt failed: error:0407006A:lib(4):func(112):reason(106)
...

上网搜索以后在这里有讨论,好像原因是因为A的公钥并和私钥不匹配导致的,将公钥删除即可。

引用



[1] SSH Tutorial for Linux

[2] SSH原理与运用

[3] 实战 SSH 端口转发

[4] SSH: The Secure Shell the definitive guide