kubernetes从入门到放弃4--(安全机制)
Kubernetes作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。API Server是集群内部各个组件通信的中介,也是外部控制的入口。所以Kubernetes的安全机制基本就是围绕保护API Server来设计的。
Kubernetes使用了认证(Authentication)、鉴权(Authorization)、准入控制(Admission Control)三步来保证API Server的安全。
我的理解是,认证是保证访问双方都是可信的;鉴权是检查请求者是否有所访问资源的权限;准入控制是API Server的功能插件集合,可以为API Server添加一些额外的处理请求的功能。
一 认证
推荐的API Server配置是开启TLS,关闭匿名访问。
--insecure-bind-address=127.0.0.1 \
--kubelet-https=true \
--anonymous-auth=false \
--tls-cert-file=/etc/kubernetes/ssl/kubernetes.pem \
--tls-private-key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
这样除了localhost访问外,如果没有受API Server信任的证书,就不能访问API Server。
Kubernetes提供的认证方式挺多:X509 Client Certs、Static Token File、Bootstrap Tokens、Static Password File、Service Account Tokens....具体看文档。在这里就提几个与证书访问相关的方式。
1.1 证书
TLS需要一个CA机构,用于给服务器和客户端发布证书。API Server基本只提供给集群内部访问,所以自签发一个CA根证书就好了。可以参考下kubeasz的创建CA证书和密钥
再来看看需要访问API Server的都有谁。
- 管理员:使用kubectl或是UI界面(dashborad)访问
- master节点:Controller Manager、Scheduler
- Node节点:kubelet、kube-proxy、Pod容器中的进程
可以将他们大概分为两类:
- Kubenetes组件对API Server的访问:kubectl、Controller Manager、Scheduler、kubelet、kube-proxy
- Kubernetes管理的Pod对容器的访问:Pod(dashborad也是以Service形式运行)
先看看组件,Controller Manager、Scheduler与API Server在同一台机器,所以直接使用API Server的非安全端口访问,--insecure-bind-address=127.0.0.1
。
其他组件比如kubectl、kubelet、kube-proxy访问API Server就都需要证书了。
1.1.1 手动签发
其中kubectl、kube-proxy需要手动地用CA根证书签发。首先创建证书请求文件:
{
"CN": "admin",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "HangZhou",
"L": "XS",
"O": "system:masters",
"OU": "System"
}
]
}
然后就可以向CA申请证书:
cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca-config.json \
-profile=kubernetes admin-csr.json | cfssljson -bare admin"
之后就可以在本地生成了三个文件:
- admin.csr:证书请求
- admin-key.pem:公钥
- admin.pem:私钥
1.1.2 Bootstrap Tokens
kubelet可以使用Bootstrap Tokens,动态地让master分配证书。所谓Bootstrap Tokens就是创建集群时,API Server与kubelet约定好一个token,kubelet首次访问API Server时,使用token做认证,通过后,Controller Manager会为kubelet生成一个证书,以后的访问都是用证书做认证了。
使用Bootstrap Tokens,可以不用一个一个地为Node生成kubelet的证书,而只需要一个统一的token,可以方便集群的创建。关于Bootstrap Tokens,可以看看文档和这篇博客。写到这里我有个疑问,如果Bootstrap Tokens是为了不用在创建集群时,一个个地为Node生成kubelet证书。那么同为运行在Node上的组件的kube-proxy为什么又需要先生成证书才能访问API Server呢.....
1.1.3 kubeconfig
有了证书之后,还需要生成kubeconfig文件。kubeconfig文件包含集群参数(CA证书、API Server地址),客户端参数(上面生成的证书和私钥),集群context信息(集群名称、用户名)。Kubenetes组件通过启动时指定不同的kubeconfig文件可以切换到不同的集群。kubelet的kubeconfig文件是通过Bootstrap Tokens获取到证书后生成的。关于kubeconfig的文档。
1.1.4 ServiceAccount
上面提到的是Kubenetes组件访问API Server的情况,另一种情况是Pod中的容器访问API Server。因为Pod的创建、销毁是动态的,所以要为它手动生成证书就不可行了。Kubenetes使用了Service Account解决Pod 访问API Server的认证问题。
使用Service Account要在API Server启动时,带上--admission-controller=ServiceAccount
参数。
1.1.4.1 Secret
Kubernetes设计了一种资源对象叫做Secret,分为两类,一种是用于ServiceAccount的service-account-token, 另一种是用于保存用户自定义保密信息的Opaque。我们在ServiceAccount中用到包含三个部分:Token、ca.crt、namespace。
- token是使用API Server私钥签名的JWT。用于访问API Server时,Server端认证。
- ca.crt,根证书。用于Client端验证API Server发送的证书。
- namespace, 标识这个service-account-token的作用域名空间。
root@kube-1:~# kubectl get secret --all-namespaces
NAMESPACE NAME TYPE DATA AGE
default default-token-gnlqz kubernetes.io/service-account-token 3 11d
kube-public default-token-pcql2 kubernetes.io/service-account-token 3 11d
kube-system default-token-5gm9r kubernetes.io/service-account-token 3 11d
kube-system kubernetes-dashboard-key-holder Opaque 2 11d
以上三个service-account-token都是系统默认添加的,会在每个namespace默认的ServiceAccount中使用。
root@kube-1:~# kubectl describe secret default-token-5gm9r --namespace=kube-system
Name: default-token-5gm9r
Namespace: kube-system
Labels: <none>
Annotations: kubernetes.io/service-account.name=default
kubernetes.io/service-account.uid=99473738-64d1-11e8-80e9-fa163e39e787
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1346 bytes
namespace: 11 bytes
token: eyjh......7FcRPVz0g
对token使用JWT解码,可以看到service-account-token的一些基本信息,会用于后续的鉴权。
再回过头来看看ServiceAccount,默认情况下,每个namespace都会有一个ServiceAccount,如果Pod在创建时没有指定ServiceAccount,就会使用Pod所属的namespace的ServiceAccount。
root@kube-1:~# kubectl describe sa default --namespace=kube-system
Name: default
Namespace: kube-system
Labels: <none>
Annotations: <none>
Image pull secrets: <none>
Mountable secrets: default-token-5gm9r
Tokens: default-token-5gm9r
Events: <none>
- Image pull secrets 用于指定pull镜像时的Secret。
- Mountable secrets 指定需要挂载到Pod中的Secret。exec到容器中,就可以在目录中看到Secret的三个文件了。
root@nginx-7587c6fdb6-bv84b:/# ls /run/secrets/kubernetes.io/serviceaccount/ ca.crt namespace token
- Tokens 指定用于访问API Server认证的Secret。
1.2 summary
简单总结下,基于TLS证书的API Server认证如下图所示:
二 鉴权
上面认证过程,只是确认通信的双方都确认了对方是可信的,可以相互通信。而鉴权是确定请求方有哪些资源的权限。在Kubernetes中,默认使用的,也是使用较多的是RBAC(Role-Based Access Control)。
RBAC定义了4个资源对象:Role、ClusterRole、RoleBinding、 ClusterRoleBinding。其中Role(ClusterRole)定义了一个角色(集群角色)在namespace(clutser)范围内对某些资源的操作权限。RoleBinding(ClusterRoleBinding)将用户、组、ServiceAccount同角色进行绑定,绑定的账户就拥有了角色(集群角色)所指定的权限。
需要注意的是Kubenetes并不会提供用户管理,那么User、Group、ServiceAccount指定的用户又是从哪里来的呢?
- Kubenetes组件(kubectl、kube-proxy)或是其他自定义的用户在向CA申请证书时,需要提供一个证书请求文件:
root@kube-1:~# cat /etc/kubernetes/ssl/admin-csr.json { "CN": "admin", "hosts": [], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "HangZhou", "L": "XS", "O": "system:masters", "OU": "System" } ] }
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信息。
有了用户信息,再创建一对角色/角色绑定(集群角色/集群角色绑定)资源对象,就可以完成权限绑定了。使用 RBAC 控制 kubectl 权限这篇博文的列子把这一过程解释的很清楚。
三 准入控制
准入控制是API Server的插件集合,通过添加不同的插件,实现额外的准入控制规则。甚至于API Server的一些主要的功能都需要通过Admission Controllers实现,比如ServiceAccount。
官方文档上有一份针对不同版本的准入控制器推荐列表,其中最新的1.9和1.10的推荐列表是:
NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota
列举几个插件的功能:
- NamespaceLifecycle: 防止在不存在的namespace上创建对象,防止删除系统预置namespace,删除namespace时,连带删除它的所有资源对象。
- LimitRanger:确保请求的资源不会超过资源所在Namespace的LimitRange的限制。
- ServiceAccount: 实现了自动化添加ServiceAccount。
- ResourceQuota:确保请求的资源不会超过资源的ResourceQuota限制。