首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

MySQL Audit 審計插件開發全流程:從事件結構到生產級部署的完整指南

     編輯:MySQL 綜合教學

MySQL 5.5+ 引入 Audit Plugin API,允許開發者攔截連線建立/斷開、查詢執行、結果返回、錯誤發生等核心事件,實現合規審計、安全防護、行為分析。本文從原始碼結構、事件結構體、介面函式、編譯部署、生產級最佳實踐五個維度,完整呈現 Audit 插件開發全貌。

一、核心原始碼結構

檔案用途
sql/sql_audit.h公共 API 定義:事件類別、事件結構體、介面版本、描述符結構
sql/sql_audit.cc核心調度邏輯:mysql_audit_general_log()、mysql_audit_connection_notify()
plugin/audit_null/audit_null.c官方範例插件:最小化實現,適合作為開發骨架

二、事件分類與結構體詳細定義

1. 連線類事件

類別常數:MYSQL_AUDIT_CONNECTION_CLASS

子類常數值觸發時機結構體
MYSQL_AUDIT_CONNECTION_CONNECT0認證完成、連線建立mysql_event_connection
MYSQL_AUDIT_CONNECTION_DISCONNECT1連線斷開 (正常/異常)
MYSQL_AUDIT_CONNECTION_CHANGE_USER2COM_CHANGE_USER 執行後

struct mysql_event_connection {
  unsigned int event_subclass;      // 子類別 (上表)
  int status;                       // 0=成功, 非0=錯誤碼
  unsigned long thread_id;          // 線程 ID
  const char *user;                 // 客戶端用戶名
  unsigned int user_length;
  const char *priv_user;            // 認證後實際用戶
  unsigned int priv_user_length;
  const char *external_user;        // 外部認證用戶 (PAM/LDAP)
  unsigned int external_user_length;
  const char *proxy_user;           // 代理用戶
  unsigned int proxy_user_length;
  const char *host;                 // 客戶端主機名
  unsigned int host_length;
  const char *ip;                   // 客戶端 IP
  unsigned int ip_length;
  const char *database;             // 預設資料庫
  unsigned int database_length;
};

2. 一般操作類事件

類別常數:MYSQL_AUDIT_GENERAL_CLASS

子類常數值觸發時機結構體
MYSQL_AUDIT_GENERAL_LOG0寫入 General Log 前mysql_event_general
MYSQL_AUDIT_GENERAL_ERROR1發送錯誤給客戶端前
MYSQL_AUDIT_GENERAL_RESULT2結果集發送給客戶端後
MYSQL_AUDIT_GENERAL_STATUS3發送結果集或錯誤後 (統一收尾)

struct mysql_event_general {
  unsigned int event_subclass;
  int general_error_code;           // 錯誤碼 (ERROR 子類有效)
  unsigned long general_thread_id;
  const char *general_user;         // 執行用戶
  unsigned int general_user_length;
  const char *general_command;      // 命令類型 (Query, Execute, Prepare...)
  unsigned int general_command_length;
  const char *general_query;        // SQL 文本 (可能為 NULL)
  unsigned int general_query_length;
  struct charset_info_st *general_charset;
  unsigned long long general_time;  // 執行耗時 (微秒)
  unsigned long long general_rows;  // 影響/返回行數
};

三、插件介面函式完整實現


#include 
#include 
#include 

static FILE *audit_log_fp = NULL;
static unsigned long long call_count = 0;

/* 1. 插件初始化 */
static int audit_demo_init(void *arg) {
  audit_log_fp = fopen("/var/log/mysql/audit.log", "a");
  if (!audit_log_fp) return 1;
  return 0;
}

/* 2. 事件通知核心函式 */
static void audit_demo_notify(MYSQL_THD thd, unsigned int event_class, const void *event) {
  if (!audit_log_fp) return;
  call_count++;

  const struct mysql_event_general *gen = (const struct mysql_event_general *) event;

  if (event_class == MYSQL_AUDIT_GENERAL_CLASS) {
    // 僅記錄成功執行的非空查詢 (RESULT 子類)
    if (gen->event_subclass == MYSQL_AUDIT_GENERAL_RESULT &&
        gen->general_query && *(gen->general_query)) {
      time_t now = time(NULL);
      fprintf(audit_log_fp, "%s user=%s query=%s rows=%llu time_us=%llu\n",
              ctime(&now), gen->general_user, gen->general_query,
              gen->general_rows, gen->general_time);
      fflush(audit_log_fp);
    }
  }
  else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS) {
    const struct mysql_event_connection *conn = (const struct mysql_event_connection *) event;
    if (conn->event_subclass == MYSQL_AUDIT_CONNECTION_CONNECT) {
      time_t now = time(NULL);
      fprintf(audit_log_fp, "%s CONNECT user=%s host=%s ip=%s db=%s\n",
              ctime(&now), conn->user, conn->host, conn->ip, conn->database);
      fflush(audit_log_fp);
    }
  }
}

