k8s - 安全
文章目录一、机制说明二、认证(Authentication)1. 认证方式2. 需要认证的组件3. kubeconfig4. ServiceAccount(SA)(给Pod颁发证书)Secret 与 SA 的关系三、授权(Authorization)1. RBAC2. Role / ClusterRole3. RoleBinding / ClusterRoleBinding4. Resources
官方文档:
https://kubernetes.io/zh/docs/concepts/security/
一、机制说明
Kubernetes
作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。API Server
是集群内部各个组件通信的中介,也是外部控制的入口。所以 Kubernetes
的安全机制基本就是围绕保护 API Server
来设计的。
Kubernetes
使用了认证(Authentication)、鉴权(Authorization)、准入控制(Admission
Control)三步来保证 API Server
的安全
二、认证(Authentication)
1. 认证方式
-
HTTP Token 认证:通过一个 Token 来识别合法用户
- HTTP Token 的认证是用一个很长的特殊编码方式的并且难以被模仿的字符串 Token 来表达客户的一种方式。Token 是一个很长的很复杂的字符串,每一个 Token 对应一个用户名存储在 API Server 能访问的文件中。当客户端发起 API 调用请求时,需要在 HTTP Header 里放入 Token
-
HTTP Base 认证:通过 用户名+密码 的方式认证
用户名+:+密码
用BASE64
算法进行编码后的字符串放在 HTTP Request 中的 Heather
Authorization 域里发送给服务端,服务端收到后进行编码,获取用户名及密码
-
最严格的 HTTPS 证书认证:基于 CA 根证书签名的客户端身份认证方式
k8s 采用的就是 HTTPS
的双向认证方式:
2. 需要认证的组件
需要认证的组件分两种类型:
Kubenetes
组件对 API Server 的访问:kubectl
、Controller Manager
、Scheduler
、kubelet
、kubeproxy
Kubernetes
管理的 Pod 对容器的访问:Pod(dashborad
也是以 Pod 形式运行)
端口访问说明:
Controller Manager
、Scheduler
与API Server
在同一台机器(本机组件),所以直接使用API Server
的非安全端口访问(非安全端口可以禁用)- 默认为端口
8080
,使用--insecure-port
进行更改 - 默认 IP 为
localhost
,使用--insecure-bind-address
进行更改 - 请求 绕过 身份认证和鉴权模块
- 由准入控制模块处理的请求
- 默认为端口
kubectl
、kubelet
、kube-proxy
(远程组件)访问 API Server 就都需要证书进行 HTTPS 双向认证- 默认端口
6443
,使用--secure-port
更改 - 默认 IP 是第一个非本地网络接口,使用 --bind-address 更改
- 请求须经身份认证和鉴权组件处理
- 请求须经准入控制模块处理
- 默认端口
证书颁发有两种方式:
- 手动签发:通过 k8s 集群的跟 ca 进行签发 HTTPS 证书
- 自动签发:
kubelet
首次访问 API Server 时,使用token
做认证,通过后,Controller Manager
会为 kubelet 生成一个证书,以后的访问都是用证书做认证了
3. kubeconfig
kubeconfig
是一个文件类型,文件包含集群参数(CA证书、API Server地址),客户端参数(上面生成的证书和私钥),集群 context 信息(集群名称、用户名),可以理解为一个认证函,里面包含了怎么访问服务的信息以及认证信息。Kubenetes 组件通过启动时指定不同的 kubeconfig
文件可以切换到不同的集群
-
查看主节点下的
~/.kube/config
的文件$ ls ~/.kube cache config:kubeconfig文件 http-cache
4. ServiceAccount(SA)(给Pod颁发证书)
Pod 中的容器访问 API Server。因为 Pod 的创建、销毁是动态的,所以要为它手动生成证书就不可行了。
Kubenetes 使用了 Service Account
解决 Pod 访问 API Server 的认证问题
Secret 与 SA 的关系
Kubernetes 设计了一种资源对象叫做 Secret
,分为两类,一种是用于 ServiceAccount
的 service-accounttoken
, 另一种是用于保存用户自定义保密信息的 Opaque
。ServiceAccount
中包含三个部分:
Token
是使用 API Server 私钥签名的 JWT。用于访问 API Server 时,Server 端认证ca.crt
:根证书。用于 Client 端验证 API Server 发送的证书namespace
,标识这个service-account-token
的作用域名空间
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
默认情况下,每个 namespace
都会有一个 ServiceAccount
,如果 Pod 在创建时没有指定 ServiceAccount
,就会使用 Pod 所属的 namespace
的 ServiceAccount
。
Service Account
用来访问 Kubernetes API
,由 Kubernetes
自动创建,并且会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount
目录中
# 1. 随便找一个需要访问 Kubernetes API 的 Pod
$ kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
kube-proxy-2pqkk 1/1 Running 6 40d
# 2. 查看该 Pod 中 /run/secrets/kubernetes.io/serviceaccount 目录下的文件
$ kubectl exec kube-proxy-2pqkk -n kube-system -it -- ls /run/secrets/kubernetes.io/serviceaccount
ca.crt:访问 API Service 时的证书
namespace:名称空间
token:认证的密钥信息
三、授权(Authorization)
上面认证过程,只是确认通信的双方都确认了对方是可信的,可以相互通信。而鉴权是确定请求方有哪些资源的权限。API Server 目前支持以下几种授权策略 (通过 API Server 的启动参数 “--authorization-mode
” 设置):
AlwaysDeny
:表示拒绝所有的请求,一般用于测试AlwaysAllow
:允许接收所有请求,如果集群不需要授权流程,则可以采用该策略ABAC
(Attribute-Based Access Control):基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制Webbook
:通过调用外部 REST 服务对用户进行授权RBAC
(Role-Based Access Control)(默认):基于角色的访问控制,现行默认规则
1. RBAC
RBAC
(Role-Based Access Control)基于角色的访问控制,在 Kubernetes 1.5 中引入,现行版本成为默认标准。相对其它访问控制方式,拥有以下优势:
- 对集群中的资源和非资源均拥有完整的覆盖
- 整个 RBAC 完全由几个 API 对象完成,同其它 API 对象一样,可以用 kubectl 或 API 进行操作
- 可以在运行时进行调整,无需重启 API Server
RBAC 引入了 4 个新的顶级资源对象:Role
、ClusterRole
、RoleBinding
、ClusterRoleBinding
,4 种对象类型均可以通过 kubectl
与 API 操作
-
Role
和RoleBinding
是名称空间级别资源,ClusterRole
和ClusterRoleBinding
是集群级别资源 -
Role
与RoleBinding
之间的关系如下图所示,Role 用来指定一些角色,每个角色对资源的权限不同,RoleBinding 将这些角色赋予给用户、组和SA 。
需要注意的是 Kubenetes
并不会提供用户管理,那么 User
、Group
、ServiceAccount
指定的用户又是从哪里来的呢? Kubenetes 组件(kubectl、kube-proxy)或是其他自定义的用户在向 CA 申请证书时,需要提供一个证书请求文件:
{
"CN": "admin", # Common Name(CN),公用名,一般是主机名+网站域名
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN", # Country Code(C):国家,只能是两个字母的国家码
"ST": "HangZhou", # State or Province(S),省名或者州名
"L": "XS", # Locality(L),城市名
"O": "system:masters", # Organization Name(O),单位名称
"OU": "System" # Organization Unit(OU),部门
}
]
}
- API Server 会把客户端证书的
CN
字段作为User
,把names.O
字段作为Group
- kubelet 使用 TLS Bootstaping 认证时,API Server 可以使用 Bootstrap Tokens 或者 Token authentication file 验证 =token,无论哪一种,Kubenetes 都会为 token 绑定一个默认的 User 和 Group
- Pod 使用 ServiceAccount 认证时,service-account-token 中的 JWT 会保存 User 信息
有了用户信息,再创建一对 角色/角色绑定(集群角色/集群角色绑定)资源对象,就可以完成权限绑定了
2. Role / ClusterRole
在 RBAC API 中,Role
表示一组规则权限,权限只会增加(累加权限),不存在一个资源一开始就有很多权限而通过
RBAC 对其进行减少的操作;Role
可以定义在一个 namespace
中,如果想要跨 namespace
则可以创建 ClusterRole
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default # 名称空间
name: pod-reader
rules:
- apiGroups: [""] # api组,"" 为空代表的是 core 核心组
resources: ["pods"] # 资源对象
verbs: ["get", "watch", "list"] # 操作动作
ClusterRole
具有与 Role
相同的权限角色控制能力,不同的是 ClusterRole
是集群级别的,ClusterRole
可以用
于:
- 集群级别的资源控制( 例如
node
访问权限 ) - 非资源型
endpoints
( 例如/healthz
访问 ) - 所有命名空间资源控制(例如 pods )
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: secret-reader
rules:
- apiGroups: [""] # api 组
resources: ["secrets"] # 资源对象
verbs: ["get", "watch", "list"] # 操作动作
3. RoleBinding / ClusterRoleBinding
RoloBinding
可以将角色中定义的权限授予用户或用户组,RoleBinding
包含一组权限列表(subjects
),权限列表中包含有不同形式的待授予权限资源类型(users
, groups
, SA
);RoloBinding
同样包含对被 Bind 的 Role 引用;RoleBinding
适用于某个命名空间内授权,而 ClusterRoleBinding
适用于集群范围内的授权
将 default
命名空间的 pod-reader Role 授予 jane 用户,此后 jane 用户在 default 命名空间中将具有 pod-reader 的权限
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: read-pods
namespace: default # 名称空间
subjects: # 权限列表
- kind: User # User 类型
name: jane # 用户名
apiGroup: rbac.authorization.k8s.io # api 组
roleRef: # 角色
kind: Role # 类型
name: pod-reader # Role 名字
apiGroup: rbac.authorization.k8s.io # api组
RoleBinding
同样可以引用 ClusterRole
来对当前 namespace
内用户、用户组或 ServiceAccount
进行授权,这种操作允许集群管理员在整个集群内定义一些通用的 ClusterRole
,然后在不同的 namespace
中使用 RoleBinding
来引用
例如,以下 RoleBinding
引用了一个 ClusterRole
,这个 ClusterRole
具有整个集群内对 secrets
的访问权限;但是其授权用户 dave 只能访问 development
空间中的 secrets
(因为 RoleBinding
定义在 development
命名空间)
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: read-secrets
namespace: development # 名称空间,只授予 development 名称空间内的权限
subjects:
- kind: User
name: dave
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
使用 ClusterRoleBinding
可以对整个集群中的所有命名空间资源权限进行授权;以下 ClusterRoleBinding
样例展示了授权 manager
组内所有用户在全部命名空间中对 secrets
进行访问
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: read-secrets-global
subjects:
- kind: Group # 绑定给一个组
name: manager # 组名
apiGroup: rbac.authorization.k8s.io # api接口
roleRef:
kind: ClusterRole # ClusterRole
name: secret-reader # 名称
apiGroup: rbac.authorization.k8s.io # api接口
4. Resources
Kubernetes 集群内一些资源一般以其名称字符串来表示,这些字符串一般会在 API 的 URL 地址中出现;同时某些资源也会包含子资源,例如 logs 资源就属于 pods 的子资源,API 中 URL 样例如下
GET /api/v1/namespaces/{namespace}/pods/{name}/log
如果要在 RBAC 授权模型中控制这些子资源的访问权限,可以通过 /
分隔符来实现,以下是一个定义 pods
资源 logs
访问权限的 Role 定义样例
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
namespace: default
name: pod-and-pod-logs-reader
rules:
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get", "list"]
5. Subjects
RoleBinding
和 ClusterRoleBinding
可以将 Role
绑定到 Subjects
;Subjects
可以是 groups
、users
或者 service accounts
Subjects 中 Users 使用字符串表示,它可以是一个普通的名字字符串,如 “alice”;也可以是 email 格式的邮箱地址,如 “xxx@163.com
”;甚至是一组字符串形式的数字 ID 。但是 Users 的前缀 system:
是系统保留的,集群管理员应该确保普通用户不会使用这个前缀格式
Groups
书写格式与 Users
相同,都为一个字符串,并且没有特定的格式要求;同样 system:
前缀为系统保留
四、 实践:创建一个 devuser 用户只能管理 dev 空间
(1)创建一个新用户 devuser
,并设置密码
useradd devuser
passwd devuser
(2)创建证书请求文件
$ vim /home/userdev/cert/devuser-csr.json
{
"CN": "devuser",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s", # 组名
"OU": "System"
}]
}
CN
:一般是域名,这里使用了用户名(User
)hosts
:可以使用的主机,不写的话代表所有key
:生成证书的算法names
:一些其它的属性C
: Country, 国家ST
: State,州或者是省份L
: Locality Name,地区,城市O
: Organization Name,组织名称,公司名称(Group
)OU
: Organization Unit Name,组织单位名称,公司部门
(3)生成证书
# 1. 下载证书生成工具
wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
mv cfssl_linux-amd64 /usr/local/bin/cfssl
wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
mv cfssljson_linux-amd64 /usr/local/bin/cfssljson
wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
mv cfssl-certinfo_linux-amd64 /usr/local/bin/cfssl-certinfo
chmod a+x /usr/local/bin/cfssl*
# 2. 生成证书
$ cd /etc/kubernetes/pki
$ cfssl gencert -ca=ca.crt -ca-key=ca.key -profile=kubernetes /home/devuser/cert/devuser-csr.json | cfssljson -bare devuser
# 查看生成的证书
$ ll dev*
-rw-r--r-- 1 root root 997 11月 17 19:53 devuser.csr
-rw------- 1 root root 1679 11月 17 19:53 devuser-key.pem
-rw-r--r-- 1 root root 1237 11月 17 19:53 devuser.pem
-
cfssl gencert
: 生成新的 key (密钥)和签名证书-ca
:指明 ca 的证书-ca-key
:指明 ca 的私钥文件-profile
:根据 config 中的 profile 段来生成证书的相关信息(暂时不太懂)
-
-bare
:指定文件名
(4)创建 kubeconfig
-
在 devuser 家目录中创建
.kube
目录(config
文件一般是放在~/.kube
目录中的)mkdir /home/devuser/.kube cd /home/devuser/.kube
-
设置集群参数
export KUBE_APISERVER="https://192.168.66.10:6443" kubectl config set-cluster kubernetes \ --certificate-authority=/etc/kubernetes/pki/ca.crt \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=config
(1)
kubernetes
:集群名字
(2)--certificate-authority
:指定 CA 证书
(3)--embed-certs
:是否进行加密的认证
(4)--server
:指定 APIserver 的信息
(5)--kubeconfig
:指定 kubeconfig 的文件,没有会自动创建 -
设置客户端认证参数
kubectl config set-credentials devuser \ --client-certificate=/etc/kubernetes/pki/devuser.pem \ --client-key=/etc/kubernetes/pki/devuser-key.pem \ --embed-certs=true \ --kubeconfig=config
(1)
devuser
:证书的用户名
(2)--client-certificate
:指定客户端证书
(3)--client-key
:指定客户端私钥
(4)--embed-certs
:是否开启证书认证
(5)--kubeconfig
:写入的文件,与上面相同,写入同一个文件 -
设置上下文参数
# 1. 创建 dev 的名称空间 kubectl create namespace dev # 2. 设置上下文参数 kubectl config set-context kubernetes \ --cluster=kubernetes \ --user=devuser \ --namespace=dev \ --kubeconfig=config
(1)
kubernetes
:Context 的名字
(2)--cluster
:指定集群名称
(3)--user
:指定用户名
(4)--namespace
:绑定到某个名称空间
(5)--kubeconfig
:写入的文件,与上面相同,写入同一个文件 -
更改 config 文件所属的用户和组
chown devuser:devuser /home/devuser/.kube/config
-
查看 config 文件
apiVersion: v1 clusters: # 配置kubernetes集群 - cluster: certificate-authority-data: ... server: https://192.168.66.10:6443 name: kubernetes contexts: # 配置访问kubernetes集群的具体上下文环境 - context: cluster: kubernetes namespace: dev user: devuser name: kubernetes current-context: "" # 配置当前使用的上下文环境 kind: Config preferences: {} users: # 配置访问的用户信息,用户名以及证书信息 - name: devuser user: client-certificate-data: ... client-key-data: ...
(5)创建 RoleBinding
创建 RoleBinding
给 devuser
赋予权限
kubectl create rolebinding devuser-admin-binding --clusterrole=admin --user=devuser --namespace=dev
devuser-admin-binding
:RoleBinding 的名字--clusterrole=admin
:指定要绑定的Clusterrole
名字,k8s 集群默认存在一个admin
的Clusterrole
--user=devuser
:指定要绑定的用户名为 devuser--namespace=dev
:可以访问 dev 名称空间下的所有资源
(6)切换上下文/环境(kubeconfig)
切换上下文也就是让 kubectl
读取到 config
中的配置信息,
kubectl config use-context kubernetes --kubeconfig=config
- 这行命令设置了 config 文件中的
current-context
为kubernetes
- kubernetes:上下文环境名
--kubeconfig
:指定kubeconfig
文件
(7)测试
# 1. 用户 devuser 创建一个名叫 nginx 的 pod(默认创建到 dev 名称空间下)
$ kubectl run nginx --image=wangyanglinux/myapp:v1
# 2. root 用户查看该 pod
$ kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
nginx-858f8869f8-xczt4 1/1 Running 0 66m
五、准入控制
官方文档:https://kubernetes.io/zh/docs/reference/access-authn-authz/admission-controllers/
准入控制是 API Server 的插件集合,通过添加不同的插件,实现额外的准入控制规则。甚至于 API Server 的一些主要的功能都需要通过 Admission Controllers
实现,比如 ServiceAccount
。
官方文档上有一份针对不同版本的准入控制器推荐列表,其中最新的 1.16 的推荐列表是:
NamespaceLifecycle, LimitRanger, ServiceAccount, TaintNodesByCondition, Priority, DefaultTolerationSeconds,
DefaultStorageClass, StorageObjectInUseProtection, PersistentVolumeClaimResize, MutatingAdmissionWebhook,
ValidatingAdmissionWebhook, RuntimeClass, ResourceQuota
列举几个插件的功能:
NamespaceLifecycle
: 防止在不存在的 namespace 上创建对象,防止删除系统预置 namespace,删除 namespace 时,连带删除它的所有资源对象。LimitRanger
:确保请求的资源不会超过资源所在 Namespace 的 LimitRange 的限制。ServiceAccount
: 实现了自动化添加 ServiceAccount。ResourceQuota
:确保请求的资源不会超过资源的 ResourceQuota 限制。
准入控制的操作
- 启用 NamespaceLifecycle 和 LimitRanger 准入控制插件:
kube-apiserver --enable-admission-plugins=NamespaceLifecycle,LimitRanger
- 禁用插件
kube-apiserver --disable-admission-plugins=PodNodeSelector,AlwaysDeny ...
- 查看默认启用的插件
kube-apiserver -h | grep enable-admission-plugins
更多推荐
所有评论(0)