用户访问API Server(如下简称Server),K8S的安全检查步骤:认证和受权。nginx
认证解决用户是谁的问题,就是验证用户名密码;受权解决用户能作什么的问题,就是检查该用户是否拥有权限访问请求的资源。经过合理的权限管理,可以保证系统的安全可靠。web
Server经过本地端口(Localhost Port)和安全端口(Secure Port)对外提供API服务,其中本地端口是基于HTTP协议的,用于本地无限制访问,安全端口是基于HTTPS协议的,用于远程有限制访问。docker
默认配置中,Server的本地端口默认绑定到地址127.0.0.1上,只能在本机访问。因为本地端口将绕过了Server的认证和受权,只要可以访问本地端口,能够无限制访问Server。基于安全考虑,尽可能不要将本地端口绑定到127.0.0.1之外的地址上。bootstrap
本地端口默认绑定到端口8080上,能够经过Server启动参数--insecure-port进行指定,若是为0关闭本地端口。参数--insecure-bind-address指定绑定地址。api
安全端口是Server对外提供的,用于外部访问的安全的可控的API调用接口,Server只容许经过认证的用户(认证信息来源于User Account或者Service Account)才可以经过安全端口访问,对于匿名用户401拒绝访问。缓存
默认绑定到端口6443上,经过Server的启动参数--secure-port进行指定,若是为0关闭安全端口。默认绑定到地址0.0.0.0上,经过参数--bind-address进行指定。安全
经过参数--tls-cert-file和--tls-private-key-file指定证书和私钥。服务器
在实际应用中,可能现有的认证体系没法与K8S集成或者须要执行特殊认证和受权逻辑的状况,能够考虑引入代理(Proxy)来解决认证和受权的问题,在认证经过后,代理将请求转发到Server。网络
当代理可以与Server部署在同一台主机时,建议按照下面的方式进行集成:app
关闭安全端口,确保全部的API请求只能经过代理接入 将本地端口绑定到地址127.0.0.1上,确保只能在本机访问 设置防火墙规则,仅开放本机的443端口 配置nginx监听443端口,而且在此端口上配置认证和HTTPS 配置nginx将请求转发到本地端口,默认状况下为127.0.0.1:8080
当代理没法与Server部署在同一台主机时,从安全角度看,再试图经过本地端口与Server集成不是好的选择,使用安全端口与Server集成更好更安全。
只能用于HTTPS(安全端口),对http(非安全端口)来讲无效,缘由请看上节的“本地端口”。
认证方式有不少种,这里主要介绍如下几种:
用户帐号(User Accounts,如下简称UA),也称静态密码,是用于管理者经过web访问Server的认证用户名和密码。
静态密码是提早在某个文件中保存了用户名和密码的信息,而后经过Server启动参数 --basic-auth-file=SOMEFILE指定文件路径。
文件是CSV格式,每行对应一个用户信息,前面三列密码、用户名、UID 是必须的,第四列是可选的组名(多个组,必须用双引号):password,user,uid,"group1,group2,group3" 例如 cat /etc/kubernetes/useraccount.csv 1qaz2wsx,lykops,1 1qaz2wsx,lykchat,2
若是是经过客户端发送请求时(非用户经过web页面请求的),须要指定ca证书、用户名和密码。例如:
curl -u lykops:1qaz2wsx https://192.168.20.128:6443/ --cacert /etc/ssl/kube/ca.pem
注意:不能动态更新,须要重启api server。
服务帐号(Service Accounts,如下简称SA)并非给K8S集群的用户使用的,而是用于Pod中的程序访问API的account,它为Pod中的程序提供了一种身份标识。UA的做用域为K8S集群全局,是全局性权限,SA的做用域为命名空间(简称ns)。SA做为一种资源对象存在于K8S中。具体请看资源对象—service acecount章节。
查看一下kube-system ns下名为默认sa的详细信息: kubectl describe serviceaccount/default -n kube-system Name: default Namespace: kube-system Labels: Image pull secrets: Mountable secrets: default-token-hpni0 Tokens: default-token-hpni0
看到sa并不复杂,只是关联了一个secret资源对象做为token,该token也叫service-account-token,该token才是真正在Server验证(authentication)环节起做用的: kubectl get secret -n kube-system NAME TYPE DATA AGE default-token-hpni0 kubernetes.io/service-account-token 3 140d
kubectl get secret default-token-hpni0 -o yaml -n kube-system apiVersion: v1 data: ca.crt: {base64 encoding of ca.crt data} namespace: a3ViZS1zeXN0ZW0= token: {base64 encoding of bearer token} kind: Secret metadata: annotations: kubernetes.io/service-account.name: default name: default-token-hpni0 namespace: kube-system type: kubernetes.io/service-account-token
看到这个类型为service-account-token的secret资源包含的数据有三部分:ca.crt、namespace和token。
ca.crt是Server的CA公钥证书; namespace是Secret所在ns值的base64编码 token是一段用Server私钥(在建立sa时用Server参数--service-account-key-file指定的,若是未指定,那么将默认使用--tls-private-key-file值,即Server的私钥server.key)文件内容签发(sign)的bearer tokens的base64编码。
在K8S的认证环节中,以某个sa提供身份的Pod的用户名为:system:serviceaccount:(ns):(sa)。以kube-system ns下的默认sa为例,使用它的Pod的username全称为:system:serviceaccount:kube-system:default。有了用户名,那么credentials(凭证)呢?就是上面提到的service-account-token中的token。
若是客户端使用sa token方式(携带service account token)访问api,Server采用signed bearer token方式进行身份校验。 经过身份校验后,Server将根据Pod用户名所在的group:system:serviceaccounts和system:serviceaccounts:(NAMESPACE)的权限分配权限(authority和admission control两个环节)。在这两个环节中,管理员能够对service account的权限进行细化设置。
若是Pod中没有显式指定spec.serviceAccount字段值,那么K8S会将该ns下的默认sa自动mount到在该ns中建立的Pod里。
kubectl describe pod/index-api-2822468404-4oofr Name: index-api-2822468404-4oofr Namespace: default ... ... Containers: index-api: ... ... Volume Mounts: /var/run/secrets/kubernetes.io/serviceaccount from default-token-40z0x (ro) Environment Variables: <none> ... ... Volumes: ... ... default-token-40z0x: Type: Secret (a volume populated by a Secret) SecretName: default-token-40z0x QoS Class: BestEffort Tolerations: <none> No events.
能够看到,K8S将default ns中的默认sa的service account token挂载(mount)到了Pod中容器的/var/run/secrets/kubernetes.io/serviceaccount路径下。
深刻容器内部,查看mount的sa路径下的结构:
docker exec 3d11ee06e0f8 ls /var/run/secrets/kubernetes.io/serviceaccount ca.crt namespace token
这三个文件与上面提到的sa中的数据是一一对应的。
客户端证书认证叫做TLS双向认证,就是服务器客户端互相验证证书的正确性,在都正确的状况下协调通讯加密方案。
双向认证方式是最为严格和安全的集群安全配置方式,主要配置流程以下:
生成根证书、Server服务端证书、服务端私钥、各个组件所用的客户端证书和客户端私钥。 修改K8S各个服务进程的启动参数,启用双向认证模式。
相似于静态密码,只是经过token来代替密码。
当在命令行指定--token-auth-file=SOMEFILE选项时,Server从文件中读取bearer tokens。令牌文件是一个至少包含3列的csv文件: token, user name, user uid,后跟可选的组名。注意,若是有多个组,则列必须是双引号,例如:token,user,uid,"group1,group2,group3" 当客户端使用bearer token认证时,须要发送一个“Authorization: Bearer ”+token(token必须是一个能够放在HTTP请求头中且值不须要转码和引用)的字符串。例如:token是31ada4fd-adec-460c-809a-9e56ceb75269,客户端的HTTP头必须添加:Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269
注意:不能动态更新,须要重启api server,很不灵活,也不安全,不推荐使用。
OpenID Connect是由OAuth2提供商支持的OAuth2,特别是Azure Active Directory,Salesforce和Google。OAuth2的协议的主要扩展是增长一个额外字段,返回了一个叫ID token的access token。这个token是被服务器签名的JSON Web Token (JWT) ,具备众所周知的字段,好比用户的email。
为了识别用户,验证使用来自OAuth2 token响应的idtoken (而不是accesstoken)做为bearer token。
使用OpenID认证,API Server须要配置
- --oidc-issuer-url,如https://accounts.google.com - --oidc-client-id,如kubernetes - --oidc-username-claim,如sub - --oidc-groups-claim,如groups - --oidc-ca-file,如/etc/kubernetes/ssl/kc-ca.pem
Webhook Token认证方式可让用户使用本身的认证方式(相似于单点登陆),用户按照约定的请求格式和应答格式提供HTTPS服务,当用户把Bearer Token放到请求的头部,K8S会把token发送给事先配置的地址进行认证,若是认证结果成功,则认为请求用户合法。这种方式下有两个参数 能够配置:
–authentication-token-webhook-config-file :认证URL地址的配置文件
–authentication-token-webhook-cache-ttl :认证结果要缓存多久,默认是两分钟
这种方式下,自定义认证的请求和应答都有必定的格式,具体的规范请参考官方文档。
Server配置文件须要制定如下配置
--requestheader-username-headers=X-Remote-User --requestheader-group-headers=X-Remote-Group --requestheader-extra-headers-prefix=X-Remote-Extra- # 为了防止头部欺骗,证书是必选项 --requestheader-client-ca-file # 设置容许的CN列表。可选。 --requestheader-allowed-names
Keystone是OpenStack提供的认证和受权组件,这个方法对于已经使用openstack来搭建Iaas平台的公司比较适用,直接使用keystone能够保证Iaas和Caas平台保持一致的用户体系。
须要Server在启动时指定--experimental-keystone-url=,而https时还须要设置--experimental-keystone-ca-file=SOMEFILE。
在v1.6版本中,这个特性仍是alpha特性。为了可以在新的集群中使用bootstrapping认证。Kubernetes包括一种动态管理的Bearer(持票人) token,这种token以Secrets的方式存储在kube-system命名空间中,在这个命名空间token能够被动态的管理和建立。Controller Manager有一个管理中心,若是token过时了就会删除。
建立的token证书知足[a-z0-9]{6}.[a-z0-9]{16}格式,Token的第一部分是一个Token ID,第二部分是token的秘钥。你须要在http协议头中加上相似的信息:
Authorization: Bearer 781292.db7bc3a58fc5f07e
若是要使用Bootstrap,须要在API Sever中开启--experimental-bootstrap-token-auth。同时必须在Controller Manager中开启管理中心的设置--controllers=*,tokencleaner。
在使用kubeadm部署Kubernetes时,kubeadm会自动建立默认token,可经过kubeadm token list命令查询。
若是请求没有经过以上任何方式的认证,正常状况下应该是直接返回 401 错误。但K8S还提供另一种选择,给没有经过认证的请求一个特殊的用户名system:anonymous和组名 system:unauthenticated 。 这样能够跟下面要讲的受权结合起来,为匿名请求设置一些特殊的权限,好比只能读取当前ns的pod信息,方便用户访问。 若是使用AlwaysAllow、AlwaysDeny之外的认证模式,则匿名请求默认开启,但可用--anonymous-auth=false禁止匿名请求。
K8S的受权是经过插件方式来实现的,目前K8S内置提供了AlwaysDeny、AlwaysAllow、ABAC、RBAC以及WebHook等几种受权插件(在1.5.2版本中,只有这五种),经过配置Server启动参数--authorization-mode来指定受权模式。
受权处理如下的请求属性: user, group, extra API、请求方法(如get、post、update、patch和delete)和请求路径(如/api) 请求资源和子资源 Namespace API Group
该模式将会拒绝任何对安全端口的请求。任何访问,服务端老是返回Forbidden: "URL",表示访问被拒绝。
AlwaysDeny模式主要用于测试,固然也能够用来暂时中止集群的对外服务。
默认模式,该模式下只要经过认证,服务端将会接受任何对安全端口的请求,换句话说就是除了认证没有任何权限限制。
当集群不须要作受权限制时,直接使用该模式,以下降配置的复杂性。
ABAC(Attribute-based access control,基于属性的访问控制),ABAC的核心是根据请求的相关属性,例如用户属性、资源属性以及环境属性等属性,做为受权的基础来进行访问控制,以解决分布式系统的可信任关系的访问控制问题。
ABAC的策略文件是一个one JSON object per line格式的文本文件。ABAC的受权过程能够简单的理解为将请求属性转换为一个spec对象,而后拿到这个spec对象与策略文件中定义的spec对象进行匹配,若是这个spec对象可以与策略文件中定义的任何一条规则容许的spec对象匹配,那么受权经过;若是这个spec对象没法与任何一条规则匹配,那么受权失败。
WebHook模式是一种扩展受权模式,在这种模式下,API Server将受权过程委派到外部的一个REST服务,由外部的服务决定是否授予指定请求继续访问的权限。
WebHook模式的开启很是的简单,只须要经过API Server的启动参数--authorization-mode设置为WebHook而且经过启动参数--authorization-webhook-config-file将外部受权服务的配置信息告诉API Server便可。
当请求经过了前面的认证和受权后,还须要通过准入控制处理经过以后,server才会处理这个请求。Admission Control 有一个准入控制列表,能够经过命令行设置选择执行哪几个准入控制器。只有全部的准入控制器都检查经过以后,apiserver 才执行该请求,不然返回拒绝。