/* 3. 釋放 THD 相關資源 (可選) */
static void audit_demo_release_thd(MYSQL_THD thd) {
  // 若 notify 中分配了記憶體,此處釋放
}

/* 4. 插件卸載清理 */
static int audit_demo_deinit(void *arg) {
  if (audit_log_fp) {
    fclose(audit_log_fp);
    audit_log_fp = NULL;
  }
  return 0;
}

/* 5. 狀態變數 (SHOW STATUS) */
static struct st_mysql_show_var audit_demo_status[] = {
  {"audit_demo_calls", (char *)&call_count, SHOW_ULONGLONG, SHOW_SCOPE_GLOBAL},
  {0,0,0,0}
};

/* 6. 插件描述符 */
struct st_mysql_audit audit_demo_descriptor = {
  MYSQL_AUDIT_INTERFACE_VERSION,  // 介面版本
  audit_demo_release_thd,         // release_thd
  audit_demo_notify,              // event_notify
  { (unsigned long) MYSQL_AUDIT_GENERAL_CLASS | (unsigned long) MYSQL_AUDIT_CONNECTION_CLASS } // class_mask
};

/* 7. 插件庫描述符 */
mysql_declare_plugin(audit_demo)
{
  MYSQL_AUDIT_PLUGIN,
  &audit_demo_descriptor,
  "AUDIT_DEMO",           // 安裝名稱: INSTALL PLUGIN AUDIT_DEMO SONAME 'audit_demo.so'
  "Your Name",
  "Demo Audit Plugin",
  PLUGIN_LICENSE_GPL,
  audit_demo_init,
  audit_demo_deinit,
  0x0100,                 // 版本 1.0
  audit_demo_status,      // 狀態變數
  NULL, NULL, 0
}
mysql_declare_plugin_end;

四、編譯與部署步驟

  1. 取得 MySQL 原始碼:apt source mysql-server 或官網下載對應版本
  2. 編譯插件:
    
    cd mysql-source/plugin/audit_null
    # 修改 audit_null.c → audit_demo.c
    gcc -shared -fPIC -I../../include -I../../sql -o audit_demo.so audit_demo.c
        
  3. 安裝至插件目錄:
    
    cp audit_demo.so /usr/local/mysql/lib/plugin/
    chown mysql:mysql /usr/local/mysql/lib/plugin/audit_demo.so
        
  4. MySQL 內安裝:
    
    INSTALL PLUGIN AUDIT_DEMO SONAME 'audit_demo.so';
    SHOW PLUGINS;  -- 確認 ACTIVE
        
  5. 配置自動載入 (my.cnf):
    
    [mysqld]
    plugin-load-add=AUDIT_DEMO=audit_demo.so
    audit_demo=FORCE_PLUS_PERMANENT  -- 防止誤卸載
        

五、生產級最佳實踐

維度建議
效能僅記錄必要事件 (如 DDL、DML、失敗登入);避免在 notify 內做複雜解析、網路 I/O、鎖操作
非同步寫入使用 libuv 或自建無鎖佇列 + 專用寫入線程,避免阻塞 SQL 執行緒
日誌輪轉配合 logrotate,按日/大小切割,保留 90 天
結構化輸出輸出 JSON Lines 格式,直接接入 ELK/Loki/ClickHouse 分析
權限最小化插件以 mysql 系統用戶運行,日誌目錄 chown mysql:mysql、chmod 750
審計完整性關鍵操作 (GRANT、DROP、TRUNCATE) 強制記錄;配合 class_mask 精準過濾
相容性測試每次 MySQL 小版本升級後,必須在測試環境驗證插件載入、事件觸發、日誌輸出正常

六、現有成熟方案對比

方案優點缺點適用場景
官方 audit_log (Enterprise)原生、高效、JSON/舊版 XML 格式、過濾規則豐富需商業授權企業合規、審計強制要求
MariaDB Audit Plugin開源、支援 MariaDB/MySQL、配置靈活、Syslog/檔案雙輸出需替換 Server 或動態載入相容版本開源環境、合規預算有限
McAfee MySQL Audit / Percona Audit社區維護、功能完整更新頻率不一特定合規需求
自研 (本文範例)完全可控、業務深度整合、零授權成本開發維護成本高、需自行保證相容性特殊業務邏輯審計、學習研究

Audit 插件是 MySQL 可擴展性的典範:核心內核保持精簡,業務邏輯下沉插件層。掌握事件結構、介面契約、部署流程,您即可根據合規需求、安全策略、分析目標,打造專屬的資料庫審計體系。下一篇我們將探討 Amoeba MySQL Proxy 配置文件深度解析與分片規則實戰,敬請期待。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved