前言

ssh这一部分可以说是web开发的基础之一,ssh取代了telnet成为更为安全的远程服务器登录方式不是一天两天的事情了,远程操作离不开ssh,之前自己搭在腾讯云的小博客,用的是debian系统,也是用ssh登录,一开始是用密码登录,后面改成密钥登录,就不用每次都打密码这么麻烦了。然而,就是这么一个简单又基础的步骤,其实也是容易出问题的,特别是一台新搭建(或者新虚拟)的主机,最近在配置一台CentOS主机的时候就遇到问题了,现在趁还记得,马上mark down!

首先我们讨论的前提是,你拿到的是一台能够通过密码ssh上去的主机 [为了讨论方便,我们假定这台主机的公网IP为104.23.136.17]


一.服务器端

1.修改默认端口

默认端口22,如果一直以默认的端口打开,会造成很多安全问题,如果你的机子还映射了一个公网ip,每天成千上万的0-1000端口的全网扫描(nmap为例),想想都觉得不安全,所以我们要找一个冷门的端口数字,嗯,2333,就是你了

   vim /etc/ssh/sshd_config
   找到Port 22,修改为Port 2333
   sudo service sshd restart

2.修改root的默认密码

一般它给我的root密码都很弱,而且没有什么艺术感,所以我要修改默认密码

  原来ssh登录密码就是你所要登录的用户(比如root)的密码
  假设你现在是root身份
  passwd
  然后输入两次新密码

3.新建用户

都在强调root的权限过大,所以得另外新建一个普通用户,本人因为懒癌末期没救了,所以没有新建用户,一直跑在root上,而且跑在root上虽然容易出问题,但是爽啊,咳咳,言归正传,如果你不懒,最好就是新建一个用户,具体可以阮一峰老师的这篇博客,本篇博文也是再此基础上做的完善.

4.上传公钥

把你的公钥上传到服务器的当前用户目录下(如果是root用户,就是/root,如果是其他比如是foo,那么就在/home/foo/)的.ssh/目录下 如果你说没有公私钥对,那么请自行用ssh-keygen新生成一对,并把公钥的名字修改为authorized_keys

5.检查权限

检查服务器上的.ssh/和其下的刚上传的公钥权限是不是分别为700和600,如果不是请chmod之

6.进一步配置服务器的/etc/ssh/sshd_config

刚才我们修改默认22端口也修改过这个文件,现在来进一步配置:

  ①禁止root用户登陆[如果你用其他用户登陆的话最好吧root用户登陆这项ban掉]
    PermitRootLogin no
  ②禁止空密码登陆[虽然说待会儿会把密码登陆也ban了,但是万全的考虑,还是把空密码登录也ban了]:
    PermitEmptyPasswords no
  ③禁止密码登陆:
    PasswordAuthentication no
  ④启用秘钥登陆:
    PubkeyAuthentication yes
    RSAAuthentication yes
  ⑤指定公钥位置[为什么要把公钥改名为authorized_keys的原因]
    AuthorizedKeysFile .ssh/authorized_keys
  ⑥允许foo用户登陆[如果你不用root用户的话,就得把这个加上]
    AllowUsers foo
  ⑦服务器端保持ssh链接长时间不断开[可选]:
    #ssh默认一段时间后就会broken断开链接,用起来十分不爽,我是属于那种喜欢自己决定什么时候退出的人,嗯!
    TCPKeepAlive yes  #保持tcp连接
    ClientAliveInterval 60  #每60由服务器往ssh连上来的客户端发送一个心跳包以保持ssh连接
    ClientAliveCountMax 5  #客户端没有响应最大次数5,超过5次才断开ssh

配置保存sshd_config完成后,记得重启sshd服务[sudo service sshd restart]

7.检查selinux配置(特别是RedHat/CentOS系列的要注意,貌似debian系列都不会有问题)

vim打开/etc/selinux/config,如果发现SELINUX=enforcing,那么你会发现你只能用密码登陆,即便你打开允许了秘钥登录,并且服务器也有你的公钥,本机也有你的私钥,但是你就得打密码,这都是这行的问题,当然你可以学习怎么配置SELinux以放行ssh,不过我偷懒了,直接把SELINUX=enforcing改成SELINUX=permissive,之前的是执行,后面是许可,变成了只会提出安全警告,而并不会做任何动作,最后reboot重启,让刚才的配置生效

