理论基础
认证:
如上图步骤 1 所示,建立 TLS 后, HTTP 请求将进入认证(Authentication)步骤。认证模块包含客户端证书、密码、普通令牌、引导令牌等。可以指定多个认证模块,在这种情况下,服务器依次尝试每个验证模块,直到其中一个成功。
授权:
如上图的步骤 2 所示,将请求验证为来自特定的用户后,请求必须被鉴权。
请求必须包含请求者的用户名、请求的行为以及受该操作影响的对象。 如果现有策略声明用户有权完成请求的操作,那么该请求被鉴权通过。
准入控制:
如上图的步骤 3 所示,准入控制模块是可以修改或拒绝请求的软件模块。 除鉴权模块可用的属性外,准入控制模块还可以访问正在创建或修改的对象的内容。
Kubernetes中的账户
Kubernetes的账户类型有两种:User Account和Service Account
User Account
理论基础
User Account是给使用人员用的,用于与API Server交互进行集群操作与管理。
使用HTTP 请求kubernetes API:
[root@clientvm ~]# kubectl proxy --port=8080
Starting to serve on 127.0.0.1:8080
[root@clientvm ~]# curl http://localhost:8080/api/v1/namespaces
{
"kind": "NamespaceList",
"apiVersion": "v1",
"metadata": {
"selfLink": "/api/v1/namespaces",
"resourceVersion": "3866057"
},
[root@clientvm ~]# curl http://localhost:8080/apis/apps/v1/namespaces/kube-system/deployments
{
"kind": "DeploymentList",
"apiVersion": "apps/v1",
"metadata": {
"selfLink": "/apis/apps/v1/namespaces/kube-system/deployments",
"resourceVersion": "3870674"
},
"items": [
{
"metadata": {
"name": "coredns",
Kubernetes 使用身份认证插件利用客户端证书、持有者令牌(Bearer Token)、身份认证代理(Proxy) 或者 HTTP 基本认证机制来认证 API 请求的身份。
X509客户端证书认证
为了让普通用户能够通过认证并调用 API,需要执行一下几个步骤。
首先,该用户必须拥有 Kubernetes 集群签发的证书, 然后将该证书作为 API 调用的 Certificate 头或通过 kubectl 提供。
1) 创建私钥
[root@clientvm ~]# openssl genrsa -out user1.key 2048
2) 创建CSR
openssl req -new -key user1.key -out user1.csr -subj "/CN=user1/O=kubernetes"
注: 以下步骤为使用CA证书签名,如果无法访问CA证书,也可以替换为步骤3、4直接使用在集群中签名,任选其一
## SCP user1.csr到master节点
[root@clientvm ~]# scp user1.csr master:/etc/kubernetes/pki/
## 登录到master节点,签名证书
[root@master pki]# pwd
/etc/kubernetes/pki
[root@master pki]# openssl x509 -req -in user1.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out user1.crt
## 将生成的crt证书 copy 回clientvm
[root@master pki]# scp user1.crt clientvm:/root/
3) 证书签名
[root@clientvm ~]# cat user1.csr | base64 | tr -d "\n"
替换入下文件中的request内容
[root@clientvm ~]# cat user1-csr.yaml
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: user1
spec:
groups:
- system:authenticated
request: NEED TO REPLACE
signerName: kubernetes.io/kube-apiserver-client
usages:
- client auth
4)签名,获取证书
[root@clientvm ~]# kubectl apply -f user1-csr.yaml
certificatesigningrequest.certificates.k8s.io/user1 created
[root@clientvm ~]# kubectl get csr
NAME AGE SIGNERNAME REQUESTOR CONDITION
user1 18s kubernetes.io/kube-apiserver-client kubernetes-admin Pending
[root@clientvm ~]# kubectl certificate approve user1
certificatesigningrequest.certificates.k8s.io/user1 approved
[root@clientvm ~]# kubectl get csr/user1 -o yaml
## 证书的内容使用 base64 编码,存放在字段 status.certificate
保存status.certificate内容到一个文件aaa,生成证书 crt
[root@clientvm ~]# cat aaa |base64 --decode >user1.crt
5) 添加user1到kubeconfig
[root@clientvm ~]# kubectl config set-credentials user1 --client-key=user1.key --client-certificate=user1.crt --embed-certs=true
User "user1" set.
6) 添加上下文
[root@clientvm ~]# kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* kubernetes-admin@kubernetes kubernetes kubernetes-admin
[root@clientvm ~]# kubectl config set-context user1@kubernetes --cluster=kubernetes --user=user1
Context "user1@kubernetes" created.
7) 把上下文切换为user1
[root@clientvm ~]# kubectl config use-context user1@kubernetes
Switched to context "user1@kubernetes".
##此处报权限错误是因为只做了认证,还没有授权
[root@clientvm ~]# kubectl get pod
Error from server (Forbidden): pods is forbidden: User "user1" cannot list resource "pods" in API group "" in the namespace "default"
管理配置上下文
基本步骤
1) 添加用户
kubectl config set-credentials -h
2) 添加 cluster
kubectl config set-cluster -h
3) 添加 context
kubectl config set-context
4) 切换使用
[root@clientvm ~]# kubectl config current-context
kubernetes-admin@kubernetes
[root@clientvm ~]# kubectl config use-context kubernetes-admin@kubernetes
Switched to context "kubernetes-admin@kubernetes".
[root@clientvm ~]# kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* kubernetes-admin@kubernetes kubernetes kubernetes-admin
user1@kubernetes kubernetes user1
Service Account
当你(自然人)访问集群时(例如,使用 kubectl
),API 服务器将你的身份验证为 特定的用户帐户(当前这通常是 admin
,除非你的集群管理员已经定制了你的集群配置)。 Pod 内的容器中的进程也可以与 api 服务器接触。 当它们进行身份验证时,它们被验证为特定的服务帐户(例如,default
)
服务账号通常由 API 服务器自动创建并通过 ServiceAccount
准入控制器 关联到集群中运行的 Pod 上。 相关Token会挂载到 Pod 中可预知的位置,允许集群内进程与 API 服务器通信。
当你创建 Pod 时,如果没有指定服务账户,Pod 会被指定给命名空间中的 default
服务账户。 也可以在Pod中添加serviceAccountName明确指定SA。
[root@clientvm ~]# kubectl get pod nginx2 -o yaml | grep -i serviceaccount
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
serviceAccount: default
serviceAccountName: default
创建SA:
[root@clientvm ~]# kubectl create serviceaccount -h
Create a service account with the specified name.
Aliases:
serviceaccount, sa
Examples:
# Create a new service account named my-service-account
kubectl create serviceaccount my-service-account
创建SA后,系统会自动创建一个token
[root@clientvm ~]# kubectl get sa,secret -n mytest
NAME SECRETS AGE
serviceaccount/default 1 18d
serviceaccount/my-sa 1 15s
NAME TYPE DATA AGE
secret/default-token-64sch kubernetes.io/service-account-token 3 18d
secret/my-sa-token-w6d7f kubernetes.io/service-account-token 3 15s
[root@clientvm ~]# kubectl get serviceaccounts my-sa -n mytest -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: "2020-12-16T05:24:27Z"
name: my-sa
namespace: mytest
resourceVersion: "4060881"
selfLink: /api/v1/namespaces/mytest/serviceaccounts/my-sa
uid: 85bd0753-5ebf-4c5f-8046-5dbd4391f739
secrets:
- name: my-sa-token-w6d7f
从私有镜像仓库获取镜像
Option 1) 在Pod中引 ImagePullSecrets
# pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: foo
namespace: awesomeapps
spec:
containers:
- name: foo
image: janedoe/awesomeapp:v1
imagePullSecrets:
- name: myregistryke
Option 2)添加 docker-registry 类似的secret到default SA
kubectl create secret docker-registry myregistrykey --docker-server=DUMMY_SERVER \
--docker-username=DUMMY_USERNAME --docker-password=DUMMY_DOCKER_PASSWORD
kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myregistrykey"}]}'
RBAC授权
基于角色(Role)的访问控制(RBAC)是一种基于组织中用户的角色来调节控制对 计算机或网络资源的访问的方法。
RBAC 鉴权机制使用 rbac.authorization.k8s.io
API 组 来驱动鉴权决定,允许你通过 Kubernetes API 动态配置策略。
要启用 RBAC,在启动 API 服务器 时将 --authorization-mode
参数设置为一个逗号分隔的列表并确保其中包含 RBAC
RBAC API 声明了四种 Kubernetes 对象:Role、ClusterRole、RoleBinding 和 ClusterRoleBinding。
Role 和 ClusterRole
RBAC 的 Role 或 ClusterRole 中包含一组代表相关权限的规则。 这些权限是纯粹累加的(不存在拒绝某操作的规则)。
Role 总是用来在某个名字空间 内设置访问权限;在你创建 Role 时,你必须指定该 Role 所属的名字空间。
与之相对,ClusterRole 则是一个集群作用域的资源。这两种资源的名字不同(Role 和 ClusterRole)是因为 Kubernetes 对象要么是名字空间作用域的,要么是集群作用域的, 不可两者兼具。
如果你希望在名字空间内定义角色,应该使用 Role; 如果你希望定义集群范围的角色,应该使用 ClusterRole。
role可包含的操作有:
"get", "list", "watch", "create", "update", "patch", "delete"
role常用可操作资源有:Pods, ConfigMaps, Deploments, Nodes, Secrets, Namespaces
核心组件角色
默认 ClusterRole | 默认 ClusterRoleBinding | 描述 |
---|---|---|
cluster-admin | system:masters 组 | 允许超级用户在平台上的任何资源上执行所有操作。 当在 ClusterRoleBinding 中使用时,可以授权对集群中以及所有名字空间中的全部资源进行完全控制。 当在 RoleBinding 中使用时,可以授权控制 RoleBinding 所在名字空间中的所有资源,包括名字空间本身。 |
admin | 无 | 允许管理员访问权限,旨在使用 RoleBinding 在名字空间内执行授权。 如果在 RoleBinding 中使用,则可授予对名字空间中的大多数资源的读/写权限, 包括创建角色和角色绑定的能力。 但是它不允许对资源配额或者名字空间本身进行写操作。 |
edit | 无 | 允许对名字空间的大多数对象进行读/写操作。 它不允许查看或者修改角色或者角色绑定。 不过,此角色可以访问 Secret,以名字空间中任何 ServiceAccount 的身份运行 Pods, 所以可以用来了解名字空间内所有服务账号的 API 访问级别。 |
Role范例:
[root@clientvm ~]# kubectl create role -h
Usage:
kubectl create role NAME --verb=verb --resource=resource.group/subresource [--resource-name=resourcename]
[--dry-run=server|client|none] [options]
Examples:
# Create a Role named "pod-reader" that allows user to perform "get", "watch" and "list" on pods
kubectl create role pod-reader --verb=get --verb=list --verb=watch --resource=pods
# Create a Role named "pod-reader" with ResourceName specified
kubectl create role pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
# Create a Role named "foo" with API Group specified
kubectl create role foo --verb=get,list,watch --resource=rs.extensions
# Create a Role named "foo" with SubResource specified
kubectl create role foo --verb=get,list,watch --resource=pods,pods/status
[root@clientvm ~]# cat role-example.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: mytest
name: pod-reader
rules:
- apiGroups: [""] # "" 标明 core API 组
resources: ["pods"]
verbs: ["get", "watch", "list"]
[root@clientvm ~]# kubectl apply -f role-example.yaml
role.rbac.authorization.k8s.io/pod-reader created
ClusterRole范例
[root@clientvm ~]# kubectl create clusterrole -h
Usage:
kubectl create clusterrole NAME --verb=verb --resource=resource.group [--resource-name=resourcename]
[--dry-run=server|client|none] [options]
Examples:
# Create a ClusterRole named "pod-reader" that allows user to perform "get", "watch" and "list" on pods
kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods
# Create a ClusterRole named "pod-reader" with ResourceName specified
kubectl create clusterrole pod-reader --verb=get --resource=pods --resource-name=readablepod
--resource-name=anotherpod
# Create a ClusterRole named "foo" with API Group specified
kubectl create clusterrole foo --verb=get,list,watch --resource=rs.extensions
# Create a ClusterRole named "foo" with SubResource specified
kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status
# Create a ClusterRole name "foo" with NonResourceURL specified
kubectl create clusterrole "foo" --verb=get --non-resource-url=/logs/*
# Create a ClusterRole name "monitoring" with AggregationRule specified
kubectl create clusterrole monitoring --aggregation-rule="rbac.example.com/aggregate-to-monitoring=true"
[root@clientvm ~]# cat clusterrole-example.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: secret-reader
rules:
- apiGroups: [""]
# 在 HTTP 层面,用来访问 Secret 对象的资源的名称为 "secrets"
resources: ["secrets"]
verbs: ["get", "watch", "list"]
[root@clientvm ~]# kubectl apply -f clusterrole-example.yaml
clusterrole.rbac.authorization.k8s.io/secret-reader created
[root@clientvm ~]# kubectl get role -n mytest
NAME CREATED AT
pod-reader 2020-12-16T04:41:56Z
[root@clientvm ~]# kubectl get clusterrole | grep secret
secret-reader 2020-12-16T04:44:26Z
RoleBinding 和 ClusterRoleBinding
角色绑定(Role Binding)是将角色中定义的权限赋予一个或者一组用户。RoleBinding 在指定的名字空间中执行授权,而 ClusterRoleBinding 在集群范围执行授权。
一个 RoleBinding 可以引用同一的名字空间中的任何 Role。 或者,一个 RoleBinding 可以引用某 ClusterRole 并将该 ClusterRole 绑定到 RoleBinding 所在的名字空间。 如果你希望将某 ClusterRole 绑定到集群中所有名字空间,你要使用 ClusterRoleBinding。
RoleBinding 范例
[root@clientvm ~]# kubectl create rolebinding -h
Usage:
kubectl create rolebinding NAME --clusterrole=NAME|--role=NAME [--user=username] [--group=groupname]
[--serviceaccount=namespace:serviceaccountname] [--dry-run=server|client|none] [options]
Examples:
# Create a RoleBinding for user1, user2, and group1 using the admin ClusterRole
kubectl create rolebinding admin --clusterrole=admin --user=user1 --user=user2 --group=group1
将clusterrole admin 绑定到之前创建的user account user1
[root@clientvm ~]# kubectl create rolebinding admin-to-user1 --clusterrole=admin --user=user1
rolebinding.rbac.authorization.k8s.io/admin-to-user1 created
[root@clientvm ~]# kubectl --context=user1 get pod
NAME READY STATUS RESTARTS AGE
nginx-taint 1/1 Running 0 37h
nginx2 1/1 Running 0 4d20h
ClusterRoleBinding 范例
[root@clientvm ~]# kubectl create clusterrolebinding -h
Usage:
kubectl create clusterrolebinding NAME --clusterrole=NAME [--user=username] [--group=groupname]
[--serviceaccount=namespace:serviceaccountname] [--dry-run=server|client|none] [options]
Examples:
# Create a ClusterRoleBinding for user1, user2, and group1 using the cluster-admin ClusterRole
kubectl create clusterrolebinding cluster-admin --clusterrole=cluster-admin --user=user1 --user=user2 --group=group1
准入控制(Admission Controllers)
中文:
https://kubernetes.io/zh/docs/reference/access-authn-authz/admission-controllers/
启用额外的准入控制器:
[root@master manifests]# grep admission ./kube-apiserver.yaml
- --enable-admission-plugins=NodeRestriction
关闭准入控制器:
## 添加如下参数
--disable-admission-plugins=PodNodeSelector,AlwaysDeny ...
默认启用的控制器:
NamespaceLifecycle, LimitRanger, ServiceAccount, TaintNodesByCondition, Priority, DefaultTolerationSeconds, DefaultStorageClass, StorageObjectInUseProtection, PersistentVolumeClaimResize, RuntimeClass, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, MutatingAdmissionWebhook, ValidatingAdmissionWebhook, ResourceQuota
网络策略
理论基础
如果你希望在 IP 地址或端口层面(OSI 第 3 层或第 4 层)控制网络流量, 则你可以考虑为集群中特定应用使用 Kubernetes 网络策略(NetworkPolicy)。 NetworkPolicy 是一种以应用为中心的结构,允许你设置如何允许 Pod 与网络上的各类网络“实体” 通信。
Pod 可以通信的 Pod 是通过如下三个标识符的组合来辩识的:
- 其他被允许的 Pods(例外:Pod 无法阻塞对自身的访问)
- 被允许的名字空间
- IP 组块(例外:与 Pod 运行所在的节点的通信总是被允许的, 无论 Pod 或节点的 IP 地址)
在定义基于 Pod 或名字空间的 NetworkPolicy 时,你会使用 选择算符 来设定哪些流量 可以进入或离开与该算符匹配的 Pod。 当基于 IP 的 NetworkPolicy 被创建时,我们基于 IP 组块(CIDR 范围) 来定义策略。
网络策略通过网络插件 来实现。要使用网络策略,你必须使用支持 NetworkPolicy 的网络解决方案。
语法说明
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
role: db
##1)对default NameSpace中包含标签role=db的Pod做控制
policyTypes:
- Ingress
- Egress
##2)控制出入流量,未明确指定都是拒绝
ingress:
- from:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
##3)允许的网段,例外网段
- namespaceSelector:
matchLabels:
project: myproject
##4)允许的NameSpace
- podSelector:
matchLabels:
role: frontend
##5)允许的Pod
ports:
- protocol: TCP
port: 6379
##6) 定义被访问的端口
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978
##7)允许要控制的Pod访问哪个 "网段:端口" 的流量
注意From和To的行为
1)表示交集,and 运算
...
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
podSelector:
matchLabels:
role: client
...
2)表示并集,or 运算
...
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
- podSelector:
matchLabels:
role: client
...
默认策略:拒绝所有进入流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
默认策略:允许所有进入流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
spec:
podSelector: {}
ingress:
- {}
policyTypes:
- Ingress
范例1: 允许指定网段的入站流量
[root@clientvm ~]# cat deny-all-except.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-ingress-except
spec:
podSelector:
matchLabels:
run: nginx1
policyTypes:
- Ingress
ingress:
- from:
- ipBlock:
cidr: 10.244.0.0/16
except:
- 10.244.102.0/24
- ipBlock:
cidr: 192.168.241.0/24
[root@clientvm ~]#
[root@clientvm ~]# kubectl get pod --show-labels -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
busybox 1/1 Running 0 5m23s 10.244.102.144 worker1.example.com <none> <none> run=busybox
nginx1 1/1 Running 0 10h 10.244.71.195 worker2.example.com <none> <none> run=nginx1
nginx2 1/1 Running 0 10h 10.244.71.196 worker2.example.com <none> <none> run=nginx2
nginx3-default 1/1 Running 0 8h 10.244.102.133 worker1.example.com <none> <none> run=nginx3-
[root@master ~]# ip a | grep 192
inet 192.168.241.129/24 brd 192.168.241.255 scope global eth0
[root@master ~]# ping 10.244.71.195
PING 10.244.71.195 (10.244.71.195) 56(84) bytes of data.
64 bytes from 10.244.71.195: icmp_seq=1 ttl=63 time=0.484 ms
64 bytes from 10.244.71.195: icmp_seq=2 ttl=63 time=0.276 ms
64 bytes from 10.244.71.195: icmp_seq=3 ttl=63 time=0.217 ms
^C
--- 10.244.71.195 ping statistics ---
[root@clientvm ~]# kubectl run busybox --image=busybox --image-pull-policy=IfNotPresent -- sleep 10000
pod/busybox created
[root@clientvm ~]#
[root@clientvm ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox 1/1 Running 0 10s 10.244.102.144 worker1.example.com <none> <none>
nginx1 1/1 Running 0 10h 10.244.71.195 worker2.example.com <none> <none>
nginx2 1/1 Running 0 10h 10.244.71.196 worker2.example.com <none> <none>
nginx3-default 1/1 Running 0 8h 10.244.102.133 worker1.example.com <none> <none>
[root@clientvm ~]# kubectl exec busybox -it -- sh
/ # ping 10.244.71.195
PING 10.244.71.195 (10.244.71.195): 56 data bytes
^C
--- 10.244.71.195 ping statistics ---
5 packets transmitted, 0 packets received, 100% packet loss
/ # ping 10.244.71.196
PING 10.244.71.196 (10.244.71.196): 56 data bytes
64 bytes from 10.244.71.196: seq=0 ttl=62 time=0.547 ms
64 bytes from 10.244.71.196: seq=1 ttl=62 time=0.341 ms
^C
--- 10.244.71.196 ping statistics ---
范例2: 允许到指定端口的出站流量
目的:Pod Nginx1可以访问namespace内的Pod的80端口,不允许访问其他
[root@clientvm ~]# cat allow-egress-to-port.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-ingress-except
spec:
podSelector:
matchLabels:
run: nginx1
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
app: myapp
ports:
- protocol: TCP
port: 80
验证:
[root@clientvm ~]# kubectl get pod -o wide -n mytest
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginxaa 1/1 Running 0 10m 10.244.71.200 worker2.example.com <none> <none>
[root@clientvm ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox 1/1 Running 0 33m 10.244.102.144 worker1.example.com <none> <none>
nginx1 1/1 Running 0 10h 10.244.71.195 worker2.example.com <none> <none>
[root@clientvm ~]# kubectl exec nginx1 -it -- bash
root@nginx1:/#
root@nginx1:/# curl 10.244.71.200
^C
root@nginx1:/# curl 10.244.71.195
AAAAAA
root@nginx1:/# exit