安全组规则的日志 API¶
https://bugs.launchpad.net/neutron/+bug/1468366
本规范实现了一种捕获和存储与安全组相关的事件的方式。
问题描述¶
操作员(包括云管理员和开发人员)或项目希望存储网络流量的日志,
南北向网络流量是在实例和外部网络之间传输的。
东西向网络流量是在实例之间传输的。
为了检测非法通信或攻击模式,警报异常活动以符合合规性要求。
此外,这些日志还可以用于调试安全组(帮助项目确保安全组规则按预期工作)。但是,这不是主要目标,因为存在其他方法(例如 TaaS、tcpdump)来解决相同的问题。
在 Neutron 中,所有在实例上允许/丢弃的流量都由安全组管理。但是,日志记录是安全组中当前缺失的功能。
提议的变更¶
为了解决这个问题,我们希望提出一个具有以下目标的日志 API
定义与安全组相关的事件的格式和收集方式。
日志 API 将收集与配置在端口上的安全组相关的 ACCEPT 或 DROP 事件,或两者。有关更多详细信息,请参阅 预期 API 行为。
本规范为仅限操作员的 OVS 本机防火墙 (OVS) 和基于 Iptables (Linux Brigde) 的安全组实现日志 API。它还提出了一个可以轻松扩展到其他资源(例如防火墙日志)的模型。
最初,我们将为 OVS 本机防火墙和基于 Iptables 的日志记录提供 PoC。OVS 本机防火墙日志记录将首先完全支持,因为它现在正成为默认驱动程序,基于 Iptables 的日志记录将在稍后完成。该 API 被认为在 OVS 本机防火墙和基于 Iptables 的日志记录之间一致工作。
关于多驱动环境,此功能将采用类似 QoS [1] 的首次方法进行类似的验证。[2]
日志实现¶
日志 API 被设计为服务插件。它被定义为安全组和防火墙等资源的通用日志 API。
在服务器端,LoggingApiPlugin 的类设计如下
+-----------------------+
| LoggingApiPluginBase |
+----------^------------+
|
+-----------------------+
| LoggingApiPlugin |
+-----------------------+
| _init_() |
| get_logs() |
| get_log() |
| create_log() |
| update_log() |
| delete_log() |
+-----------------------+
参考实现可以在 [3] 中找到。
LoggingExtension 是一个代理扩展。它适用于所有日志资源,例如安全组和防火墙。LoggingExtension 将接收日志资源的 CREATED/UPDATED/DELETED 事件,并将这些事件传递给日志驱动程序。每个日志驱动程序定义它支持的资源。对于安全组,我们可以实现一个安全组日志驱动程序,作为 OVSFirewallLogging 或 IptablesLoggingDriver。
LoggingExtension 的类设计如下
+-----------------------+
| AgentExtension |
+------------^----------+
|
+------------+----------+
| LoggingExtension |
+-----------------------+
| initialize() |
| consume_api() |
| _handle_notification()|
+-----------------------+
LoggingDriver 的类设计如下
+-----------------------------+
| LoggingDriver |
+-----------------------------+
|SUPPORTED_LOGGING_TYPES=set()|
|initialize() |
|start_logging(log_resource) |
|stop_logging(log_resource) |
+--------------^--------------+
|
+----------------+-----------------+
| |
+--------------+---------------+ +--------------+---------------+
| IptablesLoggingDriver | | OVSFirewallLoggingDriver |
+------------------------------+ +------------------------------+
| SUPPORTED_LOGGING_TYPES=[...]| | SUPPORTED_LOGGING_TYPES=[...]|
| initialize() | | initialize() |
| start_logging(log_resource) | | start_logging(log_resource) |
| stop_logging(log_resource) | | stop_logging(log_resource) |
| _handle_logging() | | |
| _create_security_group_log | +------------------------------+
| _delete_security_group_log |
| ... |
+------------------------------+
对于 OVS 防火墙:OVSFirewallLoggingDriver 将充当控制器程序。它在每个计算节点上运行,以处理 packet_in 以生成每个数据包的日志行到文件。要为 ACCEPT 事件生成数据包消息:OVSFirewallLoggingDriver 将插入“actions=controller”到与 conntrack 状态为 NEW(第一个数据包)的安全组规则匹配的流中。它还将插入在具有 ‘actions=drop’ 且 conntrack 状态为 INVALID 或 NOT ESTABLISHED 的流之前插入具有 ‘actions=controller’ 的流,以通过端口号进行项目隔离生成 DROP 事件的数据包消息。一个流的示例如下
对于入站表
table=82, priority=73,ct_state=+new-est,icmp,reg5=0xa,dl_dst=fa:16:3e:1d:d0:8b actions=ct(commit,zone=NXM_NX_REG6[0..15]),strip_vlan,output:10,CONTROLLER:65535 table=82, priority=70,ct_state=+new-est,icmp,reg5=0xa,dl_dst=fa:16:3e:1d:d0:8b actions=ct(commit,zone=NXM_NX_REG6[0..15]),strip_vlan,output:10 table=82, priority=70,ct_state=+est-rel-rpl,icmp,reg5=0xa,dl_dst=fa:16:3e:1d:d0:8b actions=strip_vlan,output:10 table=82, priority=53,ct_mark=0x1,reg5=0xa actions=CONTROLLER:65535 table=82, priority=50,ct_mark=0x1,reg5=0xa actions=drop table=82, priority=50,ct_state=+inv+trk actions=drop table=82, priority=50,ct_state=+est-rel+rpl,ct_zone=1,ct_mark=0,reg5=0xa,dl_dst=fa:16:3e:1d:d0:8b actions=strip_vlan,output:10 table=82, priority=50,ct_state=-new-est+rel-inv,ct_zone=1,ct_mark=0,reg5=0xa,dl_dst=fa:16:3e:1d:d0:8b actions=strip_vlan,output:10 table=82, priority=43,ct_state=-est,reg5=0xa actions=CONTROLLER:65535 table=82, priority=40,ct_state=-est,reg5=0xa actions=drop table=82, priority=40,ct_state=+est,ip,reg5=0xa actions=ct(commit,zone=NXM_NX_REG6[0..15],exec(load:0x1->NXM_NX_CT_MARK[])) table=82, priority=40,ct_state=+est,ipv6,reg5=0xa actions=ct(commit,zone=NXM_NX_REG6[0..15],exec(load:0x1->NXM_NX_CT_MARK[])) table=82, priority=0 actions=drop
对于出站表
table=72, priority=73,ct_state=+new-est,icmp,reg5=0xa,dl_src=fa:16:3e:1d:d0:8b actions=resubmit(,73),CONTROLLER:65535 table=72, priority=70,ct_state=+new-est,icmp,reg5=0xa,dl_src=fa:16:3e:1d:d0:8b actions=resubmit(,73) table=72, priority=70,ct_state=+est-rel-rpl,icmp,reg5=0xa,dl_src=fa:16:3e:1d:d0:8b actions=resubmit(,73) table=72, priority=53,ct_mark=0x1,reg5=0xa actions=CONTROLLER:65535 table=72, priority=50,ct_mark=0x1,reg5=0xa actions=drop table=72, priority=50,ct_state=+inv+trk actions=drop table=72, priority=50,ct_state=+est-rel+rpl,ct_zone=1,ct_mark=0,reg5=0xa actions=NORMAL table=72, priority=50,ct_state=-new-est+rel-inv,ct_zone=1,ct_mark=0,reg5=0xa actions=NORMAL table=72, priority=43,ct_state=-est,reg5=0xa actions=CONTROLLER:65535 table=72, priority=40,ct_state=-est,reg5=0xa actions=drop table=72, priority=40,ct_state=+est,ip,reg5=0xa actions=ct(commit,zone=NXM_NX_REG6[0..15],exec(load:0x1->NXM_NX_CT_MARK[])) table=72, priority=40,ct_state=+est,ipv6,reg5=0xa actions=ct(commit,zone=NXM_NX_REG6[0..15],exec(load:0x1->NXM_NX_CT_MARK[])) table=72, priority=0 actions=drop
参考实现可以在 [5] 中找到。
对于基于 Iptables 的:IptablesLoggingDriver 实现非常清晰。IptablesLoggingDriver 只是将 NFLOG 规则添加到 iptables [4]。
云部署者可以选择 OVS 本机防火墙驱动程序或 iptables 驱动程序的 log_driver,方法是指定 ovs_fw_log 或 iptables_log。他们还可以配置 rate_limit 和 burst_limit [8] 以限制每秒记录的最大数据包数。日志输出文件可以通过选项 local_output_log_base 指定。这些选项在 /etc/neutron/plugins/ml2/ml2_conf.ini 中定义
[log]
# Driver for security group logging in the L2 agent (string value)
# 'ovs_fw_log' or 'iptables_log'
log_driver = ovs_fw_log
# Maximum packets logging per second. (integer value, at least 100)
rate_limit = 100
# Maximum number of packets per rate_limit(integer value, at least 25)
burst_limit = 25
# Output logfile
local_output_log_base = /var/log/syslog
要使用日志数据,操作员可以
使用第三方服务,例如 Monasca 服务来使用日志数据。
直接访问 ‘local_output_log_base’ 以获取日志数据。
预期 API 行为¶
本规范以安全组日志记录为例
操作员可以收集安全事件(ACCEPT/DROP 或 ALL(ACCEPT 和 DROP)),在某些情况下
通过将其安全组 ID 传递给 ‘resource_id’ 来收集应用于所有 VM 的特定安全组相关的事件。
通过将其安全组 ID 传递给 ‘resource_id’ 及其绑定到 Neutron 端口 ID 的 ‘target_id’ 来收集应用于特定 VM 的特定安全组相关的事件。
通过将其 Neutron 端口 ID 传递给 ‘target_id’ 来收集应用于特定 VM 的所有安全组相关的事件。
收集项目中安全组相关的事件:在这种情况下,操作员不向 ‘resource_id’ 或 ‘target_id’ 传递任何值。
更详细地说,此 API 将记录与安全组规则匹配的第一个数据包以进行 ACCEPT 事件。它还将记录与任何安全组规则不匹配的所有数据包以进行 DROP 事件。
请注意,取决于上述用例,如果启动新的 VM 或新的安全组或新的安全组规则,其相关的安全事件也将被收集。另一方面,如果删除 VM 或安全组或安全组规则,其相关的安全事件将不会被记录。但是,与剩余部分相关的安全事件仍将被记录。
超出本规范的未来工作¶
扩展 FWaaS v2 的日志记录。
将日志 API 公开给项目(例如通过使用 RBAC)。
API 操作示例¶
以以下场景为例
操作员启动多个 VM,这些 VM 的流量受到安全组规则的限制。
检查支持的日志记录功能
GET /v2.0/logging/loggable-resources
{ "loggable_resources":[{"type": "security_group"}] }
创建一个 logging-resource 并定义需要收集的项目 demo 中所有安全组相关的事件。例如,操作员希望收集所有应用于项目 demo 的 ACCEPT & DROP 事件(有关更多详细信息,请参阅 预期 API 行为 部分)
POST /v2.0/logging/logs
{ "log": { "name": "create_log_test1", "description": "Collecting all security events in project demo", "project_id": "8d4c70a21fed4aeba121a1a429ba0d04", "resource_type": "security_group", "event": "ALL"} }
响应
{ "log": { "id": "5f126d84-551a-4dcf-bb01-0e9c0df0c793", "project_id": "8d4c70a21fed4aeba121a1a429ba0d04", "name": "create_log_test1", "description": "Collecting all security events in project demo", "enabled": True, "resource_type": "security_group", "event": "ALL", "resource_id": None, "target_id": None} }
操作员使用外部服务(例如 Monasca)来使用日志数据。
示例日志格式¶
日志输出应包括时间戳、action(ACCEPT/DROP) 项目 ID、logging-resource ID、VM 端口 ID、源 MAC、目标 MAC、源 IP 地址、目标 IP 地址、协议、源 L4 端口和目标 L4 端口。虽然使格式可配置不在范围内,但格式可以在实施阶段定义。
初始实现如下所示
ACCEPT 事件的日志数据
May 5 09:05:07 action=ACCEPT project_id=736672c700cd43e1bd321aeaf940365c log_resource_ids=['4522efdf-8d44-4e19-b237-64cafc49469b', '42332d89-df42-4588-a2bb-3ce50829ac51'] vm_port=e0259ade-86de-482e-a717-f58258f7173f ethernet(dst='fa:16:3e:ec:36:32',ethertype=2048,src='fa:16:3e:50:aa:b5'), ipv4(csum=62071,dst='10.0.0.4',flags=2,header_length=5,identification=36638,offset=0, option=None,proto=6,src='172.24.4.10',tos=0,total_length=60,ttl=63,version=4), tcp(ack=0,bits=2,csum=15097,dst_port=80,offset=10,option=[TCPOptionMaximumSegmentSize(kind=2,length=4,max_seg_size=1460), TCPOptionSACKPermitted(kind=4,length=2), TCPOptionTimestamps(kind=8,length=10,ts_ecr=0,ts_val=196418896), TCPOptionNoOperation(kind=1,length=1), TCPOptionWindowScale(kind=3,length=3,shift_cnt=3)], seq=3284890090,src_port=47825,urgent=0,window_size=14600)
DROP 事件的日志数据
May 5 09:05:07 action=DROP project_id=736672c700cd43e1bd321aeaf940365c log_resource_ids=['4522efdf-8d44-4e19-b237-64cafc49469b'] vm_port=e0259ade-86de-482e-a717-f58258f7173f ethernet(dst='fa:16:3e:ec:36:32',ethertype=2048,src='fa:16:3e:50:aa:b5'), ipv4(csum=62071,dst='10.0.0.4',flags=2,header_length=5,identification=36638,offset=0, option=None,proto=6,src='172.24.4.10',tos=0,total_length=60,ttl=63,version=4), tcp(ack=0,bits=2,csum=15097,dst_port=80,offset=10,option=[TCPOptionMaximumSegmentSize(kind=2,length=4,max_seg_size=1460), TCPOptionSACKPermitted(kind=4,length=2), TCPOptionTimestamps(kind=8,length=10,ts_ecr=0,ts_val=196418896), TCPOptionNoOperation(kind=1,length=1), TCPOptionWindowScale(kind=3,length=3,shift_cnt=3)], seq=3284890090,src_port=47825,urgent=0,window_size=14600)
数据模型影响¶
此模型定义了所有日志资源(例如安全组和防火墙)的通用模型。
日志模型如下所示
属性名称 |
类型 |
访问 |
默认值 |
验证/转换 |
描述 |
|---|---|---|---|---|---|
id |
字符串 (UUID) |
RO |
生成 |
uuid |
Identity |
project_id |
字符串 (UUID) |
RW(无更新) |
N/A |
uuid |
日志对象所有者的项目 ID |
name |
string |
RW |
N/A |
无 |
日志对象名称 |
description |
string |
RW |
N/A |
无 |
日志对象描述 |
enabled |
bool |
RW |
True |
布尔值 |
启用/禁用日志 |
resource_type |
string |
RW(无更新) |
N/A |
无 |
日志资源类型,在初始实现中可能是 ‘security_group’ |
resource_id |
字符串 (UUID) |
RW(无更新) |
N/A |
uuid |
ID of resource which is enabled to be logged. When a resource_id (which could be a security group ID in the initial implementation) is specified, its related events will be collected. For detail usage refer to 预期 API 行为 |
event |
string |
RW(无更新) |
N/A |
无 |
可以指定 ACCEPT/DROP 或 ALL。ALL 为默认值。 |
target_id |
字符串 (UUID) |
RW(无更新) |
N/A |
uuid |
ID of resource (which could be a port ID in the initial implementation), where we want to collect log. When a target_id is specified, events related to it will be colleted. For detail usage refer to 预期 API 行为 |
REST API 影响¶
将添加新的资源。
LOGGING_PREFIX = '/logging'
EVENTS = ['ACCEPT', 'DROP', 'ALL']
RESOURCE_ATTRIBUTE_MAP = {
'log': {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True, 'primary_key': True},
'project_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True,
'validate': {'type:string': attr.TENANT_ID_MAX_LEN},
'is_visible': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': attr.NAME_MAX_LEN},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': attr.DESCRIPTION_MAX_LEN},
'default': '', 'is_visible': True},
'resource_type': {'allow_post': True, 'allow_put': False,
'required_by_policy': True,
'validate': {'type:validate_log_resource_type': None},
'is_visible': True},
'resource_id': {'allow_post': True, 'allow_put': False,
'is_visible': True, 'default': None,
'validate': {'type:uuid_or_none': None}},
'event': {'allow_post': True, 'allow_put': False,
'validate': {'type:values': EVENTS},
'is_visible': True, 'default': 'ALL'},
'target_id': {'allow_post': True, 'allow_put': False,
'is_visible': True, 'default': None,
'validate': {'type:uuid_or_none': None}},
'enabled': {'allow_post': True, 'allow_put': True,
'is_visible': True, 'default': True,
'convert_to': attr.convert_to_boolean},
},
'loggable_resources':{
'type': {'allow_post': False, 'allow_put': False,
'is_visible': True}
}
}
Object |
URI |
类型 |
|---|---|---|
log |
/logging/logs |
POST |
log |
/logging/logs |
GET |
log |
/logging/logs/{logging-resource-id} |
GET |
log |
/logging/logs/{logging-resource-id} |
DELETE |
log |
/logging/logs/{logging-resource-id} |
PUT |
loggable-resource |
/logging/loggable-resources |
GET |
安全影响¶
此 REST API 只能由 admin_only 访问,该访问由 policy.json 控制。
日志量高度依赖于用户流量。它会影响性能,并且是一种潜在的安全影响(一种 DoS)。
通知影响¶
无
操作员 CLI 影响¶
将向 neutron OSC 插件添加其他方法以创建、更新、删除、列出和获取日志资源。
对于日志资源
openstack network logging create
--resource-type <resource-type>
[--description <description>]
[--enable | --disable]
[--resource-id <resource-id>]
[--event=<ACCEPT/DROP/ALL>]
[--target-id=<port-id>]
[--project <project> [--project-domain <project-domain>]]
name
openstack network logging list
openstack network logging set
[--name <new-name>]
[--description <description>]
[--enabled |--disabled]
<logging-resource-id>
openstack network logging show <logging-resource-id>
openstack network logging delete <logging-resource-id>
检查支持的日志记录功能
openstack network loggable resources list
性能影响¶
目前,已记录的数据将存储到文件中。因此,这可能会稍微增加一些性能开销。
IPv6 影响¶
支持 IPv4 和 IPv6。
其他部署者影响¶
配置以启用日志 API 服务。
开发人员影响¶
无
社区影响¶
无
备选方案¶
通过向安全组 API 资源添加 ‘logging’ 属性来更改安全组 API。这将破坏与 EC2 API 的兼容性 [6]。我们应该避免通过扩展它来更改 FWaaS API [7]。
因此,日志 API 允许启用日志记录是必要的。
实现¶
负责人¶
- 主要负责人
y-furukawa-2
- 其他贡献者
hoangcx, annp, tuhv
工作项¶
最终确定记录数据的方式
实现
REST API + API 测试
数据库模型和数据库迁移
Oslo 版本化对象数据库实现
基于 Iptables 的参考实现
OSC 支持
依赖项¶
无
测试¶
单元测试
功能测试
全栈测试
Tempest 测试¶
无,因为这包含在树内 API 测试中。
功能测试¶
使用启用日志记录功能的回归测试。测试日志记录功能是否实际有效。
API 测试¶
将通过 API 测试测试新的 API 接口,以确保所有操作按预期工作。
文档影响¶
用户文档¶
将一些用法添加到网络指南中供操作员使用。
编写如何启用日志 API 插件。