Skip to main content

Kubernetes 理论与实践-3-安全与权限管理

· 12 min read

前文回顾

Kubernetes 理论与实践-1-基础-Pods, ReplicaSets, Services, Deployments

Access Kubernetes API as Administrator

kubectl config view \
-o jsonpath='{.clusters[?(@.name=="k3d-mycluster")].cluster.server}'

ls usercode/certs

Access Kubernetes API for users

Understanding the process

每一个 API 请求都会经过三个阶段确认

  • Authentication 认证: 确定用户身份
  • Authorization 授权: 认证成功后,将向用户发放授权证明/token 等信息 (可选择不同的认证机制: Node, ABAC, Webhooks, RBAC)
  • Passing the admission control 通过准入控制: 准入控制器在 API 请求被执行应用为持久化状态前会拦截它们,并且可以修改请求,属于高级主题。

认证机制

  • 节点(Node): 节点授权根据 kubelet 被调度运行的 Pod 来授予权限。
  • ABAC (Attribute-based access control): 基于属性的访问控制(ABAC)是通过属性结合策略实现的,现已被视为过时,推荐使用 RBAC 替代。
  • Webhooks: Webhook 通过 HTTP POST 请求用于事件通知。
  • RBAC (Role-based access control): 基于角色的访问控制(RBAC)根据单个用户或组的角色来授予(或拒绝)对资源的访问权限。

实践-用于测试的服务定义

go-demo-2.yml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: go-demo-2
annotations:
kubernetes.io/ingress.class: "nginx"
ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- host: go-demo-2.com
http:
paths:
- path: /demo
pathType: ImplementationSpecific
backend:
service:
name: go-demo-2-api
port:
number: 8080

---
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-demo-2-db
spec:
selector:
matchLabels:
type: db
service: go-demo-2
strategy:
type: Recreate
template:
metadata:
labels:
type: db
service: go-demo-2
vendor: MongoLabs
spec:
containers:
- name: db
image: mongo:3.3

---
apiVersion: v1
kind: Service
metadata:
name: go-demo-2-db
spec:
ports:
- port: 27017
selector:
type: db
service: go-demo-2

---
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-demo-2-api
spec:
replicas: 3
selector:
matchLabels:
type: api
service: go-demo-2
template:
metadata:
labels:
type: api
service: go-demo-2
language: go
spec:
containers:
- name: api
image: vfarcic/go-demo-2
env:
# - name: DB
# value: go-demo-2-db
- name: DB_ENV
value: GO_DEMO_2_DB_SERVICE_HOST
readinessProbe:
httpGet:
path: /demo/hello
port: 8080
periodSeconds: 1
livenessProbe:
httpGet:
path: /demo/hello
port: 8080

---
apiVersion: v1
kind: Service
metadata:
name: go-demo-2-api
spec:
ports:
- port: 8080
selector:
type: api
service: go-demo-2
kubectl create \
-f go-demo-2.yml \
--record --save-config

实践-创建用户

mkdir keys

# Generate private key
openssl genrsa \
-out keys/jdoe.key 2048

# Generate a certificate by the private key
# CN: username, O: organization
openssl req -new \
-key keys/jdoe.key \
-out keys/jdoe.csr \
-subj "/CN=jdoe/O=devs"

We have keys/jdoe.key and keys/jdoe.csr now.

我们将使用集群的证书来生成最后的证书。

ls usercode/certs # 集群证书的位置
# /usercode/certs/client-ca.key: 私匙
# /usercode/certs/client-ca.crt: 证书签名请求

openssl x509 -req \
-in keys/jdoe.csr \
-CA /usercode/certs/client-ca.crt \
-CAkey /usercode/certs/client-ca.key \
-CAcreateserial \
-out keys/jdoe.crt \
-days 365
# /usercode/certs/jdoe.crt: 数字证书

We made the certificate keys/jdoe.crt valid fro a whole year (365 days).

cp /usercode/certs/server-ca.crt /usercode/certs/keys

ls -1 keys

Jdoe 不需要 jdoe.csr 文件(仅用来生成 jdoe.crt 文件),其他文件他们团队还是需要的。

获得集群地址

SERVER=$(kubectl config view \
-o jsonpath='{.clusters[?(@.name=="k3d-mycluster")].cluster.server}')

echo $SERVER

最终需要的配置与文件

  • 新证书(jdoe.crt)
  • 密钥(jdoe.key)
  • 集群证书颁发机构(ca.crt)
  • 服务器地址: Jdoe 可以配置其 kubectl 安装

实践-以用户身份访问集群

# SERVIER=https://172.30.1.2:6443

# 配置集群 create cluster called jdoe
kubectl config set-cluster jdoe \
--certificate-authority \
/usercode/certs/keys/server-ca.crt \
--server $SERVER

