DevOps系列文章之 SSH 协议及 OpenSSH 实现

SSH

SSH(Secure Shell 的缩写)是一种网络协议,用于加密两台计算机之间的通信,并且支持各种身份验证机制

1996年提出了 SSH 2 协议(或者称为 SSH 2.0),为现在各种实现采用的协议

实现

协议只是理论,只有实现(程序)才能应用

程序软件架构采用服务器-客户端模式(Server - Client),客户端向服务器发出请求,服务器接收客户端的请求

各种实现

  • putty

    SSH client,支持 Windows、Linux。只有客户端实现,需要配合 OpenSSH Server 使用

    https://www.ssh.com/academy/ssh/putty#how-to-get-an-ssh-server

  • OpenSSH

    SSH 2 协议的开源实现,客户端实现为 ssh,服务器实现为 sshd

    目前最流行的 SSH 实现。Linux 的所有发行版都自带 OpenSSH。

    https://www.ssh.com/academy/ssh/openssh

客户端

OpenSSH 的客户端实现是二进制程序 ssh

命令

ssh [options] user@hostname [command]

登出:logout、exit、ctrl + D

options

-i

指定用于登录的私钥,默认为~/.ssh/id_rsa(rsa算法)

-p

指定客户端连接远程服务器端口,默认为22,如果服务器监听其他端口,则客户端需要通过该参数指定

-t

在 ssh 直接运行远端命令时,提供一个互动式 Shell

command

SSH 登录成功后,用户就进入了远程主机的命令行环境,终端提示符是远程主机的提示符。默认情况下会分配一个 tty,提供交互式 Shell 环境

如果命令后面直接添加 command,则不分配tty,不提供交互式 Shell 环境,而是直接执行 command,并将 command 的执行结果输出到本地终端,然后退出登录。如果命令需要交互,则可以通过 -t 参数,分配一个tty——交互式 Shell 环境,ssh 保持登录状态,直到登出

https://www.cnblogs.com/sparkdev/p/6842805.html

  • 命令要放在单引号中,多个命令用分号隔开

    ssh [email protected] 'ls; cat hello.txt'
    
  • 执行远程服务器脚本

    ssh [email protected] '/root/update.sh'
    
    • 需要脚本的绝对路径
  • 执行本地脚本

    ssh [email protected] < update.sh
    
    • 通过重定向 stdin,本地的脚本在远程服务器上被执行

相关文件

  • known_hosts

    客户端保存已登录过的 SSH 服务器的公钥

  • id_rsa

    用于 SSH 协议版本2 的 RSA 私钥

  • id_rsa.pub

    用于SSH 协议版本2 的 RSA 公钥

  • /etc/ssh/ssh_config

    全局配置文件

  • ~/.ssh/config

    用户个人的配置文件,优先级高于全局配置文件

    默认没有该文件,自己创建

配置

  • 配置文件的每一行,就是一个配置命令。配置命令与对应的值之间,可以使用空格,也可以使用等号

    Compression yes
    # 等同于
    Compression = yes
    
  • 配置命令

    • Host

      指定连接的域名或 IP 地址,也可以是别名,支持通配符。Host命令后面的所有配置,都是针对该主机的,直到下一个Host命令为止。

      Host *.example.com
          PreferredAuthentications publickey
          IdentityFile ~/.ssh/996icu_id_rsa
          ForwardAgent yes
      Host github
          HostName github.com
          PreferredAuthentications publickey
          IdentityFile ~/.ssh/996icu_id_rsa
      

      使用缩进来更好的表示

    • HostName

      Host命令使用别名的情况下,HostName指定域名或 IP 地址。

      HostName myserver.example.com
      
    • IdentityFile

      指定私钥文件

      IdentityFile keyfile
      
    • ForwardAgent

      是否允许代理

       ForwardAgent yes
      
    • PreferredAuthentications

      指定各种登录方法的优先级

      PreferredAuthentications publickey,hostbased,password
      

长连接

用ssh连接服务端,一段时间不操作或屏幕没输出(比如复制文件)的时候,会自动断开

服务端设置

找到/etc/ssh/sshd_config,大约126-127行,取消注释,并修改数值

# 30表示30s给客户端发送一次心跳
ClientAliveInterval 30
# 3此客户端没有返回心跳,则会断开连接
ClientAliveCountMax 3

修改服务端的配置往往会比较麻烦,也涉及到权限问题,以及安全问题。推荐修改客户端

