保障集群内节点和网络安全 (SecurityContext PodSecurityPolicy NetworkPolicy)

本贴最后更新于 1486 天前,其中的信息可能已经时移世改

一、在 pod 中使用宿主节点的 Linux 命名空间

每一个 pod 拥有自己的 IP 和端口空间,因为他们有自己的网络命名空间
每一个 pod 拥有自己的进程树,因为它有自己的 PID 命名空间
每一个 pod 拥有自己的 IPC 命名空间,仅允许同一个 pod 内的进程通过进程间通信机制交流

在 pod 使用宿主节点的网络命名空间
可以再 pod spec 中的 hostNetwork 设置为 tree,从而共享宿主机的网络命名空间,这也意味着该 pod 没有自己的 IP 地址。

...
spec:
    hostNetwork: tree
    containers:
    - name: xxx
      image: xxx  
123456

绑定宿主节点上的端口而不是使用宿主节点的网络命名空间
将 pod 端口绑定到宿主机的某一个端口,可以通过配置 pod 的 spec.containers.ports 字段中某个容器某一个端口的 hostPort 属性来实现。
需要注意的是与 NodePort service 服务暴露的 pod 进行区别,使用 hostPort 时,到达宿主节点的端口连接会直接被转发到 pod 对应的端口上。而在 NodePort 服务暴露中,到达宿主节点的端口连接会被随机转发到被选取的 pod。
另一个区别是,使用了 hostPort 的 pod,仅有运行了该类 pod 的节点会绑定对应的端口,而 NodePode 类型的服务会在所有的节点上绑定端口,即使这个节点上没有 pod 运行。

还有一个要注意的是使用了 hostPod 的 pod,在一个节点上只能运行一个该类 pod,因为两个 pod 不能同时绑定到宿主机上的同一个端口。

...
spec:
  containers:
  - name: xxx
    imange: xxx
    - containerPort: 8080  // 容器的端口
      hostPod: 9000  // 绑定到宿主机上的端口
      protocol: TCP 
12345678

最初 hostPod 功能是用于暴露用过 DaemonSet 部署在每个节点上的系统服务,从而保证一个 pod 的副本不会被调用到同一个节点,但是后面有更好的机制来实现该需求。

使用宿主节点的 PID 和 IPC 命名空间
pod spec 中的 hostPID 和 hostIPC 选项与 hostNetwork 相似,当它们被设置成 tree 时,pod 中的容器将使用宿主节点的 PID 和 IPC 命名空间,分别允许它们看到宿主机上的全部进程,或通过 IPC 与宿主节点上的进程进行通信

apiVersion: v1
kind: Pod
metadata:
  name: xxx
spec:
  hostPID: true
  hostIPC: true
  containers:
  - name: xxx
   iamge: xxx   
12345678910

通常容器只能看到内容的进程,但这个 pod 可以列出宿主机上的所有进程。

二、配置节点的安全上下文

除了让 pod 使用宿主节点的 Linux 命名空间,还可以在 pod 或其所属容器的描述中通过 security-context 选项配置其他安全相关的特性,这个选项可以运用于整个 pod,或者每个 pod 中的单独容器。

安全上下文可以配置的内容:

  • 指定容器运行进程的用户(用户 ID)
  • 阻止容器用 root 用户启动(容器的默认运行用户通常可以在其镜像中指定,所以可能需要阻止容器以 root 用户运行)
  • 使用特权模式运行容器,使其对宿主节点的内核具有完全的访问权限
  • 与上相反,通过添加禁用内核功能,配置细粒度的内核访问权限
  • 设置 SELinux 选项,加强对容器的限制
  • 阻止进程写入容器的根文件系统

运行 pod 而不配置安全上下文
可以通过 kubectl exec podname id 查看没有任何安全上下文配置的 pod
可以看到这个容器在用户 ID(UID)为 0 的用户,即 root 用户,用户组 ID(gid)为 0(同样是 root)的用户组下运行。同样它还属于一些其他的用户组
注意:容器运行时使用的用户是在镜像中指定,在 Dockerfile 中,是通过使用 USER 命令实现的,如果该命令被忽略,容器将使用 root 用户运行。

