iptables + SELinux控制socket packet

SELinux对socket的控制包含这几个方面:

  • context语法中的nodecon, portcon, netifcon:这一类控制本端系统资源,确保合法进程才能访问本端网络资源
  • 通过iptables工具的SECMARK/CONNSECMARK扩展为socket packet打上的标签:这一类控制socket包的流向,确保只有合法进程才能访问某些网络报文
  • NetLable/Labeled IPSec:这一类还没看,留待后面继续学习
    本文关注第二种控制方法,并结合iptables,简要阐述其工作原理和使用方法。

    iptables简介

    from wiki
    iptables is a user-space utility program that allows a system administrator to configure the IP packet filter rules of the Linux kernel firewall, implemented as different Netfilter modules. The filters are organized in different tables, which contain chains of rules for how to treat network traffic packets. Different kernel modules and programs are currently used for different protocols; iptables applies to IPv4, ip6tables to IPv6, arptables to ARP, and ebtables to Ethernet frames.

iptables其实就是Linux的防火墙,用户通过配置各种规则来实现数据包的过滤。他利用了Linux的Netfilter框架的hook点对数据包进行判断,并执行action。例如:

1
2
iptables -A INPUT -s 10.10.10.10 -j DROP # 来自10.10.10.10的packet全部被丢弃
iptables -A INPUT -s 10.10.10.0/24 -j DROP # 来自10.10.10.0/255.255.255.0(24表示掩码位数)的packet全部被丢弃

iptables支持的规则很多,具体可以参考iptables(8) - Linux man page

table & chain

如wiki page所述,iptables通过各种tables和chains来管理所有rules。其实chain就是对应了netfilter的hook点。netfilter的hook点有:

The following hooks represent various well-defined points in the networking stack:

  • NF_IP_PRE_ROUTING: This hook will be triggered by any incoming traffic very soon after entering the network stack. This hook is processed before any routing decisions have been made regarding where to send the packet.
  • NF_IP_LOCAL_IN: This hook is triggered after an incoming packet has been routed if the packet is destined for the local system.
  • NF_IP_FORWARD: This hook is triggered after an incoming packet has been routed if the packet is to be forwarded to another host.
  • NF_IP_LOCAL_OUT: This hook is triggered by any locally created outbound traffic as soon it hits the network stack.
  • NF_IP_POST_ROUTING: This hook is triggered by any outgoing or forwarded traffic after routing has taken place and just before being put out on the wire.

而chain就是和这些hook点一一对应的,例如:

  • PREROUTING: Triggered by the NF_IP_PRE_ROUTING hook.
  • INPUT: Triggered by the NF_IP_LOCAL_IN hook.
  • FORWARD: Triggered by the NF_IP_FORWARD hook.
  • OUTPUT: Triggered by the NF_IP_LOCAL_OUT hook.
  • POSTROUTING: Triggered by the NF_IP_POST_ROUTING hook.

而iptables又是怎么遍历这些table和chain的呢?
iptable在各个NetFilter的hook点上,依次遍历各个table的各个chain。其顺序按照如下表格:

  • chain从左到右
  • table从上到下
Tables↓/Chains→ PREROUTING INPUT FORWARD OUTPUT POSTROUTING
(routing decision)
raw
(connection tracking enabled)
mangle
nat (DNAT)
(routing decision)
filter
security
nat (SNAT)

每个chain和table在整个协议栈的位置如下图。

fcbd15b235c892be235e74cb0e2f537f

用户还可以创建自己的chain,使用命令iptables -N <chain name>。然后使用-j参数跳转,例如:iptables -A INPUT -p tcp -j tcp_packets

When/If we reach the end of that chain, we get dropped back to the INPUT chain and the packet starts traversing from the rule one step below where it jumped to the other chain (tcp_packets in this case). If a packet is ACCEPTed within one of the sub chains, it will be ACCEPT’ed in the superset chain also and it will not traverse any of the superset chains any further. However, do note that the packet will traverse all other chains in the other tables in a normal fashion.

总结一下:

  • 如果自定义chain遍历结束,则回到原先跳出的chain(super chain),继续遍历
  • 如果子链(sub chain)中有ACCEPT,则父链中的规则不会再被继续遍历,但仍然会继续遍历其他chain中的规则,以及其他table中的规则。
  • 如果在任意位置,packet被DROP,则后续的chain和table都不会被执行

    target

    -j除了指定自定义chain外,一个更重要的用途就是指定packet的目的地,也就是target。

Targets on the other hand specify an action to take on the packet in question. We could for example, DROP or ACCEPT the packet depending on what we want to do. There are also a number of other actions we may want to take.
For example: ACCEPT, DROP, CONNMARK, CLASSIFY, LOG …

  • Some targets will cause the packet to stop traversing that specific chain and superior chains as described above. ex. ACCEPT, DROP
  • Other targets, may take an action on the packet, after which the packet will continue passing through the rest of the rules. ex. LOG, ULOG, TOS
  • Some targets will accept extra options (What TOS value to use etc), while others don’t necessarily need any options.