客户端配置(推荐)

如果是想让主机所有用户都生效,修改/etc/ssh/ssh_config
如果只想让本人生效,则修改 ~/.ssh/config

Host *
    ServerAliveInterval 30
    ServerAliveCountMax 3

如果此处还有一个配置项叫 SendEnv LANG LC_*,老高建议最好注释掉,否则如果本地是中文环境,而服务器没有对应的中文语言选项时系统可能会出现很多莫名其妙的BUG,所以保持原始英文语言环境为上。

一次性配置

如果只是想临时使用一次,完全可以不用大动干戈地找配置文件改,ssh命令支持直接注入参数,如下:

ssh -o ServerAliveInterval=30 user@host

查看是否生效

想测试是否生效,我们直接ssh到服务器后等一会儿看效果就行,但是如果想看到服务器和客户端发送心跳包的过程,可以这样

ssh -o ServerAliveInterval=30 -vvv user@host

等30s后我们应该可以看到如下字样,说明我们在指定时间发送了心跳给服务器,服务器也有应答。同理如果我们在服务器也打开了心跳,则应该是先收到服务器的心跳然后再应答。

debug3: send packet: type 80
debug3: receive packet: type 82

更多研究可以参考Linux使用ssh超时断开连接的真正原因

服务器

密钥变更

  1. 重装SSH服务器
  2. 两台服务器拥有同样的IP,不同的密钥

客户端连接时就会发生公钥不吻合的情况,中断连接,此时客户端需要将原来的公钥从~/.ssh/known_hosts文件中删除,重新验证远程服务器。因此

会出现一个 konw_hosts.old 的文件

ssh-keygen -R hostname
# ssh-keygen -f "/home/lfp/.ssh/known_hosts" -R "192.168.200.2"
  • hostname 公钥发生变更的主机名

相关文件

  • authorized_keys

    服务器保存已登录的客户端的公钥

    如果不存在则手动创建,文件的权限要设为644,即只有文件所有者才能写

    chmod 644 ~/.ssh/authorized_keys
    

远程登录

密码登录

流程

  1. 服务器验证

    连接远程服务器需要验证其是否可信,远程服务器会发送自己的指纹(公钥),如果在本地~/.ssh/known_hosts 文件中没有该公钥,则提示用户该服务器指纹是陌生的,是否需要继续连接,输入yesno,如果输入yes则认为该服务器的公钥可信,加入本地~/.ssh/known_hosts文件中,下次再次连接时则不提示。

    服务器指纹指 SSH-Server 生成的密钥对,在安装 sshd 时默认会在/etc/ssh目录下生成密钥对,例如 ssh_host_xxx_key 及 ssh_host_xxx_key.pub

    ssh [email protected]
    The authenticity of host 'foo.com (192.168.121.111)' can't be established.
    ECDSA key fingerprint is SHA256:Vybt22mVXuNuB5unE++yowF7lgA/9/2bLSiO3qmYWBY.
    Are you sure you want to continue connecting (yes/no)? yes # 输入yes
    Warning: Permanently added 'foo.com (192.168.121.111)' (RSA) to the list of known hosts
    
  2. 输入登录密码

    客户端就会跟服务器建立连接后,SSH-Client 用服务器的公钥,将所要登录的用户的密码加密后,发送给服务器

  3. 登录密码验证

    远程服务器的SSH-Server利用自己的私钥解密,验证登录密码是否正确,如果正确则允许登录

密钥登录

SSH 默认采用密码登录,这种方法有很多缺点,简单的密码不安全,复杂的密码不容易记忆,每次手动输入也很麻烦。密钥登录是比密码登录更好的解决方案

SSH 密钥登录采用非对称加密,两个密钥成对使用,分为公钥(public key)和私钥(private key)

  • 使用公钥加密,只有使用对应的私钥解密
  • 使用私钥签名,只有使用对应的公钥解密

流程

  1. 手动将客户端的公钥放入远程服务器的指定位置
  2. 客户端向服务器发起 SSH 登录的请求
  3. 服务器收到用户 SSH 登录的请求,发送一些随机数据给用户,要求用户证明自己的身份。
  4. 客户端收到服务器发来的数据,使用自己的私钥对数据进行签名,然后再发还给服务器。
  5. 服务器收到客户端发来的加密签名后,使用对应的公钥解密,然后跟原始数据比较。如果一致,就允许用户登录

生成密钥

