在本小节中,让我花点篇幅绕个弯子解释下文章标题是什么意思,以及这篇文章到底讲的是什么,这将有助于理解本文的内容。shell
有时,可能因为审计须要或修复漏洞的须要,咱们可能会遇到这么一个需求:升级操做系统的openssl。vim
那,怎么升级操做系统的openssl呢?那很简单,一条命令搞定:centos
[root@gw ~]# yum update openssl -y缓存 |
没错,这的确能够升级操做系统的openssl。这只是小版本的升级,好比将openssl从1.0.1e-43版本升级到1.0.1e-57版本,也能够修补一些漏洞。可是,在审计时,审计人员会告诉你,这不行,他要的是跨版本的升级。好比,将操做系统的openssl从1.0.1e版本升级到1.0.2h版本,啥,新出了个1.1.0h版本,那就升级到最新的1.1.0h版本吧。他就认为版本越高越好,漏洞越少,他也无论你到底有没有进行跨版本升级的必要性,究竟是不是真的技术上可行。而他怎么看openssl版本的呢?可能就是登陆到系统中,执行下面的命令:安全
[root@gw ~]# ssh -V服务器 |
就由于ssh -V命令执行后显示出来的openssl版本较低,就说要(跨版本)升级操做系统的openssl。这里面存在逻辑问题,让我逐个地解释。session
首先,我给个结论:跨版本升级操做系统的openssl是不可能的。app
你能够在系统中尝试执行下yum remove openssl命令,你就能够看到,很是很是多的软件是依赖于openssl软件。openssl是一个很是基础的软件。假设你升级了操做系统的openssl,好比说,编译安装一个新版本的openssl覆盖掉操做系统自带的openssl。这就会致使那些依赖于openssl的软件的openssl相关的功能变得不可用,好比说,某软件本来是支持https的,如今可能就不支持了。除非你能将系统中全部依赖于openssl的软件基于新版本的openssl全都编译一遍,而这一般是不可能的。ssh
其次,咱们并不须要升级操做系统的openssl。tcp
我想,不少人在碰到这个需求时,可能都有去百度过,而后百度出了一大堆文章,而后了解到也须要从新编译安装openssh。可是,几乎我看到的全部文章,都采用的是错误的作法,因此才有了我这篇文章。他们的作法虽然各不相同,但大概也能够归纳为:先强制卸载操做系统的openssl、openssh,再编译安装一个新版本的openssl(和其它可能的附带软件),而后各类莫名其妙的操做,最后编译安装一个新版本的openssh。这种作法没法达到目的吗?那倒也不是。但可能代价就是,全部其它依赖于操做系统的openssl的软件的openssl相关的功能都不能用了。
所以来讲,正确的作法是什么?这就是本文所要介绍的。
ssh命令是openssh软件的一部分。咱们没法升级操做系统的openssl,可是咱们能够另外编译一个openssl,放到单独的应用目录中,与操做系统的openssl互不干涉。再基于新编译出来的openssl,将新的openssh软件编译出来。 而操做系统的openssh是能够被替换的。若是你尝试执行 yum remove openssh* 命令就能够看到,没有其它软件依赖于openssh。此外,openssh软件提供了sshd服务。因此,咱们只要还要配置并搭建好sshd服务,就能够替代操做系统自带的openssh了。
简单来讲,这篇文章讲的就是,如何升级openssh及其所依赖的openssl。
要升级openssh,咱们须要先搞懂openssl是怎么安装的。
我使用的操做系统是centos 6的,我以安装openssh 7.5.p1为例。
从openssh官网下载openssh 7.5.p1源码包,查看里面的INSTALL文件,里面有对它的依赖关系作说明。
openssh 7.5.p1对下列软件的依赖是必选的:
openssh依赖的软件(必选) |
备注 |
Zlib |
要求1.1.4或1.2.1.2或更新的版本(1.2.x早期的版本有问题)。 |
libcrypto (LibreSSL或OpenSSL) |
OpenSSH依赖于libcrypto,而libcrypto能够由LibreSSL或OpenSSL提供。若是是使用的OpenSSL,要求OpenSSL的版本要大于等于0.9.8f并小于1.1.0。因为API不一样,如今还不支持OpenSSL 1.1.x版本。 LibreSSL/OpenSSL应该编译成位置无关的库(position-independent library),好比使用-fPIC选项,不然OpenSSH会没法连接它;若是你必须使用一个非位置无关(non-position-independent)的libcrypto,那么你在编译OpenSSH时必须加上--without-pie选项。 |
openssh 7.5.p1对下列软件的依赖是可选的:
openssh依赖的软件(可选) |
备注 |
PAM |
若是操做系统支持PAM(Pluggable Authentication Modules),那么OpenSSH就能够被编译成支持PAM功能的。大多数的Linux发行版,天然也包括RedHat/CentOS系统,都是支持PAM的。因此,若是咱们要编译出一个能够替换操做系统自带OpenSSH的完整功能的OpenSSH,天然也是要支持PAM的。 |
其它软件: NB PRNGD EGD GNOME S/Key LibEdit LDNS Autoconf Basic Security Module (BSM) |
这些就不展开来讲了。 |
基本上来讲,要编译出一个功能相似于操做系统自带的OpenSSH软件,咱们至少须要先准备好Zlib、OpenSSL(或LibreSSL)和PAM软件。下面,咱们就逐个逐个地来进行安装。
Zlib用于提供压缩和解压缩功能。操做系统已经自带了zlib,版本也符合要求。实际上,openssl和openssh都依赖于zlib。执行下面的命令,安装zlib开发包:
[root@gw ~]# yum install zlib-devel -y |
PAM(Pluggable Authentication Modules,可插拔认证模块)用于提供安全控制。操做系统也已经自带了PAM,版本也是能够的。执行下面的命令,安装PAM开发包:
[root@gw ~]# yum install pam-devel -y |
tcp_wrappers是一种安全工具,一般,咱们在/etc/hosts.allow或/etc/hosts.deny文件中配置的过滤规则就是使用的tcp_wrappers的功能了。openssh在编译时的确是能够选择支持tcp_wrappers的,但我不知道为何它的安装要求里面没有体现。操做系统自带的tcp_wrappers的版本是能够的。执行下面的命令,安装tcp_wrappers开发包:
[root@gw ~]# yum install tcp_wrappers-devel -y |
因为OpenSSH 7.5.p1要求OpenSSL的版本大于等于0.9.8f并小于1.1.0,所以,当前(2017年6月)符合要求的最新OpenSSL版本为1.0.2l。
首先,从openssl官网下载源码包openssl-fips-2.0.16.tar.gz,将其安装到/opt/fips-2.0.16目录下。
编译安装FIPS模块:
[root@gw OpenSSL]# export FIPSDIR=/opt/fips-2.0.16 [root@gw OpenSSL]# tar -xvf openssl-fips-2.0.16.tar.gz [root@gw OpenSSL]# cd openssl-fips-2.0.16 [root@gw openssl-fips-2.0.16]# ./config [root@gw openssl-fips-2.0.16]# make [root@gw openssl-fips-2.0.16]# make install |
说明:
在编译前先设定环境变量FIPSDIR,这是用于指定FIPS模块的安装目录,这是fips软件特有的安装特性。软件编译时会检测该环境变量是否存在。若不指定,默认会安装在/usr/local/ssl/fips2.0目录。
从openssl官网下载源码包openssl-1.0.2l.tar.gz,将其安装到/opt/openssl1.0.2l_20170617目录下。将openssl安装到专门的目录,这是为了不对操做系统自带的openssl形成影响。
编译安装OpenSSL:
[root@gw OpenSSL]# tar -xvf openssl-1.0.2l.tar.gz [root@gw OpenSSL]# cd openssl-1.0.2l [root@gw openssl-1.0.2l]# ./config --prefix=/opt/openssl1.0.2l_20170617 --openssldir=/opt/openssl1.0.2l_20170617/openssl fips --with-fipsdir=/opt/fips-2.0.16 zlib-dynamic shared -fPIC [root@gw openssl-1.0.2l]# make depend [root@gw openssl-1.0.2l]# make [root@gw openssl-1.0.2l]# make test [root@gw openssl-1.0.2l]# make install |
下面是编译时使用到的选项:
--prefix:指定openssl的安装目录。按本例中的安装方式,安装完成后该目录下会包含bin(含二进制程序)、lib(含动态库文件)、include/openssl(含报头文件)及openssl(--openssldir选项指定的)这些子目录。
--openssldir:指定openssl文件的安装目录。按本例中的安装方式,安装完成后该目录下会包括certs(存放证书文件)、man(存放man文件)、misc(存放各类脚本)、private(存放私钥文件)这些子目录及openssl.cnf配置文件。
fips:集成FIPS模块。
--with-fipsdir:指向FIPS模块的安装目录位置。
zlib-dynamic:编译支持zlib压缩/解压缩,让openssl加载zlib动态库。该选项只在支持加载动态库的操做系统上才支持。这是默认选项。
shared:除了静态库之外,让openssl(在支持的平台上)也编译生成openssl动态库。
-fPIC:将openssl动态库编译成位置无关(position-independent)的代码。
安装完成后,将OpenSSL的库文件目录添加到/etc/ld.so.conf文件中,并加载到系统内存缓存中:
[root@gw openssl-1.0.2l]# echo '/opt/openssl1.0.2l_20170617/lib' >> /etc/ld.so.conf [root@gw openssl-1.0.2l]# ldconfig |
从openssl官网下载源码包openssh-7.5p1.tar.gz,将其安装到/opt/openssh7.5.p1_20170617目录下。将openssh安装到专门的目录,这是为了不与操做系统自带的openssh形成没必要要的冲突,增长复杂度。
编译安装OpenSSH:
[root@gw OpenSSH]# tar -xvf openssh-7.5p1.tar.gz [root@gw OpenSSH]# cd openssh-7.5p1 [root@gw openssh-7.5p1]# ./configure --prefix=/opt/openssh7.5.p1_20170617 --with-ssl-dir=/opt/openssl1.0.2l_20170617 --with-pam --with-tcp-wrappers [root@gw openssh-7.5p1]# make [root@gw openssh-7.5p1]# make install |
下面是编译时使用到的选项:
--prefix:指定安装目录
--with-ssl-dir=DIR:指向LibreSSL/OpenSSL库的安装目录的所在路径。
--with-pam:启用PAM支持。根据OpenSSH的安装说明,若是在编译时启用了PAM,那么在安装完成后,也必须在sshd服务的配置文件sshd_config中启用它(使用UsePAM指令)。
根据OpenSSH的安装说明,若是有启用PAM,那么就须要手工安装一个给sshd程序使用的PAM配置文件,不然安装好OpenSSH后你可能会没法使用密码登陆系统。在编译时,我使用 --with-pam 选项启用了对PAM的支持,可是,编译OpenSSH时并无编译选项让你指定PAM配置文件的位置,那么咱们要怎么提供这个配置文件呢?
事实上,OpenSSH有另一个编译选项--with-pam-service=name能够指定PAM服务名,它的默认值是sshd。而操做系统自带的PAM软件默认将全部PAM配置文件都放置在/etc/pam.d目录下。结合这两个信息,就可肯定OpenSSH的PAM配置文件应为/etc/pam.d/sshd文件。而这个文件原来就有了,因此咱们不用额外手工建立一个。
设置PATH路径:
[root@gw ~]# echo 'export PATH=/opt/openssh7.5.p1_20170617/bin:/opt/openssh7.5.p1_20170617/sbin:$PATH' >> /etc/profile.d/path.sh [root@gw ~]# . /etc/profile.d/path.sh |
此时,使用ssh -V命令就能够看到新版本号了:
[root@gw ~]# ssh -V OpenSSH_7.5p1, OpenSSL 1.0.2l-fips 25 May 2017 |
前面已经安装好了openssh,可是咱们还须要配置它,以保证sshd服务能够启起来。
咱们能够先看一下原有的sshd服务(属于openssh-server软件包)都有哪些配置文件:
[root@gw ~]# rpm -ql openssh-server | grep -i --color etc /etc/pam.d/ssh-keycat /etc/pam.d/sshd /etc/rc.d/init.d/sshd /etc/ssh/sshd_config /etc/sysconfig/sshd |
能够看到,sshd服务的配置文件为/etc/ssh/sshd_config,它的pam配置文件为/etc/pam.d/sshd和/etc/pam.d/ssh-keycat,启动脚本文件为/etc/rc.d/init.d/sshd,启动脚本里面有引用到文件/etc/sysconfig/sshd。
参照系统原有的配置文件修改咱们软件的sshd_config配置文件,这是sshd服务的配置文件(红色字体的为新增或修改的部分):
[root@gw ~]# vim /opt/openssh7.5.p1_20170617/etc/sshd_config Protocol 2 SyslogFacility AUTHPRIV PermitRootLogin yes AuthorizedKeysFile .ssh/authorized_keys PasswordAuthentication yes ChallengeResponseAuthentication no #GSSAPIAuthentication yes //该选项目前还不支持 #GSSAPICleanupCredentials yes //该选项目前还不支持 UsePAM yes
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE AcceptEnv XMODIFIERS
X11Forwarding yes Subsystem sftp /opt/openssh7.5.p1_20170617/libexec/sftp-server |
注意,UsePAM必定要启用,OpenSSH的安装说明里有提到,若是编译时启用了PAM支持,那么就必须在sshd_config文件中启用它。
拷贝系统原有的配置文件/etc/sysconfig/sshd到咱们软件下面,这个配置文件用于设置启动sshd服务所需的环境变量,在sshd服务的启动脚本里有调用到该配置文件:
[root@gw ~]# cp -a /etc/sysconfig/sshd /opt/openssh7.5.p1_20170617/etc/sshd |
接下来要修改sshd服务的启动脚本/etc/rc.d/init.d/sshd。先将启动脚本备份一份为sshd.old,并添加至chkconfig管理:
[root@gw ~]# cp /etc/rc.d/init.d/sshd /etc/rc.d/init.d/sshd.old [root@gw ~]# chkconfig --add sshd.old |
再根据咱们的OpenSSH的安装路径,来修改原有的启动脚本(红色字体为有新增或修改的部分):
[root@gw ~]# vim /etc/rc.d/init.d/sshd ### BEGIN INIT INFO # Provides: sshd # Required-Start: $local_fs $network $syslog # Required-Stop: $local_fs $syslog # Should-Start: $syslog # Should-Stop: $network $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start up the OpenSSH server daemon # Description: SSH is a protocol for secure remote shell access. # This service starts up the OpenSSH server daemon. ### END INIT INFO
. /etc/rc.d/init.d/functions
[ -f /opt/openssh7.5.p1_20170617/etc/sshd ] && . /opt/openssh7.5.p1_20170617/etc/sshd
RETVAL=0 prog="sshd" lockfile=/var/lock/subsys/$prog
KEYGEN=/opt/openssh7.5.p1_20170617/bin/ssh-keygen SSHD=/opt/openssh7.5.p1_20170617/sbin/sshd RSA1_KEY=/etc/ssh/ssh_host_key RSA_KEY=/opt/openssh7.5.p1_20170617/etc/ssh_host_rsa_key DSA_KEY=/opt/openssh7.5.p1_20170617/etc/ssh_host_dsa_key PID_FILE=/var/run/sshd.pid # PID文件的所在路径,这个变量的值不要改
runlevel=$(set -- $(runlevel); eval "echo \$$#" )
fips_enabled() { if [ -r /proc/sys/crypto/fips_enabled ]; then cat /proc/sys/crypto/fips_enabled else echo 0 fi }
do_rsa1_keygen() { if [ ! -s $RSA1_KEY -a `fips_enabled` -eq 0 ]; then echo -n $"Generating SSH1 RSA host key: " rm -f $RSA1_KEY if test ! -f $RSA1_KEY && $KEYGEN -q -t rsa1 -f $RSA1_KEY -C '' -N '' >&/dev/null; then chmod 600 $RSA1_KEY chmod 644 $RSA1_KEY.pub if [ -x /sbin/restorecon ]; then /sbin/restorecon $RSA1_KEY.pub fi success $"RSA1 key generation" echo else failure $"RSA1 key generation" echo exit 1 fi fi }
do_rsa_keygen() { if [ ! -s $RSA_KEY ]; then echo -n $"Generating SSH2 RSA host key: " rm -f $RSA_KEY if test ! -f $RSA_KEY && $KEYGEN -q -t rsa -f $RSA_KEY -C '' -N '' >&/dev/null; then chmod 600 $RSA_KEY chmod 644 $RSA_KEY.pub if [ -x /sbin/restorecon ]; then /sbin/restorecon $RSA_KEY.pub fi success $"RSA key generation" echo else failure $"RSA key generation" echo exit 1 fi fi }
do_dsa_keygen() { if [ ! -s $DSA_KEY -a `fips_enabled` -eq 0 ]; then echo -n $"Generating SSH2 DSA host key: " rm -f $DSA_KEY if test ! -f $DSA_KEY && $KEYGEN -q -t dsa -f $DSA_KEY -C '' -N '' >&/dev/null; then chmod 600 $DSA_KEY chmod 644 $DSA_KEY.pub if [ -x /sbin/restorecon ]; then /sbin/restorecon $DSA_KEY.pub fi success $"DSA key generation" echo else failure $"DSA key generation" echo exit 1 fi fi }
do_restart_sanity_check() { $SSHD -t RETVAL=$? if [ $RETVAL -ne 0 ]; then failure $"Configuration file or keys are invalid" echo fi }
start() { [ -x $SSHD ] || exit 5 [ -f /opt/openssh7.5.p1_20170617/etc/sshd_config ] || exit 6 # Create keys if necessary if [ "x${AUTOCREATE_SERVER_KEYS}" != xNO ]; then do_rsa_keygen if [ "x${AUTOCREATE_SERVER_KEYS}" != xRSAONLY ]; then #do_rsa1_keygen # 注释掉这条语句 do_dsa_keygen fi fi
echo -n $"Starting $prog: " $SSHD $OPTIONS && success || failure RETVAL=$? [ $RETVAL -eq 0 ] && touch $lockfile echo return $RETVAL }
stop() { echo -n $"Stopping $prog: " killproc -p $PID_FILE $SSHD RETVAL=$? # if we are in halt or reboot runlevel kill all running sessions # so the TCP connections are closed cleanly if [ "x$runlevel" = x0 -o "x$runlevel" = x6 ] ; then trap '' TERM killall $prog 2>/dev/null trap TERM fi [ $RETVAL -eq 0 ] && rm -f $lockfile echo }
reload() { echo -n $"Reloading $prog: " killproc -p $PID_FILE $SSHD -HUP RETVAL=$? echo }
restart() { stop start }
force_reload() { restart }
rh_status() { status -p $PID_FILE openssh-daemon }
rh_status_q() { rh_status >/dev/null 2>&1 }
case "$1" in start) rh_status_q && exit 0 start ;; stop) if ! rh_status_q; then rm -f $lockfile exit 0 fi stop ;; restart) restart ;; reload) rh_status_q || exit 7 reload ;; force-reload) force_reload ;; condrestart|try-restart) rh_status_q || exit 0 if [ -f $lockfile ] ; then do_restart_sanity_check if [ $RETVAL -eq 0 ] ; then stop # avoid race sleep 3 start else RETVAL=6 fi fi ;; status) rh_status RETVAL=$? if [ $RETVAL -eq 3 -a -f $lockfile ] ; then RETVAL=2 fi ;; *) echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}" RETVAL=2 esac exit $RETVAL |
因为OpenSSH依赖的OpenSSL已不支持rsa1,因此我将启动脚本中生成rsa1秘钥的指令注释掉了。
接下来,关键的一步来了,咱们要关闭旧的sshd服务,启动新的sshd服务。这个操做若是失败了,不会致使现有的ssh远程链接断开。因此咱们能够先关闭旧的sshd程序,再启动新的sshd程序:
[root@gw ~]# service sshd.old stop [root@gw ~]# service sshd start |
若是新的sshd服务启动成功了,咱们能够先简单测试下,好比,看看普通用户和root用户是否能正常经过ssh登陆。若是没有没有问题,咱们能够在测测其它的,好比scp、sftp是否正常等。固然,若是有条件的话,可使用漏洞扫描工具扫一下,看看有没有什么咱们没有注意到的地方。
最后,就能够删除掉旧sshd服务的启动脚本了,以避免冲突:
[root@gw ~]# rm -f /etc/init.d/sshd.old |
如今,全部操做都完成了。总的来讲,整个升级过程应该仍是挺明了的,不会有太多把系统搞挂的风险,可重复操做性强,重复升级也没有问题,也不会影响系统中的其它软件。
固然,也有能够继续完善的地方,可能有两个方面吧。
一是,openssl和openssh的编译选项基原本说我也只是使用了必要的选项,因为不知道操做系统自带的openssl和openssh本来的编译选项是什么,因此咱们编译出来的openssl和openssh软件在功能特性上只是尽量地接近原有的,安全性和性能可能也是有差别的。
二是,我复用了操做系统原有的sshd服务的配置文件和启动脚本,这可能没法充分利用新版本openssh的特性。openssl和openssh原本也是挺复杂的东西,一时半会可能也很难彻底弄明白。
可是,无论怎么说,这种升级方式,应该会比强制升级openssl和openssh的方式好不少。
我如今以为实际不必定须要手工编译升级OpenSSH,咱们使用yum update升级openssh小版本后已经能修复一些严重的漏洞了,固然这个时候使用漏洞扫描工具仍是能扫出一些漏洞,毕竟未编译升级的话openssh版本仍是较低。可是,在生产环境中,咱们一般会使用堡垒机,对全部生产环境服务器的ssh访问均需经过堡垒机进行,为达到该目的,咱们一般会在服务器的/etc/hosts.allow文件中放通堡垒机的IP以容许堡垒机访问,而在/etc/hosts.deny文件中拒绝全部其它IP的登陆访问。这样一来,即使是在服务器内网,漏洞扫描工具在对服务器进行漏洞扫描时,扫描工具根本就链接不上全部生产服务器的ssh端口,这样漏洞扫描工具根本就扫不出ssh的漏洞。我不以为这是自欺欺人,由于IP防御也是系统防御的一种,IP防御已经能解决不少问题了。假设有人想要利用openssh的漏洞来黑系统的话,他就会跟漏洞扫描工具同样,根本找不到漏洞。