基于Multus与Kube-ipam实现Web和数据库分层网络安全访问

图片

在互联网飞速发展的今天,网络已经成为人们日常生活和企业生产经营所不可或缺的一部分。尤其是在新冠疫情的背景之下,疫情防控、远程教育、视频会议、在线订购等网络应用越来越广泛。网络给我们带来便利的同时,各种安全威胁也在十面埋伏。

[1] 网络分层治理概述

对于企业而言,需要建立好内外部的网络防御体系,防止因某个部分的侵入而导致整个系统的崩溃。基于网络系统之间逻辑关联性、物理位置、功能特性等划分好安全层次和网络区域,在网络中部署安全产品与策略时就可以做到有的放矢。

图片

通常我们采用层次分析法,按照不同的安全等级将网络划分成用户访问区域、Web应用区域、数据存储区域、管理控制区域等区域。不同安全层次等级之间由于存在较大安全级差,区域之间和区域内部均利用硬件或软件的安全系统进行隔离防御,以形成一个“垂直分层”+“水平分区”的网络安全治理模型。在Kubernetes集群网络中实现这种模型似乎有点麻烦,尤其是在有虚拟机和容器混合的场景。今天云君给大家演示一种基于multus-cni与kube-ipam来实现Kubernetes集群网络区域分层架构,将用户区域、Web网络安全区域、与数据库网络安全区域进行分层隔离,中间亦可以辅以防火墙/WAF等安全设备进行网络安全的管控,希望对你的企业网络安全治理有一定启发与帮助。

图片

在上图中的kubernetes集群中,每个Pod均具有2个虚拟网卡接口:eth0、net1。其中eth0接口作为外界用户访问web pod的网络接口,而net1接口是附加的容器网卡,用于web Pod到database Pod的内部网络通信。

关于multus-cni与Kube-ipam的简介:

  • Multus-cni支持同时添加多个网络接口到kubernetes环境中的Pod。这样的部署方式有利于安全人员把应用网络和数据库等多个网络区域进行相互隔离,有效控制容器集群网络架构。

  • Kube-ipam支持给kubernetes集群中的Pod固定IP地址。一些场景往往对IP地址有依赖,需要使用固定IP地址的Pod,可以使用kube-ipam轻松解决这类问题。例如,mysql主从架构的时候,主database与从database之间的同步;例如集群HA的时候,两个节点之间检测通信等;例如某些安全防护设备,需要基于IP地址进行网络安全访问策略限制的场景等。

[2]  Multus与Kube-ipam的部署

2.1 安装基础CNI插件

基础CNI插件是kubernetes集群内容器基本通信所需的,这里你可以根据自己选所设计网络环境来自由选择。例如在本次体验的网络拓扑中,我们web区域采用了flannel网络、database区域采用了macvlan网络,那么我们将给kubernetes集群同时安装上flannel CNI插件和macvlan CNI插件。

安装macvlan CNI插件:

macvlan CNI插件是在containernetworking中,所以我们可以直接将containernetworking/plugins的二进制文件下载下来,放到/opt/cni/bin目录即可。

# wget https://github.com/containernetworking/plugins/releases/download/v0.9.1/cni-plugins-linux-amd64-v0.9.1.tgz# tar-zxvf cni-plugins-linux-amd64-v0.9.1.tgz -C /opt/cni/bin/

安装flannel CNI插件:

可以使用daemonset来部署flanneld,也可以使用二进制方式部署。下面简单演示二进制方式部署的方法:

创建flanneld所需的subnet网段

# etcdctl --endpoints=https://192.168.1.11:2379,https://192.168.1.12:2379,https://192.168.1.13:2379 --ca-file=/etc/kubernetes/ssl/k8s-root-ca.pem --cert-file=/etc/kubernetes/ssl/kubernetes.pem --key-file=/etc/kubernetes/ssl/kubernetes-key.pem set /kubernetes/network/config '{"Network":"10.244.0.0/16","SubnetLen":24,"Backend":{"Type":"vxlan"}}'

下载flanneld软件包,并放到/opt/cni/bin目录

# wget https://github.com/flannel-io/flannel/releases/download/v0.11.0/flannel-v0.11.0-linux-amd64.tar.gz# tar -zxvf flannel-v0.11.0-linux-amd64.tar.gz -C /opt/cni/bin/

编辑/etc/systemd/system/flanneld.service

