CIS安全标准
互联网安全中心(CIS,Center for Internet Security),是一个非盈利组织,致力互联网提供免费的安全防御解决方案。
官网:https://www.cisecurity.org/
Kubernetes CIS基准:https://www.cisecurity.org/benchmark/kubernetes/
CIS安全机制Kube-bench
Kube-bench是容器安全厂商Aquq推出的工具,以CIS K8s基准作为基础,来检查K8s是否安全部署。主要查找不安全的配置参数、敏感的文件权限、不安全的帐户或公开端口等等。CIS基准测试工具:kube-bench部署
1、下载二进制包
https://github.com/aquasecurity/kube-bench/releases
2、解压使用
tar zxvf kube-bench_0.6.10_linux_amd64.tar.gz
mkdir /etc/kube-bench # 创建默认配置文件路径
mv cfg /etc/kube-bench/cfg
mv kube-bench /usr/local/bin/
cd /etc/kube-bench/cfg && ls
kube-bench命令:
使用kube-bench run进行测试,该指令有以下常用参数:
常用参数:
-s, --targets 指定要基础测试的目标,这个目标需要匹配cfg/中的文件名称,已有目标:master, controlplane, node, etcd, policies
--version:指定k8s版本,如果未指定会自动检测
--benchmark:手动指定CIS基准版本,不能与--version一起使用
检查master组件安全配置
kube-bench run -s master
执行后会逐个检查安全配置并输出修复方案及汇总信息输出:
方案使用方式:
kube-bench run -s master
给出问题及解决方案:
修改方式:
vi /etc/kubernetes/manifests/kube-apiserver.yaml
systemctl restart kubelet.service
kube-bench run -s master
执行命令后1.2.20 这个报错成功修复
配置文件修改:
cat /etc/kube-bench/cfg/cis-1.6/master.yaml
修改配置文件参数的场景:
如果apiserver组件某个参数已经弃用(假如apiserver的--profiling参数被弃用),则可以使用参数skip
这里假如这一条1.2.20已弃用不需要重视,不需要显示FAIL:
vi /etc/kube-bench/cfg/cis-1.6/master.yaml
kube-bench run -s master
网络访问控制
网络策略应用场景
默认情况下,Kubernetes 集群网络没任何网络限制,Pod 可以与任何其他 Pod 通信,在某些场景下就需要进行网络控制,减少网络攻击面,提高安全性,这就会用到网络策略。
网络策略(Network Policy):是一个K8s资源,用于限制Pod出入流量,提供Pod级别和Namespace级别网络访问控制。
网络策略的应用场景:
1、应用程序间的访问控制,例如项目A不能访问项目B的Pod
2、开发环境命名空间不能访问测试环境命名空间Pod
3、当Pod暴露到外部时,需要做Pod白名单
4、多租户网络环境隔离
Network Policy实现工作流程
Network Policy的主要功能是对Pod间的网络通信进行限制和准入控制,设置方式为将Pod的Label作为查询条件,设置允许访问或禁止访问的客户端Pod列表。目前查询条件可以作用于Pod和Namespace级别。
为了使用Network Policy,Kubernetes引入了一个新的资源对象NetworkPolicy,供用户设置Pod间网络访问的策略。但仅定义一个网络策略是无法完成实际的网络隔离的,还需要一个策略控制器(Policy Controller)进行策略的实现。策略控制器由第三方网络组件提供,目前Calico、Cilium、Kube-router、Romana、Weave Net等开源项目均支持网络策略的实现。
Network Policy的工作原理如图所示,policy controller需要实现一个API Listener,监听用户设置的NetworkPolicy定义,并将网络访问规则通过各Node的Agent进行实际设置(Agent则需要通过CNI网络插件实现)
网络策略概述
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
role: db
policyTypes:
- Ingress
- Egress
ingress:
- from:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 6379
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978
选项:
和其它资源一样,NetworkPolicy也需要apiVersion、kind和metadata字段,配置方法和其它资源类似,对于NetworkPolicy的策略配置,从Spec开始,下面对每个字段进行讲解:
- podSelector:表示该策略对哪些Pod生效。示例中配置的是role=db,代表该策略应用于该NetworkPolicy所在的Namespace(NetworkPolicy具有Namespace隔离性)下符合role=db的Pod上。如果该选项为空,则对该Namespace下所有的Pod生效。
- policyTypes:网络策略的类型。目前网络策略支持入站(Ingress)和出站(Egress)的规则配置,出站代表符合podSelector规则的Pod的出口流量限制,入站是其它Pod对符合podSelector规则的Pod的访问流量限制。如果未指定policyTypes,则默认为Ingress,如果未指定policyTypes,且有出口规则的话,也会配置Egress。
- ingress:如policyTypes所述,Ingress是对入口流量的管控。每个NetworkPolicy可以包含一个Ingress白名单列表,每个规则都允许匹配from和ports部分的流量。其中from表示访问来源,可以配置为ipBlock、namespaceSelector、podSelector,如果此项为空则表示匹配所有来源。另外一个ports表示可被访问的端口列表,如果此项为空则表示匹配所有端口。
- egress:如policyTypes所述,Egress是对出口流量的管控。同样,NetworkPolicy可以包含一个Egress白名单列表,不同的是Egress的可配置参数为to和ports。其中ports表示可以访问的目标端口,to表示可以访问的目的,也可以配置为ipBlock、namespaceSelector、podSelector。
- namespaceSelector:匹配具有指定标签的Namespace下的所有Pod。当前示例的ingress的from参数配置了namespaceSelector,表示匹配具有project=myproject标签的Namespace下的所有Pod。
- ipBlock:表示匹配的IP块,可以是单个IP,也可以是网段,同时也支持IPV6格式。Except为可选参数,表示从IPBlock排除一些地址。
根据上述的概念,就可以知道上面示例的含义如下:
- 隔离default命名空间下具有role=db标签的Pod;
- 允许IP地址范围为172.17.0.0–172.17.0.255 和 172.17.2.0–172.17.255.255访问default命名空间下具有role=db的Pod的6379且协议为TCP的端口;
- 允许具有project=myproject的Namespace下的所有Pod访问default命名空间下具有role=db的Pod的6379且协议为TCP的端口;
- 允许default命名空间(和NetworkPolicy同一命名空间)下具有role=frontend标签的Pod访问default命名空间下具有role=db的Pod的6379且协议为TCP的端口;
- 允许带有role=db标签的Pod可以访问10.0.0.0/24网段下的5978且协议为TCP的端口
网络策略注意事项
在配置网络策略时,有很多细节需要注意,比如上述的示例中,一段关于ingress的from配置:
- from:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
需要注意的是在ipBlock、namespaceSelector和podSelector前面都有一个“-”,如果前面没有这个横杠将是另外一个完全不同的概念。可以看一下下面的示例:
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
podSelector:
matchLabels:
role: client
此时的namespaceSelector有“-”,podSelector没有“-”,那此时的配置,代表的含义是允许具有user=alice标签的Namespace下,并且具有role=client标签的所有Pod访问,namespaceSelector和podSelector是且的关系。那我们继续看一下示例:
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
- podSelector:
matchLabels:
role: client
此时的namespaceSelector和podSelector都有“-”,配置的含义是允许具有user=alice标签的Namespace下的所有Pod和当前Namespace下具有role=client标签的Pod访问,namespaceSelector和podSelector是或的关系。
除了上述的差别外,在配置ipBlock时,可能也会出现差异性。因为我们在接收或者发送流量时,很有可能伴随着数据包中源IP和目标IP的重写,也就是SNAT和DNAT,此时会造成流量的目标IP和源IP与配置的ipBlock出现了差异性,造成网络策略不生效,所以在配置IPBlock时,需要确认网络交换中是否存在源目地址转换,并且IPBlock最好不要配置Pod的IP,因为Pod发生重建时,它的IP地址一般就会发生变更,所以IPBlock一般用于配置集群的外部IP。
网络访问控制5个案例
案例1:拒绝命名空间下所有Pod出入站流量
创建一个test命名空间
kubectl create ns test
在test命名空间中创建web1、web2,default命名空间中创建web3,镜像使用busybox
kubectl run web1 --image=busybox -n test -- sleep 12h
kubectl run web2 --image=busybox -n test -- sleep 12h
kubectl run web3 --image=busybox -- sleep 12h
此时进入命名空间测试下ping
记住web1的pod IP,用外部测试
测试外部访问
kubectl exec web1 -n test -- ping www.baidu.com
测试外部pod访问
kubectl exec web3 -n default -- ping 10.244.196.162
测试内部pod之间访问
kubectl exec web2 -n test -- ping 10.244.196.162
创建网络策略
deny-all.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: test
spec:
podSelector: {} # 匹配本命名空间所有pod
policyTypes:
- Ingress
- Egress
# ingress和egress没有指定规则,则不允许任何流量进出pod
kubectl apply -f deny-all.yaml
测试:
案例2:test命名空间下所有pod可以互相访问,也可以访问其他命名空间Pod,但其他命名空间不能访问test命名空间Pod。
kubectl delete -f deny-all.yaml
allow-internal.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-internal
namespace: test
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {}
测试外部Namespace pod访问
测试内部pod之间访问
案例3:允许其他命名空间Pod访问test命名空间标签为run=web1应用,其他标签不能访问
kubectl delete networkpolicy allow-internal -n test
kubectl get pod -n test --show-labels
allow-all-namespaces.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-namespaces
namespace: test
spec:
podSelector:
matchLabels:
run: web1
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector: {} # 匹配所有命名空间的pod
apply一下
测试外部web3 pod访问web1
kubectl exec web3 -n default -- ping 10.244.196.162
测试外部web3 pod访问web2
kubectl exec web3 -n default -- ping 10.244.196.163
为什么都能访问呢?因为k8s默认策略都是允许通过,匹配规则是从前到后的匹配顺序,此时配合案例一策略进行限制即可
kubectl apply -f deny-all.yaml
测试:
案例4:同一个命名空间下应用之间限制访问,将test命名空间中标签为run=web1的pod隔离,只允许标签为run=web4的pod访问80端口
将web1的容器镜像修改为nginx
kubectl edit pod web1 -n test
此时在test命名空间中创建一个web4,在default命名空间创建一个web5的pod进行访问测试
kubectl run web4 --image=busybox -n test -- sleep 12h
kubectl run web5 --image=busybox -n default -- sleep 12h
app-to-app.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: app2app
namespace: test
spec:
podSelector:
matchLabels:
run: web1
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
run: web4
ports:
- protocol: TCP
port: 80
测试内部pod web4访问web1
kubectl exec -it web4 -n test -- sh
wget 10.244.196.165
测试内部pod web2访问web1
kubectl exec -it web2 -n test -- sh
wget 10.244.196.165
修改app-to-app.yaml
修改网络安全规则
kubectl apply -f app-to-app.yaml
测试外部pod web5访问web1
kubectl exec -it web5 -n default -- sh
wget 10.244.196.165
由此见得,podSelector入规则只在test命名空间中生效
案例5:只允许指定命名空间中的应用访问
需求:限制dev命名空间标签为env=dev的pod,只允许prod命名空间中的pod访问和其他所有命名空间,app=client1标签pod访问
创建dev和prod命名空间
kubectl create ns dev && kubectl create ns prod && kubectl create ns other
kubectl label ns prod env=prod
在dev空间中创建一个pod,label为env=dev
kubectl run web --image=nginx --labels="env=dev" -n dev -- sleep 12h
kubectl get pods -n dev --show-labels
在prod命名空间中创建一个pod
kubectl run web-test1 --image=busybox -n prod -- sleep 12h
在other命名空间中创建一个pod作为其他命名空间
kubectl run web-test2 --image=busybox -n other -- sleep 12h
kubectl label pod web-test2 app=client1 -n other
kubectl run web-test3 --image=busybox -n other -- sleep 12h
vi dev-web.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: dev-web
namespace: dev
spec:
podSelector:
matchLabels:
env: dev
policyTypes:
- Ingress
ingress:
# 满足允许prod命名空间中的pod访问
- from:
- namespaceSelector:
matchLabels:
env: prod
# 允许pod标签为app=client1的pod访问,所有命名空间
- from:
- namespaceSelector: {}
podSelector:
matchLabels:
app: client1
kubectl apply -f dev-web.yaml
测试:
看一下IP地址
这里为10.244.196.162
进入prod命名空间的pod测试
测试其他命名空间other下标签为app=client1的pod可以访问,没有此标签的不能访问
kubectl exec -it web-test2 -n other -- sh
ping 10.244.196.162
kubectl exec -it web-test3 -n other -- sh
ping 10.244.196.162
生产案例:隔离中间件服务,服务发布限制于Ingress
- 有一个项目,它有自己数据库MySQL和缓存Redis中间件,我们只希望这个项目的应用能够访问该中间件
- 假如有一个项目需要通过Ingress进行对外发布,我们想要除了Ingress外,其他任何Namespace下的Pod都不能访问该项目。
假设有一个项目叫nw-demo,里面部署了三个微服务,分别是MySQL、Redis和Nginx。现需要对MySQL、Redis、Nginx进行隔离,分别实现如下效果:
- MySQL、Redis只能被该Namespace下的Pod访问;
- Nginx可以被Ingress-nginx命名空间下的Pod和该Namespace下的Pod访问;
隔离中间件服务
# kubectl create ns nw-demo
创建MySQL服务,MySQL以容器启动时,必须配置root的密码,或者设置密码为空,所以需要设置一个MYSQL_ROOT_PASSWORD的变量:
# kubectl create deploy mysql --image=registry.cn-beijing.aliyuncs.com/dotbalo/mysql:5.7.23 -n nw-demo
# 设置密码
# kubectl set env deploy/mysql MYSQL_ROOT_PASSWORD=mysql -n nw-demo
创建Redis服务:
# kubectl create deploy redis --image=registry.cn-beijing.aliyuncs.com/dotbalo/redis:5.0.9-alpine3.11 -n nw-demo
确认容器是否启动:
# kubectl get po -n nw-demo -owide
在没有配置任何网络策略时,测试下网络的连通性,可以在任意Kubernetes节点上执行telnet命令:
telnet 172.25.244.196 3306
telnet 172.27.14.196 6379
可以看到此时的网络都是可以通信的。接下来可以根据Pod的标签进行网络隔离,首先查看一下Pod的标签:
kubectl get po -n nw-demo --show-labels
然后根据标签配置网络策略,本示例的配置将MySQL和Redis进行了拆分,配置了两个网络策略,当然也可以给两个Pod配置一个相同的标签,这样就可以使用同一个网络策略进行限制。但是在生产环境中,并不推荐使用同一个网络策略,因为有时候需要更细粒度的策略,同一个网络策略可能会有局限性,也会导致配置失败,所以本示例采用分开的网络策略进行配置
mysql-redis-nw.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mysql-np
namespace: nw-demo
spec:
podSelector:
matchLabels:
app: mysql
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
access-nw-mysql-redis: "true"
ports:
- protocol: TCP
port: 3306
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: redis-np
namespace: nw-demo
spec:
podSelector:
matchLabels:
app: redis
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
access-nw-mysql-redis: "true"
ports:
- protocol: TCP
port: 6379
kubectl create -f mysql-redis-nw.yaml
kubectl get networkpolicy -n nw-demo
该yaml含有两个NetworkPolicy,其中mysql-np是对具有app=mysql标签的Pod进行管理,redis-np是对具有app=redis标签的Pod进行管理。但是需要注意的是该网络策略的ingress from是以namespaceSelector的标签进行匹配的,并非podSelector,或者是两者的结合。因为在生产环境中,同一个Namespace下可能会有很多不同类型、不同标签的Pod,并且它们可能并不具有一个相同的标签,所以如果通过podSelector进行选择,可能会比较麻烦,因为Pod一旦创建,对其标签的修改是很不方便的(apps/v1一旦创建就不可修改)。而使用namespaceSelector另一个好处是,可以很方便的对某个Namespace下的Pod进行管控,直接给指定Namespace添加标签即可,当然,如果需要更细粒度的管控,也可以结合podSelector使用。
创建后宿主机和任何Pod都已不能访问该Namespace下的MySQL和Redis:
# telnet 172.25.244.196 3306
Trying 172.25.92.93...
^C
# telnet 172.27.14.196 6379
Trying 172.17.125.28...
^C
在nw-demo命名空间下创建一个用于测试连通性的工具,然后进行测试,也是不能访问该服务的:
kubectl run -ti debug-tools --image=registry.cn-beijing.aliyuncs.com/dotbalo/debug-tools:latest -n nw-demo
curl 172.25.244.196:3306 --connect-timeout 3
由于之前的from配置的是namespaceSelector,所以如果想要某一个Namespace下的Pod能够访问,直接给该Namespace添加一个NetworkPolicy中配置的标签即可,比如允许nw-demo命名空间下的所以Pod访问该NetworkPolicy隔离的服务:
kubectl label ns nw-demo access-nw-mysql-redis=true
使用nw-demo命名空间下的debug-tools再次测试:
kubectl exec -ti debug-tools -n nw-demo -- curl 172.25.244.196:3306
kubectl exec -ti debug-tools -n nw-demo -- nc -v 172.27.14.196 6379
此时nw-demo下的Pod已经可以访问MySQL和Redis,可以对其他Namespace下的Pod进行测试,比如在default命名空间进行测试:
kubectl run -ti debug-tools --image=registry.cn-beijing.aliyuncs.com/dotbalo/debug-tools:latest -n default
curl 172.25.244.196:3306 --connect-timeout 3
可以看到此时default命名空间下的Pod并不能访问nw-demo的服务,如果想要MySQL和Redis对default命名空间开放,只需要在default命名空间下添加一个access-nw-mysql-redis=true的标签即可。
相对于传统架构,对中间件的访问限制,在Kubernetes中实现同样的效果,可能配置更加方便且易于管理。
服务发布限制于Ingress
一般情况下,一个项目的服务发布,会把域名的根路径指向前端应用,接口路径指向对应的网关或者微服务。假设现在创建一个Nginx服务充当前端页面,配置网络策略只让Ingress Controller访问该应用:
# 创建应用
kubectl create deploy nginx --image=registry.cn-beijing.aliyuncs.com/dotbalo/nginx:latest -n nw-demo
# 暴露服务
kubectl expose deploy nginx -n nw-demo --port=80
# 查看创建的服务
kubectl get svc,po -n nw-demo -l app=nginx
在没有任何网络策略的情况下,该服务可以被任何Pod访问:
配置网络策略只让Ingress Controller访问该服务:
nginx-nw.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: nginx-np
namespace: nw-demo
spec:
podSelector:
matchLabels:
app: nginx
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
podSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
- podSelector: {}
ports:
- protocol: TCP
port: 80
该条策略对具有app=nginx标签的Pod生效,只让具有app.kubernetes.io/name=ingress-nginx标签的Namespace下的具有app.kubernetes.io/name=ingress-nginx标签的Pod访问,同时还有一个允许当前Namespace下的Pod访问的策略- podSelector: {}。需要注意的是,读者的集群中已经安装了相关Ingress,并且需要根据实际的标签进行更改,如果读者集群目前并未安装Ingress,可以完成Ingress的学习后,再来测试该实验。
创建该NetworkPolicy,并测试连通性:
没有被允许的命名空间无法访问(这里使用svc方式会有无法通问题,尚未解决,可以使用pod尝试)
没有被允许的命名空间无法访问
kubectl exec debug-tools -- curl --connect-timeout 2 -Is nginx.nw-demo
允许范围内的Pod可以访问
在nw-demo名称空间下测试
kubectl -n nw-demo exec debug-tools -- curl --connect-timeout 2 -Is nginx.nw-demo
在ingress-nginx名称空间下测试
kubectl get po -n ingress-nginx -owide
kubectl -n ingress-nginx exec -it ingress-nginx-controller-wcszv -- bash
curl --connect-timeout 2 -Is nginx.nw-demo
可以看到Ingress Controller和该Namespace下的Pod可以访问,其他Namespace不可以访问。此时可以创建一个Ingress,然后用域名测试
发布者:LJH,转发请注明出处:https://www.ljh.cool/33941.html