收集 Atlassian Confluence 日志
本文档介绍了如何将 Atlassian Confluence 日志注入到 Google Security Operations。解析器首先尝试使用专为 Atlassian Confluence 日志设计的正则表达式(Grok 模式)从原始日志消息中提取字段。如果 grok 解析失败或日志采用 JSON 格式,则代码会尝试将消息解析为 JSON。最后,提取的字段会映射到 Google SecOps UDM 架构,并添加更多上下文信息。
准备工作
请确保满足以下前提条件:
- Google SecOps 实例
- 具有审核日志访问权限的 Atlassian Confluence Cloud 账号,或者具有管理员权限的 Confluence Data Center/Server 账号
- 对于基于 AWS 的方法:对 AWS(S3、IAM、Lambda、EventBridge)的特权访问权限
- 对于 Bindplane 方法:Windows 2016 或更高版本,或者具有
systemd的 Linux 主机
集成选项概览
本指南提供了两种集成途径:
- 方法 1:通过 BindPlane + Syslog 监控 Confluence Data Center/Server
- 选项 2:通过 AWS Lambda + S3 获取 Confluence Cloud 审核日志(JSON 格式)
请选择最适合您的 Confluence 部署类型和基础架构的选项。
选项 1:通过 Bindplane + Syslog 监控 Confluence Data Center/Server
此选项用于将 Confluence Data Center 或 Server 配置为通过 syslog 将日志发送到 Bindplane 代理,然后由该代理将日志转发到 Google SecOps。
获取 Google SecOps 注入身份验证文件
- 登录 Google SecOps 控制台。
- 依次前往 SIEM 设置 > 收集代理。
- 下载注入身份验证文件。将文件安全地保存在将要安装 Bindplane 的系统上。
获取 Google SecOps 客户 ID
- 登录 Google SecOps 控制台。
- 依次前往 SIEM 设置 > 个人资料。
- 复制并保存组织详细信息部分中的客户 ID。
安装 Bindplane 代理
按照以下说明在 Windows 或 Linux 操作系统上安装 Bindplane 代理。
Windows 安装
- 以管理员身份打开命令提示符或 PowerShell。
运行以下命令:
msiexec /i "https://github.com/observIQ/bindplane-agent/releases/latest/download/observiq-otel-collector.msi" /quiet
Linux 安装
- 打开具有 root 或 sudo 权限的终端。
运行以下命令:
sudo sh -c "$(curl -fsSlL https://github.com/observiq/bindplane-agent/releases/latest/download/install_unix.sh)" install_unix.sh
其他安装资源
- 如需了解其他安装选项,请参阅此安装指南。
配置 Bindplane 代理以注入 Syslog 并将其发送到 Google SecOps
访问配置文件:
- 找到
config.yaml文件。通常,它位于 Linux 上的/etc/bindplane-agent/目录中或 Windows 上的安装目录中。 - 使用文本编辑器(例如
nano、vi或记事本)打开该文件。
- 找到
按如下方式修改
config.yaml文件:receivers: udplog: # Replace the port and IP address as required listen_address: "0.0.0.0:514" exporters: chronicle/chronicle_w_labels: compression: gzip # Adjust the path to the credentials file you downloaded creds_file_path: '/path/to/ingestion-authentication-file.json' # Replace with your actual customer ID customer_id: YOUR_CUSTOMER_ID endpoint: malachiteingestion-pa.googleapis.com log_type: 'ATLASSIAN_CONFLUENCE' raw_log_field: body ingestion_labels: service: pipelines: logs/confluence: receivers: - udplog exporters: - chronicle/chronicle_w_labels- 根据基础架构的需要替换端口和 IP 地址。
- 将
<YOUR_CUSTOMER_ID_HERE>替换为实际的客户 ID。 - 将
/path/to/ingestion-authentication-file.json更新为获取 Google SecOps 注入身份验证文件部分中保存身份验证文件的路径。
重启 Bindplane 代理以应用更改
如需在 Linux 中重启 Bindplane 代理,请运行以下命令:
sudo systemctl restart bindplane-agent如需在 Windows 中重启 Bindplane 代理,您可以使用服务控制台,也可以输入以下命令:
net stop BindPlaneAgent && net start BindPlaneAgent
在 Confluence Data Center/Server 上配置 Syslog 转发
选项 A:配置 Log4j SyslogAppender(推荐)
- 通过 SSH 或 RDP 登录您的 Confluence 服务器。
- 找到 Log4j 配置文件:
- 对于 Log4j2:
<confluence-install>/confluence/WEB-INF/classes/log4j2.xml
- 对于 Log4j2:
修改配置文件以添加 SyslogAppender:
<Configuration> <Appenders> <!-- Existing appenders --> <Syslog name="SyslogAppender" host="BINDPLANE_AGENT_IP" port="514" protocol="UDP" facility="LOCAL0" format="RFC5424"> <PatternLayout pattern="%d{ISO8601} %p [%t] [%c{1}] %m%n"/> </Syslog> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="SyslogAppender"/> <!-- Other appender refs --> </Root> <!-- Audit logger --> <Logger name="com.atlassian.confluence.event.events.security.AuditEvent" level="info" additivity="false"> <AppenderRef ref="SyslogAppender"/> </Logger> </Loggers> </Configuration>- 将
BINDPLANE_AGENT_IP替换为 BindPlane 代理的 IP 地址。
- 将
重启 Confluence 以应用更改:
sudo systemctl restart confluence
选项 B:将 rsyslog 配置为转发本地日志文件
- 将 Confluence 配置为将日志写入文件(默认行为)。
如果未安装 rsyslog,请进行安装:
sudo apt-get install rsyslog # Debian/Ubuntu sudo yum install rsyslog # RHEL/CentOS创建 rsyslog 配置文件
/etc/rsyslog.d/confluence.conf:# Forward Confluence logs to BindPlane $ModLoad imfile # Application logs $InputFileName /opt/atlassian/confluence/logs/atlassian-confluence.log $InputFileTag confluence-app: $InputFileStateFile stat-confluence-app $InputFileSeverity info $InputFileFacility local0 $InputRunFileMonitor # Audit logs (JSON format in DC/Server) $InputFileName /var/atlassian/application-data/confluence/log/audit/*.json $InputFileTag confluence-audit: $InputFileStateFile stat-confluence-audit $InputFileSeverity info $InputFileFacility local1 $InputRunFileMonitor # Forward to BindPlane agent *.* @@BINDPLANE_AGENT_IP:514- 将
BINDPLANE_AGENT_IP替换为 Bindplane 代理的 IP 地址。 - 根据您的 Confluence 安装情况调整日志文件路径:
- 应用日志通常:
<confluence-install>/logs/或<local-home>/logs/ - 审核日志:
<confluence-home>/log/audit/*.json
- 应用日志通常:
- 将
重启 rsyslog:
sudo systemctl restart rsyslog
选项 2:通过 AWS Lambda + S3 获取 Confluence Cloud 审核日志
收集 Confluence Cloud API 凭据
- 登录您的 Atlassian 账号。
- 前往 https://id.atlassian.com/manage-profile/security/api-tokens。
- 点击创建 API 令牌。
- 为令牌输入标签(例如,
Google Security Operations Integration)。 - 点击创建。
- 复制并妥善保存 API 令牌。
- 记下您的 Confluence Cloud 网站网址(例如
https://yoursite.atlassian.net)。 - 记下您的 Atlassian 账号电子邮件地址(用于身份验证)。
为 Google SecOps 配置 AWS S3 存储桶和 IAM
- 按照以下用户指南创建 Amazon S3 存储桶:创建存储桶
- 保存存储桶名称和区域以供日后参考(例如
confluence-audit-logs)。 - 按照以下用户指南创建用户:创建 IAM 用户。
- 选择创建的用户。
- 选择安全凭据标签页。
- 在访问密钥部分中,点击创建访问密钥。
- 选择第三方服务作为使用情形。
- 点击下一步。
- 可选:添加说明标记。
- 点击创建访问密钥。
- 点击下载 CSV 文件,保存访问密钥和不公开的访问密钥以供日后参考。
- 点击完成。
- 选择权限标签页。
- 在权限政策部分中,点击添加权限。
- 选择添加权限。
- 选择直接附加政策。
- 搜索 AmazonS3FullAccess 政策。
- 选择相应政策。
- 点击下一步。
- 点击添加权限。
为 S3 上传配置 IAM 政策和角色
- 在 AWS 控制台中,依次前往 IAM > 政策 > 创建政策 > JSON 标签页。
输入以下政策:
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutObjects", "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::confluence-audit-logs/*" }, { "Sid": "AllowGetStateObject", "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::confluence-audit-logs/confluence-audit/state.json" } ] }- 如果您输入了其他存储桶名称,请替换
confluence-audit-logs。
- 如果您输入了其他存储桶名称,请替换
依次点击下一步 > 创建政策。
将政策命名为
ConfluenceAuditToS3Policy。依次前往 IAM > 角色 > 创建角色 > AWS 服务 > Lambda。
附加新创建的政策
ConfluenceAuditToS3Policy。将角色命名为
ConfluenceAuditLambdaRole,然后点击创建角色。
创建 Lambda 函数
- 在 AWS 控制台中,依次前往 Lambda > 函数。
- 依次点击创建函数 > 从头开始创作。
提供以下配置详细信息:
设置 值 名称 ConfluenceAuditToS3运行时 Python 3.13 架构 x86_64 执行角色 ConfluenceAuditLambdaRole创建函数后,打开 Code 标签页,删除桩代码并输入以下代码:
import json import os import boto3 from datetime import datetime, timezone, timedelta from urllib import request, parse, error from base64 import b64encode # Environment variables S3_BUCKET = os.environ['S3_BUCKET'] S3_PREFIX = os.environ.get('S3_PREFIX', 'confluence-audit/') STATE_KEY = os.environ.get('STATE_KEY', 'confluence-audit/state.json') CONFLUENCE_URL = os.environ['CONFLUENCE_URL'] # e.g., https://yoursite.atlassian.net CONFLUENCE_EMAIL = os.environ['CONFLUENCE_EMAIL'] CONFLUENCE_API_TOKEN = os.environ['CONFLUENCE_API_TOKEN'] MAX_RECORDS = int(os.environ.get('MAX_RECORDS', '1000')) s3_client = boto3.client('s3') def lambda_handler(event, context): """Fetch Confluence Cloud audit logs and write to S3.""" # Read last execution state start_date = get_last_execution_time() end_date = datetime.now(timezone.utc) print(f"Fetching audit logs from {start_date} to {end_date}") # Fetch audit records records = fetch_audit_logs(start_date, end_date) if not records: print("No new audit records found.") save_state(end_date) return {'statusCode': 200, 'body': 'No new records'} # Write to S3 timestamp = end_date.strftime('%Y%m%d_%H%M%S') object_key = f"{S3_PREFIX}audit_{timestamp}.json" s3_client.put_object( Bucket=S3_BUCKET, Key=object_key, Body='\n'.join(json.dumps(record) for record in records), ContentType='application/json' ) print(f"Wrote {len(records)} records to s3://{S3_BUCKET}/{object_key}") # Update state save_state(end_date) return { 'statusCode': 200, 'body': f"Processed {len(records)} records" } def get_last_execution_time(): """Retrieve the last execution timestamp from S3 state file.""" try: response = s3_client.get_object(Bucket=S3_BUCKET, Key=STATE_KEY) state = json.loads(response['Body'].read()) return datetime.fromisoformat(state['last_execution']) except s3_client.exceptions.NoSuchKey: # First run: fetch logs from last 24 hours return datetime.now(timezone.utc) - timedelta(hours=24) except Exception as e: print(f"Error reading state: {e}") return datetime.now(timezone.utc) - timedelta(hours=24) def save_state(execution_time): """Save the execution timestamp to S3 state file.""" state = {'last_execution': execution_time.isoformat()} s3_client.put_object( Bucket=S3_BUCKET, Key=STATE_KEY, Body=json.dumps(state), ContentType='application/json' ) def fetch_audit_logs(start_date, end_date): """Fetch audit logs from Confluence Cloud REST API.""" records = [] start_param = int(start_date.timestamp() * 1000) # milliseconds end_param = int(end_date.timestamp() * 1000) # Build authentication header auth_string = f"{CONFLUENCE_EMAIL}:{CONFLUENCE_API_TOKEN}" auth_bytes = auth_string.encode('ascii') auth_b64 = b64encode(auth_bytes).decode('ascii') headers = { 'Authorization': f'Basic {auth_b64}', 'Accept': 'application/json' } # Confluence Cloud Audit API endpoint url = f"{CONFLUENCE_URL}/wiki/rest/api/audit?startDate={start_param}&endDate={end_param}&limit=1000" try: req = request.Request(url, headers=headers) with request.urlopen(req) as response: data = json.loads(response.read()) records = data.get('results', []) print(f"Retrieved {len(records)} audit records") except error.HTTPError as e: print(f"HTTP Error: {e.code} - {e.reason}") print(e.read().decode()) except Exception as e: print(f"Error fetching audit logs: {e}") return records[:MAX_RECORDS]依次前往配置 > 环境变量。
依次点击修改 > 添加新的环境变量。
输入以下环境变量,并将其替换为您的值。
键 示例值 S3_BUCKETconfluence-audit-logsS3_PREFIXconfluence-audit/STATE_KEYconfluence-audit/state.jsonCONFLUENCE_URLhttps://yoursite.atlassian.netCONFLUENCE_EMAILyour-email@example.comCONFLUENCE_API_TOKENyour-api-token-hereMAX_RECORDS1000选择配置标签页。
在常规配置面板中,点击修改。
将超时更改为 5 分钟(300 秒),然后点击保存。
创建 EventBridge 计划
- 依次前往 Amazon EventBridge > 调度器 > 创建调度。
- 提供以下配置详细信息:
- 周期性安排:费率 (
1 hour)。 - 目标:您的 Lambda 函数
ConfluenceAuditToS3。 - 名称:
ConfluenceAuditToS3-1h。
- 周期性安排:费率 (
- 点击创建时间表。
可选:为 Google SecOps 创建只读 IAM 用户和密钥
- 前往 AWS 控制台 > IAM > 用户。
- 点击 Add users(添加用户)。
- 提供以下配置详细信息:
- 用户:输入
secops-confluence-reader。 - 访问类型:选择访问密钥 - 以程序化方式访问。
- 用户:输入
- 点击下一步。
- 依次点击直接附加政策 > 创建政策。
在 JSON 编辑器中,输入以下政策:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": "arn:aws:s3:::confluence-audit-logs/*" }, { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::confluence-audit-logs" } ] }将名称设置为
secops-reader-policy。依次前往创建政策 > 搜索/选择 > 下一步 > 添加权限。
依次前往安全凭据 > 访问密钥 > 创建访问密钥。
下载 CSV(这些值会输入到 Feed 中)。
在 Google SecOps 中配置 Feed 以注入 Confluence 日志
- 依次前往 SIEM 设置 > Feed。
- 点击添加新 Feed。
- 在Feed 名称字段中,输入 Feed 的名称(例如
Confluence Cloud Audit Logs)。 - 选择 Amazon S3 V2 作为来源类型。
- 选择 Atlassian Confluence 作为日志类型。
- 点击下一步。
- 为以下输入参数指定值:
- S3 URI:
s3://confluence-audit-logs/confluence-audit/ - 源删除选项:根据您的偏好选择删除选项。
- 文件存在时间上限:包含在过去指定天数内修改的文件。默认值为 180 天。
- 访问密钥 ID:有权访问 S3 存储桶的用户访问密钥。
- 私有访问密钥:有权访问 S3 存储桶的用户私有密钥。
- 资产命名空间:资产命名空间。
- 注入标签:应用于此 Feed 中事件的标签。
- S3 URI:
- 点击下一步。
- 在最终确定界面中查看新的 Feed 配置,然后点击提交。
UDM 映射表
| 日志字段 | UDM 映射 | 逻辑 |
|---|---|---|
| 代理 | read_only_udm.network.http.user_agent | 从“agent”字段中获取的值。 |
| app_protocol | read_only_udm.network.application_protocol | 派生自“app_protocol”字段。如果“app_protocol”包含“HTTPS”“HTTP”“SSH”或“RDP”,则使用相应协议。否则,默认值为“UNKNOWN_APPLICATION_PROTOCOL”。 |
| app_protocol | read_only_udm.network.application_protocol_version | 值取自“app_protocol”字段。 |
| auditType.action | read_only_udm.security_result.action | 派生自“auditType.action”字段。如果“auditType.action”包含“successful”,则该值设置为“ALLOW”。如果包含“restricted”,则将值设置为“BLOCK”。 |
| auditType.action | read_only_udm.security_result.summary | 当“auditType”不为空且“auditType_area”为“SECURITY”时,从“auditType.action”字段中获取的值。 |
| auditType.actionI18nKey | read_only_udm.metadata.product_event_type | 当“auditType”不为空时,从“auditType.actionI18nKey”字段中获取的值。 |
| auditType.area | read_only_udm.security_result.detection_fields.value | 从“auditType.area”字段中获取值,并将其分配给检测字段的“value”字段,同时将“key”字段设置为“auditType area”。当“auditType”不为空时,系统会执行此映射。 |
| auditType.category | read_only_udm.security_result.category_details | 当“auditType”不为空时,从“auditType.category”字段中获取的值。 |
| auditType.categoryI18nKey | read_only_udm.security_result.detection_fields.value | 从“auditType.categoryI18nKey”字段中获取值,并将其分配给检测字段的“value”字段,同时将“key”字段设置为“auditType categoryI18nKey”。当“auditType”不为空时,系统会执行此映射。 |
| auditType.level | read_only_udm.security_result.detection_fields.value | 从“auditType.level”字段中获取值,并将其分配给检测字段的“value”字段,其中“key”字段设置为“auditType level”。当“auditType”不为空时,系统会执行此映射。 |
| author.displayName | read_only_udm.principal.user.user_display_name | 取自“author.displayName”字段的值。 |
| author.externalCollaborator | read_only_udm.security_result.about.resource.attribute.labels.value | 从“author.externalCollaborator”字段中提取值,并将其分配给“key”字段设置为“externalCollaborator”的标签的“value”字段。 |
| author.id | read_only_udm.principal.user.userid | 当“author.type”为“user”且“principal_user_present”为“false”时,从“author.id”字段中获取的值。 |
| author.isExternalCollaborator | read_only_udm.security_result.about.resource.attribute.labels.value | 从“author.isExternalCollaborator”字段中提取值,并将其分配给“key”字段设置为“isExternalCollaborator”的标签的“value”字段。 |
| author.name | read_only_udm.principal.user.user_display_name | 当“author.type”为“user”且“principal_user_present”为“false”时,从“author.name”字段中获取的值。 |
| bytes_in | read_only_udm.network.received_bytes | 如果“bytes_in”字段包含数字,则取自该字段的值。否则,默认值为 0。 |
| 类别 | read_only_udm.security_result.category_details | 值取自“category”字段。 |
| changedValues | read_only_udm.principal.resource.attribute.labels | 遍历“changedValues”中的每个元素,并创建具有“changedValue[index][key]”等键的标签,以及“changedValues”数组中相应的值。 |
| 创建日期 | read_only_udm.metadata.event_timestamp | 从“creationDate”字段中获取的值,解析为 UNIX 或 UNIX_MS 时间戳。 |
| extraAttributes | read_only_udm.principal.resource.attribute.labels | 遍历“extraAttributes”中的每个元素,并根据“name”和“nameI18nKey”字段创建键,根据相应的“value”字段创建值。 |
| http_verb | read_only_udm.network.http.method | 值取自“http_verb”字段。 |
| ip | read_only_udm.target.ip | 从“ip”字段中获取的值。 |
| principal_host | read_only_udm.principal.hostname | 从“principal_host”字段中获取的值。 |
| referral_url | read_only_udm.network.http.referral_url | 从“referral_url”字段中获取的值。 |
| remoteAddress | read_only_udm.principal.ip | 从“remoteAddress”字段中获取的值,解析为 IP 地址。 |
| response_code | read_only_udm.network.http.response_code | 从“response_code”字段中获取的值。 |
| session_duration | read_only_udm.additional.fields.value.string_value | 从“session_duration”字段中获取值,并将其分配给“key”字段设置为“Session Duration”的标签的“string_value”字段。 |
| 来源 | read_only_udm.principal.ip | 从“来源”字段中获取的值,解析为 IP 地址。 |
| src_ip | read_only_udm.principal.ip | 如果“remoteAddress”为空,则取自“src_ip”字段的值。 |
| 摘要 | read_only_udm.security_result.summary | 值取自“摘要”字段。 |
| sysAdmin | read_only_udm.security_result.about.resource.attribute.labels.value | 从“sysAdmin”字段中提取的值,并分配给“key”字段设置为“sysAdmin”的标签的“value”字段。 |
| superAdmin | read_only_udm.security_result.about.resource.attribute.labels.value | 从“superAdmin”字段中获取值,并将其分配给“key”字段设置为“superAdmin”的标签的“value”字段。 |
| target_url | read_only_udm.target.url | 从“target_url”字段中获取的值。 |
| 时间戳 | read_only_udm.metadata.event_timestamp | 从“时间戳”字段中获取的值,解析为日期和时间字符串。 |
| user_id | read_only_udm.principal.user.userid | 值取自“user_id”字段。 |
| read_only_udm.metadata.event_type | 此字段的值由一系列检查确定,默认值为“GENERIC_EVENT”。系统会根据“principal_host”“user_id”“has_principal”和“author.type”等其他字段的存在情况和内容,将其设置为特定值,例如“NETWORK_HTTP”“USER_UNCATEGORIZED”或“STATUS_UPDATE”。 | |
| read_only_udm.metadata.vendor_name | 设置为“ATLASSIAN”。 | |
| read_only_udm.metadata.product_name | 设置为“CONFLUENCE”。 | |
| read_only_udm.metadata.log_type | 设置为“ATLASSIAN_CONFLUENCE”。 | |
| read_only_udm.principal.user.user_display_name | 此字段的值可能来自“author.displayName”或“affectedObject.name”,具体取决于上下文。 | |
| read_only_udm.target.process.pid | 此字段的值可以来自“principal_host”或“pid”,具体取决于上下文。 | |
| read_only_udm.principal.resource.attribute.labels | 此字段填充了从“affectedObjects”“changedValues”和“extraAttributes”等字段派生的各种标签。这些标签的键和值会根据这些字段的具体内容动态生成。 |
需要更多帮助?获得社区成员和 Google SecOps 专业人士的解答。