关于ssh

Posted on January 24, 2014

最近解决一个git配置的问题,然后又牵扯到了公钥密钥认证问题,然后又去思考了ssh的一些东西…

感觉就是很多东西开始没有厘清,没有确立一个深刻的认识,即使多次解决过类似的问题,但是之后再次碰到,还是懵懵懂懂。另一个体会就是,真是知道得越多,不知道也越多。

好记性不如烂笔头。


加密方式

  • 不可逆加密

    特征:

    • 不可逆性
    • 雪崩效应: 对数据改动敏感
    • 防碰撞性高

    常见:

    • 信息摘要(Message Digest): MD5
    • 安全散列(Secure Hash): SHA1, SHA-256
    • CRC: 循环冗余校验
  • 可逆加密

    • 基于算法的加密算法: 安全性依赖于算法是否外泄, base64
    • 对称加密算法: 算法公开、计算量小、加密速度快、加密效率高, 安全性依赖于密钥, DES、3DES、AES
    • 非对称加密算法: 安全性更高, 加密和解密花费时间长、速度慢, 解决了对称加密过程中密钥分发的安全问题, RSA, DH, ECC
  可逆性 对称性 密文 note
MD5 不可逆 - 128 bit 算力敏感, 加密存储,数字签名,文件完整性验证
bcrypt 不可逆 -   专为密码加密设计, 计算很慢
SHA1 不可逆 - 160bit 弱抗碰撞性
SHA2(-256, -512…) 不可逆 -    
HMAC   -    
base64 可逆 基于算法(算法即秘钥)    
DES、3DES、AES 可逆 对称    
RSA、DH、ECC 可逆 非对称    

不对称加密方式

前提:

用公钥加密message,可以用私钥解密

用私钥加密message,可以用公钥解密

目的:

如果A和B互相持有对方公钥

  1. 保密性

    发送方使用接收方公钥加密,信息保密:

    A使用公钥B加密message,发送给B

    B用私钥B解密

    只要私钥方保证私钥安全,message不怕被窃取

    要保证双向信息保密,双方要互相持有对方公钥,并使用对方公钥加密信息

  2. 防抵赖

    发送方使用自己私钥加密,接收方对发送方身份认证和防止抵赖

    A使用私钥A加密message,发送给B

    B用公钥A解密

    公钥A只能解密私钥A加密的信息,所以可以起到认证A的身份,防止A抵赖

非对称加密有2个常见使用场景:ssh和https的证书校验

常见场景:

  • 公钥加密,私钥解密,主要用于通信
  • 私钥签名,公钥验证,主要用于签名

对称加密

文件加密和解密使用相同的密钥,即加密密钥也可以用作解密密钥

  • 简单快捷,密钥较短, 效率高
  • 要求提供一条安全的渠道使通讯双方在首次通讯时协商一个共同的密钥
  • 无法进行完整性和发送者身份验证

数字签名

目的:

  1. 完整性/防篡改/防抵赖

    防止私钥方信息被篡改,防止私钥方抵赖

    发送方对message hash生成摘要(digest), 再用自己私钥对摘要加密生成数字签名(signature),把message和数字签名一起发送

    接收方对信息的数字签名进行公钥解密得到摘要,再对message hash生成摘要,比对二者,达到防止篡改和防止抵赖


数字证书

目的:

  1. 防止虚假公钥:

    权威机构“证书中心”(certificate authority,简称CA)对公钥做认证。用CA的私钥对个人公钥加密,生成“数字证书”(Digital Certificate)

    发送方用自己私钥加密信息时,除了带上数字签名,再带上数字证书,接收方用CA的公钥对数字证书解密,得到可靠的个人公钥。


ssh

SSH 为 Secure Shell 的缩写, SSH 为建立在应用层和传输层基础上的安全协议

SSH提供两种级别的安全验证:

  1. 第一种级别(基于口令的安全验证)

    公钥加密私钥解密

    $ ssh user@host

    如果本地用户名与远程用户名一致,登录时可以省略用户名: $ ssh host

    过程:

    • ssh接收到口令登录请求,ssh server把自己的公钥发送给请求者
    • 请求者用server的公钥加密登录密码,发送给server
    • server 验证密码

    中间人攻击

    这个过程本身是安全的,但是实施的时候存在一个风险:如果有人截获了登录请求,然后冒充远程主机,将伪造的公钥发给用户,那么用户很难辨别真伪。因为不像https协议,SSH协议的公钥是没有证书中心(CA)公证的,也就是说,都是自己签发的

    为了应对中间人攻击,ssh在连接时会提醒客户端:

     The authenticity of host 'host (12.18.429.21)' can't be established.
     RSA key fingerprint is 98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d.
     Are you sure you want to continue connecting (yes/no)?
    

    公钥指纹是对公钥md5后的一个指纹,客户端如果确认无误(一般去server的网站核对),yes后:

     Warning: Permanently added 'host,12.18.429.21' (RSA) to the list of known hosts.
    

    用户输入密码,被接受的公钥将会存入$HOME/.ssh/known_hosts 另外/etc/ssh/ssh_known_hosts存有对所有用户都可信赖的远程主机的公钥

  2. 第二种级别(基于密匙的安全验证)

    公钥加密私钥解密

    前提:客户端把自己的公钥放到ssh server上。

    过程:

    • ssh接收到客户端公钥登录请求,用预先放置的客户端公钥加密一串随机字符串,发送给客户端(TODO测试是否有以上警告)
    • 客户端用私钥解密,发回server
    • server验证成功,允许登录

    优点:

    • 不用输入密码
    • 没有中间人攻击

