最近公司在做資訊稽核,其中有一塊牽涉到主機上的帳號管理。根據公司的資安規範,每個使用者帳號 (系統內建帳號除外) 必須符合以下限制:
- 密碼長度不得小於6個字元
- 密碼有效期為90天 (也就是每90天就要變更一次密碼的意思)
- 不得使用三代以內的密碼
- 密碼不得與帳號相同
除了第四點是系統內建,不需要調整設定以外,其餘幾項都需要調整系統設定 (Solaris 只要修改系統內建的設定檔即可;針對第1和第3項要求,Linux 則必須要使用
PAM 模組的設定來達成,下次再撰文說明),本文先針對第二點進行說明。
要設定「密碼有效期為90天」並不困難,不管在 Solaris / Linux 上都只要透過 passwd 指令或者
chage 指令即可完成,但由於我覺得 chage 指令很容易使用,查詢所得的結果也很容易閱讀,所以這次就以 chage 來實作。
有關 chage 指令的用法,除了參考
鳥哥的網站,也可以直接下 man chage 指令來查詢內建的 man page,我用的指令如下:
chage -I 1 M 90 -W 30
[username] chage -d 2010-03-17
[username]
(第二個指令是為了避免設定好第一個指令以後,所有系統上 90 天內不曾修改密碼的使用者下次登入系統時都要重新設定密碼,因此把「最後一次修改密碼」的日期改為今天)
設定完畢之後,再下 chage –l
[username] 指令 (英文的 l) 即可查詢該使用者的帳號狀態。
OK, 既然 chage 指令這麼簡單,那本文有甚麼好講的呢?如果只需要在單一主機上對少數幾個帳號作如此的設定,當然是直接下幾個指令就搞定,問題是這次需要修改的主機超過 50 台,每台主機上面又有一大堆帳號,真要這樣下指令的話會瘋掉!(而且還有第1和第3項要求要作到,那又是另外兩行 Perl 程式) 所以我就把這些機械化的動作寫成一個 script 啦!
第一步:詢問使用者 chage -d 要使用的日期參數:
echo “set Last password change day (YYYY-MM-DD): “ read lDays
#把使用者輸入的參數存到 $lDays 變數中
接下來判斷使用者輸入的字串是否符合 YYYY-MM-DD 的格式:
syntaxOK=`echo $lDays | perl –ne ‘print “OK” if /
^\d{4}-\d{2}-\d{2}/’`
(`` 是 backtick,不是單引號哦~)
if [ "$syntaxOK" != "OK" ]; then echo "Wrong syntax! Usage: YYYY-MM-DD" exit fi
這裡用 perl 來寫 regular expression 會比用 shell script 簡單很多,shell script 要這樣寫 (因為沒有支援 \d 這樣的表示法,而且也不支援 {4} 的寫法):
syntaxOK=`echo $lDays | grep "
^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]
$"`
神奇的是用 perl 的話不用加上「
$」來限制結尾要是 2 個數字,就自然可以判斷出「1111-11-
111」是不符合 pattern 的,但是用 shell script 中的 grep 的話就要 (用 egrep 還是要加上「
$」)。
第二步:列出系統中所有的使用者,除了系統內建帳號以外:
我參考的是
Listing all users on the system 一文,但是在 awk 的主程式中加上 regular expression 的判斷:
for name in $(awk 'BEGIN{FS=":"} { if($3 ~
/^5[0-9][0-9]/) {print $1}}' < "$PASSWORD_FILE" )
利用這個 pattern 可以過濾出第三欄 ($3) 的值是「5xx」(xx 為數字) 的資料,且印出第一欄 ($1)。
第三步:過濾 uid > 500 的帳號中,屬於系統帳號 or 給程式使用的帳號:
一般來說 uid < 500 的是保留給系統使用的帳號,但 uid > 500 的帳號中仍有可能存在單純給程式使用的帳號 (e.g., 給 FTP /
scp 使用),因此需要進一步過濾。我的作法世新增一個 AccountExcluded.txt 檔案,以一行一個帳號的方式,將欲排除的帳號寫入這個檔案,接下來用一個迴圈就可以比對:
accountExcluded=`cat ./AccountExcluded.txt` excluded="0" for exclude in $accountExcluded do if [ $name = $exclude ]; then excluded="1" fi done
第四步:執行 chage 指令
到了這個步驟才是整個 script 的核心,對於每個不在 AccountExcluded.txt 檔案中的帳號,只要在以上的迴圈中執行以下兩行即可:
chage -M 90 -W 30 $name chage -d $lDays $name
整個 script 其實就只有這樣而已,為了避免更動到系統帳號才額外加了一堆有的沒的程式碼 (為了寫 log 又另外加了一堆),在這次練習的過程中,發現還是寫 RegExp 最好玩 (awk 也很有趣),其他的程式碼都挺無聊的。
這個 script 未來還有很多可以 enhance 的地方,依照重要性由高到低排列:
- 在本機上登入所有需要修改的主機,將本機的 shell script 檔&設定檔複製到所有的主機上執行 (不然一台一台的登入也是很累人的)。將這個架構建立起來以後,未來有遇到類似要再每台主機上調整設定的需求,就可按照同樣的模式寫成一個 script 來執行。
- 加強 YYYY-MM-DD 的判斷,找找看有沒有轉換成 Date 之類型態的方式可用,順便避免 2/29 的問題。
- 把讀取兩次 /etc/passwd 的寫法改成只讀取一次,第一次讀出資料以後存到一個 array 裡面就好。
- 加強過濾 /etc/passwd 檔案時使用的 regular expression,雖然目前實務上接觸到的主機還不至於帳號多到 uid > 600 或者更高,但可以當作一個練習 regular expression 的機會。
有興趣參考完整原始碼的朋友請到
這裡下載 (V1)。
參考:[2010-12-13]
[Shell Script] 批次修改使用者的密碼有效期限 - V2