# cat /etc/systemd/system/flanneld.service[Unit]Description=Flanneldoverlay address etcd agentAfter=network.targetAfter=network-online.targetWants=network-online.targetAfter=etcd.serviceBefore=docker.service [Service]Type=notifyExecStart=/opt/cni/bin/flanneld\ -etcd-cafile=/etc/kubernetes/ssl/k8s-root-ca.pem\ -etcd-certfile=/etc/kubernetes/ssl/kubernetes.pem\ -etcd-keyfile=/etc/kubernetes/ssl/kubernetes-key.pem\ -etcd-endpoints=https://192.168.1.11:2379,https://192.168.1.12:2379,https://192.168.1.13:2379\ -etcd-prefix=/kubernetes/network \ -ip-masqExecStartPost=/usr/local/bin/mk-docker-opts.sh-k DOCKER_NETWORK_OPTIONS -d /run/flannel/dockerRestart=alwaysRestartSec=5StartLimitInterval=0 [Install]WantedBy=multi-user.targetRequiredBy=docker.service

修改/etc/systemd/system/docker.service,注意增加EnvironmentFile,并修改ExecStart参数:​​​​​​​

...[Service]EnvironmentFile=/run/flannel/dockerExecStart=/usr/bin/dockerd$DOCKER_NETWORK_OPTIONS...

说明:如果你是使用kube-install工具一键安装的kubernetes集群,macvlan CNI插件和flannel CNI插件均会被自动部署好,那么上面这两步操作就可以省略。

2.2 Kube-ipam的安装部署

你可以通过下载或编译获得kube-ipam的二进制文件,然后将kube-ipam的二进制文件拷贝到kubernetes node主机的/opt/cni/bin/ 目录中。​​​​​​​

# wget https://github.com/cloudnativer/kube-ipam/releases/download/v0.2.0/kube-ipam-v0.2.0-x86.tgz# tar -zxvf kube-ipam-v0.2.0-x86.tgz# mv kube-ipam-v0.2.0-x86/kube-ipam /opt/cni/bin/kube-ipam

2.3 Multus-cni的安装部署

下载multus-cni包:

# wget https://github.com/k8snetworkplumbingwg/multus-cni/releases/download/v3.8/multus-cni_3.8_linux_amd64.tar.gz

把解压出来的二进制文件拷贝到所有Kubernetes的worker节点的/opt/cni/bin目录​​​​​​​

# tar -zxvf multus-cni_3.8_linux_amd64.tar.gz# mv multus-cni_3.8_linux_amd64/multus-cni /opt/cni/bin/

[3] CNI配置的清理与生效

3.1 清理旧CNI配置

为了确保主机环境的干净,请先执行如下命令删除kubernetes node主机上的已有的旧CNI配置:

# rm -rf /etc/cni/net.d/*

3.2 重建CNI配置

然后重新创建/etc/cni/net.d/10-multus.conf,编辑内容如下:​​​​​​​​​​​​​​

# cat /etc/cni/net.d/10-multus.conf    {      "cniVersion":"0.3.1",      "name":"multus-demo",      "type": "multus-cni",      "delegates": [        {          "name":"k8snet1",          "type":"flannel",          "masterplugin": true,          "delegate": {             "isDefaultGateway":true,             "bridge":"docker0",             "mtu": 1400          }        },        {          "name":"k8snet2",          "type":"macvlan",          "master": "eth0",          "ipam": {                "name": "kube-subnet",                "type":"kube-ipam",                "kubeConfig":"/etc/kubernetes/ssl/kube.kubeconfig",                "etcdConfig": {                        "etcdURL":"https://192.168.1.11:2379,https://192.168.1.12:2379,https://192.168.1.13:2379",                       "etcdCertFile": "/etc/kubernetes/ssl/kubernetes.pem",                       "etcdKeyFile":"/etc/kubernetes/ssl/kubernetes-key.pem",                       "etcdTrustedCAFileFile":"/etc/kubernetes/ssl/k8s-root-ca.pem"                },                "subnet":"10.188.0.0/16",                "rangeStart":"10.188.0.10",                "rangeEnd":"10.188.0.200",                "gateway":"10.188.0.1",                "routes": [{                        "dst":"0.0.0.0/0"                }],                "resolvConf":"/etc/resolv.conf"          }        }      ]    }

multus使用"delegates"的概念将多个CNI插件组合起来,并且指定一个masterplugin来作为POD的主网络并且被Kubernetes所感知。

3.3 重启kubelet服务

编辑好CNI配置之后,重启kubelet服务:

#systemctl restart kubelet

[4] 创建各层Pod和service

为kubernetes集群安装和配置好CNI插件之后,接下来就可以开始创建Pod容器了。

4.1 创建Database Pod

首先,我们来创建存储数据的MySQL Pod,由于它是有状态的,所以我们会挂载持久卷以及固定容器的IP地址。​​​​​​​

