運維防背鍋的辦法之一:做好審計

该写代码了發表於2024-07-21

需求

運維工程師在工作中經常會遇到這樣的問題:

  • 伺服器的檔案被刪除了,誰幹的?
  • 伺服器的整個服務被刪除了,誰幹的?
  • 伺服器上啟動了一個奇怪的程序佔據了大量的CPU,誰幹的?

這個時候,如果我們找不到始作俑者,那毫無疑問得運維自己背鍋。如果有證據證明是別人乾的,我們才能坦然地(甩鍋)找到對應的人問清楚原因。這個時候,我們就需要做好伺服器的命令審計。

下面,給大家介紹一種非常簡單實用的伺服器命令審計方式,設定一個命令審計日誌系統,非常好用!

配置

我們只需要把這一段程式碼執行,就完成了配置:

# 建立目錄和檔案
sudo mkdir -p /var/log/cmd_audit
sudo touch /var/log/cmd_audit/audit.log

# 設定目錄和檔案許可權
sudo chmod 755 /var/log/cmd_audit
sudo chmod 662 /var/log/cmd_audit/audit.log

# 設定檔案僅允許追加屬性
sudo chattr +a /var/log/cmd_audit/audit.log

# 寫入內容到 /etc/profile.d/cmd_audit.sh
sudo tee /etc/profile.d/cmd_audit.sh > /dev/null << 'EOF'
################## Audit ##################
export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL
HISTTIMEFORMAT="%Y/%m/%d %T"; export HISTTIMEFORMAT

# 定義審計命令
audit_command='{ thisHistID=`history 1 | awk "{print \\$1}"`; lastCommand=`history 1 | awk "{\\$1=\"\"; print}"`; user=`id -un`; pwd=`pwd`; whoStr=(`who -u am i`); realUser=${whoStr[0]}; logMonth=${whoStr[2]}; logDay=${whoStr[3]}; logTime=${whoStr[4]}; pid=${whoStr[6]}; ip=${whoStr[7]}; if [ ${thisHistID}x != ${lastHistID}x ]; then echo -E `date "+%Y/%m/%d %H:%M:%S"` $user\($realUser\)@$ip[IP:$pid][PWD:$pwd][LOGIN:$logMonth $logDay $logTime] --- $lastCommand; lastHistID=$thisHistID; fi; } >> $HISTORY_FILE'

# 檢查和設定 PROMPT_COMMAND
if [ -z "${PROMPT_COMMAND_READONLY}" ]; then
    if [ -z "${PROMPT_COMMAND}" ]; then
        export PROMPT_COMMAND="$audit_command"
    else
        export PROMPT_COMMAND="$PROMPT_COMMAND; $audit_command"
    fi
    export HISTORY_FILE=/var/log/cmd_audit/audit.log
    export PROMPT_COMMAND_READONLY=1
    readonly PROMPT_COMMAND_READONLY
fi
################## Audit_end ##############

EOF

# 使配置立即生效
source /etc/profile.d/cmd_audit.sh

你可以直接複製後貼上到伺服器上,也可以執行下面這條命令幫你配置完成:

curl -s http://8.138.10.37/tools/setup_cmd_audit.sh | tee /tmp/setup_cmd_audit.sh && read -p "可以執行此檔案嗎? (y/n): " user_input && [ "$user_input" = "y" ] && bash /tmp/setup_cmd_audit.sh && rm /tmp/setup_cmd_audit.sh

效果

執行後,登入到伺服器上操作的命令列都會被記錄到/var/log/audit/cmd_audit.log,我們看看效果:

[root@devops ~]# tail -f /var/log/cmd_audit/audit.log 
2024/07/21 19:11:32 root(root)@[IP:(172.30.0.254)][PWD:/root][LOGIN:2024-07-21 19:11 .] --- 2024/07/21 19:10:46 date
2024/07/21 19:11:38 root(root)@[IP:(172.30.0.254)][PWD:/root][LOGIN:2024-07-21 19:11 .] --- 2024/07/21 19:11:37 top
2024/07/21 19:11:40 root(root)@[IP:(172.30.0.254)][PWD:/root][LOGIN:2024-07-21 19:11 .] --- 2024/07/21 19:11:40 touch demo
2024/07/21 19:11:43 root(root)@[IP:(172.30.0.254)][PWD:/root][LOGIN:2024-07-21 19:11 .] --- 2024/07/21 19:11:43 rm -rf demo
# 這是root切換到jaywin使用者
2024/07/21 19:11:59 jaywin(root)@[IP:(172.30.0.254)][PWD:/home/jaywin][LOGIN:2024-07-21 19:11 .] --- 2024/07/21 19:08:17 touch
2024/07/21 19:12:05 jaywin(root)@[IP:(172.30.0.254)][PWD:/home/jaywin][LOGIN:2024-07-21 19:11 .] --- 2024/07/21 19:12:05 touch somefile

