在本小節中,讓我花點篇幅繞個彎子解釋下文章標題是什麼意思,以及這篇文章到底講的是什麼,這將有助於理解本文的內容。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的漏洞來黑系統的話,他就會跟漏洞掃描工具同樣,根本找不到漏洞。