本文详细介绍SSH(Secure Shell Protocol)
相关知识,如如何在一台服务器中,生成 ssh-key,并为不同地址配置不同 ssh key,该方法适合为不同 git client 配置不同 key。
介绍
high level
+-------------------------+---------------------+
| Authentication Protocol | Connection Protocol |
+-------------------------+---------------------+
| Transport Layer Protocol |
+-----------------------------------------------+
| Underlying Connection |
+-----------------------------------------------+
low level
部署 SSH
服务端
SSH 服务端
是一个守护讲程 (daemon)
:sshd
,用来响应来自客户端的连接请求和响应,一般包括公共密钥认证、密钥交换、对称密钥加密和非安全连接等
# 安装服务端
$ apt install openssh-server
$ dpkg -S `which sshd`
openssh-server: /usr/sbin/sshd
$ dpkg -L openssh-server
# 配置文件
$ tree /etc/ssh/
/etc/ssh/
├── moduli
├── ssh_config
├── ssh_config.d
├── sshd_config # 配置文件
├── sshd_config.d
│ └── 50-cloud-init.conf
├── sshd_config.ucf-dist
├── ssh_host_dsa_key # 下面 key 和 pub 是在启动时生成的,在第一次启动服务时自动生成
├── ssh_host_dsa_key.pub
├── ssh_host_ecdsa_key
├── ssh_host_ecdsa_key.pub
├── ssh_host_ed25519_key
├── ssh_host_ed25519_key.pub
├── ssh_host_rsa_key
├── ssh_host_rsa_key.pub
└── ssh_import_id
3 directories, 14 files
启动服务端
systemctl status ssh.service
# 启动前,可以使用以下命令检测配置文件是否合法
sshd -t /etc/ssh/sshd_config
客户端
SSH 客户端
包括:
ssh
远程登陆,软连接 slogin
scp
远程拷贝
sftp(Secure File Transfer Protocol, 安全文件传送协议)
等应用程序
# 安装客户端
$ apt install openssh-client
$ dpkg -S `which ssh`
openssh-client: /usr/bin/ssh
$ dpkg -L openssh-client | grep bin
/usr/bin
/usr/bin/scp
/usr/bin/sftp # 安全FTP文件传输
/usr/bin/ssh
/usr/bin/ssh-add
/usr/bin/ssh-agent
/usr/bin/ssh-argv0
/usr/bin/ssh-copy-id
/usr/bin/ssh-keygen
/usr/bin/ssh-keyscan
/usr/bin/slogin
$ ls -lh /usr/bin/slogin
lrwxrwxrwx 1 root root 3 Feb 15 10:13 /usr/bin/slogin -> ssh
ssh-keygen 生成 ssh key
通常,要为多个邮箱账号分别生成公钥,公私钥都是放到~/.ssh下面,生成步骤如下:
$ ssh-keygen -t ed25519 -b 4096 -C "me@xiexianbin.cn" # 推荐,比 rsa 短,安全性高
$ ssh-keygen -t rsa -C "me@xiexianbin.cn" # 把这个文件命名为id_rsa_github,然后一路回车
$ ssh-keygen -t rsa -C "10972072@qq.com" # 把这个文件命名为id_rsa_aliyun,然后一路回车
$ ssh-keygen -t rsa -b 4096 -C "me@xiexianbin.cn"
将 SSH 私钥增加到 ssh-agent:
ssh-add ~/.ssh/id_rsa
查看已经 add 的SSH KEY:
ssh-add -l
若报错:Could not open a connection to your authentication agent.
,执行:
ssh-agent bash
PS:将 ~/.ssh/id_rsa.pub
添加到目标及其的 .ssh/authorized_keys
可以实现 ssh 证书登录。
修改key密码
ssh-keygen -f ~/.ssh/id_rsa -p
ssh-copy-id 分发公钥
ssh-copy-id $IP #$IP为本虚机地址,按照提示输入yes 和密码,然后可以无密码登录服务器
上述过程,是将 id_rsa.pub
添加到对应主机的 ~/.ssh/authorized_keys
目录下。
多个服务器分别配置key
此时在~/.ssh
下面生成了两对公私钥,把id_rsa_aliyun.pub的内容贴到aliyun的git服务的ssh keys中,把id_rsa_github.pub的内容贴到github的ssh keys中。然后touch一个配置文件:
touch ~/.ssh/config
chmod 600 ~/.ssh/*
最后在~/.ssh/config中添加如下内容即可:
host code.aliyun.com
# user xiexianbin
hostname code.aliyun.com
port 22
identityfile ~/.ssh/id_rsa_aliyun
host github.com
User git
hostname github.com
port 22
identityfile ~/.ssh/id_rsa_github
Host 10.0.0.* 10.0.1.*
port 22
IdentityFile ~/.ssh/id_rsa
User root
host vm1
User root
hostname 10.0.0.2
port 22
identityfile ~/.ssh/id_rsa
可以直接 ssh vm1
登录该机器。
测试
然后用ssh命令分别测试:
ssh -T git@github.com
调试
如果到这里你没有成功的话,可以 –debug,比如测试 github:
ssh -vT git@github.com
-v 是输出编译信息,然后根据编译信息自己去解决问题吧。
配置免确认
修改 ~/.ssh/config
,内容如下:
UserKnownHostsFile=/dev/null
StrictHostKeyChecking=no
LogLevel=ERROR
修改权限:
chmod 400 ~/.ssh/config
ssh 免 hostkey check 登录:
sudo ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i .ssh/ssh_key.key ubuntu@10.0.0.1
自动登陆文件
cat 10.0.0.1
#!/bin/sh
user="root"
host=`basename $0`
ssh -i ~/.ssh/id_rsa $user@$host
TCP port or Unix socket 转发
man ssh
查看
本地端口转发
$ man ssh
-C Requests compression of all data (including stdin, stdout, stderr, and data for forwarded X11, TCP and
UNIX-domain connections). The compression algorithm is the same used by gzip(1). Compression is de‐
sirable on modem lines and other slow connections, but will only slow down things on fast networks.
The default value can be set on a host-by-host basis in the configuration files; see the Compression
option.
-g Allows remote hosts to connect to local forwarded ports. If used on a multiplexed connection, then
this option must be specified on the master process.
-L [bind_address:]port:host:hostport
-L [bind_address:]port:remote_socket
-L local_socket:host:hostport
-L local_socket:remote_socket
Specifies that connections to the given TCP port or Unix socket on the local (client) host are to be
forwarded to the given host and port, or Unix socket, on the remote side. This works by allocating a
socket to listen to either a TCP port on the local side, optionally bound to the specified
bind_address, or to a Unix socket. Whenever a connection is made to the local port or socket, the
connection is forwarded over the secure channel, and a connection is made to either host port
hostport, or the Unix socket remote_socket, from the remote machine.
Port forwardings can also be specified in the configuration file. Only the superuser can forward
privileged ports. IPv6 addresses can be specified by enclosing the address in square brackets.
By default, the local port is bound in accordance with the GatewayPorts setting. However, an explicit
bind_address may be used to bind the connection to a specific address. The bind_address of
“localhost” indicates that the listening port be bound for local use only, while an empty address or
‘*’ indicates that the port should be available from all interfaces.
-N Do not execute a remote command. This is useful for just forwarding ports.
-
使用场景
- 远程机器监听在
127.0.0.1:3306
的MySQL想在本地访问
- 远程机器(到本地近通 22 端口)能访问的某个服务端口想在本地访问
-
示例
# 远程机器启动本地服务
$ python -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
# 本地转发,监听在本地的 5000 端口
ssh -L 127.0.0.1:5000:127.0.0.1:8000 root@<server-ip>
# ssh -v -CNg -L 127.0.0.1:5000:127.0.0.1:8000 root@<server-ip> -p 22
# 本地查看 ssh 监听在 5000 端口
$ ss -lpn | grep 5000
tcp LISTEN 0 128 127.0.0.1:5000 0.0.0.0:* users:(("ssh",pid=133,fd=4))
# 访问本地的 5000 端口(等价于访问远程的 8000 端口)
$ curl -i -v 127.0.0.1:5000
* Trying 127.0.0.1:5000...
* Connected to 127.0.0.1 (127.0.0.1) port 5000
...
< HTTP/1.0 200 OK
远程端口转发
- 目标:将本地主机(或局域网)服务监听的端口转发到远程服务器
- 参数
-R [bind_address:]port:host:hostport
-R [bind_address:]port:local_socket
-R remote_socket:host:hostport
-R remote_socket:local_socket
-R [bind_address:]port
需要配合修改 /etc/ssh/sshd_config
中 GatewayPorts yes
-
使用场景
- 远程服务器想访问本地监听在
127.0.0.1:8000
上的服务
-
示例
# 本地启动本地服务
$ python -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
# 远程端口转发,监听在远程的 5000 端口
ssh -R 127.0.0.1:5000:127.0.0.1:8000 root@<server-ip>
# 远程查看 sshd 监听在 5000 端口
$ ss -lpn | grep 5000
tcp LISTEN 0 128 127.0.0.1:5000 0.0.0.0:* users:(("sshd",pid=31006,fd=6))
# 远程访问自己的 5000 端口(等价于访问客户端的 8000 端口)
$ curl -i -v 127.0.0.1:5000
* Trying 127.0.0.1:5000...
* Connected to 127.0.0.1 (127.0.0.1) port 5000
...
< HTTP/1.0 200 OK
- 扩展,也可利用
yum install autossh
实现
动态转发
- 目标:将本地主机(或局域网)服务监听的端口转发到远程服务器
-D [bind_address:]port
Specifies a local “dynamic” application-level port forwarding. This works by allocating a socket to
listen to port on the local side, optionally bound to the specified bind_address. Whenever a con‐
nection is made to this port, the connection is forwarded over the secure channel, and the applica‐
tion protocol is then used to determine where to connect to from the remote machine. Currently the
SOCKS4 and SOCKS5 protocols are supported, and ssh will act as a SOCKS server. Only root can for‐
ward privileged ports. Dynamic port forwardings can also be specified in the configuration file.
IPv6 addresses can be specified by enclosing the address in square brackets. Only the superuser can
forward privileged ports. By default, the local port is bound in accordance with the GatewayPorts
setting. However, an explicit bind_address may be used to bind the connection to a specific ad‐
dress. The bind_address of “localhost” indicates that the listening port be bound for local use
only, while an empty address or ‘*’ indicates that the port should be available from all interfaces.
# 本地创建 SOCKS 代理,且监听在 5000 端口
$ ssh -D 5000 root@192.168.179.159
# 本地监听 5000 端口
$ ss -lpn | grep 5000
tcp LISTEN 0 128 127.0.0.1:5000 0.0.0.0:* users:(("ssh",pid=279,fd=4))
# 本地指定代理,可以访问远程服务器可以访问的任意服务?
HTTP_PROXY=127.0.0.1:50000 curl 192.168.179.159:8000
高级用法
执行多个命令
ssh -CT -o BatchMode=yes c1
F&Q
ssh 链接慢
UseDNS no
# GSSAPI options, 使用 ssh -v xiexianbin@git.xiexianbin.cn 调试发现连接gssapi-with-mic消耗时间较长
GSSAPIAuthentication no
GSSAPICleanupCredentials no
w
命令查看当前ssh远程连接数,是否达到最大连接数
vim /etc/ssh/sshd_config
LoginGraceTime 2m # 参数表示登录验证时间
MaxAuthTries 6 # 最大验证重试次数
MaxSessions 10 # 最大远程连接数
last
命令查看最近登录日志
/var/log/secure
系统安全日志
/etc/hosts.all
和/etc/hosts.deny
查看限定的ip
ssh 连接超时中断
超时时,ssh console
的错误日志
client_loop: send disconnect: Broken pipe
- ssh 指定超时时长
ssh -o ServerAliveInterval=600 xxx
- 配置文件指定
echo "ServerAliveInterval 600" >>> ~/.ssh/config
- 或在
~/.ssh/config
中配置
Host *
ServerAliveInterval 600
服务器端,修改/etc/ssh/sshd_config
,重启 sshd 后生效:
# 单位:秒,SSH 客户端不活动时间间隔
ClientAliveInterval 200
# SSH 服务器向客户端尝试发送活动消息的次数,超过该次数后将中断 SSH 连接
ClientAliveCountMax 3
禁用密码登录
编辑/etc/ssh/sshd_config
,将PasswordAuthentication
参数值修改为no
:
sed 's/#PasswordAuthentication yes/PasswordAuthentication no/g' -i /etc/ssh/sshd_config
systemctl restart ssh.service # sshd.service
禁止提示 Warning: Permanently added ’’ (ED25519) to the list of known hosts.
Host *.mydomain.com
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
User foo
LogLevel QUIET
# 或
ssh -o LogLevel=ERROR xxx
ssh -o LogLevel=quiet xxx
扩展
- 其他的实现
- dropbear 是 Matt Johnston 编写的一个软件包,提供与安全壳兼容的服务器和客户端
- 它被设计为标准 OpenSSH 的替代品,适用于内存和处理器资源较少的环境,如嵌入式系统
- 它是 OpenWrt 和其他路由器发行版的核心组件
- 官网