36、K8S-安全机制-ServiceAccount(SA)
1、基础知识
1.1、场景基础
1.1.1、应用场景
对于任何一种应用场景,其权限的认证管理都是非常重要的,对于linux系统来说,selinux、防火墙、
pam、sudo等等,其核心的目的都是为了保证我们的环境是一个安全的。
对于k8s的这种大型的任务编排系统来说,设计到的认证远远超出了一般平台对认证的技术要求,在k8s平台
中,陆陆续续的产生了一些列对平台的权限认证、对业务的权限认证、对网络的安全认证等等一些了认证体
系。由于篇幅限制,我们这里专门针对平台和应用的权限认证来进行详细的学习。
1.1.2、业务流程
用户访问k8s业务应用的流程: 方法一:无需api_server认证 用户 -- ingress|service -- pod 方法二:基于api_server认证
管理k8s平台上各种应用对象 对于Kubernetes平台来说,几乎所有的操作基本上都是通过kube-apiserver这个组件进行的,该组件提供 HTTP RESTful形式的API供集群内外客户端调用,对于kubernetes集群的部署样式主要有两种:http形 式和https形式。我们采用kubeadm部署的形式默认对外是基于https的方式,而内部组件的通信时基于 http方式,而k8s的认证授权机制仅仅存在于https形式的api访问中,也就是说,如果客户端使用HTTP连接 到kube-apiserver,那么是不会进行认证授权的,这样既增加了安全性,也不至于太复杂。
1.2、认证简介
对APIServer的访问一般都要经过的三个步骤,认证(Authn)+授权(Authz)、准入控制(Admission),它
也能在一定程度上提高安全性,不过更多是资源管理方面的作用。
注意:认证和授权功能都是以插件化的方式来实现的,这样可以最大化的用户自定义效果。
1.2.1、认证-(Authn)
对用户身份进行基本的认证,只允许被当前系统许可的人进入集群内部
1.2.2、授权(Authz)
不同的用户可以获取不同的资源操作权限,比如普通用户、超级用户、等
1.2.3、准入控制(Admission)
类似于审计,主要侧重于 操作动作的校验、语法规范的矫正等写操作场景。
1.3、认证流程
1.3.1、认证流程图
1.3.2、认证流程说明
左侧: 对于k8s来说,它主要面对两种用户: 普通人类用户(交互模式) - User Account 集群内部的pod用户(服务进程) - Service Account
中间: 每个部分都是以插件的方式来整合到 k8s 集群环境中: - 认证和授权是按照 插件的顺序进行依次性的检测,并且遵循 "短路模型" 的认证方式 所谓的"短路"即,失败的时候,到此结束,后续的规则不会进行。 - 准入控制 由于仅用户写操作场景,所以它不遵循 "短路模型",而是会全部检测,原因在于,它要记录为什么不执行,原因是什么,方便后续审计等动作。
2、原理解析
2.1、认证用户
认证用户,在我们的认证范围中,有一个术语叫Subject,它表示我们在集群中基于某些规则和动作尝试操作
的对象,在k8s集群中定义了两种类型的subject资源:
2.2.1、User Account
这是有外部独立服务进行管理的,对于用户的管理集群内部没有一个关联的资源对
象,所以用户不能通过集群内部的 API 来进行管理。常见的管理方式就是 openssl等
2.2.2、Service Account
通过Kubernetes API 来管理的一些用户帐号,和 namespace 进行关联的,适用于
集群内部运行的应用程序,需要通过 API 来完成权限认证,所以在集群内部进行权限操作,我们都需要使用到 ServiceAccount
2.2、用户组
在k8s集群中,为了更方便的对某一类用户进行细节化的管理,我们一般会通过用户组的方式来进行管理,常见的用户组有四类
2.2.1、system:unauthenticated
未能通过任何一个授权插件检验的账号的所有未通过认证测试的用户统一隶属的用户组;
2.2.2、system:authenticated
认证成功后的用户自动加入的一个专用组,用于快捷引用所有正常通过认证的用户账号;
2.2.3、system:serviceaccounts
所有名称空间中的所有ServiceAccount对象
2.2.4、system:serviceaccounts:
特定名称空间内所有的ServiceAccount对象
2.3、插件认证方式
kubernetes提供了多种认证方式,我们可以同时使用一种或多种认证方式,只要通过任何一个都被认作是认证通过。
2.3.1、客户端证书认证
客户端证书认证叫作TLS双向认证,也就是服务器客户端互相验证证书的正确性,在都 正确的情况下协调通信加密方案。,最常见的方式X509数字证书,证书中的Subject中 的 CommonName 或 Orgnization等信息进行校验 对于这种客户的账号,k8s的平台一般是无法管理的。为了使用这个方案,api-server 需要用--client-ca-file、--tls-private-key-file、--tls-cert-file选项来开启。
2.3.2、令牌认证(Token)
在结点数量非常多的时候,大量手动配置TLS认证比较麻烦,我们可以通过在apiserver开启 experimental-bootstrap-token-auth 特性,通过对客户端的和k8s平台预 先定义的token信息进行匹配,认证通过后,自动为结点颁发证书,可以大大减轻我们 的工作压力,而且应用场景非常广。 常见的令牌 在k8s集群中,为了更方便的对某一类用户进行细节化的管理,我们一般会通过用户组的方式来进行管理,常见的用户组有以下四类: kubernetes提供了多种认证方式,我们可以同时使用一种或多种认证方式,只要通过任何一个都被认作是认证通过。常见的认证方式如下: 引导令牌: kubeadm创建集群环境的时候,自动创建好的令牌,用于其他节点加入到集群环境中 静态令牌: 存储于API Server进程可直接加载到的文件中保存的令牌,该文件内容会由API Server缓存于内存中 比如我们在使用kubelet的时候,需要依赖的token文件 静态密码: 存储于API Server进程可直接加载到的文件中保存的账户和密码令牌,该文件内容会由API Server缓存于内存中 比如我们在使用kubelet的时候,需要依赖的.kube/config文件
SA令牌: SA 就是 ServiceAccount,集群内部账号之间操作相关资源的一些认证方式。 OIDC令牌: OIDC 就是 OpenID Connect,一般应用于集成第三方认证的一种集中式认证方式,一般遵循OAuth 2协议。尤其是第三方云服务商的认证。 Webhook令牌: 常应用于触发第三个动作时候的一些认证机制,主要侧重于http协议场景。
2.3.3、代理认证
一般借助于中间代理的方式来进行统用的认证方式,样式不固定
3、授权
3.1、简介
授权主要是在认证的基础上,用于对集群资源的访问控制权限设置,通过检查请求包含的相关属性值,与相对
应的访问策略相比较,API请求必须满足某些策略才能被处理。
3.2、授权方式
对于k8s集群来说,它会根据实际的业务应用场景,采用不同级别的授权方式,有时候也称为授权策略。这些授权策略很多
3.2.1、Node
主要针对节点的基本通信授权认证,比如kubelet的正常通信
3.2.2、ABAC
(Attribute Based Access Control):基于属性的访问控制,在apiserver本地的某一个文件里写入策略规则,如果满足其中一条,就算授权通过。现阶段如果想新
增规则,那么必须重启apiserver,在生产环境中使用几率较小,但未来可能会使用API动态管理。
更多的关于动态属性控制可以参考 OPA(open policy agent)相关资料。
3.2.3、RBAC
(Role Based Access Control):基于角色的访问控制,这是1.6+版本主推的授权策略,可以使用API自定义角色和集群角色,并将角色和特定的用户,用户组, Service Account关联起来,可以用来实现多租户隔离功能(基于namespace资源)
3.2.4、Webhook
使用第三方授权组件,以 HTTP Callback 的方式,利用外部授权接口对于已有访问控制组件实现权限控制的无缝衔接
3.2.5、AlwaysDeny
此标志阻止所有请求. 仅使用此标志进行测试
3.2.6、AlwaysAllow
此标志允许所有请求. 只有在您不需要API请求授权的情况下才能使用此标志
3.3.7、默认的认证插件
对于k8s集群来说,默认的认证插件有 Node 和 RBAC,其他的都是使用大量的证书来进行的。
master1 ~]# cat /etc/kubernetes/manifests/kube-apiserver.yaml apiVersion: v1 kind: Pod metadata: annotations: kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 192.168.10.26:6443 creationTimestamp: null labels: component: kube-apiserver tier: control-plane name: kube-apiserver namespace: kube-system spec: containers: - command: - kube-apiserver - --advertise-address=192.168.10.26 - --allow-privileged=true - --authorization-mode=Node,RBAC - --client-ca-file=/etc/kubernetes/pki/ca.crt - --enable-admission-plugins=NodeRestriction - --enable-bootstrap-token-auth=true - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key - --etcd-servers=https://127.0.0.1:2379 - --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname - --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt - --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key - --requestheader-allowed-names=front-proxy-client - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt - --requestheader-extra-headers-prefix=X-Remote-Extra- - --requestheader-group-headers=X-Remote-Group - --requestheader-username-headers=X-Remote-User - --secure-port=6443 - --service-account-issuer=https://kubernetes.default.svc.cluster.local - --service-account-key-file=/etc/kubernetes/pki/sa.pub - --service-account-signing-key-file=/etc/kubernetes/pki/sa.key - --service-cluster-ip-range=10.96.0.0/12 - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key image: registry.aliyuncs.com/google_containers/kube-apiserver:v1.26.0 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 8 httpGet: host: 192.168.10.26 path: /livez port: 6443 scheme: HTTPS initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 15 name: kube-apiserver readinessProbe: failureThreshold: 3 httpGet: host: 192.168.10.26 path: /readyz port: 6443 scheme: HTTPS periodSeconds: 1 timeoutSeconds: 15 resources: requests: cpu: 250m startupProbe: failureThreshold: 24 httpGet: host: 192.168.10.26 path: /livez port: 6443 scheme: HTTPS initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 15 volumeMounts: - mountPath: /etc/ssl/certs name: ca-certs readOnly: true - mountPath: /etc/pki name: etc-pki readOnly: true - mountPath: /etc/kubernetes/pki name: k8s-certs readOnly: true hostNetwork: true priorityClassName: system-node-critical securityContext: seccompProfile: type: RuntimeDefault volumes: - hostPath: path: /etc/ssl/certs type: DirectoryOrCreate name: ca-certs - hostPath: path: /etc/pki type: DirectoryOrCreate name: etc-pki - hostPath: path: /etc/kubernetes/pki type: DirectoryOrCreate name: k8s-certs status: {}
4、准入控制
4.1、简介
准入控制(Admission Control),实际上是一个准入控制器(Admission Controller)插件列表,发送
到APIServer的请求都需要经过这个列表中的每个准入控制器插件的检查,如果某一个控制器插件准入失败,
就准入失败。
它主要涉及到pod和容器对特定用户和特定权限之间的关联关系。
对于k8s来说,他支持的准入控制器有数十种,分别应用于不同的场景功能。一般情况下,k8s的不同版本的
默认准入控制器相差不大。但是其他的功能场景的准入控制器相差还是比较大的,尤其是最近两三年版本中所
支持的准入控制器。
到目前位置,k8s由于其灵活性和云原生的特性,它在安全层面做的不是太完善,每个版本更新的时候,都会
在安全层面上面发现新的问题并进行解决。希望以后会好一点
4.2、常见控制器
4.2.1、LimitRanger
对一些没有做资源限制的pod,赋予一些默认的资源配置属性,主要针对的是pod个体。
4.2.2、ResourceQuota
对一些没有做资源限额的pod,赋予一些默认的资源限额,主要针对的是ns的范围限制。
4.2.3、PSP
PodSecurityPolicy,在集群层面来限制,pod内部使用的某些用户特权级别资源。
5、SA认证-解析
5.1、SA简介
K8S自动为每个Pod注入一个 ServiceAccount 及 配套的令牌
在每个名称空间中,会自动存在(由ServiceAccount准入控制器负责)一个ServiceAccount,将被该空间下的每个Pod共享使用。
认证令牌保存于该空间下的一个Secret对象中,该对象中共有三个信息:namespace、ca.crt、token
5.2、认证示例
5.2.1、样式1-Secret
Volumes: ... default-token-9vs4d: Type: Secret (a volume populated by a Secret) SecretName: default-token-9vs4d Optional: false 这里的SecretName就是一个认证信息,来源于我们创建k8s的时候,默认创建的一个secret
]# kubectl get secrets NAME TYPE DATA AGE default-token-9vs4d kubernetes.io/service-account-token 3 3d # 这是k8s提供的一种认证模式,只不过默认的认证权限不是太大。 token是有限期限的,如果过期的话,token就没有了,那么就会出现第二种方式。
5.2.2、样式2-Projected
Volumes: kube-api-access-hvv4j: Type: Projected (a volume that contains injected data from multiple sources) TokenExpirationSeconds: 3607 ConfigMapName: kube-root-ca.crt ConfigMapOptional: <nil> DownwardAPI: true
5.3、资源属性
apiVersion: v1 # ServiceAccount所属的API群组及版本 kind: ServiceAccount # 资源类型标识 metadata: name <string> # 资源名称 namespace <string> # ServiceAccount是名称空间级别的资源 automountServiceAccountToken <boolean> # 是否让Pod自动挂载API令牌 secrets <[]Object> # 以该SA运行的Pod所要使用的Secret对象组成的列表 apiVersion <string> # 引用的Secret对象所属的API群组及版本,可省略 kind <string> # 引用的资源的类型,这里是指Secret,可省略 name <string> # 引用的Secret对象的名称,通常仅给出该字段即可 namespace <string> # 引用的Secret对象所属的名称空间 uid <string> # 引用的Secret对象的标识符; imagePullSecrets <[]Object> # 引用的用于下载Pod中容器镜像的Secret对象列表 name <string> # docker-registry类型的Secret资源的名称
6、ServiceAccount-实践
6.1、查看空间的信息
6.1.1、查看普通用户空间的信息
master1 ]# kubectl get sa NAME SECRETS AGE default 0 13d master1 ]# kubectl get serviceaccounts NAME SECRETS AGE default 0 13d 注意: 每个命名空间都会有一个默认的default的sa账号名称
6.1.2、查看kube-system的sa信息
master1 ~]# kubectl -n kube-system get sa | grep -v SECRETS | wc -l 36 可以看到:在kube-system的namespace中sa的条目多大31条
6.1.3、sa属性查看
master1 ~]# kubectl get sa default -o yaml apiVersion: v1 kind: ServiceAccount metadata: creationTimestamp: "2023-03-16T12:07:15Z" name: default namespace: default resourceVersion: "304" uid: ee1771e2-a8ba-42f9-82eb-dd35c78e52cf 结果显示: 对于一个serviceaccount来说,其属性信息在metadata部分,在这里比较重要的是对于sa的信息交流还是需要通过secrets的认证信息进行通信, 而这个认证信息它是自动帮我们附加的,我们无需关心。
6.1.4、secrets信息查看
master1 ~]# kubectl get secrets k8s-auth -o yaml apiVersion: v1 data: .dockerconfigjson: eyJhdXRocyI6eyIxOTIuMTY4LjEwLjIyOjgwIjp7InVzZXJuYW1lIjoiY3ljIiwicGFzc3dvcmQiOiJBYTEyMzQ1NiIsImF1dGgiOiJZM2xqT2tGaE1USXpORFUyIn19fQ== kind: Secret metadata: creationTimestamp: "2023-03-16T16:47:27Z" name: k8s-auth namespace: default resourceVersion: "32313" uid: d7640f43-1f8c-4f67-9fcd-82f56ecc7203 type: kubernetes.io/dockerconfigjson 这个secrets是一个dockerconfigjson类型的
6.2、创建sa账号
6.2.1、创建sa命令
命令格式: kubectl create serviceaccount NAME [--dry-run] [options] 作用:创建一个"服务账号" 参数详解 --dry-run=false 模拟创建模式 --generator='serviceaccount/v1' 设定api版本信息 -o, --output='' 设定输出信息格式,常见的有:json|yaml|name|template|... --save-config=false 保存配置信息 --template='' 设定配置模板文件
6.2.2、打印出创建sa yaml格式
master1 ~]# kubectl create serviceaccount mysa --dry-run=client -o yaml apiVersion: v1 kind: ServiceAccount metadata: creationTimestamp: null name: mysa
6.2.3、创建一个sa并且关联token
# 创建sa帐号admin master1 ~]# kubectl create serviceaccount admin serviceaccount/admin created master1 ~]# kubectl get sa NAME SECRETS AGE admin 0 3s # 生成了一个sa账号 default 0 13d # 在1.25及以上版本的集群中,ServiceAccount将不会自动创建对应的Secret。所以创建token,给sa关联 kubectl apply -f - <<EOF apiVersion: v1 kind: Secret metadata: name: build-admin-secret annotations: kubernetes.io/service-account.name: admin type: kubernetes.io/service-account-token EOF master1 ]# kubectl describe sa admin Name: admin Namespace: default Labels: <none> Annotations: <none> Image pull secrets: <none> # 下载镜像时候的认证信息 Mountable secrets: <none> # 挂载数据时候的认证信息 Tokens: build-admin-secret # 原始的账户认证信息 Events: <none>
6.3、给sa增加 Image pull secrets
6.3.1、创建docker登陆的帐号信息
# 创建docker登陆的帐号信息 kubectl create secret docker-registry myregistrykey --docker-server=192.168.10.88:80 \ --docker-username=cyc \ --docker-password=12345678 \ --docker-email=test@qq.com
6.3.2、将docker登陆的帐号信息增加至sa
kubectl patch serviceaccount admin -p '{"imagePullSecrets": [{"name": "myregistrykey"}]}'
6.3.3、检查是否增加成功
master1 ~]# kubectl describe serviceaccounts admin Name: admin Namespace: default Labels: <none> Annotations: <none> Image pull secrets: myregistrykey Mountable secrets: <none> Tokens: build-admin-secret Events: <none>
6.4、将pod关联创建的sa帐号-实践
6.4.1、定义资源配置清单
cat > security-sa-admin.yml <<'EOF' apiVersion: v1 kind: ServiceAccount metadata: name: admin --- apiVersion: v1 kind: Secret metadata: name: build-admin-secret annotations: kubernetes.io/service-account.name: admin type: kubernetes.io/service-account-token --- apiVersion: v1 kind: Pod metadata: name: pod-sa-admin spec: containers: - name: pod-sa-admin image: 192.168.10.33:80/k8s/my_nginx:v1 imagePullPolicy: IfNotPresent volumeMounts: - mountPath: /var/run/secrets/tokens name: vol-admin-secret serviceAccountName: admin volumes: - name: vol-admin-secret secret: secretName: build-admin-secret EOF
6.4.2、应用资源配置清单
kubectl apply -f security-sa-admin.yml master1 ]# kubectl get secrets NAME TYPE DATA AGE build-admin-secret kubernetes.io/service-account-token 3 8m22s myregistrykey kubernetes.io/dockerconfigjson 1 25m master1 ]# kubectl get sa NAME SECRETS AGE admin 0 8m26s master1 ]# kubectl describe secrets build-admin-secret ... token: eyJhbGciOiJSUzI1NiIsImtpZCI6I...
6.4.3、检查pod里面secrets挂载是否正常
master1 ~]# kubectl exec -it pod-sa-admin -- cat /var/run/secrets/tokens/token eyJhbGciOiJSUzI1NiIsImtpZCI6I... # token一致,表示挂载成功