使用指定用户来运行容器
为了使用一个与镜像中不同的用户 ID 来运行 pod,需要设置该 pod 的 securityContext.runAsUser 选项

apiVersion: v1
kind: Pod
metadata:
  name: xxx
spec:
  containers:
  - name: xxx
    image: alpine
    command: ["/bin/sleep", "9999"]  
    securityContext:
      runAsUser: 405    // 需要的是用户ID,而不是用户名(ID 405对应guest用户)  
1234567891011

kubectl exec podname id 查看结果 uid=405(guest) gid=100(users)

阻止容器以 root 用户运行
如果攻击者获取了访问镜像仓库的权限,并上传一个标签一样的镜像,并以 root 用户运行。kubernetes 的调度器运行该 pod 实例,kubelet 会拉取攻击者的镜像,并运行该镜像中的任何代码。
虽然容器与宿主节点基本上是隔离的,使用 root 用户运行容器中的进程然然是不好的实践。例如,当宿主节点上的一个目录被挂载到容器中,如果该容器使用了 root 用户运行,那它就用于该目录的所有权限,如果是非 root 用户运行,它就只有部分权限。

apiVersion: v1
kind: Pod
metadata:
  name: xxx
spec:
  containers:
  - name: xxx
    image: alpine
    command: ["/bin/sleep", "9999"]
    securityContext:
      runAsNonRoot: true  
1234567891011

使用特权模式运行 pod
有时 pod 需要做它们的宿主节点上能够做的任何事情,例如操作被保护的系统设备,或使用其他通常容器中不能使用的内核功能。

这种 pod 的一个例子就是 kube-proxy,该 pod 需要修改主机的 iptables
规则来让 kubernetes 中服务规则生效。为获取宿主机完整的内核权限,该 pod 需要在特权模式下运行,可以将容器的 securityContext 中的 privileged 设置为 true 实现

apiVersion: v1
kind: pod
metadata:
  name: xxx
spec:
  containers:
  - name: xxx
    image: alpine
    command: ["/bin/sleep", "9999"]
    securityContext:
      privileged: true  // 指定给容器在特权模式下运行  
1234567891011

部署该 pod,可以跟没有使用特权模式运行的 pod 进行对比。
可以通过查看/dev 这个目录进行对比(或其他的方式也可以,只要能体现不一样就行)
进入在非特权模式下运行的 pod 中,ls /dev 可以看到一个比较短的列表
进入以特权模式运行的 pod 中,ls /dev 可以看到一个很长的列表,其中就包含了宿主节点上所有的设备。这也意味着它可以自由使用任何设备。
举个例子,如果要再一个树莓派上运行一个 pod,用这个 pod 来控制相连的 LED, 那么必须使用特权模式来运行该 pod。

为容器单独添加内核功能
传统的 Linux 值区分特权和非特权进程,但是经过多件的发展,Linux 已经可以通过内核功能支持更细粒度的权限系统。
相对于让容器在特权模式下给予所有的权限,一个更加安全的做法是给予它需要的内核功能特权,kubernetes 允许为特定的容器添加内核功能,或禁用部分内核功能,以允许容器进行更加精细的权限控制,限制攻击者潜在的影响。
例如,一个容器是不允许修改系统时间的(硬件时钟的时间)。可以通过在一个非特权模式运行的 pod 中来实验。会发现提示没有权限修改。
如果需要容器修改系统时间,可以再容器的 capabilities 里 add 一项名为 CAP_SYS_TIME 的功能

apiVersion: v1
kind: Pod
metadata:
  name: xxx
spec:
  containers:
  - name: xxx
    image: alpine
    commadn: ["/bin/sleep", "9999"]
    securityContext:
        capabilities:
            add:
            - SYS_TIME  
12345678910111213

此时就可在 pod 中修改系统时间了
注意:Linux 内核功能的名称通常是以 CAP_开头的,但是在 pod spec 中指定内核功能时,必须省略 CAP_前缀。
警告:自行尝试修改系统时间,可能导致节点不可用
添加指定的内核功能项,固然比使用 privileged: true 更好,可以在 Linux 手册送查看 Linux 内核功能列表

