堡壘跳板機實現——架構實現
總述
這是關于堡壘機實現的第二篇文章,主要闡述三層架構分別如何實現,包括***層&第二層的設計與實現,即用戶登錄堡壘機的入口 & 授權驗證, 第三層,如何通過ldap來統一管理服務器賬號權限。
關于堡壘機三層架構可以參見前一篇blog:堡壘跳板機實現——整體架構
登錄入口
先說***層,這層的主要功能為檢測用戶是否有使用堡壘機的權限。這個很好理解,總不能來個匿名用戶就可以讓他隨意使用堡壘機,雖說在二層授權驗證這里可以有效的抵擋,但是,既然可以在***層有效的對無效用戶做快捷的攔截,為什么要放后呢?
驗證的方式可以有很多種,比如,如果用linux用作***層架構中的服務器,那么可以天然的使用linux的user auth作為檢驗機制,單純為有使用權限的用戶在服務器上adduser,單***建一個唯一的32位的密碼。
服務器定制
我們在這層的做法是 將用戶驗證與我們內部的動態Token服務相結合(類似google authenticator),同時還要提供友好的登錄shell界面,這樣的話單純的使用user auth就不太能夠達到我們的目的,這里,我們對一層服務器做了ssh登錄all permit定制,修改/etc/pam.d/sshd:
- auth required pam_permit.so
- account required pam_permit.so
- password required pam_permit.so
- session required pam_permit.so
開啟sshd的pam認證,修改sshd_config:
- UsePAM yes
然后重啟sshd:
- /etc/ini.d/sshd restart
同時將用戶對應的默認login shell改為我們自定義的。
代碼結構
放上我們的代碼結構,主要結構如下:
- ├── gateway-shell
- ├── login-shell
- ├── mshell
- └── sdshell
功能描述
接下來對上述文件做下功能描述:
- login-shell: 用戶的login shell,調用sdshell驗證用戶的login passwd,初始化用戶的監控日志路徑,并開啟監控。
- sdshell:讀取用戶的login passwd并判斷驗證結果,以exit code來對表示驗證結果,0表示驗證成功,1表示驗證失敗,2表示root用戶登錄并且驗證成功。
- mshell:驗證用戶passwd成功能,循環調用gateway-shell來讀取用戶的action操作選擇,并執行。
- gateway-shell:提供給用戶操作cli界面,供用戶選擇操作。
調用順序為:
- login_shell(入口) -> sdshell(判斷PIN+TOKEN) -> mshell -> gateway-shell(判斷/獲取 用戶選項)
代碼解讀
login_shell:
當用戶登錄時首先進入到這里進行驗證
- 1 #!/bin/bash
- 2
- 3 WORKDIR=`dirname $0`
- 4 source $WORKDIR/sentry.env
- 5 BINDIR="$WORKDIR"
- 6 #LOGDIR="/data0/logdir"
- 7
- 8 #1, 驗證token
- 9 $BINDIR/sdshell
- 10 sdstats=$?
- 11 #sdstats=0
- 12
- 13 # 當sdshell返回為0時,表示當前用戶登錄成功
- 14 # 當sdshell返回為1時,表示當前用戶驗證失敗
- 15 # 當sdshell返回為2時,表示root登錄成功
- 16 if [[ $sdstats -eq 1 ]];then
- 17 exit 1
- 18 elif [[ $sdstats -eq 2 ]];then
- 19 /bin/bash
- 20 exit 0
- 21 fi
- 22
- 23 #2, gen_log_dir
- 24 user=`/usr/bin/whoami`
- 25 if [ ! -d $LOGDIR/$user ];then
- 26 mkdir -p $LOGDIR/$user
- 27 chown -R $user $LOGDIR/$user
- 28 chmod a+w $LOGDIR/$user
- 29 fi
- 30
- 31 #3, param
- 32 lip=`/usr/bin/env|grep SSH_CONNECTION|cut -f 2 -d =|cut -f 1 -d ' '`
- 33 now=`date '+%F-%k:%M:%S'|tr -d " "`
- 34 user=`/usr/bin/whoami`
- 35 sip=`/usr/bin/env|grep SSH_CONNECTION|cut -f 2 -d =|cut -f 3 -d ' '`
- 36
- 37 #4, begin script
- 38 export TMOUT=10
- 39 export SHELL=$BINDIR/mshell
- 40 /usr/bin/script -q -t 2>$LOGDIR/$user/$user-$lip-$sip-$now.time -f $LOGDIR/$user/$user-$lip-$sip-$now.txt
其中,3-5行做基本的環境變量初始化,第9行調用 sdshell 驗證用戶輸入的動態碼的驗證結果,即,用戶在這時進入到 sdshell 做驗證,此時,就可以隨意對 sdshell 做功能定制了,我們實現的功能定制界面類如:
是不是比較酷炫~~
當一切都驗證完畢,我們進入到login_shell第24-35行,初始化用戶行為日志記錄的初始化。
我們這里使用linux自帶的script命令做用戶的記錄操作,簡單有效到沒有朋友啊有木有!!
***39-40兩行,將用戶的shell指向 mshell ,同時,開始記錄用戶行為。
mshell
現在用戶的行為已經進入到這里,說明這個用戶通過了***層校驗,現在需要他進行相關action操作。
- 1 #!/bin/bash
- 2
- 3 WORKDIR=`dirname $0`
- 4 source $WORKDIR/sentry.env
- 5 user=`/usr/bin/whoami`
- 6 GWDIR=$WORKDIR
- 7 privatekey=$RSAFILE
- 8 mkdir /home/$user/.ssh 2>/dev/null
- 9 cat $privatekey > /home/$user/.ssh/id_rsa
- 10 chmod 600 /home/$user/.ssh/id_rsa
- 11
- 12 while true
- 13 do
- 14 clear
- 15 $GWDIR/gateway-shell
- 16 gsstats=$?
- 17
- 18 # 返回值說明:
- 19 # 0 表示正常,要登錄了;
- 20 # 1 表示立即quit
- 21 # 2 表示因為超時而quit
- 22 # 3 表示登錄本地服務器
- 23 # 其他未異常
- 24 case $gsstats in
- 25 0)
- 26 ssh -2 -i ~/.ssh/id_rsa -l $user `cat $ACTIONDIR/$user.action`
- 27 ;;
- 28 1)
- 29 exit
- 30 ;;
- 31 2)
- 32 echo "action timeout"
- 33 exit
- 34 ;;
- 35 3)
- 36 /bin/bash
- 37 ;;
- 38 *)
- 39 echo "error"
- 40 sleep 2
- 41 ;;
- 42 esac
- 43 done
3-10行環境初始化,然后開啟循環監聽用戶輸入的action,為用戶展示可使用的action list以及判斷用戶是否有對應操作的權限則都是第15行的 mshell 來進行操作,之后同樣根據exit code來執行相關的動作。
對于 mshell ,同樣可以自行設計相關的功能,我們設計的shell操作界面如下:
同樣酷炫有木有!
一層架構總結
此時,一層架構中的流程實現基本完成,剩下的就是具體控制登錄服務器的技術管理工作,即第三層的工作了,稍后為大家分享。
再次總結下一層架構中的調用關系:
- login_shell(入口) -> sdshell(判斷PIN+TOKEN) -> mshell -> gateway-shell(判斷/獲取 用戶選項)
咦,好像缺少第二層的說明?
其實第二層主要做action行為授權&校驗工作,還記得 gateway-shell 的作用嗎:
“為用戶展示可使用的action list以及判斷用戶是否有對應操作的權限 ”
在這里 gateway-shell 通過api與第二層做交互,對用戶的行為及授權范圍作了操作,具體交互的功能包括:
- 搜索服務器信息;
- 獲取用戶有權限的服務器list;
- 獲取用戶有權限的服務器Group list;
- 獲取服務器Group下的server list;
- 獲取當前所有有權限的user list
具體怎么實現? 相信1000個工程師有1001中不同的做法,這里就不在闡述~
如何統一管理服務器的登錄賬號
接入我們有1000臺服務器,有100個人,需要統一管理 哪些賬號 可以登錄 哪些服務器,哪些賬號 在 哪些服務器 上可以 sudo哪些命令 。
問題拋出來了,下面就是如何解決它。
單刀直入的方案,我為每個服務器根據需要創建對應的人的賬號,在分別配置sudoer的權限。缺點就不說了,改個配置能累死人。。。。。
所以,我們***能有個地方能夠統一來配置:
- 創建一個人的賬號后,那么這個人就能登錄所有的服務器;
- 管理這個人的登錄服務器的范圍;
- 為這個人分配統一的sudo權限,及在哪些目標服務器上有執行哪些命令的sudo權限;
上述的需求可以通過服務器接入ldap,通過ldap管理在實現上述的需求。
具體的過程這里不再累贅,可以參見網上的博文,還是挺多的,比如:
這里只貼上完成后類似的ldap結構:
- [root@testldap openldap]# ldapsearch -x -LLL -D "cn=admin,dc=lianjia,dc=com" uid=test -W
- Enter LDAP Password:
- dn: uid=test,ou=people,dc=lianjia,dc=com
- objectClass: posixAccount
- objectClass: shadowAccount
- objectClass: person
- objectClass: inetOrgPerson
- objectClass: ldapPublicKey
- cn: System
- sn: li
- givenName: test
- displayName: test
- uid: test
- userPassword:: testpasswd
- uidNumber: 1001
- gidNumber: 1001
- gecos: System Manager
- homeDirectory: /home/test
- shadowLastChange: 16020
- shadowMin: 0
- shadowMax: 999999
- shadowWarning: 7
- shadowExpire: -1
- employeeNumber: 20248353
- mobile: 18519199234
- mail: 654306390@qq.com
- postalAddress: beijing
- initials: test
- loginShell: /bin/bash
- sshPublicKey: ssh-rsa Axxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx32x9jF5cFDqktiTQIdD9
- PMq8b86v2p8Es4us7OTzo7XomcjEPpfP/Realy9BOuteohA4JzezrAyFQhJui6BdovkzhnVRyFERJ
- uTA/19biQkCZB91XrWxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxJXH+f0
- VOvx5FiF0bV3IfJt32cdmI8O7hNI+ttPCQ4V1R8vr0wIhCmUcKzD5vOx+0H9B1EY4d/imSxFHIebe
- 4l//rthyAr3x0XmNvuFD9khqfDK7bmXnHu26s++O8A1SDJ5beuu4xXl/mN8mc5WPmoQQSjIzruWPa
- jLx8m6HF root@channel.lianjia.com
這樣,對于此用戶,就可以通過password 或者 publickey的方式,登錄服務器,同理,也可以對sudo進行類似的管理,還是真心強大的。