ServiceAccount深度解析
ServiceAccount为Pod钟的进程提供身份信息。当用户访问集群时(例如使用kubectl命令的时候),apiserver会将用户认证为一个特定的User Account(目前通常是admin,除非系统管理员自定义了集群配置)。Pod容器中的进程也可以与apiserver联系,当他们在连接apiserver的时候,他们会被认证为一个特定的service account(例如default)。
User account是为人设计的,service account是为Pod中的进程调用Kubernetes API而设计的。
-
User account是跨namespace的,而service account则是仅局限于它所在的namespace。
-
每个namespace都会自动创建一个default service account
-
Token controller检测service account的创建,并为他们创建secret
-
开启ServiceAccount Admission Controller后
-
每个Pod在创建后都会自动设置
spec.serviceAccount
为default(除非指定了其他ServiceAccount) -
验证Pod引用的service account已经存在,否则拒绝创建
-
如果Pod没有指定ImagePullSecretes,则把service account的ImagePullSecretes加到Pod中
什么是ImagePullSecretes?
是k8s中用于拉去私有容器镜像的机制。当你在集群中部署使用私有容器镜像的Pod时,通常需要提供身份验证凭据以获取访问权限。
ImagePullSecrets
是一个或多个凭据的集合,用于访问私有的 Docker 镜像仓库。这些凭据通常是通过 Kubernetes 中的Secret
对象来存储,并在 Pod 配置中引用。Pod 使用这些凭据来获取镜像仓库中的镜像,以确保能够成功拉取私有镜像。以下是一个示例,展示了如何在 Pod 配置中使用
ImagePullSecrets
:yamlCopy codeapiVersion: v1 kind: Pod metadata: name: my-app spec: containers: - name: my-container image: private-registry.com/my-image:latest imagePullSecrets: - name: my-secret
在上面的示例中,
imagePullSecrets
部分指定了一个名为my-secret
的Secret
,这个Secret
包含了访问私有镜像仓库所需的凭据。这样,Pod 就可以使用这个凭据来拉取private-registry.com/my-image:latest
镜像。要使用
ImagePullSecrets
,你需要执行以下步骤:- 创建一个
Secret
对象,其中包含访问私有镜像仓库的凭据(如用户名和密码)。 - 在 Pod 的配置中的
imagePullSecrets
部分引用这个Secret
。 - 当 Pod 启动时,Kubernetes 将使用
ImagePullSecrets
中的凭据来拉取所需的私有镜像。
总之,
ImagePullSecrets
是 Kubernetes 中用于访问私有容器镜像的机制,它允许在 Pod 配置中引用存储在Secret
中的凭据,以确保能够拉取私有镜像。 - 创建一个
-
每个container启动后都会挂载该service account的token和ca.crt到
/var/run/secrets/kubernetes.io/serviceaccount/
-
在 Kubernetes 中,每个 Service Account 都有一个关联的安全令牌(Token),用于对 Kubernetes API 进行身份验证,以及一个与集群通信的根证书(ca.crt)。这些元素有助于确保 Service Account 在与 Kubernetes API 交互时的安全性。
- Token(令牌):每个 Service Account 都会被分配一个安全令牌,用于身份验证。这个令牌是一个长字符串,它允许 Service Account 访问 Kubernetes API 中特定于命名空间的资源。当 Service Account 的 Pod 尝试与 API 服务器通信时,它会使用这个令牌进行身份验证,以获取访问权限。令牌会自动挂载到 Pod 中的
/var/run/secrets/kubernetes.io/serviceaccount/token
文件中,供应用程序使用。 - ca.crt(根证书):与每个 Service Account 关联的根证书用于验证与 Kubernetes API 服务器之间的通信的安全性。根证书是集群的 CA(Certificate Authority)颁发的,用于加密和验证通信。在与 Kubernetes API 服务器进行通信时,Pod 可以使用这个根证书来验证服务器证书的有效性,确保通信的机密性和完整性。
这两个元素结合在一起,确保了 Service Account 的安全性和通信的安全性。Pod 使用令牌来证明自己的身份,同时使用根证书来验证服务器的身份,以确保与 Kubernetes API 服务器的交互是安全的。
请注意,Service Account 的 Token 和根证书是自动生成的,并在创建 Service Account 时为其分配。这些元素对于运行在 Kubernetes 集群中的应用程序来说是透明的,但它们在底层提供了身份验证和通信安全性的支持。
使用默认的Service Account访问API Server
当创建 pod 的时候,如果没有指定一个 service account,系统会自动得在与该pod 相同的 namespace 下为其指派一个default service account。如果获取刚创建的 pod 的原始 json 或 yaml 信息(例如使用kubectl get pods podename -o yaml命令),将看到spec.serviceAccountName字段已经被设置为 default。
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
filebeat-ds-hxgdx 1/1 Running 1 34d
filebeat-ds-s466l 1/1 Running 2 34d
myapp-0 1/1 Running 0 3h
myapp-1 1/1 Running 0 3h
myapp-2 1/1 Running 0 4h
myapp-3 1/1 Running 0 4h
pod-vol-demo 2/2 Running 0 2d
redis-5b5d6fbbbd-q8ppz 1/1 Running 1 2d
[root@k8s-master ~]# kubectl get pods/myapp-0 -o yaml |grep "serviceAccountName"
serviceAccountName: default
[root@k8s-master ~]# kubectl describe pods myapp-0
Name: myapp-0
Namespace: default
......
Volumes:
......
default-token-j5pf5:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-j5pf5
Optional: false
从上面可以看到每个Pod无论定义与否都会有一个存储卷,这个存储卷为default-token-*** token令牌,这就是service account是为Pod中的进程与apiserver联系提供身份认证信息。通过secret进行定义,由于认证信息属于敏感信息,所以需要保存在secret资源当中,并以存储卷的方式挂载到Pod当中。从而让Pod内运行的应用通过对应的secret中的service account来连接apiserver,并完成认证。每个 namespace 中都有一个默认的叫做 default 的 service account资源。可以使用kubectl get secret命令当前名称空间内的secret,也可以看到对应的default-token。可以使用的预制认证信息让当前名称空间中所有的pod连接至apiserver,从而保证pod与apiserver之间的通信。
[root@k8s-master ~]# kubectl get sa
NAME SECRETS AGE
default 1 50d
[root@k8s-master ~]# kubectl get sa -n ingress-nginx #前期创建的ingress-nginx名称空间也存在这样的serviceaccount
NAME SECRETS AGE
default 1 11d
nginx-ingress-serviceaccount 1 11d
[root@k8s-master ~]# kubectl get secret
NAME TYPE DATA AGE
default-token-j5pf5 kubernetes.io/service-account-token 3 50d
mysecret Opaque 2 1d
tomcat-ingress-secret kubernetes.io/tls 2 10d
[root@k8s-master ~]# kubectl get secret -n ingress-nginx
NAME TYPE DATA AGE
default-token-zl49j kubernetes.io/service-account-token 3 11d
nginx-ingress-serviceaccount-token-mcsf4 kubernetes.io/service-account-token 3 11d
默认的service account仅仅只能获取当前Pod自身的相关属性,无法观察到其他名称空间Pod的相关属性信息。如果想要拓展Pod,假设有一个Pod需要用于管理其他Pod或者是其他资源对象(例如dashboard),是无法通过自身的名称空间的default service account进行获取其他Pod的相关属性信息的,此时就需要进行手动创建一个serviceaccount,并在创建Pod时进行定义。
创建一个ServiceAccount
[root@master-1 app]# vim serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin
namespace: default
[root@master-1 app]# kubectl apply -f serviceaccount.yaml
serviceaccount/admin created
[root@master-1 app]# kubectl get sa
NAME SECRETS AGE
admin 1 31s
default 1 20d
nfs-provisioner 1 18d
[root@master-1 app]# kubectl get secret
NAME TYPE DATA AGE
admin-token-j7n8j kubernetes.io/service-account-token 3 27m
default-token-zmv4x kubernetes.io/service-account-token 3 20d
mysecret Opaque 2 19d
nfs-provisioner-token-xjn7p kubernetes.io/service-account-token 3 18d
[root@master-1 app]# kubectl get sa admin -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"admin","namespace":"default"}}
creationTimestamp: "2019-02-12T03:07:07Z"
name: admin
namespace: default
resourceVersion: "2606053"
selfLink: /api/v1/namespaces/default/serviceaccounts/admin
uid: 48250903-2e73-11e9-a8c7-d8490b8af3ae
secrets:
- name: admin-token-j7n8j
看到有一个 token 已经被自动创建,只需要在 pod 的spec.serviceAccountName 字段中将name设置为您想要用的 service account 名字即可。在 pod 创建之初 service account 就必须已经存在,否则创建将被拒绝。需要注意的是不能更新已创建的 pod 的 service account
ServiceAccount的自定义使用
这里在default名称空间创建了一个serviceaccount为admin,可以看到已经自动生成了一个Tokens:admin-token-j7n8j,下面展示如何使用自定义的serviceaccount
[root@master-1 app]# vim pod-sa-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-sa-demo
namespace: default
labels:
app: myapp
tier: frontend
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
serviceAccountName: admin
[root@master-1 app]# kubectl apply -f pod-sa-demo.yaml
pod/pod-sa-demo created
[root@master-1 app]# kubectl describe pods pod-sa-demo
......
Volumes:
admin-token-j7n8j:
Type: Secret (a volume populated by a Secret)
SecretName: admin-token-j7n8j
Optional: false
......
User Account的定义以及使用
在K8S集群当中,每一个用户对资源的访问都是需要通过apiserver进行通信认证才能进行访问的,那么在此机制当中,对资源的访问可以是token,也可以是通过配置文件的方式进行保存和使用认证信息,kubectl命令行工具使用kubeconfig文件来查找选择群集并与群集的APIserver进行通信。可以通过kubectl config进行查看编辑kubeconfig配置文件,配置文件路径$HOME/.kube/config文件,eg:/root/.kube/config 如下:
[root@k8s-master mainfests]# kubectl config view
apiVersion: v1
clusters: #集群列表
- cluster:
certificate-authority-data: REDACTED
server: https://192.168.56.11:6443
name: kubernetes
contexts: #上下文列表
- context: #定义哪个集群被哪个用户访问
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes #当前上下文
kind: Config
preferences: {}
users: #用户列表
- name: kubernetes-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
在上面的配置文件当中,定义了集群、上下文以及用户。其中Config也是K8S的标准资源之一,在该配置文件当中定义了一个集群列表,指定的集群可以有多个;用户列表也可以有多个,指明集群中的用户;而在上下文列表当中,是进行定义可以使用哪个用户对哪个集群进行访问,以及当前使用的上下文是什么。如图:定义了用户kubernetes-admin可以对kubernetes该集群的访问,用户kubernetes-user1对Cluster1集群的访问。
自建证书和账号进行访问apiserver演示
注意:此步骤在
/etc/kubernetes/pki
路径下执行,因为需要用到Kubernetes CA证书。
-
生成证书
[root@k8s-master pki]# (umask 077;openssl genrsa -out magedu.key 2048) Generating RSA private key, 2048 bit long modulus ............................................................................................+++ ...................................................................................+++ e is 65537 (0x10001)
[root@k8s-master pki]# ll magedu.key -rw------- 1 root root 1675 Oct 12 23:52 magedu.key
-
使用ca.crt进行签署
[root@k8s-master pki]# openssl req -new -key magedu.key -out magedu.csr -subj "/CN=magedu" 证书签署请求(CN表示用户名,O表示组)
[root@k8s-master pki]# openssl x509 -req -in magedu.csr -CA ./ca.crt -CAkey ./ca.key -CAcreateserial -out magedu.crt -days 365 #证书签署 Signature ok subject=/CN=magedu Getting CA Private Key
[root@k8s-master pki]# openssl x509 -in magedu.crt -text -noout
生成证书和签署证书是证书颁发的两个不同步骤,这两个步骤涉及到不同的操作和角色,用于创建和验证数字证书的完整新和合法性。
- 生成证书:在这个步骤中,证书的申请者(通常是实体,服务器胡总和客户端)生成一个密钥对,包括一个私钥和一个公钥。 私钥用于加密和解密数据,而公钥用于加密和验证签名。然后证书申请者会将公约和一些标识信息(如域名,组织名称等)提供给证书颁发机构(CA)
- 签署证书:在这个步骤中,证书颁发机构(CA)使用其自己的私钥来对证书申请者提供的公钥和标识信息进行签名。这个签名包括一些元数据,比如颁发者,有效期,颁发日期等。
-
添加到用户认证
[root@k8s-master pki]# kubectl config set-credentials magedu --client-certificate=./magedu.crt --client-key=./magedu.key --embed-certs=true User "magedu" set.
[root@k8s-master pki]# kubectl config set-context magedu@kubernetes --cluster=kubernetes --user=magedu Context "magedu@kubernetes" created.
[root@k8s-master pki]# kubectl config view apiVersion: v1 clusters: - cluster: certificate-authority-data: REDACTED server: https://192.168.56.11:6443 name: kubernetes contexts: - context: cluster: kubernetes user: kubernetes-admin name: kubernetes-admin@kubernetes - context: cluster: kubernetes user: magedu name: magedu@kubernetes current-context: kubernetes-admin@kubernetes kind: Config preferences: {} users: - name: kubernetes-admin user: client-certificate-data: REDACTED client-key-data: REDACTED - name: magedu user: client-certificate-data: REDACTED client-key-data: REDACTED
[root@k8s-master pki]# kubectl config use-context magedu@kubernetes Switched to context "magedu@kubernetes".
[root@k8s-master pki]# kubectl get pods No resources found. Error from server (Forbidden): pods is forbidden: User "magedu" cannot list pods in the namespace "default"