基于 NeuVector 发现和管理 Docker macvlan 网络安全

概述

NeuVector 是 SUSE 开源的专为 Kubernetes 等容器环境设计的零信任安全平台,Docker 环境也是原生支持 。本章主要讲解如何基于 NeuVector 的网络防护能力,自动发现和管理 Docker macvlan 网络安全。

环境

版本:

Env Version
Ubuntu 22.04.5
Docker 29.3.1/29.6.0
Neuvector 5.4.9/5.6.1

虚拟机环境:

节点 IP 地址
u69 192.168.2.69
u71 192.168.2.71

额外准备:

虚拟机要多一张网卡,为 Macvlan 网络提供接口。这里我的环境中两张网卡名称分别是 enp1s0 和 enp2s0

前置准备

检查 macvlan 模块是否加载:

1
lsmod | grep macvlan

如果没有加载,可以通过该命令加载:

1
modprobe macvlan

如果 enp2s0 网卡未启用,需要先启用:

1
ip link set dev enp2s0 up

某些环境下,需要开启网卡的混杂模式,我的是 Harvester 虚拟机,不开启也可以使用。

1
ip link set dev eth0 promisc on

创建 macvlan 网桥

默认情况下,macvlan 的一个内核级限制是宿主机无法通过同一个物理接口与 macvlan 容器直接通信。直接 ping 容器 IP 会失败,为了让本机和外部服务能够访问 macvlan 网络,可以在宿主机上创建一个 macvlan 虚拟接口作为“桥接”。

在 u69 节点上执行:

1
2
3
ip link add macvlan-br link enp2s0 type macvlan mode bridge
ip addr add 192.168.3.69/24 dev macvlan-br
ip link set macvlan-br up

在 u71 节点上执行:

1
2
3
ip link add macvlan-br link enp2s0 type macvlan mode bridge
ip addr add 192.168.3.71/24 dev macvlan-br
ip link set macvlan-br up

Docker macvlan 环境搭建

分别在 u69 和 u71 节点上,使用 Docker 命令创建 macvlan 的容器网络:

1
2
3
4
5
docker network create --scope global --driver macvlan \
-o parent=enp2s0 \
--subnet=192.168.3.0/24 \
--gateway=192.168.3.254 \
macvlan-net

参数说明:

  • scope:默认是 local,但是这种模式无法让 NeuVector 发现跨主机网络,需要设置为 global 或者 swarm。其中 swarm 与 macvlan 兼容性不好,创建容器时无法通过 --ip参数设置 IP,所以这里设置为 global。
  • deiver:默认是 bridge,这里需要设置为 macvlan。
  • o:设置驱动参数,这里 parent 设置 macvlan 设备对接的网卡名称。
  • subnet:子网范围。
  • gateway:网关地址。

验证网络连通性

在 u69 节点创建 macvlan 容器 m-11 和 m-12:

1
2
3
4
5
6
7
8
9
10
11
12
13
docker run -itd \
--network macvlan-net \
--ip 192.168.3.11 \
--label io.neuvector.service.name=m-11 \
--name m-11 \
zerchin/network

docker run -itd \
--network macvlan-net \
--ip 192.168.3.12 \
--label io.neuvector.service.name=m-12 \
--name m-12 \
zerchin/network

参数说明:

  • io.neuvector.service.name:默认 Docker 环境,NeuVector 会识别镜像名称作为 Group 名称,可以通过这个 label 实现自定义 Group 名称。

确认 IP 是否生效:

1
docker exec -it m-11 ip a

在 u71 节点创建 macvlan 容器 m-21 和 m-22:

1
2
3
4
5
6
7
8
9
10
11
12
13
docker run -itd \
--network macvlan-net \
--ip 192.168.3.21 \
--label io.neuvector.service.name=m-21 \
--name m-21 \
zerchin/network

docker run -itd \
--network macvlan-net \
--ip 192.168.3.22 \
--label io.neuvector.service.name=m-22 \
--name m-22 \
zerchin/network

确认 IP 是否生效:

1
docker exec -it m-21 ip a

在 u69 上测试是否与 u71 的 macvlan 容器网络连通:

1
docker exec -it m-11 ping 192.168.3.21

生成 NeuVector Internal 证书

从 5.4.2 版本开始,这些内置的证书不会自动生成,需要手动生成。

  1. 创建证书目录
1
cd && mkdir -p neuvector/cert && cd neuvector/cert
  1. 创建一个ca.cfg文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
C = US
ST = California
L = San Jose
O = NeuVector Inc.
OU = Neuvector
CN = Neuvector
[v3_req]
keyUsage = digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = Neuvector
  1. 生成ca.crt, tls.key, tls.crt