在容器中禁用内核功能

前面我们已经了解了如何给容器添加内核功能,同样我们可以禁用容器使用某项内核功能。例如,默认情况下容器拥有 CAP_CHOWN 内核功能,可以修改文件系统中文件的所有者。为阻止容器的此种行为,可以在容器的 securityContext.capabilities.drop
列表中添加此项,以禁用这个修改文件所有者的内核功能。

apiVersion: v1
kind: Pod
metadata:
  name: xxx
spec:
  containers:
  - name: xxx
    image: alpine
    command: ["/bin/sleep", "9999"]  
    securityContext:
        capabilities:
            drop:
            - CHOWN  
12345678910111213

阻止对容器根文件系统的写入
因为安全原因,你可以能需要阻止容器中的进程对容器的根文件系统进行写入,仅允许它们写入挂载的存储卷。
假如你在运行一个有隐藏的漏洞,可以允许攻击者写入文件系统的应用(使用非编译型语言编写的应用),这些应用文件在构建容器时放入容器镜像中,并且在容器根文件系统中提供服务。由于漏洞的存在,攻击者可以修改这些文件,在其中注入恶意代码。
这一类攻击可以通过阻止容器写入自己的根文件系统(应用的可执行代码通常存储的位置)来阻止。将容器的 securityContext.readOnlyRootFilesystem 设置成 true 来实现

apiVersion: v1
kind: Pod
metadata:
  name: xxx
spec:
  containers:
  - name: xxx
    image: alpine
    command: ["/bin/sleep", "9999"]   
    securityContext:  
      readOnlyRootFilesystem: true  // 设置禁止修改容器的根文件系统
    volumeMounts:
    - name: my-volume
      mountPath: /volume
      readOnly: false  // 运行写入挂载卷
  volumes:
  - name: my-volume
    emptyDir:  
   
12345678910111213141516171819

实验
kubectl exec -it podname touch /new-file // 会提示 Read-only file system
kubectl exec -it podname touch /volume/newfile
kubectl exec -it podname – ls -la /volumenewfile // 可以看到创建成功了

如上例子,如果容器的根文件系统是只读的,那很可能要为应用程序会写入的每一个目录(如日志、磁盘缓存)挂载一个数据卷了
提示:为了增强安全性,建议生产环境中容器的 readOnlyRootFilesystem 设置成 true

设置 pod 级别的安全上下文

上的例子都可以单独对容器的安全上下文进行设置。这些选项中的一部分也可以从 pod 级别进行设置,可设置 pod.spec.securityContext
属性进行设置。它们会作为 pod 中每一个容器的安全上下文,但是会被容器的安全上下文所覆盖。下面来介绍一些 pod 级别独有的安全上下文属性。

容器使用不同用户运行时共享存储卷

在 pod 中不同的容器可以共享一个挂载存储卷,在一个容器中写,在另外一个容器中读。但这是因为两个容器都是以 root 用户运行的,拥有存储卷中文件的所有权限。如果使用了 runAsUser 选项。可能要在一个 pod 中用两个不同的用户来运行两个容器了(可能是一个第三方的容器,需要以它们特定的用户来运行进程),如果这样两个容器共享同一个存储卷,它们并不一定能读写另一个容器的文件。
因此,kubernetes 允许为 pod 中所有的容器指定 supplemental 组,以允许它们无论以哪个用户 ID 运行都可以共享文件,可以通过以下两个属性进行设置:

  • fsGroup
  • supplementalGroups
apiVersion: v1
kind: Pod
metadata:
  name: xxx
spec:
  securityContext:
    fsGroup: 555
    supplementalGroups: [666, 777]   
  containers:
  - name: c1
    image: alpine
    command: ["/bin/sleep", "9999"]  
    volumeMounts:
    - name: shared-volume
      mountPath: /volume
      readOnly: false
    securityContext:
      runAsUser: 1111
  - name: c2
    image: alpine
    command: ["/bin/sleep", "9999"]
    securityContext:
      runAsUser: 2222
    volumeMounts: 
      -name: shared-volume
       mountPath: /volume
       readOnly: false
  volumes:
  - name: shared-volume
    emptyDir:    