ssh的连接重用

原理很简单,开一个ssh连接在后台放着,以后再有需要用到ssh到同样主机的时候,直接使用这个连接的socket文件,不用再创建连接了,同理,也不需要再进行用户身份验证

默认是关闭的,可以在~/.ssh/config中打开:

Host *
  ControlMaster auto
  ControlPath ~/.ssh/master-%r@%h:%p

检查当前是否已经创建Master连接: ssh 连接Host名 -O check

发送断开当前Master连接的请求: ssh 连接Host名 -O exit

连接保持

ServerAliveInterval 60

这样ssh会每60秒发送一个KeepAlive请求,保证终端不会因为超时空闲而断开连接

连接新主机时,不进行公钥确认

StrictHostKeyChecking no

在首次连接服务器时,会弹出公钥确认的提示。这会导致某些自动化任务,由于初次连接服务器而导致自动化任务中断.


生成公钥密钥

  • ssh-keygen 在$HOME/.ssh/目录下,会新生成两个文件:id_rsa.pub和id_rsa。前者是你的公钥,后者是你的私钥

  • ssh-copy-id user@host 将公钥传送到远程主机host上面

  • authorized_keys文件 远程主机将用户的公钥,保存在登录后的用户主目录的$HOME/.ssh/authorized_keys文件中

  • 手动复制公钥到ssh server:ssh user@host 'umask 033;mkdir -p .ssh && cat >> .ssh/authorized_keys' < ~/.ssh/id_rsa.pub

    注意umask 033; 是为了保证ssh服务器上的.ssh/authorized_keys 权限是744 (或者700)

  • 服务器端的配置:

    chmod 700 .ssh/ 该目录的权限需要是700或者755

      # vi /etc/ssh/sshd_config
    
      Port 22
      Protocol 2
    
      #PasswordAuthentication no 有的教程把这个值为no,测试其实没必要,我的理解是要同时支持公钥认证和密码认证
      PubkeyAuthentication yes
      AuthorizedKeysFile     .ssh/authorized_keys
    

利用ssh的用户配置文件config管理ssh会话

客户端可以有多个公钥认证账号连接不同的ssh server,默认使用的密钥是$HOME/.ssh/id_rsa,但是可以配置:

对基于密匙的ssh连接,允许不同的ssh会话使用不同的密钥,客户端只需配置~/.ssh/config,格式:

    Host    别名
      HostName        主机名
      Port            端口
      User            用户名
      IdentityFile    密钥文件的路径

进行ssh连接命令:ssh 别名


git 使用ssh

ssh访问git语法:

  1. ssh://[username@]server[:port]/path_to_git_repo.git 可以指定特定端口

  2. [username@]server:/path_to_git_repo.git 如果需要指定非22端口,只能使用~/.ssh/config

说明:

path_to_git_repo是绝对路径,如果是相对路径,是相对username主目录

同样支持基于口令和基于公钥认证2种方式

对于公钥认证方式,还可以让git server的所有客户端用户使用同一个ssh用户,缺点是无法区分客户端ssh连接用户(但是git可以区分用户)

如本地配置使用公钥认证方式和github版本库通信

git的ssh地址:[email protected]:zhongfox/zhongfox.github.com.git 是第二种格式,用户名是git,使用的相对路径

需要把本地的公钥复制一份放到github里(添加SSH Keys)

疑问:为什么第一次clone时,github还是会发送自己的公钥给客户端(提示存储到known hosts)

TODO

gitlab 通过密钥反查用户名: ssh [email protected] -i ~/.ssh/id_rsa

http://www.ruanyifeng.com/blog/2011/12/ssh_port_forwarding.html


ssh 隧道

SSH的隧道: 其实就是端口映射,或者叫端口转发

SSH一共支持三种映射方式:

  • 动态映射: ssh -D 8080 [email protected]

    这个命令行会在本机上监听8080端口,成为一个sock5代理,只要简单设定一下代理,就可以以192.168.201.100作为代理服务器传送流量了

  • 本地映射: ssh -L 8080(本地端口):192.168.201.101(目标主机):3306(目标端口) [email protected](跳板机)

    前提:SSH Server必须要能通过ssh够登陆到Target Host上去

    场景: 跳板机 和 目标主机在内网, 本地机器只能直接访问跳板机, 这样本地机器可以直接访问目标机器

  • 远端映射: ssh -R 3307:192.168.202.244:3306(from) [email protected](to)

    将一个远端服务器的端口,隐射到另一个远端服务器

    前提:执行这条命令的主机,必须同时能够访问SSH Server和Target Server

    场景1:SSH Server在外部,跳板机在内网,Target Host和跳板机同网段但无法直接从互联网访问到,或者Target Host处在更深层的内网

    场景2: 内网端口映射到外网, 也叫远程端口转发(remote forwarding)

辅助参数:

  • N参数:表示只连接远程主机,不打开远程shell

    使用-N建立静默连接: 建立了连接,但是你看不到会话

  • f参数:表示连接成功后,转入后台运行

    -f对ssh进行后台执行: 这个选项会把ssh转入后台,即使用户登出,ssh会话也不会终端,除非超时

  • T参数:表示不为这个连接分配TTY

参考:


参考

http://www.ruanyifeng.com/blog/2011/08/what_is_a_digital_signature.html

http://blog.csdn.net/tanyujing/article/details/17348321

http://www.ruanyifeng.com/blog/2011/12/ssh_remote_login.html

http://dhq.me/use-ssh-config-manage-ssh-session

2016.9.10 补充: http://www.cnblogs.com/cnblogsfans/p/5124388.html