一、原理:基于环境变量的条件日志
Apache 本身没有区分国密流量的内置功能,但可以通过 `SetEnvIf` 或 `SetEnvIfExpr` 指令,根据 SSL/TLS 连接的特征设置自定义环境变量,再由 `CustomLog` 配合 `env=` 参数将流量写入不同日志文件。
鉴别的关键在于识别国密流量。Apache 支持国密通常有两种场景:
- 使用国密专用模块(如 Tongsuo、GMSSL 等):握手日志中会包含特定关键词(如 `GMSSLv1.2`)
- 使用通用国密加密套件:流量使用的密码套件名称包含国密算法标识(如 `SM2-SM3-SM4`、`ECC-SM4-SM3` 等)
二、在访问日志中识别并记录国密流量
方案 1:在通用访问日志中增加国密标识字段
修改 Apache 配置(如 `/etc/httpd/conf.d/ssl.conf`),创建或修改日志格式,在原有日志行末尾增加国密协议和密码套件信息:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %{SSL_PROTOCOL}x %{SSL_CIPHER}x" ssl_combined
CustomLog logs/ssl_access.log ssl_combined
效果示例:
192.168.1.100 - - [27/Apr/2026:21:30:15 +0000] "GET / HTTP/1.1" 200 1234 "-" "curl/8.10.1" TLSv1.3 TLS_AES_256_GCM_SHA384
如需在日志行中明确标记为国密请求,可以利用 `LogFormat` 中的条件判断,或配合后续方案进行文件分离。
方案 2:将国密流量单独记录到独立文件(推荐)
核心思路:先用 `SetEnvIf` 设置标记变量,再用 `CustomLog` 按标记分流。
步骤 1:定义日志格式
apache
# 定义国密专用日志格式(可包含额外字段)
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %{SSL_PROTOCOL}x %{SSL_CIPHER}x" gm_log_format
步骤 2:设置国密流量标记变量**
根据国密密码套件特征设置标记。此处以套件名称包含 `SM` 为例,实际需根据所用的国密模块和套件名称进行调整:
apache
# 方法 A:使用 SetEnvIf 匹配套件名称中的国密标识
SetEnvIf SSL_CIPHER "SM[2-4]" is_gm_traffic
SetEnvIf SSL_CIPHER "SM2-SM3-SM4" is_gm_traffic
# 方法 B:若国密协议握手日志包含 GMSSL 关键词,也可匹配(需确认实际关键词)
SetEnvIf SSL_PROTOCOL "GMSSL" is_gm_traffic
# 方法 C:利用 HTTPS 环境变量区分 SSL/非 SSL(如果需要将国密与非 SSL 分离)
# 此方案仅用于分离 HTTPS 和 HTTP 流量,不区分国密与普通 HTTPS
SetEnv HTTPS
步骤 3:配置分流日志
apache
# 国密流量写入专用日志
CustomLog logs/gm_access.log gm_log_format env=is_gm_traffic
# 非国密 SSL 流量写入普通 SSL 日志(或通配日志)
CustomLog logs/ssl_access.log gm_log_format env=!is_gm_traffic
其中 `env=is_gm_traffic` 表示仅记录标记为 `is_gm_traffic` 的请求,`env=!is_gm_traffic` 表示仅记录未标记的请求。
可选:使用 `SetEnvIfExpr` 实现更灵活的匹配
若 `SetEnvIf` 无法识别指定变量,可以改用 Apache 2.4 引入的表达式引擎:
apache
# 匹配国密套件名称(正则表达式需根据实际套件名称调整)
SetEnvIfExpr "%{SSL_CIPHER} =~ /SM\d+/" is_gm_traffic
# 匹配 GMSSL 协议标识
SetEnvIfExpr "%{SSL_PROTOCOL} =~ /^GMSSL/" is_gm_traffic
这种写法在某些场景下兼容性更好,但仍需确保相关 SSL 变量已被正确导出。
三、将国密握手及错误日志分离
如果需要将国密相关的握手信息和错误日志单独存储,可以调整 `LogLevel` 级别:
apache
# 将 SSL 模块的日志级别提升为 debug,用于国密握手调试
LogLevel ssl:debug
# 将 SSL 调试日志输出到单独文件
SSLLogFile /var/log/httpd/gm_ssl_engine.log
注意:`SSLLogFile` 指令在某些编译版本的 Apache 中可能不可用。如果不支持,可以使用 `ErrorLog` 配合 `LogLevel` 将 SSL 相关信息输出到指定错误日志文件:
apache
ErrorLog /var/log/httpd/gm_error.log
LogLevel ssl:debug
需要特别注意的是,将 `LogLevel` 调至 `debug` 会产生大量日志,仅建议在调试国密连接问题或短期监控期间开启,不应在生产环境长期使用。调试完成后,将 `LogLevel` 恢复为 `info` 或 `warn` 级别,以免影响磁盘空间和系统性能。
四、完整配置示例
以下是一个完整的配置片段,可直接放入 Apache 配置目录(如 `/etc/httpd/conf.d/gm-ssl.conf`):
apache
# 加载必要模块(根据实际国密环境调整)
LoadModule ssl_module modules/mod_ssl.so
LoadModule setenvif_module modules/mod_setenvif.so
# SSL 全局配置
Listen 443
<VirtualHost *:443>
SSLEngine on
# 国密证书配置
SSLCertificateFile /path/to/gm_server.crt
SSLCertificateKeyFile /path/to/gm_server.key
# 定义国密专用日志格式
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %{SSL_PROTOCOL}x %{SSL_CIPHER}x" gm_log_format
# 设置国密流量标记(请根据实际套件名称调整正则规则)
SetEnvIf SSL_CIPHER "SM[2-4]" is_gm_traffic
# 分流:国密流量与普通 SSL 流量分别记录
CustomLog logs/gm_access.log gm_log_format env=is_gm_traffic
CustomLog logs/ssl_access.log gm_log_format env=!is_gm_traffic
# 可选:SSL 引擎日志,用于国密握手调试(注意:生产环境请谨慎开启 debug 级别)
# LogLevel ssl:debug
# SSLLogFile /var/log/httpd/gm_ssl_engine.log
ServerName gm.example.com
DocumentRoot /var/www/html
</VirtualHost>
五、注意事项
1. 国密套件名称取决于具体的国密实现(如 Tongsuo、GMSSL 等),请先通过访问日志确认实际输出的 `SSL_CIPHER` 字段内容,再调整正则表达式。
2. `SetEnvIf` 默认只能读取 HTTP 请求头字段,如需直接读取 `SSL_PROTOCOL` 和 `SSL_CIPHER` 变量,可以尝试使用 `SetEnvIfExpr`。
3. 若 SSL 相关变量未正确传递,需要在虚拟主机配置中显式启用 `SSLOptions +StdEnvVars` 指令,确保所有 SSL 环境变量可用于日志记录。
4. 建议同时配置日志轮转(如 `logrotate`),避免国密访问日志占用过多磁盘空间。
六、快速验证步骤
1. 查看当前 SSL 日:先不设置分流,使用 `LogFormat "%{SSL_PROTOCOL}x %{SSL_CIPHER}x"` 记录几分钟,观察实际输出的国密套件名称和协议标识。
2. 根据输出调整正则:将实际观测到的国密特征字符串填入 `SetEnvIf` 的正则表达式中。
3. 重启 Apache 并观察:`systemctl restart httpd`,然后用国密浏览器访问站点,检查 `gm_access.log` 是否产生记录。