123456789101112131415161718192021222324252627282930

查看容器信息

kubectl  exec -it podname -c c1 sh 
$ id
uid=1111 gid=0(root) groups=555,666,777
$ ls -l / grep volume
total 4
-rw-r--r--    1 1111    555  ...
123456

可以看到该容器以 ID 为 1111 的用户运行,用户组为 0(root),但同时 555,666,777 的用户组也关联到了该用户下。在 pod 中定义 fsGroup 为 555,所以挂载的存储卷属于用户组 ID 为 555 的

此时在/volume
目录下创建一个文件,查看文件属性,该文件所属用户情况跟通常设置下的新建文件不同。在通常情况下,某一个用户创建的文件所属的用户组 ID,应该与创建用户的所属组 ID 一致,在这种情况下应该为 0。在这个容器的根文件系统创建一个文件,可以验证这一点。
由此可见,安全上下文中的 fsGroup 属性当进程在存储卷中创建文件时起到了作用,而 supplemental 属性定义了某个用户所关联的额外的用户组。

三、限制 pod 使用安全相关的特性

在上面已经介绍了如何在部署一个 pod 时在一个宿主节点上做任何想做的事情,很明显要有一种机制来限制用户使用其中部分功能,集群管理员可以使用 PodSecurityPolicy 资源来限制对以上安全相关的特性的使用。

PodSecurityPolicy 资源介绍
PodSecurityPolicy 是一种集群资源(无命名空间)的资源,它定义了用户能否在 pod 中使用各种安全相关的特性。维护 PodSecurityPolicy 资源中的配置策略的工作由集成在 API 服务器中的 PodSecurityPolicy 准入插件来完成。
注意:你的集群不一定开启了 PodSecurityPoicy 准入控制插件,需要开启它才行

当向 API 服务器发送 pod 资源时,PodSecurityPolicy 准入控制插件会将这个 pod 与已经配置的 PodSecurityPolicy 进行校验,如果这个 pod 符合集群中已有的安全策略,它会被存入 etcd。如果不符合将会被直接拒绝。这个插件也会根据安全策略中配置的默认值对 pod 进行修改。

了解 PodSecurityPolicy 可以做的事情
一个 PodSecurityPolicy 资源可以定义以下事项:

  • 是否允许 pod 使用宿主节点的 PID、IPC 和网络命名空间
  • pod 允许绑定宿主节点端口
  • 容器运行时允许使用用户的 ID
  • 是否允许拥有特权模式容器的 pod
  • 允许添加哪些内核功能、默认使用哪些内核功能,总是禁用哪些内核功能
  • 允许使用哪些 SELinux 选项
  • 容器是否允许使用可写入的根文件系统
  • 允许容器在哪些文件系统组下运行
  • 允许 pod 使用哪些类型的存储卷

查看一个 PodSecurityPolicy 样例

apiVersion: extensions/v1beta1
kind: PodSecurityPolicy
metadata:
  name: default
spec:
  hostPID: false      // 禁用宿主节点的PID命名空间
  hostIPC: false      // 禁用宿主节点的IPC命名空间
  hostNetwork: false  // 禁用宿主节点的网络命名空间
  hostPorts:   // 限制容器可以绑定宿主节点的哪些端口
  - min: 10000       // 10000-110000 可以绑定
    max: 11000
  - min: 13000       // 13000-14000 可以绑定
    max: 14000
  privileged: false  // 禁止使用特权模式的容器运行
  readOnlyRootFilesystem: true  // 容器强制使用只读的根文件系统
  runAsUser:        // 以supplemental、fsGroup 
    rule: RunAsAny  // 一起允许用户使用任何的用户或用户组运行容器
  fsGroup:     
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  seLinux:         // 可以使用SELinux的任何选项
    rule: RunAsAny
  volumes:         // 可以使用任何类型的存储卷
  - '*'
12345678910111213141516171819202122232425

部署了该 PodSecurityPolicy 后,集群创建新的资源时将要符合该安全策略才行,比如不能使用 privileged 特权模式运行容器等。

