由於實驗室產品的監控模塊的需求,需要繞過zabbix的驗證模塊,實現從二級平台到zabbix的無縫接入。
測試發現,zabbix的身份驗證並不是想象的那么簡單,為了實現功能,遂進行源碼分析。
zabbix常規登陸驗證流程:
分析./include/classes/user/CWebUser.php中的login和logout可以了解到zabbix的常規驗證流程。
主要邏輯如下:
login
- 查詢出對應用戶名密碼的user對象
- 檢查該用戶登陸嘗試次數是否超出限制
- 根據查詢的userid驗證用戶權限,將頁面是否允許訪問存入guiAccess
- 登陸方式選擇(ldap、數據庫)
- 開始新的session: 生成sessionId,將(sessionId、userid、當前時間、激活狀態)插入數據庫
- 根據第3步的guiAccess判斷是否結束並拋出異常
- 成功登陸后,清除訪客的session
- 將新的sessionId存入cookie
- 對該次用戶登錄做審計
logout
- 從cookie里得到sessionId
- 從數據庫里刪除該登出用戶對應的所有狀態為PASSIVE的session記錄
- 將cookie中得到的這個sessionId對應的session記錄的狀態修改為PASSIVE
- 從cookie里刪除該sessionId
然而,從一個靜態URL想要繞過登陸直接跳轉到zabbix的某個監控頁面,肯定是不能走這個常規流程,所以,我們以zabbix的監控圖表界面對應的charts.php代碼來理清zabbix對於URL跳轉這類請求的驗證流程。
URL跳轉驗證流程
包括chart.php的幾乎每一個zabbix的展示頁面的php代碼都會在開頭包含一個config.inc.php,且並無其他驗證相關代碼存在,config.inc.php中只有一行主要代碼Z::getInstance()->run(),追蹤到這個run函數里,就能找到疑似的驗證流程入口$this->authenticateUser()。
該函數位於 ./include/classes/core/ZBase.php 中。
checkAuthentication
ZBase.php中的函數,是對./include/classes/user/CWebUser.php中同名函數的封裝,CWebUser.php中的checkAuthentication又是對zabbix API即./include/classes/api/services/CUser.php中的同名函數的封裝。
具體流程如下:
- 從cookie中取出
zbx_sessionid的值存入sessionId - 如果該sessionId不為空,根據這個sessionId查詢出對應的用戶userid、autologout字段和處於ACTIVE狀態session的lastaccess字段,如果sessionId為空,以訪客模式調用login函數並得到新的sessionId,該sessionId對應的GUEST特殊用戶將不能通過后面的權限認證
- 如果步驟2沒有查出記錄,那么會報錯
Session terminated, re-login, please - 調用
check_perm2system檢查該用戶的權限,若有問題,報錯No permissions for system access - 如果查出的記錄的
autologout> 0, 刪除數據庫中對應用戶的過期的session - 更新數據庫中當前sessionId和userId對應的session記錄的最近訪問是時間(lassaccess)字段
- 檢查該用戶的頁面訪問權限(檢查gui_access,與login流程的驗證guiAccess一樣)
- 將userid,sessionid,gui_access存入data變量中
- 如果gui_access為GROUP_GUI_ACCESS_DISABLED,即禁用,拋異常,退出。
- 將sessionId保存到cookie中
- 用sessionId為API設置驗證令牌
分析得出結論,如果要實現在任何環境下的一次URL跳轉都能成功以管理員的權限進入zabbix,關鍵在於以上流程的第2步,不能以訪客模式進入zabbix,即需要讓sessionId不為空且可用,自然得出的辦法就是,提前保存好一個可用的sessionId到cookie中。
多番測試后,證實了以上方案是可行的,於是,實施方案如下:
手動在zabbix增加一個session記錄,每次直接請求URL的時候,先調用一個自定義php,存入這個session的id,再繼續訪問,bingo!
后續發現問題,會回來更新。