# 配置用户凭证 (如客户端证书)
kubectl config set-credentials jdoe \
--client-certificate keys/jdoe.crt \
--client-key keys/jdoe.key

# 创建上下文(绑定用户和集群)
kubectl config set-context jdoe \
--cluster jdoe \
--user jdoe

kubectl config use-context jdoe

kubectl config view

未授权访问 kubernetes 对象

kubectl get pods

kubectl get all

可以看到所有对象却无法访问,说明 jdoe 已经有访问集群的权限,但是没有获取相关对象的能力。

访问控制-RBAC 基于角色的访问控制

Rules 规则

  • Rule is a set of operations (verbs), resources, and API groups.
  • Verbs: get, list, create, update, patch, watch, proxy, redirect, delete, deletecollection, *.
  • RBAC uses the rbac.authorization.k8s.io group.

Roles 角色

  • Role is a collection of Rules.
  • 角色绑定到某个 Namespace 上,我们也可以用 ClusterRole 创建在整个集群可用的角色。

Subjects 主体

  • 主体定义了执行操作的实体。主体可以是用户、组或服务账户。
  • 用户 user 是位于集群外部的人员或进程。服务账户 service account 用于在 Pod 内部运行的进程,这些进程需要使用 API。
  • 组是用户或服务账户的集合。某些组是默认创建的(例如,cluster-admin)。

RoleBindings 角色绑定

  • RoleBindings 将 Subjects 绑定到 Roles。
  • 由于 Subject 定义了用户,RoleBinding 实际上就是将用户(或用户组或服务账户)绑定到 Role,并授予他们在特定命名空间内对某些对象执行操作的权限。
  • 与 Role 类似,RoleBinding 也有一个集群范围的替代品叫做 ClusterRoleBinding。唯一的区别在于它们的范围不受限于命名空间,而是应用于整个集群。

实践-查看预定义的 ClusterRole

以 admin 的用户查看集群

kubectl config use-context k3d-mycluster
kubectl get all

使用指令确认用户权限

kubectl auth can-i get pods --as jdoe
# no
kubectl get roles
# No resources found: kubernete cluster doesn't have default predefined roles.

kubectl get clusterroles

我们会看到一些内容,对于 system: 开头的角色,不要去更改它,会造成集群正常运行。

kubectl get clusterroles | grep -v system

kubectl describe clusterrole view
kubectl describe clusterrole edit
kubectl describe clusterrole admin
kubectl describe clusterrole cluster-admin

view

edit

admin

cluster-admin

kubectl auth can-i "*" "*"

实践-创建 RoleBinding

创建对默认 Namespace 中所有对象有查看访问权利的 RoleBInding。

kubectl create rolebinding jdoe \
--clusterrole view \
--user jdoe
--namespace default \
--save-config

kubectl get rolebindings
  • 创建一个 jdoe rolebinding,绑定 jdoe user 与 view clusterrole,namespace 为 default。
  • clusterrole 对象也可以用于在某些特定的 namespace, 权利的范围是时使用绑定类型 rolebinding 定义的,而不是 role 类型。
kubectl describe rolebinding jdoe

  • 这里没有看到 Namespace,可能会误以为适用于所有命名空间,这个假设是错误的。
  • 请记住,RoleBinding 始终与特定的命名空间相关联。(?当前的运行环境是 default namespace ?)

检查命名空间范围: 相同的 RoleBinding 不应在其他任何位置可用:

kubectl --namespace kube-system \
describe rolebinding jdoe

使用 kubectl auth can-i

kubectl auth can-i get pods \
--as jdoe
# Yes

kubectl auth can-i get pods \
--as jdoe --all-namespaces
# No

删除 rolebinding

kubectl delete rolebinding jdoe

实践-创建 ClusterRoleBinding

crb-vew.yml

apiVersion: rbac.authorization.k8s.io/v1
kind: CLusterRoleBinding
metadata:
name: view
subjects:
- kind: User
name: jdoe
apiGroup: rbac.authorization.k8s.io
- roleRef:
kind: ClusterRole
name: view
apiGroup: rbac.authorization.k8s.io
kubectl create -f crb-view.yml \
--record --save-config

kubectl describe clusterrolebinding \
view

kubectl auth can-i get pods \
--as jdoe --all-namespaces
# Yes

实践-RoleBindings 与命名空间组合使用

团队命名空间

创建 dev命名空间,允许执行任何操作。

rb-dev.yml

apiVersion: v1
kind: Namespace
metadata:
name: dev

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev
namespace: dev
subjects:
- kind: User
name: jdoe
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: admin
apiGroup: rbac.authorization.k8s.io

create

kubectl create -f rb-dev.yml \
--record --save-config

verify

kubectl --namespace dev auth can-i \
create deployments --as jdoe
# Yes

kubectl --namespace dev auth can-i \
delete deployments --as jdoe
# Yes

