Kubernetes in Action笔记 - (15) 节点与网络安全
文章目录
在pod中使用宿主节点的Linux命名空间
pod中的容器通常在分开的Linux命名空间中运行。 这些命名空间将容器中的进程与其他容器中,或者宿主机默认命名空间中的进程隔离开来。
使用宿主节点的网络命名空间
部分pod(特别是系统pod)需要在宿主节点的默认命名空间中运行,以允许它们看到和操作节点级别的资源和设备。例如,某个pod可能需要使用宿主节点上的网络适配器,而不是自己的虚拟网络设备。这可以通过将pod spec中的hostNetwork设置为true实现。
在这种情况下,这个pod可以使用宿主节点的网络接口,而不是拥有自己独立的网络。这意味着这个pod没有自己的IP地址;如果这个pod中的一某进程绑定了某个端口,那么该进程将被绑定到宿主节点的端口上。
绑定宿主节点上的端口而不使用宿主节点的网络命名空间
这个功能可以让 pod 在拥有自己的网络命名空间的同时,将端口绑定到宿主节点的端口上。可以通过配置 pod 的spec.containers.ports
字段中某个容器某一端口的 hostPort 属性来实现。
不要混淆使用 hostPort 的 pod 和通过 Node Port 服务暴露的 pod。如果一个 pod 绑定了宿主节点上的一个特定端口,每个宿主节点只能调度-个这样的 pod 实例,因为两个进程不能绑定宿主机上的同一个端口。调度器在调度 pod 时会考虑这一点, 所以它不会把两个这样的 pod 调度到同一个节点上,
使用宿主节点的 PID 与 IPC 命名空间
pod spec 中的 hostPID 和 host IPC 选项与 hostNetwork 相似。当它们被设 置为 true 时, pod 中的容器会使用宿主节点的 PID 和 IPC 命名空间,分别允许它们看到宿主机上的全部进程,或通过 IPC 机制与它们通信。
配置节点的安全上下文
除了让 pod 使用宿主节点的 Linux 命名空间,还可以在 pod 或其所属容器的描 述中通过 security-Context 选项配置其他与安全性相关的特性。这个选项可以 运用于整个 pod ,或者每个 pod 中单独的容器。
配置安全上下文可以做很多事情:
- 指定容器中运行进程的用户(用户ID)。
- 阻止容器使用 root 用户运行(容器的默认运行用户通常在其镜像中指定,所 以可能需要阻止容器以 root 用户运行)。
- 使用特权模式运行容器, 使其对宿主节点的内核具有完全的访问权限。
- 与以上相反,通过添加或禁用内核功能,配置细粒度的内核访问权限。
- 设置 SELinux (Security Enhanced Linux, 安全增强型 Linux)选项,加强对容器的限制。
- 阻止进程写入容器的根文件系统。
使用指定用户运行容器
为了使用一个与镜像中不同的用户 ID 来运行 pod, 需要设置该 pod 的securityContext.runAsUser 选项。
1apiVersion: vl
2kind: Pod
3metaadata:
4 name: pod-as-user-guest
5specL
6 containers:
7 - name: main
8 image: alpine
9 command: ["/bin/sleep", "999999"]
10 securityContext:
11 # 需要指明一个用户ID,而不是用户名
12 runAsUser: 405
阻止容器以 root 用户运行
可能安全隐患场景:假设有一个已经部署好的 pod,其在 daemon 用户下运行。如果攻击者获取了访问镜像仓库的权限, 井上传了一个标签完全相同, 在root用户下运行的镜像。当Kubernetes的调度器运行该pod的新实例时, kubelet会下载攻击者的镜像, 并运行该镜像中的任何代码。虽然容器与宿主节点基本上是隔离的,使用 root 用户运行容器中的进程仍然是一种不好的实践。例如,当宿主节点上的一个目录被挂载到容器中时,如果这个容器中的进程使用了 root 用户运行,它就拥有该目录的完整访问权限 ; 如果用非 root用户运行,则没有完整权限。
runAsNonRoot设置为true后,虽然pod它会被成功调度,但是不允许运行。
1securityContext:
2 runAsNonRoot: true
使用特权模式运行 pod
有时 pod 需要做它们的宿主节点上能够做的任何事,例如操作被保护的系统设 备,或使用其他在通常容器中不能使用的内核功能。比如kube-proxy pod
1securityContext:
2 privileged : true
为容器单独添加内核功能
相比于让容器运行在特权模式下以给予其无限的权限, 一个更加安全的做法是只给予它使用真正需要的内核功能的权限。Kubernetes允许为特定的容器添加内核功能, 或禁用部分内核功能, 以允许对容器进行更加精细的权限控制, 限制攻击者潜在侵入的影响。
比如。如果需要允许容器修改系统时间, 可以在容器的capabilities里add一项名为CAP_SYS_TIME的功能
1securityContext:
2 capabilities:
3 add:
4 - SYS_TIME
注意:
- Linux内核功能的名称通常以CAP_开头。但在pod spec中指定内核功能时,必须省略CAP_前缀。
- 可以在Linux手册中查阅Linux内核功能列表。
在容器中禁用内核功能
在容器的securityContext.capabilities.drop列表中添加禁用的功能
1securityContext:
2 capabilities:
3 drop:
4 - CHOWN
阻止对容器根文件系统的写入
为了增强安全性, 请将在生产环境运行的容器的readOnlyRootF且esystem选项设置为true。
1securityContext:
2 readOnlyRootFilesystem: true
设置pod级别的安全上下文
以上的例子都是对单独的容器设置安全上下文。 这些选项中的一部分也可以从pod级别设定(通过pod.spec.securityContext属性)。 它们会作为pod中每一个容器的默认安全上下文, 但是会被容器级别的安全上下文覆盖
容器使用不同用户运行时共享存储卷
k8s允许为pod中所有容器指定supplemental组,以允许它们无论以哪个用户ID运行都可以共享文件。这可以通过以下两个属性设置:
- fsGroup
- supplementalGroups
1apiVersion: v1
2kind: Pod
3metadata:
4 name: pod-with-shared-volume-fsgroup
5spec:
6 securityContext:
7 # 这两个属性在pod级别
8 fsGroup: 555
9 supplementalGroups: [666,777]
10 containers:
11 - name: first
12 image: alpine
13 command: ["/bin/sleep", "999999"]
14 securityContext:
15 runAsUser: 1111 # 第一个容器使用的用户ID为1111
16 volumeMounts:
17 - name: shared-volume # 两个容器使用同一个存储卷
18 mountPath: /volume
19 readonly: false
20 - name: second
21 image: alpine
22 command: ["/bin/sleep", "999999"]
23 securityContext:
24 runAsUser: 2222 # 第二个容器使用的用户ID为2222
25 volumeMounts:
26 - name: shared-volume # 两个容器使用同一个存储卷
27 mountPath: /volume
28 readonly: false
29 volumes:
30 - name: shared-volume
31 emptyDir:
PodSecurityPolicy
什么是PodSecurityPolicy
PodSecurityPolicy 是一种集群级别(无命名空间)的资源, 它定义了用户能否在 pod 中使用各种安全相关的特性。维护 PodSecurityPolicy 资源中配置策略的工作由集成在 API 服务器中的 PodSecurityPolicy 准入控制插件完成。
当有人向 API 服务器发送 pod 资源时, PodSecurityPolicy 准入控制插件会将这个 pod 与已经配置的 PodSecurityPolicy 进行校验。如果这个 pod 符合集群中已有安全策略, 它会被接收并存入 etcd; 否则它会立即被拒绝。这个插件也会根据安全策略中配置的默认值对 pod 进行修改。
PodSecurityPolicy 可以做的事: 一个 PodSecurityPolicy 资源可以定义以下事项:
- 是否允许 pod 使用宿主节点的 PID、IPC、网络命名空间
- pod 允许绑定的宿主节点端口
- 容器运行时允许使用的用户 ID
- 是否允许拥有特权模式容器的 pod
- 允许添加哪些内核功能, 默认添加哪些内核功能, 总是禁用哪些内核功能
- 允许容器使用哪些 SELinux 选项
- 容器是否允许使用可写的根文件系统
- 允许容器在哪些文件系统组下运行
- 允许 pod 使用哪些类型的存储卷
下面是一个 PodSecurityPolicy 样例,它阻止了 pod 使用宿主节点的 PID 、 IPC 、 网络命名空间, 运行特权模式的容器, 以及绑定大多数宿主节点的端口(除11 000-11000和13000-14000范围内的端口)。 它没有限制容器运行时使用的用户、用户组和SELinux 选项
1apiVersion: externsions/v1beta1
2kind: PodSecurityPolicy
3metadata:
4 name: default
5spec:
6 hostIPC: false
7 hostPID: false
8 hostNetwork: false
9 hostPorts:
10 - min: 10000
11 max: 11000
12 - min: 13000
13 max: 14000
14 privileged: false
15 readOnlyRootFilesystem: true
16 runAsUser:
17 rule: RunAsAny
18 fsGroup:
19 rule: RunAsAny
20 supplementalGroups:
21 rule: RunAsAny
22 seLinux:
23 rule: RunAsAny
24 volumes:
25 - '*'
注意: 修改策略对已经存在的pod无效,因为PodSecurityPolicy资原仅在创建和升级pod时起作用。
可以使用命令查看当前PodSecurityPolicy
1kubectl get psp
runAsUser、fsGroup 和 supplementalGroup 策略
上面的例子中,没有限制用户或者用户组。如果需要限制容器可以使用的用户和用户组ID, 可以将规则改为MustRunAs
1runAsUser:
2 rule: MustRunAs
3 ranges:
4 - min: 2
5 max: 2
runAsUser字段中还可以使用另一种规则: mustRunAsNonRoot。它将阻止用户部署以 root 用户运行的容器。在此种情况下,spec 容器中必须指定runAsUser字段,并且不能为0(0为root用户的ID),或者容器的镜像本身指定了用一个非0的用户ID 运行。
配置允许、默认添加、禁止使用的内核功能
以下三个字段会影响容器可以使用的内核功能:
- allowedCapabilities
- defaultAddCapabilities
- requiredDropCapabilities
1apiVersion: externsions/v1beta1
2kind: PodSecurityPolicy
3spec:
4 # 允许的功能
5 allowedCapabilities:
6 - SYS_TIME
7 # 为容器自动添加的功能
8 defaultAddCapabilities:
9 - CHOWN
10 # 要求禁用的功能
11 requiredDropCapabilities:
12 - SYS_ADMIN
13 - SYS_MODULE
14 ...
注意:SYS_ADMIN 功能允许使用一系列的管理操作; SYS_MODULE 功能允许加载或卸载Linux内核模块。
- allowedCapabilities字段用于指定 spec 容器的 securityContext.capabilities 中可以添加哪些内核功能。
- defaultAddCapabilities字段中列出的所有内核功能将被添加到每个已 部署的 pod 的每个容器中
- requiredDropCapabilities列出的内核功能会在所有容器中被禁用(PodSecurityPolicy 访问控制插件会在所有容器的 securityContext.capabilities.drop 宇段中加入这些功能〉
限制 pod 可以使用的存储卷类型
1kind: PodSecurityPolicy
2spec:
3volumes:
4- emptyDir
5- configMap
6- secret
7- downwardAPI
8- persistentVolumeClaim
如果有多个 PodSecurityPolicy 资源, pod 可以使用 PodSecurityPolicy 中允许使用的任何一个存储卷类型(实际生效的是所有 volume 列表的并集〉。
对不同的用户与组分配不同的 PodSecurityPolicy
PodSecurityPolicy 是集群级别的资源,这意味着它不能存储和应用在某一特定的命名空间上。
对不同用户分配不同 PodSecurityPolicy 是通过RBAC机制实现的。这个方法是,创建需要的 PodSecurityPolicy 资源,然后创建 ClusterRole 资源并通过名称将它们指向不同的策略,以此便 PodSecurityPolicy 资源中的策略对不同的用户或组生效。通过 ClusterRoleBinding 资源将特定的用户或组绑定到 ClusterRole上,当 PodSecurityPolicy 访问控制插件需要决定是否接纳一个 pod 时,它只会考虑、创建 pod 的用户可以访问到 的 PodSecurityPolicy 中的策略。
隔离pod的网络
通过限制 pod 可以与其他哪些 pod 通信,来确保 pod 之间的网络安全。
是否可以进行这些配置取决于集群中使用的容器网络插件。如果网络插件支持,可以通过 NetworkPolicy 资源配置网络隔离 。
一个 NetworkPolicy 会应用在匹配它的标签选择器的 pod 上,指明这些允许访问这些 pod 的源地址,或这些 pod 可以访问的目标地址。这些分别由入向(ingress)和出向(egress)规则指定。这两种规则都可以匹配由标签选择器选出的 pod ,或者一个 namespace 中的所有 pod,或者通过无类别域间路由(Classless Inter-DomainRouting, CIDR )指定的 IP 地址段。
注意:入向规则与Ingress资源无关
在一个命名空间中启用网络隔离
在默认情况下, 某一命名空间中的 pod 可以被任意来源访问。
下面这个例子中,在任何一个特定的命名空间中创建该 NetworkPolicy 之后,任何客户端都不能访问该命名空间中的 pod 。
1apiVersion: networking.k8s.io/v1
2kind: NetworkPolicy
3metadata:
4 name: default-deny
5spec:
6 # 空的标签选择器匹配命名空间中所有pod
7 podSelector:
注意:集群中的 CNI 插件或其他网络方案需要支持 NetworkPolicy ,否则 NetworkPolicy 将不会影响 pod 之间的可达性。
允许同一命名空间中的部分pod访问一个服务端pod
为了允许同一命名空间中的客户端pod访问该命名空间的pod,需要指明哪些pod可以访问。
1apiVersion: networking.k8s.io/v1
2kind: NetworkPolicy
3metadata:
4 name: postgres-netpolicy
5spec:
6 podSelector:
7 matchLabels:
8 app: database
9 ingress:
10 - from:
11 # 只允许指定标签与端口的pod访问
12 - podSelector:
13 matchLabels:
14 app: webserver
15 ports:
16 - port: 5432
在不同 Kubernetes 命名空间之间进行网络隔离
存在一种情况,多个租户使用同一Kubernetes集群。每个租户有多个命名空间,每个命名空间中有一个标签指明它们屈于哪个租户。
1apiVervsion: networking.k8s.io/v1
2kind: NetworkPolicy
3metadata:
4 name: shoppingcart-netpolicy
5spec:
6 # 该策略应用于有app=shopping-cart标签的pod
7 podSelector:
8 matchLabels:
9 app: shopping-cart
10 ingress:
11 - from:
12 # 只有在具有tenant=manning标签的命名空间中运行的pod可以访问该服务
13 - namespaceSelector:
14 matchLabels:
15 tenant: manning
16 ports:
17 - port: 80
注意:在多租户的Kubernetes集群中,通常租户不能为他们的命名空间添加标签(或注释)。否则,他们可以规避基于namespaceSelector的入向规则。
使用CIDR隔离网络
可以通过CIDR表示法指定一个IP段来隔离网络
1ingress:
2- from:
3 - ipBlock:
4 cidr: 192.168.1.0/24
限制pod的对外访问流量
可以通过出向规则限制pod的对外访问流量
1spec:
2 podSelector:
3 matchLabels:
4 app: webserver
5 egress:
6 - to:
7 - podSelector:
8 matchLabels:
9 app: database
图书资料: