11.1 权限管理 RBAC
Kubernetes 创建集群时,会生成一个 kubeconfig 文件,位于 ~/.kube/config,其中包含管理员账号凭证、集群访问信息和上下文配置,方便用户通过 kubectl 命令行工具管理集群。该账号通常拥有完全访问权限,允许执行所有操作。然而,除了管理员,项目负责人和应用开发者等用户也可能需要访问集群,因此进行权限管理显得尤为重要。
11.1.1 RBAC 的基本概念
RBAC(基于角色的访问控制)是一种管理 Kubernetes 集群中用户权限的机制。它通过四种顶级资源对象实现:Role、ClusterRole、RoleBinding 和 ClusterRoleBinding。管理员可以使用 kubectl API 调用这些资源对象,灵活地配置和管理用户的访问权限,从而提高集群的安全性和管理效率。
其在 Kubernetes 1.5 版本中引入,在 1.6 版本中升级为 Beta 版本,并成为 Kubeadm 安装方式下的默认选项,可以通过修改 Master 节点上 apiserver 的静态 Pod 定义文件来设置启动时指定 -authorization-mode=RBAC 。
cat /etc/kubernetes/manifests/kube-apiserver.yaml
...
- --authorization-mode=Node,RBAC
...
11.1.2 Role 和 Cluster Role
Role 和 ClusterRole 的关键区别在于它们的作用范围:
- Role:用于定义特定命名空间内的权限。它只在指定的命名空间有效,允许对该命名空间中的资源进行操作。
- ClusterRole:作用于整个集群,可以在所有命名空间内使用。它允许对集群范围内的资源进行操作,或在特定命名空间中被引用。
关于权限,RBAC(基于角色的访问控制)中的规则主要是“允许”的,意味着如果没有明确的允许,访问将被拒绝,没有默认的拒绝规则。这种设计使得权限管理更加清晰和可控。
Role
以下是一个 Role 的案例
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: dev
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
-
apiVersion: 定义该资源的 API 版本。
-
kind: 定义资源类型为 Role。
-
metadata:
- name: Role 的名称。
- namespace: Role 是作用在单个 Namespace 下的,具有命名空间隔离,所以需要指定 Namespace,不指定则为 default。
-
rules: 定义具体的权限,切片类型,可以配置多个。
-
apiGroups: 定义该资源的 apiGroup 名称,比如
[""]表示核心 API 组。 -
resources: 定义对哪些资源进行授权,切片类型,可以定义多个,比如 pods、service 等。
-
verbs: 定义可以执行的操作,切片类型,可以定义多个,比如 create、delete、list、get、watch、deletecollection等。
-
切片类型表示一个数组,可以在其中定义多条规则,每条规则描述不同的权限设置。每条规则可以指定不同的 apiGroups、resources 和 verbs,从而允许更细粒度的权限控制。例如,你可以同时允许某个用户查看 Pods 和服务,或者针对不同资源设置不同的操作权限。通过这种方式,可以灵活地管理用户和服务账户的访问权限。
ClusterRole
ClusterRole可以将权限授予整个集群,主要区别在于它适用于集群范围内的资源,因此可以访问以下内容:
- 集群范围的资源(如 Node)。
- 非资源端点(如 /healthz)。
- 跨所有命名空间的资源(如 Pod)。
例如,可以授予对所有命名空间中的 Secret 的读取权限:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "watch", "list"]
可以看到,当前的 Role 和 ClusterRole 配置相似,主要区别在于将 kind 从 Role 更改为 ClusterRole。
对子资源的引用问题
在很多情况下,我们需要授权用户查看容器日志或执行容器的权限。这时,仅仅将 resources 设置为 Pods 是不够的,还需要对指定资源的子资源进行控制。在 Kubernetes 中,大多数资源通过其名称字符串表示,例如 pods。然而,一些 Kubernetes API 涉及的子资源,如 Pod 的日志,其对应的 Endpoint URL 是:
GET /api/v1/namespaces/{namespace}/pods/{name}/log
在这个示例中,pods 是命名空间级别的资源,而 log 是 Pod 的子资源。要对其进行访问控制,需要用斜杠分隔资源和子资源。
例如,可以定义一个 Role 来允许读取 Pod 及其日志:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-log-reader
namespace: default # 指定命名空间
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"] # 允许访问 Pod 和 Pod 的日志
verbs: ["get", "list", "watch"] # 允许的操作
对于特定资源,您还可以使用 resourceName 来指定单个具体资源。这允许您更精确地控制访问权限、提供了更大的灵活性和安全性。例如,您可以定义一个 Role,只授予用户对特定 Pod 的日志具有访问权限,而不是对所有 Pods 的权限。
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: specific-pod-log-reader
namespace: default
rules:
- apiGroups: [""]
resources: ["pods/log"] # 只针对子资源 pods/log
resourceNames: ["my-app"] # 指定特定的 Pod 名称
verbs: ["get", "list", "watch"]
注意事项:如果使用了 resourceNames,则 verbs 不能是 list、watch、create、deletecollection等。
11.1.3 RoleBinding 和 ClusterRoleBinding
RoleBinding(角色绑定)可以将 Role 中定义的权限授予 User、Group或 ServiceAccount。RoleBinding 和 ClusterRoleBinding 最大的区别在于 RoleBinding 作用于命名空间,ClusterRoleBinding 作用于集群。
相关字段
RoleBinding 可以引用同一命名空间的 Role 进行授权,比如将上述创建的 pod-reader 的 Role 授予 default 命名空间的用户 zs,这将允许 zs 读取 default 命名空间中的 Pod:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: User
name: zs # 用户名
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader # 绑定的角色名
apiGroup: rbac.authorization.k8s.io
- subjects: 配置被绑定对象,如用户、用户组或服务账户,可以配置多个。
- kind: 这里指定为
User,表示这个 RoleBinding 是绑定给一个用户。 - name:
test是要绑定的用户的名称。 - apiGroup: 指定了该用户的 API 组,这里是
rbac.authorization.k8s.io。
- kind: 这里指定为
- roleRef: 引用要绑定的角色。
- kind: 指定权限来源,可以是 Role 或 ClusterRole。
- name:
pod-reader是要绑定的角色的名称。 - apiGroup: 同样指定了该角色的 API 组。
1. RoleBinding 绑定 Role
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: User
name: test
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
说明:
- RoleBinding 允许在特定命名空间内将 Role 绑定到用户或服务账户。
- 在这个例子中,
read-pods绑定了pod-reader角色,允许用户test在default命名空间内执行与 Pods 相关的操作(例如:获取、列出和观察 Pods)。
2. RoleBinding 绑定 ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-all-pods
namespace: default
subjects:
- kind: User
name: test
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: pod-reader
apiGroup: rbac.authorization.k8s.io
说明:
- 在这个例子中,
RoleBinding绑定了一个 ClusterRole。 - 这种方式使得在特定命名空间(如
default)内,用户test可以拥有 ClusterRole 定义的权限,而不仅仅是一个命名空间内的角色权限。 - 例如,如果
pod-reader是一个 ClusterRole,它可能允许用户在所有命名空间中查看 Pods,但通过RoleBinding只在default命名空间内生效。
3. ClusterRoleBinding 绑定 Role
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: read-pods-global
subjects:
- kind: User
name: test
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
namespace: default
apiGroup: rbac.authorization.k8s.io
说明:
- ClusterRoleBinding 允许将 Role 绑定到用户或服务账户,但需要注意的是,Role 仍然是命名空间级的。
- 在这个例子中,
read-pods-global将用户test与default命名空间的pod-reader角色关联起来。 - 这种方式在逻辑上是合理的,但不常见,因为
ClusterRoleBinding的主要目的是绑定 ClusterRole。
4. ClusterRoleBinding 绑定 ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: read-all-pods-global
subjects:
- kind: User
name: test
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: pod-reader
apiGroup: rbac.authorization.k8s.io
说明:
- 这是一个典型的 ClusterRoleBinding 用法,允许将 ClusterRole 绑定到用户。
- 在这个例子中,
read-all-pods-global允许用户test在集群的所有命名空间中执行与 Pods 相关的操作。 - 这种绑定适用于需要跨命名空间访问的权限场景。
区别总结
-
RoleBinding 绑定 Role:
- 仅在指定命名空间内生效,适用于控制特定命名空间内的权限。
-
RoleBinding 绑定 ClusterRole:
- 允许在特定命名空间内赋予跨命名空间的权限(但仍在该命名空间内有效)。
-
ClusterRoleBinding 绑定 Role:
- 虽然可以绑定命名空间角色,但不常见,主要用于将集群级别权限与命名空间角色组合(可能有特定需求)。
-
ClusterRoleBinding 绑定 ClusterRole:
- 常用方式,允许用户在集群所有命名空间中拥有集群级权限。
11.1.4 config 文件
1. 使用不同的 config 文件
Kubernetes 的默认 kubeconfig 文件通常位于用户主目录下的 .kube/config。您也可以通过 --kubeconfig=/path/to/your/config 参数指定其他配置文件。
可以使用以下命令查看当前使用的配置文件(用户)在指定命名空间中的权限
[root@master-01 .kube]# kubectl auth can-i --list --namespace=default --kubeconfig=/root/.kube/config
Resources Non-Resource URLs Resource Names Verbs
*.* [] [] [*]
[*] [] [*]
selfsubjectaccessreviews.authorization.k8s.io [] [] [create]
selfsubjectrulesreviews.authorization.k8s.io [] [] [create]
[/api/*] [] [get]
[/api] [] [get]
[/apis/*] [] [get]
[/apis] [] [get]
[/healthz] [] [get]
[/healthz] [] [get]
[/livez] [] [get]
[/livez] [] [get]
[/openapi/*] [] [get]
[/openapi] [] [get]
[/readyz] [] [get]
[/readyz] [] [get]
[/version/] [] [get]
[/version/] [] [get]
[/version] [] [get]
[/version] [] [get]
- 资源权限 (
*.*):*.*表示对所有资源的所有操作都有权限。这是一个非常广泛的权限设置,通常意味着该用户拥有对 Kubernetes 中所有资源的完全访问权限。
- 非资源 URL:这部分列出了用户可以访问的非资源 API 端点。
- 权限:
selfsubjectaccessreviews.authorization.k8s.io:可以创建自我主体访问审查请求,通常用于检查用户是否有权限执行某个动作。selfsubjectrulesreviews.authorization.k8s.io:可以创建自我主体规则审查请求,通常用于查看用户在当前上下文中的权限。
/api/*,/api,/apis/*,/apis等:允许执行GET操作,表示可以获取 Kubernetes API 的相关信息。/healthz,/livez,/readyz:用于检查集群健康状态的端点,允许执行GET操作。/openapi/*,/openapi:可以获取 OpenAPI 规范,允许执行GET操作。/version/和/version:可以获取版本信息,允许执行GET操作。
- 权限:
2. 格式
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: <CA证书的Base64编码>
server: <API服务器地址>
name: <集群名称>
contexts:
- context:
cluster: <集群名称>
user: <用户名称>
name: <上下文名称>
current-context: <上下文名称>
kind: Config
preferences: {}
users:
- name: <用户名称>
user:
client-certificate-data: <用户证书的Base64编码>
client-key-data: <用户密钥的Base64编码>
在 Kubernetes 集群的控制节点上,CA 证书通常位于 /etc/kubernetes/pki/ca.crt。你可以用以下命令查看内容并进行 Base64 编码:
cat /etc/kubernetes/pki/ca.crt | base64 | tr -d '\n'
3. 案例
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJME1ETXlOakF4TURZek5sb1hEVE0wTURNeU5EQXhNRFl6Tmxvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBS1FkCjVXTXNjYWpvS0htUEpWb2loQ3RqL1l1SlFTVkhmd1Z5MURXZjJveDR4ai9DTkVLWC9VMUFDczZtQ3JOcElIWnEKMk9KQVFDU3pGMXRCMkF2VDIyQ1h4TitOR3VGUnQ2Rko2WWVNZkNXV3JsV2Qxa1NvRWZRWWZjYlJmR3YrcmlKZAoyeDFTRlNOOUl0dEhvNVJINU43UkF0NWowUTA5Sms0dk96aGdaZGMzK3pCQzhPTWtLVmJVMWxWUXV6anpuaTdFCmY1dVY0Mm02YW44NEhwQkE5T09saU5FWWIraXIvMFFOaThwOEZhUmJVOEk0aHgyMll5Qmdva1lVKzJMQitoOEsKRFNnQll3SWFURyt6aGV5NS85QnVTWjZZV2F2QWZsY1pFSHpqVWZPUmlvaXNzVU9ydXRlTktweDRKMWcyUTVHMgpERHhKbGsyVW5nQkdlMVNmV2MwQ0F3RUFBYU5aTUZjd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZEcHFaZW53b3k5RE9iNUtXbEpRKzVuL0V5Tk5NQlVHQTFVZEVRUU8KTUF5Q0NtdDFZbVZ5Ym1WMFpYTXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBSGtRWEtBU082YlRDRnRDZmhWNApES0dONk56ZmRRWVY0TnB0SjdwNXBaU0hNNWtLWHlqL1NLL0JqUXdWVk5aS3hCWmRHdlBtd002ZjV3VnNrYjRJCnpFcmxwZHVuNkxPRG0wYXN5R25MSnowTVlJbldQUGVpSmt4RURtUGxUWVNtTUJ1WDJjMHNOeG81WEFHbWZsMysKb3YvRWNJeitBNmRBWC9pNzU4c1p3UjJ3Wm1zREd0RmoyalQwRzVGMUlkOGJsd2gvL0cwVUhvY3A5dzNnWTdHcgppZ0NNOC91SEhsUURoOFlkSWE4Qm1qRStVbmxKVG50RnRoMmRpZG9GOTBhb3MwUk02YTBZRURBdzlzLzN6bEVpClRHSHBJQWRoWUY3K0FhUUN2VEU4dWc0bGJpU2tQNStZNGNGWjNidU5HUUNLNXRTSGhkQ2tQMnkyamMxblZjRWEKUmc0PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
server: https://192.168.17.110:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURJVENDQWdtZ0F3SUJBZ0lJZFpOdHhOa21ZLzR3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TkRBek1qWXdNVEEyTXpaYUZ3MHlOVEF6TWpZd01UQTJNemRhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQTFqTVJqd0o0REJBZ01rczUKL25qN1lxZ0VJd29la0NzSTdzZE9GeVovaC9VQVZlRnpKVHV5NFk4dnAyUWwrd21qNU1WWjlLT1J3OHJycmw5NApWb1NTTSt5TG9TWW12bkN3VXlNUGlxOGQweVNUOW9oY2VYTFZoWmN3TmFabXNvREgzK1hMM1h5S0JXS2o2QTFOClRVVzhySDNuR2NOSFpQdU5zRTBYa3NkTmtoa3A1NE03NXJQNXhwRnJqN2RIRjROUXpIOW44eTFOSXhhZ2g1MTIKZXpNdldDZkVZZFhPYzdFK1pKL0NCMHJLSlpTcFMrTGVHc3BITjdEelFpbXFacEI1QThRZk4yTWtBc1Yxb3FLUAo5ZncrQ0lkcmdvWGpnRVJ3eGpRNTFJeDBmQ3IzTHV1aXQ2ME94dlJkS1NCak9mMTByU01SaFhCemlTYzBzZWowCkNuZFZQd0lEQVFBQm8xWXdWREFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RBWURWUjBUQVFIL0JBSXdBREFmQmdOVkhTTUVHREFXZ0JRNmFtWHA4S012UXptK1NscFNVUHVaL3hNagpUVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBZUJuWllxMlhRQW9qMXB0bmtZQ3kyYXYvcWd1ODFNbFMvY0o2CkF1RDJVMzl2YUt0WnMwYUJUZllpVDVTUS92QUdJbGV5VXJZaXJiTE5HclUrNzV1ZzBGUHpDY1hhejA4cDJuT0MKdFExdmljcXdDelNwZlNIeStmSy9Wc25mMUpHL05sWDBaQjZPRFJ3aFRFNUVWQXlhM0dlUFpqbmNqS29FYTVNbwpjUXBrR2NHczN2c0NzYTJFeWFoR1JwczQ3aDVGSUFLSERoMVN3Skg5VUVJeFE2RXJnWjIrV1hSRHJzKzV3OW1LCkY3UHJVV3o4dUlDdE1LMlVmTG5IWDFlSjRPSlB0M1NwVDhhMXN6VE9TVmIvbHAvOUVsblAyeE9uRXNvNGxKZWEKNHY1L0QyZ0lsQjVGclQrMlptZ2tZUHFycmZzSmtSUzNsVFBRQk95ZEFsalBuemZ0QXc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBMWpNUmp3SjREQkFnTWtzNS9uajdZcWdFSXdvZWtDc0k3c2RPRnlaL2gvVUFWZUZ6CkpUdXk0WTh2cDJRbCt3bWo1TVZaOUtPUnc4cnJybDk0Vm9TU00reUxvU1ltdm5Dd1V5TVBpcThkMHlTVDlvaGMKZVhMVmhaY3dOYVptc29ESDMrWEwzWHlLQldLajZBMU5UVVc4ckgzbkdjTkhaUHVOc0UwWGtzZE5raGtwNTRNNwo1clA1eHBGcmo3ZEhGNE5Rekg5bjh5MU5JeGFnaDUxMmV6TXZXQ2ZFWWRYT2M3RStaSi9DQjByS0paU3BTK0xlCkdzcEhON0R6UWltcVpwQjVBOFFmTjJNa0FzVjFvcUtQOWZ3K0NJZHJnb1hqZ0VSd3hqUTUxSXgwZkNyM0x1dWkKdDYwT3h2UmRLU0JqT2YxMHJTTVJoWEJ6aVNjMHNlajBDbmRWUHdJREFRQUJBb0lCQURQOWN6NlY1OU50dXBMYQpoWG9KbldLbHkxTkI2b2N0bXQ1eUM3Yzh4ZW1YQXZ6Vld1Qm5yQlJwYjdTZWIwV09ZblJ0bmxidHVMeWdMVEpHCncrdWtBK1NNZnB1ZnFmbkJXbHJqZ0tzMXMvU0N4Vk1xWjkrbWlzTUJIbGQzU3lmNmNBa2RPcTFJeEN0b2RNWW4KTEdNY0VSNkE5SzNoSXh3VW5xQnpKY3lmaVdsek5uTjNVM0thSG1PaG5COW9OMVZ4cGlXc2dGODRVSDJvWXNIdQphZG1JT1hKQ2RUVjh2ditGR3pNaWY4TTJEYVBiRGFEdG9KU3VnV29ZODhKUWlHbCtGSEtReWV1N2lkV1VuK2E2Ck9qbVByYVd0WlRuV2JkQ21OeWliZ1BZUk5YQXdHQkNLam9Ma1JNdmpOU011TXR1SlVZb0NxR05nbmY4NDNJWDAKYXdaR1gxRUNnWUVBNXNBVnQrZWYzdUxSYzRHVGgzZ3BFSnlWSldVaytCVnBlOWp5VGhNNUIrdFF0VGEwN3Q0OApaeEg0U0pYeEp6RGQzSk9adFpKU2lrYThyZ0F0QzBrQklES05EU0l0Q3BaR0VNWStpNFE0djBHQWxtRURJb1NPCkZGSENydkIvbjRaanJVYzhaOHRiU1hSaXQyb1hZRnBvQklxK282d1B1NTJVbUlDbm5zS2poK2NDZ1lFQTdhTmEKQnhoM0RGeDNzbUxSZzhLTXBXMHNPZ2VidEpucTN4YTE2VzdaRU9jS05vemRmM3duMnFaOWhTbmRsd1FXbk9QaApDbjlmbkw2VFUwbDdVSGRHQnBXKzVHN2tyRWFHakVDU0tJZU9UWDVJZnVuK1c1dlh0Y2JoVFpUUm9iWTdpMTBECk5KZFZQR1ZxRnRBdmZZS3NRZWlNaHgyTXl1bkZ3bThCeis4MXZPa0NnWUVBeHFPNnpvcnRuREREdjl4ZkJQS1MKMzBMOFBhblpibHI0YUczMUFQTVpXRkRoTkc1NHlWeTB3TUcrYTdYd1dCMkFFQjliNWV1bGRIVzZmVXVYZXl5SQpkSkF2em91S0hLQUtodXd4OFdhTWQxQkxBcWxXeW9NejBCZ21NUFRGMkswVzU1YjRVQjNHWXZCMi9vK3N1Zng1CnFOZExqNWhTSHQ5amlQalAweUtaSmljQ2dZQVZtMy9KRzJGZnYxeW0zTE5lUHFkWk4xY0pJNzMxcXZVZk9jcHEKb2tpY2I5V081am9ScUhGYlFUNHlpSDc4aDBPakdsbFJBZ25vajYrSHFDdk5RanNjeUNybVRHc0FleDBxellnOApwUzY4UHhwS1orNVgyQkRDRjRaTzdwUXRGd3ZNL1RVWWFCWTQrcU90MVZvc0dHQ2ZVWXpOa0ZOSDgvK21VVCtoCjc0YzhXUUtCZ0FUMmV4Q2cvUytqa2ZmNXlUcmFpQkhVL29MNmZSV0I1WjBCTW9RNCtuZHNaby9pYWpQQW9wdFEKekxjbnh5b0phd1puUVVrR1ZycG5INVQ1MkNEcGVZM3VobzdUQzcyMmhNZWZRSU05ajllY3gzTTVPQVMwUzBsOAo5eGpaZ2NQczA3U1BmcVlrTnQ2R1JiNlArbnRoTU9nd2UrbmM4bGhvYlA1Z2RUb0RVVThXCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==
案例1:创建只能访问某个 namespace 的 User
我们想要创建一个 User Account,只能访问 dev-project 这个命名空间,对应的用户信息如下所示:
username: dev-user
group: developer
1. 创建用户凭证
Kubernetes 没有 User Account 的 API 对象,不过要创建一个用户帐号的话也是挺简单的,可以使用 OpenSSL 命令创建 X509证书来创建一个 User。
① 生成私钥:
openssl genrsa -out dev-user.key 2048
② 生成CSR:
openssl req -new -key dev-user.key -out dev-user.csr -subj "/CN=dev-user/O=developer"
③ 签发证书:
如果是自签名证书,执行以下操作;对于非自签名证书(通常由受信任的证书颁发机构 CA 签发),将CSR文件发给 CA机构即可。
如果使用的是 kubeadm 安装的集群,CA 相关证书位于 /etc/kubernetes/pki/ 目录下面,我们会利用该目录下面的 ca.crt 和 ca.key两个文件来批准上面的证书请求。生成最终的证书文件,我们这里设置证书的有效期为 3650 天:
openssl x509 -req -in dev-user.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out dev-user.crt -days 3650
现在查看我们当前文件夹下面是否生成了一个证书文件:
[root@node-01 dev-project]# ls
dev-user.crt dev-user.csr dev-user.key
- dev-user.crt:私钥文件
- dev-user.csr:证书请求文件
- dev-user.key:证书文件
④ 验证证书:
使用以下命令可以验证我们的证书是不是CA签发的
[root@node-01 dev-project]# openssl verify -CAfile /etc/kubernetes/pki/ca.crt dev-user.crt
dev-user.crt: OK
⑤ 创建凭证和上下文
-
先创建用户凭证:凭证用于身份验证,确保在向 Kubernetes API 服务器发起请求时,该用户身份的合法性。
-
再设置上下文:上下文包含了连接到 Kubernetes 集群所需的所有信息,包括使用的集群、命名空间和用户。
-
在创建上下文之前,通常需要先创建用户凭证,因为上下文需要引用这些凭证。
创建凭证:
kubectl config set-credentials dev-user --client-certificate=dev-user.crt --client-key=dev-user.key
创建上下文:
kubectl config set-context dev-user-context --cluster=kubernetes --namespace=dev-project --user=dev-user
到这里,我们的用户 dev-user 就已经创建成功了,现在我们使用当前的这个配置文件来操作 kubectl 命令的时候,应该会出现错误,因为我们还没有为该用户定义任何操作的权限呢:
[root@node-01 dev-project]# kubectl get pods --context=dev-user-context
Error from server (Forbidden): pods is forbidden: User "dev-user" cannot list resource "pods" in API group "" in the namespace "dev-project"
注意事项:
- 默认情况下,
kubectl编辑和使用的配置文件是~/.kube/config,可以通过--kubeconfig选项或KUBECONFIG环境变量来指定其他配置文件。 --embed-certs=true:当设置为true时,将提供的证书(如客户端证书、客户端密钥和 CA 证书)的内容直接嵌入到配置文件中,而不是仅仅保存这些证书文件的路径,可以避免外部依赖。
在没有使用 --embed-certs=true 的情况下,kubeconfig 文件中的证书条目可能如下所示:
users:
- name: dev-user
user:
client-certificate: /etc/k8s/client.crt
client-key: /etc/k8s/client.key
而如果使用了 --embed-certs=true,则 kubeconfig 文件中相应的部分会变为:
users:
- name: dev-user
user:
client-certificate-data: LS0tLS1CR...
client-key-data: LS0tLS1L...
在这里,client-certificate-data 和 client-key-data 中包含的是证书的 Base64 编码内容,而不是文件路径。
2. 创建角色
用户创建完成后,接下来就需要给该用户添加操作权限,我们来定义一个 YAML 文件,创建一个允许用户操作 Deployment、Pod、ReplicaSets 的角色,如下定义:(dev-user-role.yaml)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: dev-user-role
namespace: dev-project
rules:
- apiGroups: ["", "apps"]
resources: ["deployments", "replicasets", "pods"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # 也可以使用['*']
其中 Pod 属于 core 这个 API Group,在 YAML 中用空字符就可以,而 Deployment 和 ReplicaSet 现在都属于 apps 这个 API Group(如果不知道则可以用 kubectl explain 命令查看),所以 rules 下面的 apiGroups 就综合了这几个资源的 API Group:["", "apps"],其中verbs 就是我们上面提到的可以对这些资源对象执行的操作,我们这里需要所有的操作方法,所以我们也可以使用['*']来代替。然后直接创建这个 Role:
[root@node-01 useraccount]# kubectl apply -f dev-user-role.yaml
role.rbac.authorization.k8s.io/dev-user-role created
3. 创建角色权限绑定
Role 创建完成了,但是很明显现在我们这个 Role 和我们的用户 dev-user 还没有任何关系,所以我们就需要创建一个 RoleBinding 对象,在 dev-project 这个命名空间下面将上面的 dev-user-role 角色和用户 dev-user 进行绑定:(dev-user-rolebinding.yaml)
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-user-rolebinding
namespace: kube-system
subjects:
- kind: User
name: dev-user
apiGroup: ""
roleRef:
kind: Role
name: dev-user-role
apiGroup: rbac.authorization.k8s.io
上面的 YAML 文件中我们看到了 subjects 字段,就是我们上面提到的用来操作集群的对象,对应上面的 User 帐号 dev-user,使用 kubectl 创建上面的资源对象:
[root@node-01 useraccount]# kubectl apply -f dev-user-rolebinding.yaml
rolebinding.rbac.authorization.k8s.io/dev-user-rolebinding created
4. 验证
现在我们可以使用 dev-user-context 上下文来操作集群了:
[root@node-01 17-security]# kubectl -n dev-project apply -f nginx-dep.yaml --context=dev-user-context
deployment.apps/nginx-dep created
[root@node-01 17-security]# kubectl -n dev-project get pods --context=dev-user-context
NAME READY STATUS RESTARTS AGE
nginx-dep-79f77dd7b7-gbbb9 1/1 Running 0 50s
nginx-dep-79f77dd7b7-p4bln 1/1 Running 0 50s
[root@node-01 17-security]# kubectl -n dev-project get deployment,rs --context=dev-user-context
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-dep 2/2 2 2 2m32s
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-dep-79f77dd7b7 2 2 2 2m32s
如果去获取其他的资源对象呢,可以看到并没有权限获取,因为我们没有为当前操作用户指定其他对象资源的访问权限,是符合我们的预期的。这样我们就创建了一个只有单个命名空间访问权限的普通 User 。
[root@node-01 17-security]# kubectl -n dev-project get cm --context=dev-user-context
Error from server (Forbidden): configmaps is forbidden: User "dev-user" cannot list resource "configmaps" in API group "" in the namespace "dev-project"
[root@node-01 17-security]#
[root@node-01 17-security]# kubectl -n default get cm --context=dev-user-context
Error from server (Forbidden): configmaps is forbidden: User "dev-user" cannot list resource "configmaps" in API group "" in the namespace "default"
案例2: 创建只能访问某个 namespace 的 SA
上面我们创建了一个只能访问某个命名空间下面的 User Account,我们前面也提到过 subjects 下面还有一种类型的主题资源:ServiceAccount,现在我们来创建一个集群内部的用户只能操作 default 这个命名空间下面的 pods 、deployments 和 replicasets 对象.
1. 首先来创建一个 SA
kubectl -n default create sa namespace-user
我们也可以定义成 YAML 文件的形式来创建:
apiVersion: v1
kind: ServiceAccount
metadata:
name: namespace-user
namespace: default
2. 然后新建一个 Role
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: namespace-user-role
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list"]
- apiGroups: ["", "apps"]
resources: ["deployments", "replicasets", "pods"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
kubectl apply -f namespace-user-role.yaml
3. 然后创建一个 RoleBinding
将上面的 namespace-user 和角色 namespace-user-role 进行绑定:
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: namespace-user-rolebinding
namespace: default
subjects:
- kind: ServiceAccount
name: namespace-user
roleRef:
kind: Role
name: namespace-user-role
apiGroup: rbac.authorization.k8s.io
4. 创建 config
①创建脚本
#!/bin/bash
# The script returns a kubeconfig for the service account given
# you need to have kubectl on PATH with the context set to the cluster you want to create the config for
# Cosmetics for the created config
clusterName=kubernetes #kubernetes集群名
# your server address goes here get it via `kubectl cluster-info`
server=https://192.168.17.110:6443 #集群中api-server地址
# the Namespace and ServiceAccount name that is used for the config
namespace=default #哪个命名空间
serviceAccount=xxhf #为哪个sa生成
######################
# actual script starts
set -o errexit
secretName=$(kubectl --namespace $namespace get serviceAccount $serviceAccount -o jsonpath='{.secrets[0].name}')
ca=$(kubectl --namespace $namespace get secret/$secretName -o jsonpath='{.data.ca\.crt}')
token=$(kubectl --namespace $namespace get secret/$secretName -o jsonpath='{.data.token}' | base64 --decode)
echo "
---
apiVersion: v1
kind: Config
clusters:
- name: ${clusterName}
cluster:
certificate-authority-data: ${ca}
server: ${server}
contexts:
- name: ${serviceAccount}@${clusterName}
context:
cluster: ${clusterName}
namespace: ${namespace}
user: ${serviceAccount}
users:
- name: ${serviceAccount}
user:
token: ${token}
current-context: ${serviceAccount}@${clusterName}
脚本说明:使用脚本的时候按需求修改字段
②基于脚本创建 config
./sa-to-kubeconfig.sh >config
6. 验证
[root@node-01 .kube]# kubectl --kubeconfig=config get pods
NAME READY STATUS RESTARTS AGE
nginx-dep-587b6fd57c-dwn9g 1/1 Running 0 4d9h
redis-pv-sts-0 1/1 Running 0 4d6h
redis-pv-sts-1 1/1 Running 0 4d6h
[root@node-01 .kube]#
[root@node-01 .kube]#
[root@node-01 .kube]# kubectl --kubeconfig=conifg get cm
Error from server (Forbidden): configmaps is forbidden: User "system:serviceaccount:default:namespace-user" cannot list resource "configmaps" in API group "" in the namespace "default"
SA 和 User 的区别
在 Kubernetes 的 kubeconfig 文件中,users.user 字段的内容可以包含两种不同的身份验证方法:client-certificate-data 和 client-key-data,以及 token。这两种方法的区别主要在于它们的身份验证机制和用途。下面详细解释这两者的区别:
①Client Certificate Authentication(user)
-
字段:
client-certificate-data: 包含客户端证书的 Base64 编码数据。client-key-data: 包含客户端证书私钥的 Base64 编码数据。
-
用途:
- 客户端证书身份验证是一种基于公钥的身份验证方法。Kubernetes 集群在接收到请求时,会验证请求者提供的证书是否有效。
- 客户端证书一般是由受信任的证书颁发机构(CA)签发的。Kubernetes API 服务器会使用 CA 来验证客户端证书的有效性。
-
优点:
- 提供强身份验证,适用于对安全性要求较高的场景。
- 不需要经常更新(除非证书到期),适合长期使用。
-
配置示例:
users: - name: my-user user: client-certificate-data: <Base64-encoded-client-cert> client-key-data: <Base64-encoded-client-key>
②Token Authentication(SA)
-
字段:
token: 包含用于身份验证的令牌,通常是一个短字符串。
-
用途:
- Token 身份验证是一种简单而有效的身份验证方式。令牌通常与 Kubernetes 中的服务账户相关联,用于快速验证。
- 服务账户的令牌是由 Kubernetes 自动生成并管理的,用户无需手动创建和更新。
-
优点:
- 简单易用,适合快速配置。
- 通常用于服务账户和应用程序之间的身份验证。
-
配置示例:
users: - name: my-user user: token: <service-account-token>
③总结
-
使用场景:
- 使用 client certificate authentication 通常用于需要高安全性的场合,如管理员或需要进行敏感操作的用户。
- 使用 token authentication 更适合自动化工具和服务账户的场景,因为它的配置和管理相对简单。
-
安全性:
- 客户端证书提供了更强的安全性,因为它基于公钥基础设施(PKI),并且可以使用更复杂的策略进行管理。
- Token 在过期后可能需要更新,虽然 Kubernetes 会自动管理服务账户的令牌,但这可能在某些情况下成为管理负担。
选择使用哪种身份验证方式取决于具体的使用场景、管理策略和安全需求。
11.2 网络策略 Network Policy
11.2.1 网络策略是什么
在 Kubernetes 中部署应用时,通常会根据不同的项目、应用类型和部门划分不同的 Namespace,以实现应用的隔离。然而,这种隔离实际上是“软隔离”。例如,通过 Kubernetes 的 Service 名称结合 Namespace 名称,可以跨 Namespace 访问服务。同时,Pod 的 IP 地址也允许全局互访。
这种情况下,集群的服务安全性较低。如果某个服务被入侵,攻击者可能会利用各种手段进行全局扫描。因此,有效的访问控制显得尤为重要。在传统架构中,例如虚拟机和物理机,可以通过 iptables、firewalld 等工具进行访问控制。然而,在高度弹性和可扩展的 Kubernetes 平台上,这些方法已经无法满足需求。因此,Kubernetes 引入了网络策略(Network Policy)的概念,结合 CNI 网络插件实现细粒度的访问控制。
注意事项:在使用网络策略之前,集群中需要安装支持网络策略的插件,如 Calico 或 Weave,而 Flannel 则不支持该功能。
11.2.2 定义网络策略
以下是一个 Kubernetes 网络策略示例:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-np
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,意味着该策略作用于当前命名空间下符合该标签的 Pod。如果该字段为空,则适用于所有 Pod。 - policyTypes:定义网络策略类型。支持入站(Ingress)和出站(Egress)规则。出站表示符合
podSelector的 Pod 的出口流量限制,入站则是对符合条件的 Pod 的访问限制。如果未指定policyTypes,则默认为 Ingress;如果未指定,但有出站规则,则会自动配置 Egress。 - ingress:管理入口流量。每个 NetworkPolicy 可以包含一个 Ingress 白名单,每条规则允许匹配
from和ports部分的流量。其中,from表示访问来源,可配置为ipBlock、namespaceSelector、podSelector。如果为空,则匹配所有来源。ports表示可访问的端口列表,若为空则匹配所有端口。- ipBlock:指定匹配的 IP 块,可以是单个 IP 或网段,同时支持 IPv6 格式。
except为可选参数,用于排除某些地址。 - namespaceSelector:匹配具有指定标签的 Namespace 下的所有 Pod。当前示例中,Ingress 的
from配置了namespaceSelector,表示允许来自project=myproject标签的 Namespace 下的所有 Pod。
- ipBlock:指定匹配的 IP 块,可以是单个 IP 或网段,同时支持 IPv6 格式。
- egress:管理出口流量。NetworkPolicy 同样可以包含一个 Egress 白名单,其可配置参数为
to和ports。其中ports表示可访问的目标端口,to表示访问的目标,可以配置为ipBlock、namespaceSelector、podSelector。
根据上述概念,示例的含义如下:
- 隔离 default 命名空间下具有
role=db标签的 Pod。 - 允许 IP 范围 172.17.0.0 ~ 172.17.255.255 访问 default 命名空间下的
role=dbPod 的 TCP 6379 端口。 - 允许具有
project=myproject标签的 Namespace 下的所有 Pod 访问 default 命名空间的role=dbPod 的 TCP 6379 端口。 - 允许 default 命名空间(与 NetworkPolicy 同一命名空间)下具有
role=frontend标签的 Pod 访问 default 命名空间的role=dbPod 的 TCP 6379 端口。 - 允许带有
role=db标签的 Pod 访问 10.0.0.0/24 网段的 TCP 5978 端口。
案例1:在线电商平台
项目架构
- 前端服务(frontend):处理用户请求,显示商品。
- 后端服务(backend):处理业务逻辑,与数据库交互。
- 数据库服务(database):存储用户和商品数据。
1. 标签设置
- 前端服务 Pod 标签:role: frontend
- 后端服务 Pod 标签:role: backend
- 数据库服务 Pod 标签:role: db
2. NetworkPolicy
# 允许后端访问数据库
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-db-access
namespace: default
spec:
podSelector:
matchLabels:
role: db
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: backend
ports:
- protocol: TCP
port: 6379 # 假设数据库监听的端口
---
# 允许前端访问后端
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-backend-access
namespace: default
spec:
podSelector:
matchLabels:
role: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 8080 # 假设后端监听的端口
通过这两个网络策略:
- 前端服务无法直接访问数据库,增加了安全性。
- 只有后端服务可以与数据库进行交互,防止未授权访问。
这种网络策略确保了在线电商平台的服务之间的安全通信,增强了系统的安全性和可靠性。
评论区