kubectl --namespace dev auth can-i \
"*" "*" --as jdoe
# No

用户特定的命名空间 User specific namespace

rb-jdoe.yml

apiVersion: v1
kind: Namespace
metadata:
name: jdoe

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: jdoe
namespace: jdoe
subjects:
- kind: User
name: jdoe
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io

授予 cluster-admin 让 jdoe 能够授权其他人查看命名空间。

create

kubectl create -f rb-jdoe.yml \
--record --save-config

check

kubectl --namespace jdoe auth can-i \
"*" "*" --as jdoe
# Yes

实践-以发布管理员身份授予访问权限

发布经理职责

发布经理职责: 从团队空间发布到 default 空间,发布新版本。

发布经理所需的最低权限是对 Pod、Deployment 和 ReplicaSet 执行作。具有该角色的人应该能够做几乎所有与 Pod 相关的事情,而 Deployment 和 ReplicaSet 的允许作应该限制为  creategetlistupdate  和  watch

对比下 ClusterRole admin

kubectl describe clusterrole admin

子资源和 API 组

crb-release-manager.yml: 创建 ClusterRole

# 1. 先定义 ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: release-manager-role
rules:
- apiGroups: [""] # 核心组
resources: ["pods", "pods/attach", "pods/exec", "pods/log", "pods/status"]
verbs: ["*"]
- apiGroups: ["", "apps", "extensions"] # deployments 属于 apps/v1
resources: ["deployments", "replicasets"]
verbs: ["create", "get", "list", "watch"]
---
# 2. 再绑定 ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: jdoe
subjects:
- kind: User
name: jdoe
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: release-manager-role
apiGroup: rbac.authorization.k8s.io
  • 第一组规则指定  Pod  资源以及一些子资源(attachexeclog  和  status)。由于我们没有创建 Pod 代理 proxy 或端口转发,因此它们不包括在内。
  • 第二组规则是为  deployments  和  replicasets  资源设置的。考虑到我们决定对它们进行更严格的限制,我们指定了更具体的动词 ,只允许发布经理  creategetlist  和  watch。由于我们没有指定  deletedeletecollectionpatch  和  update  动词,因此发布经理将无法执行相关作。

创建发布经理 RoleBinding

kubectl create \
-f crb-release-manager.yml \
--record --save-config

kubectl describe \
clusterrole release-manager

分配给该角色的用户(几乎)可以使用 Pod 执行任何作,而他们对 Deployment 和 ReplicaSet 的权限仅限于创建和查看。他们将无法更新或删除它们。禁止访问任何其他资源。

kubectl --namespace default auth \
can-i "*" pods --as jdoe
# Yes

kubectl --namespace default auth \
can-i create deployments --as jdoe
# Yes

kubectl --namespace default auth can-i \
delete deployments --as jdoe
# No

# Switch to jdoe in default namespace
kubectl config use-context jdoe
kubectl --namespace default \
create deployment db \
--image mongo:3.3
kubectl --namespace default \
delete deployment db

# jdoe in his own namespace
kubectl config set-context jdoe \
--cluster jdoe \
--user jdoe \
--namespace jdoe

kubectl config use-context jdoe

kubectl create deployment db --image mongo:3.3

kubectl delete deployment db

# 将新用户添加到他的命名空间
kubectl create rolebinding mgandhi \
--clusterrole=view \
--user=mgandhi \
--namespace=jdoe

实践-将用户替换为用户组

回去查看下之前在 jdoe.csr 中写入的信息

openssl req -in /usercode/certs/keys/jdoe.csr \
-noout -subject

我们除了看到用户名 CN,之外,我们还注意到了 devs organisation,我们可以利用这一属性。

groups.yml: 将 subjects 中的 User 换成 Group,实现向组织devs中的所有用户授予权限。

apiVersion: v1
kind: Namespace
metadata:
name: dev

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev
namespace: dev
subjects:
- kind: Group
name: devs
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: admin
apiGroup: rbac.authorization.k8s.io

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: view
subjects:
- kind: Group
name: devs
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: view
apiGroup: rbac.authorization.k8s.io

首先切换回 default k3d-mycluster 上下文

kubectl config use-context k3d-mycluster

kubectl apply -f groups.yml \
--record

verify

kubectl --namespace dev auth \
can-i create deployments --as jdoe
# No

此处回复 No 是正常,目前使用的证书是 defualt k3d-mycluster 上下文里面的证书,它并没有 jdoe 用户的 organiazation 属性,所以需要先切换到 jdoe 上下文。

kubectl config use-context jdoe

kubectl --namespace dev \
create deployment new-db \
--image mongo:3.3
# OK

任何主题中含有 /O=devs 的证书的用户都将在 dev 命名空间拥有与 Jdoe 相同的权限,并在其他任何地方拥有 view 权限。