:以上摘自[5],时间有点久,可能有些已经不成立了(例如[5]中通篇未提到security table),但基本思路应该是保持的。

SECMARK

上一章介绍了iptables的基本工作原理,和table/chain的遍历方法。而可以与SELinux联动,并提供基于socket packet的MAC机制的是iptables的security表格。从上面表格可以看到,security table只有3个chain,即:INPUT,FORWARD,OUTPUT。对应了输入,输出以及转发3种数据流。可以满足基本的packet强制访问控制需求。
一条SECMARK相关的iptables语句如下:

1
iptables -t security -A INPUT -i lo -p tcp --dport 9999 -j SECMARK --selctx system_u:object_r:ext_gateway_packet_t:s0

其中iptables -t security -A INPUT表示往security table的INPUT chain插入一条规则,match部分-i lo -p tcp --dport 9999表示匹配来自lo网卡,协议为tcp的报文,目标端口为9999的数据包被SECMARK目标进行处理。SECMARK目标使用—selctx参数为该类数据包打上system_u:object_r:ext_gateway_packet_t:s0的安全上下文。则SELinux的策略语法即可以针对该类packet进行权限判断。例如:
1
allow ext_gateway_t ext_gateway_packet_t : packet { send recv };

CONNSECMARK

CONNSECMARK实际上是SECMARK的扩展。从名字就可以看出CONNSECMARK是针对连接(connection)进行mark动作。iptables的连接管理基于NetFilter的conntrack。conntrack是内核模块,针对不同的协议类型,具有相应的连接控制管理。这已经超出本文的范围了。但不管conntrack多么复杂,功能多么强大,对应到iptables的状态只有4个,即NEW, EATABLISHED, RELATED, INVALID。所有的socket packet都被映射到这4歌状态。具体可以参考文献[5]的Chapter 7. The state machine的User-land states。

  • NEW: tells us that the packet is the first packet that we see.
  • ESTABLISHED: The only requirement to get into an ESTABLISHED state is that one host sends a packet, and that it later on gets a reply from the other host.
  • RELATED: The ESTABLISHED connection will then spawn a connection outside of the main connection. The newly spawned connection will then be considered RELATED, if the conntrack module is able to understand that it is RELATED.
    INVALID: The INVALID state means that the packet can’t be identified or that it does not have any state.

有了这状态定义,iptables就可以使用—state maches。例如:iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT定义了规则,处于ESTABLISHED, RELATED状态的packet才被ACCEPT。
iptables与SELinux配合完全基于对socket packet进行过滤。并没有allow规则,或SELinux object是针对网络连接的。所以,iptables可以通过配合state match以及CONNSECMARK为来自于某个连接的packet进行打标签。方法就是—save和—restore参数。

  • —save

    • Example: iptables -t mangle -A PREROUTING -p tcp --dport 80 -j CONNSECMARK --save
    • Explanation: Save the security context mark from the packet to the connection if the connection is not marked since before.
  • —restore

    • Example: iptables -t mangle -A PREROUTING -p tcp --dport 80 -j CONNSECMARK --restore
    • Explanation: If the packet has no security context mark set on it, the —restore option will set the security context mark associated with the connection on the packet.

总结一下就是:

  • —save将packet数据包的context赋予连接
  • —restore将连接的context赋予packet
    此时SELinux具备更丰富的上下文,来对socket packet进行强制访问控制。

    Gateway实验

    简介

    文献[2]的2.2章节中提供了一个很好的练习,带我们认识iptables与SELinux配合做socket packet强制访问控制的方法。该方法通过iptables和SELinux实现客户端和服务端之间的数据包过滤。整个测试框架示意图如下所示,其中client和secure_client使用同一份源码编译,只是file context有所不同,server和secure_server同理。
    afd271fe524baadc38c595d0b74f9fae

示例代码通过定义可执行文件的安全上下文(如下所示),和typetransition规则,使得secure*进程工作在ext_gateway_t domain,而非secure进程工作在unconfined_t domain。各可执行文件的安全上下文如下所示:

1
2
3
4
/usr/local/bin/secure_client system_u:object_r:secure_services_exec_t:s0
/usr/local/bin/secure_server system_u:object_r:secure_services_exec_t:s0
/usr/local/bin/client system_u:object_r:unconfined_t:s0
/usr/local/bin/server system_u:object_r:unconfined_t:s0