1
2
3
4
5
6
7
openssl genrsa -out ca.key 2048
openssl req -x509 -sha256 -new -nodes -key ca.key -days 3650 -out ca.crt
openssl genrsa -out tls.key 2048
openssl req -new -key tls.key -sha256 -out cert.csr -config ca.cfg
openssl req -in cert.csr -noout -text
openssl x509 -req -sha256 -in cert.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt -days 3650 -extensions 'v3_req' -extfile ca.cfg
openssl x509 -in tls.crt -text

搭建 NeuVector 容器安全平台

这里以单点架构为例,生产环境建议使用高可用架构。

在 Docker 环境中,controller 和 enforcer 不支持部署在一个节点上,NeuVector 提供了 allinone 镜像来提供一体化部署。

首先在 u69 节点上部署 NeuVector 的 allinone 容器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
docker run -d --name allinone \
--pid=host \
--privileged \
-e CLUSTER_JOIN_ADDR=192.168.2.69 \
-e NV_PLATFORM_INFO=platform=Docker \
-e CONTAINER_NET_TYPE=macvlan \
-p 18300:18300 \
-p 18301:18301 \
-p 18400:18400 \
-p 18401:18401 \
-p 18301:18301/udp \
-p 8443:8443 \
-p 10443:10443 \
-v /lib/modules:/lib/modules:ro \
-v /var/neuvector:/var/neuvector \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v /sys/fs/cgroup:/host/cgroup:ro \
-v /proc:/host/proc:ro \
-v /root/neuvector/cert/ca.crt:/etc/neuvector/certs/internal/ca.cert \
-v /root/neuvector/cert/tls.crt:/etc/neuvector/certs/internal/cert.pem \
-v /root/neuvector/cert/tls.key:/etc/neuvector/certs/internal/cert.key \
neuvector/allinone:5.6.1

部分参数说明:

  • CLUSTER_JOIN_ADDR:Controller IP 地址,如果有多个需要都写进去,用,隔开。
  • NV_PLATFORM_INFO: 设置本地识别 Docker 环境。
  • CONTAINER_NET_TYPE:识别 macvlan 网络设备。

u71 节点部署 enforcer 容器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
docker run -d --name enforcer \
--pid=host \
--privileged \
-e CLUSTER_JOIN_ADDR=192.168.2.69 \
-e NV_PLATFORM_INFO=platform=Docker \
-p 18301:18301 \
-p 18401:18401 \
-p 18301:18301/udp \
-v /lib/modules:/lib/modules:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v /sys/fs/cgroup:/host/cgroup:ro \
-v /proc:/host/proc:ro \
-v /root/neuvector/cert/ca.crt:/etc/neuvector/certs/internal/ca.cert \
-v /root/neuvector/cert/tls.crt:/etc/neuvector/certs/internal/cert.pem \
-v /root/neuvector/cert/tls.key:/etc/neuvector/certs/internal/cert.key \
harbor.zerchin.xyz/neuvector/enforcer:5.6.1

浏览器访问 https://192.168.2.69:8443 ,初始账号密码是 admin/admin

容器发现和网络学习

查看 Policy - Groups,可以看到已经自动识别到容器。

在容器里,例如从 m-11 分别去访问 m-12 m-21 m-22:

1
2
3
4
5
docker exec -it m-11 curl 192.168.3.12 -o /dev/null -s -v | grep HTTP

docker exec -it m-11 curl 192.168.3.21 -o /dev/null -s -v | grep HTTP

docker exec -it m-11 curl 192.168.3.22 -o /dev/null -s -v | grep HTTP

进入 UI 查看是否学习到网络规则:

自此已验证容器发现和网络规则学习都正常运行。

已知问题

  1. 根据 SetIPAddrScopeconnectToLocal 两部分代码的逻辑,如果 docker network 的 scope 是 local,则会将 IP 映射到 CLUSIPAddrScopeLocalhost,从而容器只能学习到同一个宿主机的其他容器的网络规则,无法学习到跨主机网络规则,需要将 scope 设置为 global 实现跨主机网络规则的学习。

  2. Docker 环境下默认通过镜像名称自动识别为 Group 名称,如果启动的容器无法正确识别,需要通过 io.neuvector.service.name label 设置 Group 名称。

  3. 如果发现无法创建 global scope 的 macvlan 网络,可以检查一下 Docker 版本是不是太低了,实测发现 Docker 20.10.24 版本无法创建 global scope 的 macvlan 网络,提示需要 swarm 环境。我验证的环境的 Docker 版本是 29.xx 版本,可以直接创建 global scope 的 macvlan 网络。

  4. 根据官方文档说明,从 v5.5.0 版本开始,Allinone 支持已被弃用。后面新版本可能需要采用 controller + enforcer 方式部署,或者考虑后续迁移至 Kubernetes 环境。