了解 runAsUser、fsGroup、supplementalGroup 策略
前面的例子中没有对 runAsUser、fsGroup、supplementalGroup 等字段进行任何限制,如果要限制容器可以使用的用户和用户组 ID,可以将规则改为 MustRunAs,并制定允许使用的 ID 范围

使用 MustRunAs 规则

runAsUser:
  rule: MustRunAs
  range:
  - min: 2   // min=max 可以指定一个特定的ID
    max: 2
fsGroup:
  rule: MustRunAs
  range:    // 指定了用户组ID在 2-10,20-30(包含临界值) 范围内用户组ID
  - min: 2
   max: 10
 - min: 20
   max: 30
supplementalGropus:
  rule: MustRunAs
  range:
  - min: 2
    max: 10
  - min: 20
    max: 30   
12345678910111213141516171819

如果 pod spec 中任何一个字段向设置该范围以外的值,那该 pod 将不会被 API 服务器接收。
注意:PodSecurityPolicy 策略对已存在的 pod 无效,因为 PodeSecurityPolicy 资源只在创建和升级 pod 的时候生效

部署镜像中用户 ID 在指定范围以外的 pod
通过前面我们知道在 pod
spec 中使用范围以外的用户 ID 运行,会被 API 服务器拒绝。如果在容器镜像文件中 Dockerfile
USER 指定的用户 ID 在范围以外,此时容器能被部署,但是进入该容器中可以查看 ID,会发现用户运行时使用的 ID 为 PodSecurityPolicy 策略中指定的用户 ID。PodSecurityPolicy 可以将编码覆盖到镜像中的用户 ID。

在 runAsUser 字段中还可以指定另一种规则:mustRunAsNonRoot。此策略会阻止用户部署以 root 用户运行容器。该情况下,必须在 spec 中为容器指定 runAsUser 字段,且不能为 0(0 为 root 用户),或者在镜像中 USER 指定一个非 0 的用户 ID。

配置允许、默认添加、禁止使用的内核功能
容器可以运行在特权模式下,也可以通过对每个容器添加或禁用 Linux 内核功能来定义更加细粒度的权限设置。以下三个字段会影响容器使用内核功能。

  • allowedCapabilities 指定容器中可以添加的内核功能
  • defaultAddCapabilities 指定容器中默认添加的内核功能
  • requiredDropCapabilities 指定容器中禁止使用的内核功能
apiVersion: extensions/v1beta1
kind: PodSecurityPolicy
metadata:
  name: xxx
spec:
  allowedCapabilities:
  - SYS_TIME    // 容器可以添加修改系统时间的内核功能
  defaultAddCapabilities:
  - CHOWN       // 容器默认添加可以修改文件所属者的内核功能
  requiredDropCapabilities
  - SYS_ADMIN  // 容器禁用SYS_ADMIN、SYS_MODULE 内核功能
  - SYS_MODULE
  ...   
12345678910111213

注意:SYS_ADMIN 功能允许使用一系列的管理操作,SYS_MODULE 功能允许加载或卸载内核模块

defaultAddCapabilities 字段中列出的所有内核功能将添加到每个 pod 的每个容器中。如果用户希望某个容器不能使用其中某个内核功能,必须在容器的 spec 中显示禁用该内核功能。

requiredDropCapabilities 字段中列出的所有禁用的内核功能,用户如果在 SecurityContext.capabilities.add

字段中添加了该字段中的内核功能,将会被 API 服务器拒绝。PodSecurityPolicy 访问控制插件将会每一个容器的 SecurityContext.Capabilities.drop
字段中加入这些功能。

限制 pod 可以使用的存储类型
PodSecurityPolicy
可以定义用户再 pod 中使用哪些存储卷类型。但最低限度上,一个 PodSecurityPolicy 应该允许 pod 使用以下类型的存储卷:emptyDir、configMap、secret、downwardAPI、persistentVolumeClaim。

kind: PodSecurityPolicy
spec:
  volumes:
  - emptyDir
  - configMap
  - secret
  - downwardAPI
  - persistenVolumeClaim  
