freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

vArmor代码学习下
2024-12-09 17:44:38
所属地 浙江省

前言

上篇文章中,简单介绍了vArmo如何通过behaviorbpfenforcer两种模式实现观察和阻断两种模式。在 vArmor 的实现中,规则是核心组件之一,它们用于定义和控制对网络流量和系统调用的访问权限。所以本文将详细分析 vArmor 中规则的定义、存储、匹配及更新机制。

规则

规则数据结构

在 vArmor 中,规则主要通过以下结构体来定义:

struct net_rule {
    u32 flags;                  // 规则标志位
    unsigned char address[16];  // IP 地址
    unsigned char mask[16];     // 子网掩码
    u32 port;                   // 端口号
};
  • flags:用于标识规则的特性,例如是否启用、策略类型等。
  • address:表示允许或拒绝的 IP 地址,支持 IPv4 和 IPv6。
  • mask:定义 IP 地址的匹配范围,通常用于 CIDR 表达式。
  • port:指定规则适用的端口号。

在用户态,规则的定义类似于内核态:

type bpfNetworkRule struct {
    Flags   uint32
    Address [16]byte
    Mask    [16]byte
    Port    uint32
}

该结构体与内核态的 net_rule 结构体保持一致,以确保数据在内核态和用户态之间的一致性。

规则存储

规则的存储主要依赖于 eBPF 的映射结构。在 vArmor 中,使用了两种类型的映射:外部映射和内部映射。

外部映射

struct {
  __uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
  __uint(max_entries, OUTER_MAP_ENTRIES_MAX);
  __type(key, u32);
  __type(value, u32);
} v_net_outer SEC(".maps");
  • v_net_outer:这是一个哈希映射,类型为 BPF_MAP_TYPE_HASH_OF_MAPS,用于存储与命名空间相关的内部映射的引用。其键是一个无符号整型(u32),值则是指向内部映射的指针。

内部映射

内部映射用于存储具体的规则信息,它的定义如下:

struct {
  __uint(type, BPF_MAP_TYPE_HASH);
  __uint(max_entries, NET_INNER_MAP_ENTRIES_MAX);
  __type(key, u32);
  __type(value, struct net_rule);
} v_net_inner SEC(".maps");
  • v_net_inner:这是一个哈希映射,类型为 BPF_MAP_TYPE_HASH,用于存储具体的网络规则。其键为规则 ID,值为 net_rule结构体。

规则匹配逻辑

规则的匹配逻辑在内核态通过 varmor_socket_connect函数实现,主要步骤如下:

static u32 *get_net_inner_map(u32 mnt_ns) {
  return bpf_map_lookup_elem(&v_net_outer, &mnt_ns);
}
  • 使用当前进程的挂载命名空间 ID(mnt_ns)作为键,从 v_net_outer映射中查找对应的内部映射。

规则匹配过程

在 varmor_socket_connect函数中,执行以下逻辑:


SEC("lsm/socket_connect")
int BPF_PROG(varmor_socket_connect, struct socket *sock, struct sockaddr *address, int addrlen) {
    u32 mnt_ns = get_task_mnt_ns_id(current);
    u32 *vnet_inner = get_net_inner_map(mnt_ns);

    for(inner_id=0; inner_id<NET_INNER_MAP_ENTRIES_MAX; inner_id++) {
        struct net_rule *rule = get_net_rule(vnet_inner, inner_id);
        if (rule == NULL) {
            DEBUG_PRINT("access allowed");
            return 0; // 允许访问
        }
        // 进行规则匹配逻辑...
    }
}


  • 获取当前进程的挂载命名空间 ID。
  • 根据命名空间 ID 从 v_net_outer中获取对应的内部映射 vnet_inner
  • 遍历内部映射中的规则,进行匹配。如果没有找到匹配规则,则返回允许访问。

规则的更新

在用户态代码中,首先创建内部映射:

mapName := fmt.Sprintf("v_net_inner_%d", nsID)
innerMapSpec := ebpf.MapSpec{
  Name:       mapName,
  Type:       ebpf.Hash,
  KeySize:    4,
  ValueSize:  4*2 + 16*2,
  MaxEntries: uint32(varmortypes.MaxBpfNetworkRuleCount),
}
innerMap, err := ebpf.NewMap(&innerMapSpec)
  • 这里使用 nsID作为命名空间 ID,创建了一个新的内部映射。

接下来,解析规则并将其添加到内部映射中:

for i, network := range bpfContent.Networks {
    var rule bpfNetworkRule

    rule.Flags = network.Flags
    rule.Port = network.Port
    ip := net.ParseIP(network.Address)
    if ip.To4() != nil {
        copy(rule.Address[:], ip.To4())
    } else {
        copy(rule.Address[:], ip.To16())
    }

    if network.CIDR != "" {
        _, ipNet, err := net.ParseCIDR(network.CIDR)
        if err != nil {
            return err
        }
        copy(rule.Mask[:], ipNet.Mask)
    }

    var index uint32 = uint32(i)
    err = innerMap.Put(&index, &rule)
    if err != nil {
        return err
    }
}



  • 这里将解析的网络规则存储到内部映射中,使用规则的索引作为键。

最后,将内部映射的引用放入外部映射中:



err = enforcer.objs.V_netOuter.Put(&nsID, innerMap)
if err != nil {
    return err
}


  • 通过 nsID将 innerMap存入 v_net_outer,完成规则的更新。

总结

vArmor 的规则部分通过 eBPF 映射机制实现了灵活而高效的网络访问控制。规则的定义、存储、匹配及更新机制都经过精心设计,以确保在内核态和用户态之间的数据一致性。通过这种方式,vArmor 能够在运行时动态地管理和应用安全策略,为云原生环境提供了强大的安全防护能力。

参考

本文作者:pamela@涂鸦智能安全实验室

# 云原生安全
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录