理想情況下的運維肉迫,是不需要ops去ssh到服務(wù)器上檢查問題(包括安全問題)/日志等符衔,這些是可以通過更好
監(jiān)控,如使用newrelic,或者更好的日志收集系統(tǒng)刃麸,如splunk等去避免失暴。不過現(xiàn)實不總是完美的邀层,加上歷史遺留的原因异赫,ops總是會ssh到堡壘機(bastion host)椅挣,然后跳轉(zhuǎn)到目標服務(wù)器去做操作。
于是塔拳,就有很多人(包括我)在堡壘機上生成key/pair, 而且private key很少加密(包括我)鼠证,這個存在很嚴重的安全風(fēng)險。
一個比較合理的方式是通過ssh proxy的方式去訪問目標服務(wù)器靠抑,這樣不需要把key暴露給bastion量九,比如:
~> ssh -L 3333:destination_host:22 user@bastion
然后再啟動一個新的ssh進程去通過proxy連接:
~> ssh -p 3333 user@0
每次這么操作略麻煩,可以通過在ssh配置文件簡化:
Host bastion
HostName 192.168.1.1
HostKeyAlias bastion
LocalForward 9999 target:22
那么建立proxy就只是ssh user@bastion
就可以了颂碧,然后同理去ssh -p 9999 user@0
荠列。
這么做的壞處在于~/.ssh/config
配置可能會迅速膨脹,同時载城,每次還是啟動兩個進程去完成這件事情肌似,不開心。
于是诉瓦,我們的安全大神介紹一個更加簡單的方法川队,在~/.ssh/config
中,加入下面的內(nèi)容:
Host */*
ProxyCommand ssh $(dirname %h) -W $(basename %h):%
如此我就可以通過ssh user@bastion/target
的方式直接ssh到遠程主機睬澡,ProxyCommand
指令會
生成兩個進程固额,后臺proxy進程,前臺的進程直接通過proxy連接到目標主機煞聪。這樣從命令行窗口看來我只
是打開了一個會話斗躏。同時,你可以鏈接很多個主機米绕,如ssh user@bastion/targetA/targetB/targetC
瑟捣。
依次通過前一個主機建立的proxy連接到后面的主機上。
這個方法有一些局限:
- 不能在主機鏈上指定不同的端口栅干;
- 不能對不同的主機使用不同的登錄用戶名迈套;
- 不同鏈上建立的連接不能重用已經(jīng)建立的連接,這可能會導(dǎo)致連接的速度減緩碱鳞;
- 其實還有個問題就是不能很容易的從
targetC
退出到targetB
…… (我想的)
為了解決這些問題桑李,大神想出了終極解決方案:
Host */*
ControlMaster auto
ControlPath ~/.ssh/.sessions/%r@%h:%p
ProxyCommand /bin/sh -c 'mkdir -p -m700 ~/.ssh/.sessions/"%r@$(dirname %h)" && exec ssh -o "ControlMaster auto" -o "ControlPath ~/.ssh/.sessions/%r@$(dirname %h):%p" -o "ControlPersist 120s" -l %r -p %p $(dirname %h) -W $(basename %h):%p'
-
Host */*
: 匹配ssh到A/B/X
這樣的主機類型,然后遞歸的ssh到鏈中的主機; -
ControlMaster auto
: 這個指令的意思是指ssh應(yīng)當復(fù)用已有的control channel連接遠程主
機贵白,如果這樣的channel不存在率拒,則重新創(chuàng)建,以便以后的鏈接復(fù)用禁荒; -
ControlPath ~/.ssh/.sessions/%r@%h:%p
: 這個指令告訴ssh control channel socket
文件的位置猬膨。對于每個遠程主機,socket文件應(yīng)該是唯一的呛伴,如此我們可以重用已有連接并且跳過驗證勃痴。所
以我們用%r
(remote login name),%h
(remote host name)和%p
(端口)作為文件名的部分。
唯一的問題是因為路徑中的/
热康,這里會在%h
被當成一個目錄沛申,但是ssh不會自動創(chuàng)建目錄; -
ProxyCommand blah
: 命令開始時就先創(chuàng)建了所有必須的目錄姐军。ControlPersist
的意思是如果
control channel 2分鐘內(nèi)沒有活動則停止ssh進程铁材。如果你有兩個會話bastion/HostA
和
bastion/HostB
,如果不配置ControlPersist
奕锌,結(jié)束第一個進程時第二進程也會同時被干掉著觉。
所以,當你用上面的配置去ssh user@bastion/A/B/C
時:
- ssh 匹配到了
*/*
模式 - ssh 嘗試重用
~/.ssh/.sessions/user@bastion/A/B/C:22
的socket惊暴,如果成功則建立連接固惯,
沒有則繼續(xù)執(zhí)行 - ssh執(zhí)行
ProxyCommand
中的內(nèi)容, 創(chuàng)建目錄同時遞歸的ssh到最終的主機C - 然后ssh在主機C上進行身份驗證缴守,成功則創(chuàng)建
~/.ssh/.sessions/user@bastion/A/B/C:22
的
control channel socket文件葬毫,并且成為control channel的master - 顯示命令行提示符
你現(xiàn)在有沒有和我一樣暈,在和大神交流一番后屡穗,大神告訴我一個改進版的配置:
Host */*
ControlMaster auto
ProxyCommand /usr/bin/ssh -o "ControlMaster auto" -o "ControlPath ~/.ssh/.sessions/%%C" -o "ControlPersist 120s" -l %r -p %p $(dirname %h) -W $(basename %h):%p
Host *
ControlPath ~/.ssh/.sessions/%C
這個配置要簡單些贴捡,不過他假設(shè)你已經(jīng)創(chuàng)建了~/.ssh/.sessions
目錄。
榮耀歸于Dmitry大神村砂,雖然那個ssh keypair我還沒有刪除……烂斋。
原文