12345678

如果有多个 PodSecurityPolicy 资源,pod 可以使用 PodSecurityPolicy 中任何一个存储卷类型,实际生效的是所有 volume 列表的合集

对不同的用户或用户组分配不同的 PodSecurityPolicy
PodSecurityPolicy 是集群级别的资源,它不属于存储或使用在某一个特定的命名空间上。这是否意味着它总是会应用在所有的命名空间上呢?不是的,因为这样会使得它们相当难用。毕竟系统 pod 需要允许做一些常规 pod 不应当做的事情。

对不同的用户或组分配不同的 PodSecurityPolicy 是通过 RBAC 机制来实现的。创建你需要的 PodSecurityPolicy 资源,然后创建 ClusterRole 资源,并通过名称将它们指向不同的策略,一次使 PodSecurityPolicy 资源中的策略对不同的用户或组生效。通过 ClusterRoleBinding 资源将特定的用户或组绑定到 ClusterRole 上,当 PodSecurityPolicy 访问控制插件需要决定是否接纳一个 pod 时,它只会考虑创建 pod 的用户可以访问到的 PodSecurityPolicy 中的策略。

创建一个允许部署特权容器的 PodSecurityPolicy

apiVersion: extentsions/v1beta1
kind: PodSecurityPolicy
metadata:
  name: privileged
spec:
  privileged: true
  runAsUser:
    rule: RunAsAny
  fsGroup:
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  seLinux:
    rule: RunAsAny
  volumes:
  - '*'  
12345678910111213141516

首先创建一个 PodSecurityPolicy,允许用户创建特权模式容器的 pod

kubectl  get psp    // psp 是PodSecurityPolicy的简写
可以看到 default 和 privileged  两个psp 其中default 中PRIV是false
12

在部署 pod 时,如果任一策略允许使用 pod 中使用到的任何特性,API 服务器就会接受这个 pod。
现在考虑两个使用该集群的用户 A 和 B,其中 A 只能创建受限的非特权 pod,允许 B 可以创建特权模式的 pod。可以通过让 A 只使用 default PodSecurityPolicy,而 B 使用以上两个 PodSecurityPolicy 来做到。

使用 RBAC 将不同的 PodSecurityPolicy 分配给不同的用户
RBAC 可以给用户授予特定类型的资源的访问权限,但 RBAC 机制也可以通过使用其名字来授权对特定资源实例的访问权限。
先创建两个 ClusterRole,分别名为 psp-default、psp-privileged

kubectl create clusterrole  psp-default --verb=use \
 --resource=podsecuritypolicy  --resource-name=psp-default
kubectl create clusterrole psp-privileged --verb=use \
 --resource=podsecuritypolicy --resource-name=privileged 
1234

注意:这里使用的动词是 use,而不是 get list watch 或类似的动词

现在需要将这两个策略绑定到用户上,因为 ClusterRole 是集群资源,所以需要使用 ClusterRoleBinding 资源而非 RoleBinding。

要将 psp-default
ClusterRole 绑定到所有已认证的用户上,而不单单是用户 A。否则没有用户可以创建 pod,因为 PodSecurityPolicy 访问控制插件会因为没有找到任何策略二拒绝创建 pod。所有已认证的用户都属于 system:authenticated,因此要将 psp-default
ClusterRole 绑定到这个组。

kubectl create clusterrolebinding psp-all-user --clusterrole=psp-default \
  --group=system:authenticated
12

将 psp-privileged ClusterRole 绑定到用户 B

kubectl create clusterrolebinding psp-b  --clusterrole=psp-privileged \
  --user=b
12

作为一个已认证用户,A 现在拥有 default PodSecurityPolicy 的权限。而用户 B 拥有 default 和 privileged PodSecurityPolicy 的权限,A 不能部署特权模式的 pod,而用户 B 可以。

使用 kubectl 创建不同用户
如何以用户 A 或用户 B 身份通过认证,而非用现在的已认证的用户。首先用 kubectl 的子命令 config 来创建两个新用户

kubectl config set-credentials a --username=a  --password=password
kubectl config set-credentials b --username=b  --password=password
12