ssh-keygen -t rsa -C "[email protected]"
# 代码参数含义:
# -t 指定密钥类型,默认是 rsa ,可以省略。
# -C 设置注释文字,比如邮箱。
# -f 指定密钥文件存储文件名。

# 以上代码省略了 -f 参数,因此,运行上面那条命令后会让你输入一个文件名,用于保存刚才生成的SSH key
Generating public/private rsa key pair.
Enter file in which to save the key (/home/lfp/.ssh/id_rsa): /home/lfp/.ssh/996icu_id_rsa
# 可以不输入文件名,使用默认文件名(推荐),就会生成 id_rsa 和 id_rsa.pub 两个秘钥文件。
# 公司的可以修改文件名 

# 接着又会提示你输入两次密码(该密码是你push文件的时候要输入的密码,而不是github账号的密码),
# 可以不输入密码,直接按回车(两下)。那么push的时候就不需要输入密码,直接提交到github上了,如:

Enter passphrase (empty for no passphrase): 
Enter same passphrase again:
#接下来,就会显示如下代码提示:
Your identification has been saved in /home/lfp/.ssh/996icu_id_rsa.
Your public key has been saved in /home/lfp/.ssh/996icu_id_rsa.pub.
The key fingerprint is:
SHA256:U2QI4FzNgyBr2z2gG2iIZAPHM2FgHjGVA6FFa5w [email protected]
The key's randomart image is:
+---[RSA 2048]----+
|o&%o+oo= .o      |
|O=XB .. =o       |
|+Eoo=    ..      |
|B+.+ o   .       |
|+.+ . o S        |
|.  o   . .       |
|  .              |
|                 |
|                 |
+----[SHA256]-----+
#当你看到上面这段代码时,那就说明,你的 SSH key 已经创建成功

上传密钥

生成密钥以后,公钥必须上传到服务器,才能使用公钥登录。

OpenSSH 规定,用户公钥保存在服务器的~/.ssh/authorized_keys文件

执行如下命令或手动复制粘贴

cat ~/.ssh/id_rsa.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

端口转发

https://wangdoc.com/ssh/port-forwarding.html#远程转发

https://blog.mythsman.com/post/5d301903976abc05b3454691/

代理转发

https://docs.github.com/cn/developers/overview/using-ssh-agent-forwarding

参考

https://wangdoc.com/ssh/index.html

https://www.ssh.com/academy/ssh/openssh

应用

github

添加公钥

添加 SSH key 到 github上

  • 复制 id_rsa.pub 公钥

  • 点击头像—>Settings—>SSH and GPG keys—>New SSH key—>将公钥粘贴到里面,并取个名字即可

  • 测试是否成功

    ssh -T [email protected]
    

    看到如下内容,则设置成功

    Hi xxx! You’ve successfully authenticated, but GitHub does not provide shell access.

添加ssh后仍需要输入密码

  1. git config --list 查看当前项目对应的远程地址 remote.origin.url,要改为 git开头的地址
  2. git remote set-url origin [email protected]

FAQ

Connection closed by remote host

  • 如果原来是可以用ssh连接的, 突然连接不上通常是连接数过多导致的

  • 修改最大连接数

    # 找到配置文件 /etc/ssh/sshd_config
    # 去掉前面的 "#" 并把连接数改大
    MaxSession 10
    

    重启 service sshd reload

  • 每次退出ssh时,使用 exit 断开连接

Enter passphrase for key ‘/root/.ssh/id_rsa’

https://stackoverflow.com/a/34800766

  • 如果创建 ssh key 的时候设置了密码,则每次使用ssh免密登录的时候都需要输入密码

解决办法:

  1. 修改(删除)密码

     ssh-keygen -p [-P old_passphrase] [-N new_passphrase] [-f keyfile]
    

    将原始密码 123456 改为 ‘’(空)

    ssh-keygen -p -P 123456 -N '' -f ~/.ssh/id_rsa
    
  2. 将密码添加到 ssh-agent

    如果不想在每次使用 SSH 密钥时重新输入密码,您可以将密钥添加到 SSH 代理,让它管理您的 SSH 密钥并记住您的密码。

ssh : connect to host xxx port 22:Connection refused

原因是被访问主机没有启动 OpenSSH

查看本机是否启动 OpenSSH

ps -ef | grep ssh
# 或
systemctl status sshd

ubuntu 需要安装 openssh-server

sudo apt install openssh-server

你可能感兴趣的:(ssh,服务器)