# cat db.yamlapiVersion:apps/v1kind:StatefulSetmetadata:  name: database-1spec:  selector:    matchLabels:      app: database-1  serviceName: database-1  template:    metadata:      labels:        app: database-1      annotations:        kube-ipam.ip: "10.188.0.218"        kube-ipam.netmask: "255.255.0.0"        kube-ipam.gateway:"10.188.0.1"    spec:      terminationGracePeriodSeconds: 10      containers:        name: database1      - image: mysql:5.6        env:        - name: MYSQL_ROOT_PASSWORD          value: password        ports:        - containerPort: 3306          name: mysql        volumeMounts:      - mountPath: "/var/lib/mysql"        name: mystorage    volumes:    - name: mystorage       nfs:        path: /storage/db001        server: 172.17.0.180
--- apiVersion:apps/v1kind:StatefulSetmetadata:  name: database-2spec:  selector:    matchLabels:      app: database-2  serviceName: database-2  template:    metadata:      labels:        app: database-2      annotations:        kube-ipam.ip: "10.188.0.219"        kube-ipam.netmask: "255.255.0.0"        kube-ipam.gateway:"10.188.0.1"    spec:      terminationGracePeriodSeconds: 10      containers:        name: database2      - image: mysql:5.6        env:        - name: MYSQL_ROOT_PASSWORD          value: password        ports:        - containerPort: 3306          name: mysql        volumeMounts:      - mountPath: "/var/lib/mysql"        name: mystorage    volumes:    - name: mystorage       nfs:        path: /storage/db002        server: 172.17.0.180 

使用kubectl apply命令创建:​​​​​​​

## kubectl apply -f db.yamlstatefulset.apps/databasecreated## kubectl get pod              NAME                   READY   STATUS   RESTARTS   AGE     NODE          database-1-0           1/1     Running  0          2m5s    192.168.1.13database-2-0           1/1     Running  0          2m5s    192.168.1.12#

此时,Database区域网络的两个database数据库就创建好了。登录database-1-0容器,你会看见net1网卡的IP地址为10.188.0.218:​​​​​​​

## kubectl exec -it database-1-0 -- ip address3:eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1400 qdiscnoqueue state UP    link/ether fa:0c:af:f7:ca:4a brdff:ff:ff:ff:ff:ff    inet 10.244.12.5/24 brd 10.244.5.255 scopeglobal eth0       valid_lft forever preferred_lft forever4:net1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuestate UP    link/ether 72:17:7d:5d:fd:fa brdff:ff:ff:ff:ff:ff    inet 10.188.0.218/16 brd 10.188.255.255scope global net1       valid_lft forever preferred_lft forever#

登录database-2-0容器,你会看见net1网卡的IP地址为10.188.0.219:​​​​​​​

## kubectl exec -it database-2-0 -- ip address3:eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1400 qdiscnoqueue state UP    link/ether fa:0c:af:f7:ca:4d brdff:ff:ff:ff:ff:ff    inet 10.244.5.6/24 brd 10.244.5.255 scopeglobal eth0       valid_lft forever preferred_lft forever4:net1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuestate UP    link/ether 72:17:7d:5d:fd:fc brdff:ff:ff:ff:ff:ff    inet 10.188.0.219/16 brd 10.188.255.255scope global net1       valid_lft forever preferred_lft forever#

这两个Pod的IP地址是固定不变的,两个Pod在删除、漂移、重启之后,重新起来的Pod依然保持原有的IP地址固定不变。例如我们把database-2-0这个数据库Pod销毁掉:​​​​​​​

## kubectl delete pod database-2-0pod"database-2-0" deleted## kubectl get pod  NAME                   READY   STATUS              RESTARTS   AGE    NODE            database-1-0           1/1     Running             0          20m    192.168.1.13  database-2-0           0/1     ContainerCreating   0          1s     192.168.1.12  #

此时kubernetes会重建一个IP地址为10.244.0.219,名称为database-2-0的Pod:​​​​​​​

## kubectl get podNAME                   READY   STATUS   RESTARTS   AGE     NODE            database-1-0           1/1     Running  0          20m     192.168.1.13  database-2-0           1/1     Running  0          4s      192.168.1.14  #

查看重新启动Pod的IP地址,我们发现net1网卡的IP地址依然为10.188.0.219:​​​​​​​

## kubectl exec -it database-2-0 -- ip address3:eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1400 qdiscnoqueue state UP    link/ether fa:0c:af:f7:ca:4e brdff:ff:ff:ff:ff:ff    inet 10.244.5.8/24 brd 10.244.5.255 scopeglobal eth0       valid_lft forever preferred_lft forever4:net1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuestate UP    link/ether 72:17:7d:5d:fd:fc brdff:ff:ff:ff:ff:ff    inet 10.188.0.219/16 brd 10.188.255.255scope global net1       valid_lft forever preferred_lft forever#

两个数据库Pod可以互相通过固定IP地址进行集群的通信操作,比如HA切换、主从同步(由于与虚机效果无差异,所以这里就不贴出具体过程了)等。

4.2 创建Web Pod

有了数据库基建,接下来我们来创建上层的Web应用容器,Web应用不需要固定IP地址,而且是可以任意弹性伸缩和方便服务暴露到外网的。​​​​​​​

# catweb.yamlapiVersion:apps/v1kind:Deploymentmetadata:  labels:    app:web  name: webspec:  replicas: 2  selector:    matchLabels:      app: web  strategy: {}  template:    metadata:      labels:        app: web    spec:      containers:      - image: nginx:1.7.9        imagePullPolicy: IfNotPresent        name: web-c        ports:          name: web        - containerPort: 80

使用kubectl apply命令创建:​​​​​​​

# kubectl apply -f web.yaml              deployment.apps/webcreated### kubectl get pod -o wide               NAME                   READY   STATUS   RESTARTS   AGE     NODE          database-1-0           1/1     Running  0          2m5s    192.168.1.13database-2-0           1/1     Running  0          2m5s    192.168.1.14web-5fd8684df7-8c7zb   1/1     Running   0         3h17m   192.168.1.12web-5fd8684df7-p9g8s   1/1     Running   0         3h17m   192.168.1.14#

4.3 创建service或ingress

为了让用户可以访问到Web网络区域中的Nginx Web服务,我们可以使用ingress或nodeport service来暴露服务,或者也可以通过路由打通web区域到交换机的网络等方式。下面贴出前面两种常见方式的代码:

例如,给Web Pod配置nodeport的service:​​​​​​​

# cat web-svc.yamlapiVersion:v1kind:Servicemetadata:  name: web-svc  labels:    app: webspec:  type: NodePort  selector:    app: web  ports:  - port: 80    protocol:TCP 

使用kubectl apply命令创建:​​​​​​​

## kubectl apply -f web-svc.yamlservice/web-svccreated### kubectl get serviceNAME         TYPE        CLUSTER-IP      EXTERNAL-IP  PORT(S)      AGEkubernetes   ClusterIP   10.254.0.1      <none>       443/TCP       7d22hweb-svc      ClusterIP   10.254.150.15   <none>       80:18370/TCP  6m4s#

例如,配置ingress到Web网络区域的Web service:​​​​​​​

## cat web-ingress.yamlapiVersion:networking.k8s.io/v1kind:Ingressmetadata:  name: web-ingressspec:  defaultBackend:    service:      name: web-svc      port:        number: 80 

使用kubectl apply命令创建:​​​​​​​

#[root@localhost~]# kubectl apply -f web-ingress.yamlingress.networking.k8s.io/web-ingresscreated#

[5] 验证网络隔离与访问

经过上面的配置之后,用户就可以通过ingress、nodeport service或路由等方式来访问web服务;同时web pod也可以通过Database区域网络,调用有状态的database数据库。外面的用户是不能直接访问Database区域中的数据库的,此时还可以布设区域安全设备对其网络成分进行监测与防护。

图片

另外,Database区域网络的database Pod也可以互相通过固定IP地址进行集群的通信操作等,database Pod类似于一个轻量级的虚拟机效果。采用这种结构,尤其是在虚拟机和容器混合的网络中,安全防护设备(不论新老产品)均可以直接识别网络五元组并进行防护控制。

5.1 用户通过service访问Web

通过ingress或service来访问到web服务,效果如下:

图片

如果你没有浏览器环境,也可以使用curl在命令行进行测试:​​​​​​​

## curl http://192.168.1.12:18370<!DOCTYPEhtml><html><head><title>Welcometo nginx!</title><style>    body {        width: 35em;        margin: 0 auto;        font-family: Tahoma, Verdana, Arial,sans-serif;    }</style></head><body><h1>Welcometo nginx!</h1><p>Ifyou see this page, the nginx web server is successfully installed andworking.Further configuration is required.</p> <p>Foronline documentation and support please refer to<ahref="http://nginx.org/">nginx.org</a>.<br/>Commercialsupport is available at<ahref="http://nginx.com/">nginx.com</a>.</p> <p><em>Thankyou for using nginx.</em></p></body></html>#

5.2 Web Pod通过固定IP访问Database

查看database-2 Pod的net1网卡,10.188.0.219为固定IP地址​​​​​​​

## kubectl exec -it database-2-0 -- ip address        3:eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1400 qdisc noqueuestate UP    link/ether 26:16:04:dc:82:fe brdff:ff:ff:ff:ff:ff    inet 10.244.69.7/24 brd 10.244.69.255 scopeglobal eth0       valid_lft forever preferred_lft forever4:net1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuestate UP    link/ether c6:17:6c:da:ee:e9 brdff:ff:ff:ff:ff:ff    inet 10.188.0.219/16 brd 10.188.255.255scope global net1       valid_lft forever preferred_lft forever#

Web Pod可以通过database区域网络,访问固定IP地址的database服务​​​​​​​

## kubectl exec -it web-5fd8684df7-8c7zb -- ping 10.188.0.219PING10.188.0.219 (10.188.0.219): 48 data bytes56 bytesfrom 10.188.0.219: icmp_seq=0 ttl=64 time=0.720 ms56 bytesfrom 10.188.0.219: icmp_seq=1 ttl=64 time=0.341 ms56 bytesfrom 10.188.0.219: icmp_seq=2 ttl=64 time=0.485 ms56 bytesfrom 10.188.0.219: icmp_seq=3 ttl=64 time=0.389 ms56 bytesfrom 10.188.0.219: icmp_seq=4 ttl=64 time=0.454 ms^C---10.188.0.219 ping statistics ---5 packetstransmitted, 5 packets received, 0% packet lossround-tripmin/avg/max/stddev = 0.341/0.478/0.720/0.131 ms

5.3 database区域的Pod通过固定IP互访

database区域网络内的database-1与database-2都拥有固定IP地址,两个数据库Pod之间可以互相通过固定IP进行集群的通信操作。例如,mysql主从架构的时候,主database-1与从database-2之间的同步等。​​​​​​​

## kubectl exec -it database-1-0 -- ip address         3:eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1400 qdisc noqueuestate UP    link/ether 2a:64:f3:b8:18:01 brdff:ff:ff:ff:ff:ff    inet 10.244.5.5/24 brd 10.244.5.255 scopeglobal eth0       valid_lft forever preferred_lft forever4:net1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuestate UP    link/ether fe:9d:18:25:9c:a1 brdff:ff:ff:ff:ff:ff    inet 10.188.0.218/16 brd 10.188.255.255scope global net1       valid_lft forever preferred_lft forever

使用database-2 Pod可以正常访问database-1 Pod网络:​​​​​​​

## kubectl exec -it database-2-0 -- /bin/bashroot@database-2-0:/#root@database-2-0:/#ip address 3:eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1400 qdiscnoqueue state UP    link/ether ba:32:1c:33:dc:a6 brdff:ff:ff:ff:ff:ff    inet 10.244.69.7/24 brd 10.244.69.255 scopeglobal eth0       valid_lft forever preferred_lft forever4:net1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueuestate UP    link/ether 86:3b:09:c8:31:93 brdff:ff:ff:ff:ff:ff    inet 10.188.0.219/16 brd 10.188.255.255scope global net1       valid_lft forever preferred_lft foreverroot@database-2-0:/#root@database-2-0:/#root@database-2-0:/#ping 10.188.0.218PING10.188.0.218 (10.188.0.218): 48 data bytes56 bytesfrom 10.188.0.218: icmp_seq=0 ttl=64 time=0.335 ms56 bytesfrom 10.188.0.218: icmp_seq=1 ttl=64 time=0.246 ms56 bytesfrom 10.188.0.218: icmp_seq=2 ttl=64 time=0.484 ms56 bytesfrom 10.188.0.218: icmp_seq=3 ttl=64 time=0.371 ms^C---10.188.0.218 ping statistics ---4 packetstransmitted, 4 packets received, 0% packet lossround-tripmin/avg/max/stddev = 0.246/0.359/0.484/0.085 ms

[6] 结束语

本文提供了网络垂直分层与水平分区治理的思路,以及一种在Kubernetes集群网络中落地实操的例子,利用固定IP地址特性来兼容新老网络安全设备产品。也许你认为云原生网络中还有其他更好的方式方法来实现类似效果,但并不是所有企业的网络环境和文化都能将你的梦想落地。在某些场景下,本文所述的方法也许是折衷的选择。

参考文档:

  • https://github.com/k8snetworkplumbingwg/multus-cni

  • https://github.com/cloudnativer/kube-ipam

  • https://github.com/k8snetworkplumbingwg/multus-cni/releases

  • https://github.com/cloudnativer/kube-install

  • https://cloudnativer.github.io/kube-ipam.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值