然后使用这两个用户去创建特权模式的 pod

kubectl --user=a create -f  pod-privileged.yaml  // 会提示创建失败
kubectl --user=a create -f  pod-privileged.yaml  // 可以创建成功
12

四、隔离 pod 网络

上面所讲的安全特性配置都是 pod 和 pod 中的容器上。现在来了解一下如何通过限制 pod 与 pod 之间通信,来确保 pod 之间的网络安全。
是否可以进行这些配置取决于集群中使用的网络插件,如果网络插件支持,可以通过 NetworkPolicy 资源配置网络隔离。

一个 NetworkPolicy 会应用在匹配它的标签选择器的 pod 上,指明这些允许访问这些 pod 的源地址,或这些 pod 可以访问的目标地址。这些分别由入向(ingress)和出向(egress)规则指定。这两者都可以匹配由标签选择器选出的 pod,或者一个 namespace 中的所有 pod,或者通过无类别域间路由 CIDR 指定的 IP 地址段。

在一个命名空间中启用网络隔离

apiVersion: network.k8s.io/v1
kind: NetworkPolicy
metadata: 
  name: default-deny
spec:
  podSelector:
               // 空的标签选择器会匹配命名空间中所有的pod
1234567

在任何一个命名空间内创建该 NetworkPolicy 之后,任何客户端都不能访问该命名空间中的 pod

允许同一个命名空间中的部分 pod 访问一个服务端 pod

apiVersion: network.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: xxx
spec:
  podSelector:
    matchLabels:
      app: database  // 确保了对具有app=database标签的pod的访问安全性
  ingress:
  - from:
    - podSlecetor:
        matchLabels:
          app: webserver  // 只允许具有app=webserver标签的pod访问
      ports:
      - port: 5432   // 仅允许对该端口访问
123456789101112131415

上面的 NetworkPolicy 表示,命名空间中的数据库服务,现在只允许 webserver 才能访问,其他的 pod 都不能访问。并且,webserver 只能访问数据库的 5432 端口。
客户端 pod 通常通过 Service 而非直接访问 pod 来访问服务端拍 pod,但这对结果没有影响,NetworkPolicy 在通过 Service 访问时任然生效。

在不同 kubernetes 命名空间之间进行网络隔离

假设我们有一个多租户使用同一个 kubernetes 集群。每个租户都有多个命名空间,每个命名空间中有一个标签指明它们属于哪个租户。例如,有一个租户 manning,它所有命名空间中都有标签 tenant:manning。其中的一个命名空间中运行了一个微服务 testserver,该微服务具有标签
app:testserver,它只允许同一租户下所有命名空间下的所有 pod 访问。禁止其他租户访问。
为了确保该微服务的安全,可以创建如下的 NetworkPolicy。

apiVersion: network.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: xxx
spec:
  podSelector:
    matchLabels:
      app: testserver  // 该策略应用于具有app=testserver标签的pod
  ingress:
  - from:
    - namespaceSelector:
      matchLabels:
        tenant: manning  // 只有具有tenant=manning标签的命名空间中运行的
      ports:                //  pod可以访问该微服务
      - port: 80
123456789101112131415

注意:在多租户的 kubernetes 集群中,通常租户不能为他的命名空间添加标签或注释,否则,他们可以规避基于 namespaceSelector 的入向规则。

使用 CIDR 隔离网络

除了通过再 pod 选择器或命名空间选择器定义了哪些 pod 可以访问 NetworkPolicy 资源中指定的目标 pod,还可以通过 CIRD 表示法指定一个 IP 段。例如,允许 192.168.0.1/24
网段的客户端访问之前的 testserver,可以再入向规则中添加一下代码

ingress:
- from:
  - ipBlock:
      cidr: 192.168.0.1/24   // 允许IP在该范围内的客户端访问testserve  
1234

限制 pod 对外访问流量

spec:
  podSelector:
    matchLabels:
      app: webserver   // 这个策略应用于具有app=webserver标签的pod
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: database    // webserver的pod只能与具有app=database标签的pod通信
  • Kubernetes

    Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。

    110 引用 • 54 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...