並且,這個審計檔案只能追加,任何使用者都無法修改,刪除!只有超級管理員才能檢視,其他普通使用者都無法檢視!

解釋

這個設定的原理是透過配置特殊變數PROMPT_COMMAND ,在每次顯示提示符之前執行指定的命令。簡而言之,它允許你在每次命令提示符出現之前執行一段程式碼。這在審計、日誌記錄和其他自動化任務中非常有用。

接下來說明下具體原理,這裡涉及到幾個知識點,我們根據指令碼的內容一一來講:

# 下面的配置保證了審計檔案只有管理員才有許可權操作!
# 設定目錄和檔案許可權
sudo chmod 755 /var/log/cmd_audit    # 755表示目錄許可權是rwxr-xr-x,允許所有使用者執行,以便在這個目錄下的檔案,能寫入使用者的命令
sudo chmod 662 /var/log/cmd_audit/audit.log # 622表示檔案許可權是-rw-rw--w-,不允許其他使用者檢視檔案內容

# 設定檔案僅允許追加屬性
sudo chattr +a /var/log/cmd_audit/audit.log # chattr+a表示此檔案僅允許追加操作,任何使用者都無法修改檔案內容或刪除檔案!

以下是命令審計日誌系統的重點解釋:

export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL
  • export 命令將指定的環境變數匯出到當前環境,使它們在當前會話及其子程序中都可用。

  • PATH:指定可執行檔案的搜尋路徑。

  • USER:當前使用者的使用者名稱。

  • LOGNAME:當前使用者的登入名。

  • MAIL:當前使用者的郵件目錄。

  • HOSTNAME:當前主機的名稱。

  • HISTSIZE:命令歷史記錄的最大條數。

  • HISTCONTROL:控制歷史記錄的行為。

HISTTIMEFORMAT="%Y/%m/%d %T"; export HISTTIMEFORMAT
  • HISTTIMEFORMAT:設定歷史記錄中的時間格式。在這種情況下,時間格式為 YYYY/MM/DD HH:MM:SS

  • export HISTTIMEFORMAT:將 HISTTIMEFORMAT 變數匯出到當前環境,使其在所有子程序中也可用。

audit_command='{ thisHistID=`history 1 | awk "{print \\$1}"`; lastCommand=`history 1 | awk "{\\$1=\"\"; print}"`; user=`id -un`; pwd=`pwd`; whoStr=(`who -u am i`); realUser=${whoStr[0]}; logMonth=${whoStr[2]}; logDay=${whoStr[3]}; logTime=${whoStr[4]}; pid=${whoStr[6]}; ip=${whoStr[7]}; if [ ${thisHistID}x != ${lastHistID}x ]; then echo -E `date "+%Y/%m/%d %H:%M:%S"` $user\($realUser\)@$ip[IP:$pid][PWD:$pwd][LOGIN:$logMonth $logDay $logTime] --- $lastCommand; lastHistID=$thisHistID; fi; } >> $HISTORY_FILE'

thisHistID=`history 1 | awk "{print \\$1}"` # 獲取當前歷史ID
lastCommand=`history 1 | awk "{\\$1=\"\"; print}"` # 獲取最後一條命令
user=`id -un`;pwd=`pwd` # 獲取當前使用者和當前路徑
# 獲取使用者登入資訊
whoStr=(`who -u am i`) # 使用者登入資訊
realUser=${whoStr[0]} #  使用者名稱
logMonth=${whoStr[2]} # 登入時間
logDay=${whoStr[3]}
logTime=${whoStr[4]}
pid=${whoStr[6]} # 登入會話ID
ip=${whoStr[7]}   # 登入的客戶端IP


if [ ${thisHistID}x != ${lastHistID}x ]; then 
    echo -E `date "+%Y/%m/%d %H:%M:%S"` $user\($realUser\)@$ip[IP:$pid][PWD:$pwd][LOGIN:$logMonth $logDay $logTime] --- $lastCommand
    lastHistID=$thisHistID
fi

  • 檢查歷史記錄 ID 是否變化並記錄審計資訊
if [ -z "${PROMPT_COMMAND_READONLY}" ]; then
    if [ -z "${PROMPT_COMMAND}" ]; then
        export PROMPT_COMMAND="$audit_command"
    else
        export PROMPT_COMMAND="$PROMPT_COMMAND; $audit_command"
    fi
    export HISTORY_FILE=/var/log/cmd_audit/audit.log
    export PROMPT_COMMAND_READONLY=1
    readonly PROMPT_COMMAND_READONLY
fi
  • 配置PROMPT_COMMAND環境變數,並設定為只讀,放置其他使用者私自修改導致命令審計失敗

注意

  1. 你最好在伺服器初始化時做好此項配置;
  2. 你最好結合使用者許可權管理系統來使用,才能真正發揮這個配置的功能;

下一期,我們會簡單談談如何實現一個使用者許可權管理系統,敬請期待~

相關文章