示例代码中的策略集(.conf)文件定义了示意图中4个方向数据流的权限,包括:

  • 允许访问未标记端口的数据流(default_secmark_packet_t):auditallow unconfined_t default_secmark_packet_t : packet { send recv };
  • 允许secure_*进程访问标记了端口的数据流(ext_gateway_packet_t):allow ext_gateway_t ext_gateway_packet_t : packet { send recv };
  • 允许secure_*进程访问无SECMARK标记的数据流(unconfined_t):allow ext_gateway_t unconfined_t : packet { recv send };
    iptables_secmark使用若干iptables命令,定义数据包的标签规则,例如:
  • 来自于lo网卡的,协议为tcp,目标IP为127.0.0.0,掩码为255.0.0.0输入数据包被设置成default_secmark_packet_t
    1
    2
    # This OUTPUT rule sets all packets to default_secmark_packet_t: as it is
    iptables -t security -A INPUT -i lo -p tcp -d 127.0.0.0/8 -j SECMARK --selctx system_u:object_r:default_secmark_packet_t:s0
  • 来自于lo网卡的,协议为tcp,目标/源端口为9999的输入/输出数据包被设置成ext_gateway_packet_t(节选)
    1
    2
    3
    # These rules will replace the above context with the internal or
    iptables -t security -A INPUT -i lo -p tcp --dport 9999 -j SECMARK --selctx system_u:object_r:ext_gateway_packet_t:s0
    iptables -t security -A OUTPUT -o lo -p tcp --sport 9999 -j SECMARK --selctx system_u:object_r:ext_gateway_packet_t:s0
    两类规则定义的数据包范围实际是有重叠的,根据注释可知,实际两者的关系是,general和特例的关系。即前者将所有特定目标的数据包设置为default_secmark_packet_t,而后者将来自特定端口的数据包指定为ext_gateway_packet_t。
    经过此类策略设置,client在访问secure_server时,数据包就会被拦截。查看/var/log/audit/audit.log:
    1
    2
    3
    4
    type=AVC msg=audit(1602641071.967:3475): avc:  granted  { send } for  pid=0 comm="swapper/0" saddr=127.0.0.1 src=9999 daddr=127.0.0.1 dest=47076 netif=lo scontext=unconfined_u:message_filter_r:ext_gateway_t:s0-s0:c0.c1023 tcontext=system_u:object_r:ext_gateway_packet_t:s0 tclass=packet

    type=AVC msg=audit(1602641071.967:3476): avc: denied { recv } for pid=10 comm="ksoftirqd/0" saddr=127.0.0.1 src=9999 daddr=127.0.0.1 dest=47076 netif=lo scontext=unconfined_u:
    unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:object_r:ext_gateway_packet_t:s0 tclass=packet permissive=0
    client连接secure_server监听的9999端口,数据流为:tcp,127.0.0.1:9999 => 127.0.0.1:47076。secure_server向client发送成功,但client接收失败。因为client运行于unconfined_t,没有对应ext_gateway_packet_t的recv权限。

勘误

参考文献[2]因为成书过久(写于2014年,现在2020年),且练习基于书中前导章节中的modular-test章节,所以直接运行会产生错误。错误主要包含:

  • 基于modular-test构建ext_gateway策略。因为可能是发行版变化(笔者用的CentOS8)或者时间过久的原因,直接用书中的基础策略会造成无法开机,所以modular-test练习中的策略不可用。解决方法是,编译ext_gateway module,并用semodule插入发行版自带的策略中。此时需要针对原生策略进行适配。例如:进程拉起时报错,需要增加相应allow规则,节选如下:
    1
    2
    3
    4
    5
    allow ext_gateway_t null_device_t : chr_file { read write open };
    allow ext_gateway_t ld_so_t : file { map read execute };
    allow ext_gateway_t ld_so_cache_t : file { open map read execute getattr };
    allow ext_gateway_t lib_t : file { open map read execute getattr };
    ...
  • 本书给出的示例代码的安全上下文未给出mls描述,在编译时会报错。例如:
    1
    2
    3
    iptables -t security -A INPUT -i lo -p tcp --dport 9999 -j SECMARK --selctx system_u:object_r:ext_gateway_packet_t:s0
    iptables -t security -A INPUT -i lo -p tcp --sport 9999 -j SECMARK --selctx system_u:object_r:ext_gateway_packet_t:s0
    /usr/local/bin/secure_client system_u:object_r:secure_services_exec_t:s0
    所有的context都加上了:s0
    经过适配后,书中提供的示例可以顺利跑通,如下:
    server
    e19e063bd6c8c6ea612eab6441924451
    client端
    2cf32398daa366c050fa0a9cbebd4dae

参考文献

[1] SELinux Networking Support
[2] The SELinux Notebook - Building The Sample Policy
[3] iptables wiki page
[4] A Deep Dive into Iptables and Netfilter Architecture
[5] Oskar Andreasson, 2001-2006, Iptables Tutorial 1.2.2