一直都在使用OpenSSH这个玩意儿连接我们的Linux, 该仔细看一看他噜~
SSH概述
首先我们知道, SSH(secure shell)是一个协议, 也就是一个规范. 默认监听在22/tcp端口, 提供安全的远程登录功能, 而OpenSSH就是一个开源实现.
在机房几百几千台服务器, 我们不可能一个一个都去配置上显示器, 所以就想办法通过网络来进行连接. 这里我们所连接上的就是远程终端. 要把对方显示的结果显示到我们本地的显示器上, 并且通过本地键盘键入命令, 获得远程执行的结果. 包括我们在登录时输入的账号和密码, 都需要通过网络来传输. 因此默认情况下, 原本本地的终端使用mingetty
来附加上一个login
程序, 来让用户登录. BUT! mingetty并没有能使得远程管理的功能. 因此我们就需要在服务器(远程终端)执行一个服务端程序来将本地的基于终端的请求通过某种特定的协议映射到需要交换的对象上. 这也是C/S架构的一个应用.
早期, 上述的实现是使用的telnet. 两地终端通过telnet协议进行通信. telnet同样使用tcp, 但它监听在23端口. telnet没有涉及任何加密, 所以所有的数据都是明文传输, 这是相当于不安全的. 于是telnet的后继者ssh就出现了. 并且解决了除了远程登录的诸多功能.
在一台CentOS6的机器上实验, 注意, 这个telnet服务属于是超级守护进程的瞬时激活进程 (不知道这样说对不对?) 也就是由xinetd统一管理的进程之一, xinetd有点像我们之前说的systemd的socket激活机制, 按需激活的机制. 所以我们只要启动xinetd服务即可.
有关SSH安全机制在 补充-SSH-Secure-Shell-的原理和处理过程 说过一点.
OpenSSH也是C/S架构的, 我们的服务器端就叫做: sshd, 而对于客户端则直接叫做: ssh, 并且还有scp这样的工具, 还有sftp这样的子系统.
SSH命令和配置
SSH命令
直接man一下ssh, 其实支持的功能还是蛮多的. 最简单的方法就直接在后面加上主机名就行了. 用户名是可以省略的, 会使用当前系统登录的用户作为默认用户来登录. 除了默认的user@hostname, 也可以使用**-l**来指定user, 也就是:
1 | ssh -l root 192.168.100.10 # 就等价于ssh root@192.168.100.10 |
另外一个很重要的属性是**-p**, 后面指定port. 这个属性通常很有用, 因为作为ssh的安全措施之一就是不使用ssh的默认22号端口. (为什么?因为这不是一个公开服务.) 其他的就不多了.
但是这里再说一个有意思, 使用SSH进行隧道建立以及内网穿透.
首先我们说几个有用的参数:
1 | -f 需要和下面的-N进行连用, 意思是说让ssh在后台运行 |
比如, 下面的这个例子. 主机A(139.199.XX.130)这个IP是一个公网IP, 小明在寝室使用实验室的代理主机B(59.68.XX.126)连接互联网, 但是由于校园网的防火墙配置使得小明无法使用ssh,ping等访问到校园网以外的网络. 于是小明原来可以先登录到代理主机B, 接着在代理主机B上进行操作登录到主机A. 但是这样很麻烦, 于是小明在主机B上使用:
1 | ssh -CfNg -L 10022:localhost:1022 root@139.199.XX.130 -p 1022 |
搭建了一条SSH转发隧道. 也就是当ssh连接主机B的10022端口的时候, 会被转发到目标主机的1022端口上. 意思就是说在代理主机B上创建了一个端口为10022的套接字, 当有ssh连接请求到这个套接字的时候, 就会将接下来的请求转发到位于主机A的1022那个端口上(说到这里你也就知道了, 都能够进行端口转发了. 其实还有别的用处, 比如说进行穿透的ftp服务访问等等..)
另外, 小红在实验室遇到了一个问题, 他想让小明远程连接过来帮他解决, 但是实验室内网没有建立VPN, 由于小红能够访问到处在公网的小明. 所以小红就想到使用ssh建立一条隧道, 使得小明能够直接连接进来. 这样该怎么做呢? 反过来建立的方法是这样:
1 | ssh -CfNg -R 10022:localhost:1022 root@139.199.XX.130 -p 1022 |
唯一的区别就在于那个参数的改变, 使得监听套接字的创建换成了另外一台主机.
注意: 这个实验成功的一个注意点是你的iptables设定. 另外直接这么开着一条隧道是一件十分危险的事情! 请务必把他关闭, 或者添加安全措施(增加专用用户等等)
SSH配置
客户端的ssh配置文件在/etc/ssh/ssh_config
上 而服务器端的就在同一目录下的sshd_config
里. 首先我们看一下客户端的咯.
文件中定义了对于一个特定的Host使用什么样的配置, 而默认的*就是所有主机. 基本上默认的值我们无需进行定制. 其中有一个叫StrictHostKeyChecking
的配置, 意思是说是否进行严格主机密钥检查. 如果将该值从ask改为no, 就不会出现询问是否添加RSAkey的消息了, 会直接进行写入. 另外一个常用的值就是port了, 这样在换端口了之后就不需要再每次连接的时候加上-p参数了.
基于密钥的SSH登录
每次输入密码有点麻烦, 所以我们不如配个公私钥来免密码登录.
首先我们需要在本地生成密钥:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 [root@WWW ~]# ssh-keygen -t rsa -P "" -f "/root/.ssh/id_rsa"
Generating public/private rsa key pair.
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
fc:14:0a:bb:22:81:f7:74:21:2c:2a:c7:f4:a5:19:25 root@WWW
The key's randomart image is:
+--[ RSA 2048]----+
| E . |
| . o |
| o + + . |
| = o * = . . |
|+ = = o S . |
|.o + . . o |
| . o . . |
| . . |
| |
+-----------------+
# 如果希望得到交互式的结果,可以直接敲ssh-keygen就行了(不过还是建议起码加上-t rsa)得到了这一对密钥就可以继续. 我们把公钥发送出去. 怎么发送呢? OpenSSH提供了
ssh-copy-id
的工具帮助我们方便进行公钥的发送, 用法如下:
1 ssh-copy-id [-n] [-i [identity_file]] [-p port] [-o ssh_option] [user@]hostname一般我们只需要指定-i就行了, 这个identity文件的寻找和默认的是不一样的,所以这个参数几乎是必须要加的.
添加过后会出现:
1
2
3
4
5
6
7
8
9 [root@WWW ~]# ssh-copy-id -i .ssh/id_rsa.pub root@192.168.56.101
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@192.168.56.101's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'root@192.168.56.101'"
and check to make sure that only the key(s) you wanted were added.接着就可以直接进行免密登陆了.
接着我们再插着所以下之前提到的SCP命令. 这个SCP的意思其实就是secure copy, 也就是说基于SSH进行跨主机的文件传输, 其有两种模式: PULL和PUSH.(和你git的那两个差不多)
使用起来也很简单:
1 | scp [options] SRC... DEST/ |
存在两种情形所以命令是不一样的, 如果是PULL(拉取服务器数据保存到本地) :
1 | scp [options] [user@]host:/PATH/FROM/FILE /PATH/TO/SOMEWHERE |
而PUSH(将本地的文件推送到远端主机上)呢?
1 | scp [options] /PATH/TO/FILE [user@]host:/PATH/TO/SOMEWHERE |
常用的选项有这些:
- -r: 递归复制
- -p: 保持源文件的属性信息
- -q: 静默模式
- -P port: 远端主机监听的port
更厉害的是我们的sftp
, 他甚至可以像在自己机器上面一样可以做各种各种的事情(当然前提是有权限), 包括制作连接, 进行权限设定啥的.
OK, 现在就来说一说服务器端的配置文件吧. 这个文件的价值个人觉得是很大的.
和一般的配置文件相同, 井号开头不加空格的就是可启用的配置项, 加空格的就是注释.
看一下有哪些配置项吧:
1 | Port 20022 # 一般为了安全我们不会使用默认的22端口 |
在实验中请把SELINUX的状态改成Permissive或者直接关闭, 否则端口改变会失效.
值得一提的是, ssh服务端的一个配置项: UseDNS yes
意思是说是否使用DNS反向解析, 默认是打开的, 这会消耗巨量的时间. 短则几十秒, 长则几分钟 因此我们应该将这个关闭.
SSH服务的最佳实践
不使用默认的端口
禁止使用protocol version 1
限制可登录用户
有两个属性叫做AllowUsers和AllowGroups可以用来做白名单, 反之还有黑名单. 注意, 当你两个都设置的时候, ssh会取两者的交集(就是说:当你允许root和用户test登录, 但是只允许root组登录的时候, test依旧会被拒绝.)
设定空闲会话超时时长
利用防火墙设置ssh的访问策略
仅监听特定的IP地址
基于口令认证的时候, 使用强密码策略 ( 一个生成随机数密码的小技巧 )
tr -cd a-zA-Z0-9_ < /dev/urandom | head -c 30 | xargs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
* 使用基于密钥的验证
* 禁止使用空密码
* 禁止root用户直接登录
* 限制ssh的访问频度, 和同并发数量限制
* 做好日志记录, 保存在日志服务器上
* 经常分析记录
## OpenSSL和CA相关
我们之前已经说了好大一会openssl这个东西了, 但是当时就草草的随意提了下关于证书的种种. 今天就在这里详细说一下关于CA和证书的事情.
[OpenSSL初识](https://yaoxuannn.com/2017/09/08/OpenSSL%E5%88%9D%E8%AF%86%E4%B8%8E%E7%AE%80%E5%8D%95%E7%9A%84%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1%E5%AE%89%E5%85%A8/)
我们之前提到过 有一个实现现代互联网安全使得最根本的保证, 叫做PKI, 全称是Public Key Infrastructure. 也就是公钥基础设施. 主要有下面几个成分组成, 也就是CA(发证机构), RA(注册机构), CRL(吊销列表), 证书存取库. 其中CA是非常重要的, 而注册机构也是需要发给CA, 然后由CA做签名的. 但是总不能一直都支付那么高的费用, 例如, 日后我需要建立VPN来连接机构组织的内网, 由于这个VPN需要验证而且他本身就是用在我们这个组织内部的, 所以我们可以使用**内部证书**, 或者说**私有证书**, 这种感觉就好像是银行所使用的某某盾, 某某通行证啥的, 这些东西只有在银行内部才能够使用 如果每一个客户的支付通行证都是购买的证书的话, 那开销也太大了.
建立私有CA, 已经可以满足我们的需要了. 建立私有CA, 有很多专门的工具来做到, 其中由一个非常著名的实现叫做OpenCA, 但是过于复杂和专业, 所以我们使用openssl来实现. 其实OpenCA就是对openssl的高度抽象来实现更多强大的功能. 下面就来说怎么建立私有CA.
首先就要先了解一下我们的证书申请和签署步骤:
* 生成申请请求: 提供主机名以及一些唯一标识, 如果是主机间通信的话, 还应该有主机所属的组织, 邮箱地址等等. 这些信息RA会进行核验, 所以一定要写真实的.
* 接着就是RA的核验
* 接着交给CA做签署
* 最后获取证书
如何创建私有证书? openssl自己就可以进行证书的签署以及吊销他们. 我们来看看如何操作的:
他的配置文件在: `/etc/pki/tls/openssl.cnf` 这是作为CA时用到的默认配置文件. 其中我们只要先关注CA的部分就行:
```bash
[ CA_default ]
dir = /etc/pki/CA # Where everything is kept
certs = $dir/certs # Where the issued certs are kept 已签署的证书
crl_dir = $dir/crl # Where the issued crl are kept 吊销列表
database = $dir/index.txt # database index file. 索引文件, 已签署的
#unique_subject = no # Set to 'no' to allow creation of 证书的信息是否可一致
# several ctificates with same subject.
new_certs_dir = $dir/newcerts # default place for new certs. 签署过程中的存放位置
certificate = $dir/cacert.pem # The CA certificate CA自己的证书
serial = $dir/serial # The current serial number 序列号
crlnumber = $dir/crlnumber # the current crl number
# must be commented out to leave a V1 CRL
crl = $dir/crl.pem # The current CRL
private_key = $dir/private/cakey.pem# The private key
RANDFILE = $dir/private/.rand # private random number file
...(omitted)
default_days = 365 # how long to certify for 证书的有效期
default_crl_days= 30 # how long before next CRL 吊销列表的有效期限
后面的注释写的也很清楚了. 这里定义了很多工作目录的位置, 比如说第一个cert就是存放已经签署过的证书所在地. 而总的dir在最一开始进行定义: //etc/pki/CA
我们进去看一看
1 | [root@WWW CA]# ls |
缺少了很多东西, 所以第一步我们就需要创建所需要的文件和准备序列号:
1 | [root@WWW CA]# touch index.txt |
第二步, 我们先要给CA签署证书, 也就是自签. 要想得到证书, 我们就要先生成密钥对.
1 | [root@WWW CA]# (umask 077; openssl genrsa -out private/cakey.pem 2048) |
这样就生成了私钥, 接着就应该提取公钥了, 但是现在我们可以不使用pubout了, 可以直接使用openssl提供的req工具. 使用下面的命令:
1 | [root@WWW CA]# openssl req -new -x509 -key private/cakey.pem -days 7300 -out cacert.pem |
接着就是第三步了, 发证
其中也需要几个小步骤: 首先需要用到证书的主机发出生成证书请求. 把请求文件传输给CA. CA确认没有问题之后就直接签署证书发还给请求者.
所以现在我们的主机已经是一台CA了. 那么现在我们启动另外一台主机, 像我们这台主机发起证书请求.
就以httpd来做这个实验:
1 | [root@WWW ~]# cd /etc/httpd/ |
好了现在, 我们就已经将证书请求发送到目标CA了, 接着回到目标CA所在的主机:
1 | [root@WWW tmp]# openssl ca -in httpd.csr -out httpd.crt -days 3650 |
这样就签署完成了, 并且生成了证书文件. 同时我们注意到最后一行: 数据库更新. 那么来看一下index.txt吧:
1 | [root@WWW tmp]# cat /etc/pki/CA/index.txt |
01号签署证书, 后面还有各种记录. 而新生成的证书就保存了一份到newcerts目录. 其实就是和我们out出来的httpd.crt同一个文件.
1 | [root@WWW newcerts]# cd /etc/pki/CA/newcerts/ |
接下来我们把证书发还给客户端.
1 | [root@WWW newcerts]# scp /tmp/httpd.crt root@192.168.56.103:/etc/httpd/ssl/ |
完成了, 以后搭建https的时候就可以直接使用了.
1 | [root@WWW tmp]# cd /etc/httpd/ssl/ |
发证的过程就是这样. 如果日后忘记了可以使用x509来查看. x509就是专门用来进行证书管理的:
1 | [root@WWW ssl]# openssl x509 -in httpd.crt -text |
OK, 最后我们再来说一说如何去吊销证书.(虽然可能用的机会不多)
首先我们要获取要吊销证书的serial号码, 怎么获取呢?很简单就是用上面的x509子命令, 将-text改成-serial就好了. 接着CA要根据客户提交的信息(serial, subject)和数据库(index.txt)中的信息是否一致. 接着就可以进行证书吊销了.
怎么做? 还是使用ca子命令, 加上-revoke参数就可以了:
1 | [root@WWW newcerts]# openssl ca -revoke 01.pem |
接着生成吊销证书标号就可以了. 如果是第一次进行吊销, 要先在/etc/pki/CA/crlnumber
中加上01., 也就是:
1 | [root@WWW newcerts]# echo 01 > /etc/pki/CA/crlnumber |
最后一步, 更新证书吊销列表:
1 | [root@WWW newcerts]# openssl ca -gencrl -out /etc/pki/CA/crl/httpd.crl |
以上.