P.s.如果出现Permission denied (publickey,gssapi-keyex,gssapi-with-mic),应该是你在服务器端把密码登陆禁用了,只是允许密钥登陆,而/etc/selinux/config的SELINUX=enforcing,所以你得联系你的服务器管理员看看除了ssh以外有没有其他方式登陆你的服务器了.所以在不熟悉,不确定的情况下,请勿把ssh密码登陆禁用掉

8.小建议
大家在ssh上服务器后是不是有时候会做一些会需要很长时间的一些操作,如果ssh一断开就会前功尽弃,因为这个和ssh的原理有关,当有从客户端和服务器端成功ssh连接建立后,会新建一个进程,并且接下来用户的所有操作的父进程都是这个新建的进程,而一旦ssh连接断开,服务器就会清除这个进程,那么自然地它下面所有进程都会停止,谁叫它是爸爸….所以这里推荐tmux这个工具,如果是linux服务器的话,以前是screen,后面我也是转tmux了,在tmux中做的所有操作,在你重新ssh上服务器后可以tmux a找回来,而不至于被中断了,又得重新来过

E.g.你可以tmux,然后wget下载一个大文件,按住Ctrl,按一下B,接着按一下D,脱离tmux,再exit断开和服务器的ssh连接,再次ssh上服务器的时候,tmux a重新attach你刚才脱离的tmux session,你会发现你的wget下载还在的窝~


二.客户端

1.修改快速登陆配置~/.ssh/config

如果不去弄~/.ssh/config(如果没有,请自己touch一个,并保证600权限),那么每次你登陆都要ssh -p 2333 foo@104.23.136.17,不觉得麻烦么,还有104.23.136.17这一串数字你能记得住么,还有,如果你有多个主机需要ssh呢[比如你还有一个个人博客主机31.192.120.36,然后端口是6375],每次ssh远程登陆都要打这么长一串东西,我十分嫌麻烦!

  ~/.ssh/config配置
  
  vim打开 ~/.ssh/config  
  
  写入以下配置:
  Host hani
  HostName 104.23.136.17
  User foo
  Port 2333
  
  Host blog
  HostName 31.192.120.36
  User root
  Port 6375
  
  完了以后记得:wq保存退出

这样配置好,以后你登陆你的博客就直接ssh blog就可以了,或者登陆之前104.23.136.17这台主机直接就ssh hani就可以了 P.s.还支持Tab补全,输入”ssh h”,按一下Tab键就可以补全了,多方便,多节约时间,小技巧Get!

2.客户端保持ssh链接[Debian系列为例]

刚才说了服务器端保持ssh连接的配置,其实由客户端来做也可以

  ①客户端电脑的/etc/ssh/ssh_config配置[全局]
    
    ServerAliveInterval 60
    ServerAliveCountMax 5
    
  ②还是在上面①建立的~/.ssh/config文件加入[仅当前用户]
    
    Host *
      ServerAliveInterval 60
      ServerAliveCountMax 5
    
  表示对任意(*)的ssh连接中的服务器每60秒由客户端发送一次心跳包以保持ssh连接,如果服务器端超过5次没有响应,才断开该ssh连接

三.Mosh

17.09.03更新

告别ssh时代,升级到mosh时代吧~

最近setup了一个vultr的服务,但是由于国内线路不太好,而且用的是电信老式163网络,国际出口速度拥挤得很,所以ssh的时候会出现lagging的状况,首先我就想从ssh本身上优化,但是经过各种修改主机上的sshd_config和本地的.ssh/config文件,试过各种配置后,在ssh session里面还是会打字的时候一顿顿的,体验十分不好,然后决定试试mosh(同时也在这里推荐你使用mosh)

mosh是一个和ssh不同的软件,但是和ssh完成一样的功能——就是提供登录到主机的interface,上面说的问题主要是因为ssh是基于TCP实现导致的:

  • SSH operates strictly in character-at-a-time mode, with all echoes and line editing performed by the remote host

大概意思就是ssh是每个字符都要和server交互,所以你输入一顿顿是因为中间网络状况不好,导致ssh通过tcp和远程主机交互的时候,你所打的每个字符都要走到服务器再回来,兜这么一大圈以后才会显示。

所以mosh有个本地的预测模型,叫local-echo,顾名思义就是本地回响,也就是说把刚才说的”你输入一顿顿是因为中间网络状况不好,导致ssh通过tcp和远程主机交互的时候,你所打的每个字符都要走到服务器再回来,兜这么一大圈以后才会显示”,优化为:当你输入的时候,本地就会记录你所输入的东西,然后发送到远程主机,但是同时,不用等到远程主机的返回,local-echo就可以预测出你的输入返回,于是乎,你将不会有输入一顿顿的体验,而是和本地termianl输入一样的流畅。

但是local-echo这种本地预测不是mosh的重点,mosh的重点的SSP协议,这个协议是专门为了移动设备所设计的,因为移动设备更容易经历IP切换(比如你的手机从移动网络切换到wifi,或者相反) 以及间歇性和贫瘠的网络状况,传统的ssh已经不能满足这种移动设备需求了,最显然的是,如果你ssh断了,它并不会告诉你当前ssh连接了,而且就算你知道它断了,但是你无乱输入什么都没有反应,你又得重新ssh登陆过,多么差的用户体验……

关于SSP
SSP是基于UDP协议的,然后通过AES128-OCB保证它的保密性和可靠性(我没有搞懂究竟是mosh这么用还是说SSP协议本身就是用AES128-OCB),mosh中会分别在mosh-server服务端[远程主机]和mosh-client客户端[本地]都保存一份snapshot快照,然后通过diff这种差异信息来更新当前terminal状态,而如果只是传输差异信息。那么第一,这种量比完整传输要少;第二,SSP中可以通过控制synchroniaztion的速度来控制什么时候传递这种差异信息(根据官网给出论文的说法是通过frame rate,帧率来控制的,每一帧当然就是携带了刚才提到的差异信息,至于帧率值大小的设定论文里面说是设定为”帧与帧之间最小时间间隔被设置为sRTT时间的一半(sRTT为当前网络测量值,代表了当前网络状况,详细可以自己google)”),换句话说,当网络状况不好的时候发送的间隔就会延长,发送频率下降,不过mosh本身对用户输入的回显不完全依赖远程主机的响应,而是本地预测,但是如果你说本地预测会不会出错,根据论文里面的说法是不容易出错,但是也会出错,所以在local-echo回显的时候,没有得到远程主机回应,然后核对OK部分都会有下划线,表示未核实,然后我实际使用的时候也没有遇到这种预测出错的时候。

简单来说,mosh只是SSP和local-echo的综合体,它解决了两个问题:
1.IP切换或者断开后导致的链接失效而且需要重新登录认证的问题;
2.网络高时延导致的对用户输入反应迟钝所带来的很差的体验;

我在这里要强调一个坑:在刚开始尝试着用的时候,我以为真的像官网说的那样,可以完全用mosh代替ssh,但是不然,当我天真地只是开启了远程主机上的mosh-server,然后放行了60000-61000UDP端口,接着block了TCP22端口的时候,我发现我mosh不上远程主机了,后面阅读了官网给出的论文后才知道,原来它的登录认证还是得依赖比如ssh或者kerberos,所以我block了ssh的22端口,当然就无法登上去了

By the way, 因为mosh依赖的是ssh的登录,所以用mosh登录的时候,也是认识.ssh/目录和.ssh/config配置的~

一个完整的mosh登录过程是这样的:

The mosh program is a script that uses SSH to log into the server and execute an unprivileged mosh-server process, which prints out an AES key and binds to a high port number. The script then shuts down the SSH connection and executes the mosh-client with the supplied key and port. The client contacts the server directly over UDP.

我就不翻译了,相信这么短大家都能看懂~

还有一段要补充的是:

Mosh doesn’t support multiple windows, split-screen modes, multiple clients con- nected to the same server, or reattaching if the client has rebooted or the user has moved to a different machine. For these features, users often run terminal multi- plexers like GNU screen or OpenBSD tmux inside a Mosh session

简单地说,mosh并不能代替原来screen或者是tmux的作用,所以请像ssh配合sreen/tmux那样用mosh配合screen/tmux那样使用~

P.s.如果有了mosh,那么像现在的手机通过mosh服务对服务器进行简单管理就比较可靠了(比如ios的blink)

为什么我讲那么久原理呢?因为mosh和ssh在作用上实在太像了,而什么安装指南和使用的tutorial,我觉得Mosh官网就已经写的很好了,如果还要说什么的话,那就是我的话,是用brew install mosh和sudo apt-get install mosh装的本地(OSX)和远程主机(Ubuntu)的mosh,以及我只是简单在远程主机上输入mosh-server然后回车,最后记得别把原来ssh 22端口关了就好了,仅此而已。

如果有时间的话,强烈推荐阅读官网的两篇论文: 1.Mosh research paper 2.Mosh: A State-of-the-Art Good Old-Fashioned Mobile Shell