目录
根据 serviceaccount 生成 kubeconfig
第一个场景是由于 aws 控制台的 eks 产品并不具备导出 kubeconfig 功能,所以需要我们手动导出,但是该文件在使用的时候需要安装 aws cli 工具并通过输入 ak/sk 访问密钥等信息来通过 iam 认证,而且在使用 kubectl 过程中每次都要走这个校验流程,会增加至少 500ms 的延迟,这对使用者来说非常影响体验。
第二个场景是有时为了需要,我们需要给出一些具有特定集群权限的kubeconfig文件,这时我们可以通过使用serviceAccount来制作具有一定集群权限的kubeconfig
所以我们创建一个 serviceaccount 并配置 RBAC 权限,通过取出这个 SA 的 Secret 里的 User TOKEN 和 CA 证书来配置新的 kubeconfig 文件。
——UserAccount 和 ServiceAccount
通过 UserAccount 创建的 config 文件需要写入ca证书,客户端证书和 key。config文件的创建需要指定用户名,这个用户名和创建证书时写入的 CommonName(CN)不同,并不需要保持完全一致;在创建 binding 时指定的是用户以及对应的用户名,这个用户名需要和 CN 一致
serviceAccount 创建的 config 文件需要写入 ca 证书,以及 user token,这里同样不需要刻意指定用户名,因为真正将 config 文件与 ServiceAccount 进行关联的是 userToken。user token 是从 serviceaccount 的 secret 中取的,在创建 binding 时指定的是 sa 以及对应的 sa 名称
1.24 版本创建 serviceAccount 默认不会创建包含 ca 证书和 user token 的 secret,需要自行关联
k8s默认api server在master节点的6443端口上,我们通过curl命令请求需要经过https tls认证,否则会报没权限,一般有两种认证方式,客户端证书或者token的方式。这两种方式在curl中的参数体现分别是--cert=xxx/client.crt --key=xxx/client.key以及-H "Authorization: Bearer $TOKEN"。除此之后还需要配置服务端证书 --cacert=xxx/ca.crt或者--insecure来跳过服务端校验。
这里大概说一下生成客户端证书的过程,当k8s部署好之后会在/etc/kubenetes/pki目录下生成服务端证书文件ca.crt。我们需要在本地生成客户端私钥client.key
openssl genrsa -out client.key 2048
(如果服务端缺少证书ca.key,用同样的命令生成就可以了)
然后根据私钥文件带入一定的用户信息,生成请求文件client.csr
openssl req -new -key client.key -out client.csr -subj "CN=xxxx"
(其中CN=Common Name,这部分是用户自定义的信息)
最后再根据k8s的服务端证书生成我们用户的客户端证书
openssl x509 -req -in client.csr -CA /etc/kubenetes/pki/ca.crt -CAkey /etc/kubenetes/pki/ca.key -CAcreateserial -out client.crt -days 365
这还没完,我们现在创建了一个名为xxxx用户的证书,还需要设置上下文。
我们需要把crt贴到config文件中去:
# kubectl config —kubeconfig=/root/.kube/config set-credentials your-user —client-certificate=/root/tls/client.crt —client-key=/root/tls/client.key
User “your-user” set.
这一步把用户设置进去了,执行kubectl命令时会切换成我们的用户。
然后根据用户名和集群名生成context上下文
# kubectl config —kubeconfig=/root/.kube/config set-context your-context —cluster=your-cluster —user=your-user
Context “your-context” created.
然后就可以通过use-context指令来切换上下文。
此外,我们可能还要通过创建role和clusterrole并去binding来为刚生成的useraccount来生成对应的权限。
而token认证的方式简单一点,我们只需要创建service account,也是通过 clusterrole 和 role 去对该serviceaccount的权限进行绑定,生成serviceaccount会自动创建一个secret,我们可以在这个secret内取到服务端ca证书和token密钥,进而进行认证
首先默认已经通过官方的提供的方法生成一个 kubeconfig,这里不在赘述,具体方法见下链接:
执行文档中的命令需要在本地安装 AWS CLI,安装的命令(MacOS)为:
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target /
其他的操作系统的安装方法在下面的链接中获得:
Installing or updating the latest version of the AWS CLI
我们需要安装 jq 工具来对获取到的 secret 内容中的一些json字符串做转换处理写入到config(yaml格式)文件中去。
通过以下命令安装
brew install oniguruma
brew install jq
我们可以借用一段开源脚本来帮助我们去生成 kubeconfig,脚本入参是 SA的名称和其对应的命名空间
脚本内容如下 :
#!/bin/bash
set -e
set -o pipefail
# Add user to k8s using service account, no RBAC (must create RBAC after this script)
if [[ -z "$1" ]] || [[ -z "$2" ]]; then
echo "usage: $0 <service_account_name> <namespace>"
exit 1
fi
SERVICE_ACCOUNT_NAME=$1
NAMESPACE="$2"
KUBECFG_FILE_NAME="/tmp/kube/k8s-${SERVICE_ACCOUNT_NAME}-${NAMESPACE}-conf"
TARGET_FOLDER="/tmp/kube"
create_target_folder() {
echo -n "Creating target directory to hold files in ${TARGET_FOLDER}..."
mkdir -p "${TARGET_FOLDER}"
printf "done"
}
create_service_account() {
echo -e "\\nCreating a service account in ${NAMESPACE} namespace: ${SERVICE_ACCOUNT_NAME}"
kubectl create sa "${SERVICE_ACCOUNT_NAME}" --namespace "${NAMESPACE}"
}
get_secret_name_from_service_account() {
echo -e "\\nGetting secret of service account ${SERVICE_ACCOUNT_NAME} on ${NAMESPACE}"
SECRET_NAME=$(kubectl get sa "${SERVICE_ACCOUNT_NAME}" --namespace="${NAMESPACE}" -o json | jq -r .secrets[].name)
echo "Secret name: ${SECRET_NAME}"
}
extract_ca_crt_from_secret() {
echo -e -n "\\nExtracting ca.crt from secret..."
kubectl get secret --namespace "${NAMESPACE}" "${SECRET_NAME}" -o json | jq \
-r '.data["ca.crt"]' | base64 -D > "${TARGET_FOLDER}/ca.crt"
printf "done"
}
get_user_token_from_secret() {
echo -e -n "\\nGetting user token from secret..."
USER_TOKEN=$(kubectl get secret --namespace "${NAMESPACE}" "${SECRET_NAME}" -o json | jq -r '.data["token"]' | base64 -D)
printf "done"
}
set_kube_config_values() {
context=$(kubectl config current-context)
echo -e "\\nSetting current context to: $context"
CLUSTER_NAME=$(kubectl config get-contexts "$context" | awk '{print $3}' | tail -n 1)
echo "Cluster name: ${CLUSTER_NAME}"
ENDPOINT=$(kubectl config view \
-o jsonpath="{.clusters[?(@.name == \"${CLUSTER_NAME}\")].cluster.server}")
echo "Endpoint: ${ENDPOINT}"
# Set up the config
echo -e "\\nPreparing k8s-${SERVICE_ACCOUNT_NAME}-${NAMESPACE}-conf"
echo -n "Setting a cluster entry in kubeconfig..."
kubectl config set-cluster "${CLUSTER_NAME}" \
--kubeconfig="${KUBECFG_FILE_NAME}" \
--server="${ENDPOINT}" \
--certificate-authority="${TARGET_FOLDER}/ca.crt" \
--embed-certs=true
echo -n "Setting token credentials entry in kubeconfig..."
kubectl config set-credentials \
"${SERVICE_ACCOUNT_NAME}-${NAMESPACE}-${CLUSTER_NAME}" \
--kubeconfig="${KUBECFG_FILE_NAME}" \
--token="${USER_TOKEN}"
echo -n "Setting a context entry in kubeconfig..."
kubectl config set-context \
"${SERVICE_ACCOUNT_NAME}-${NAMESPACE}-${CLUSTER_NAME}" \
--kubeconfig="${KUBECFG_FILE_NAME}" \
--cluster="${CLUSTER_NAME}" \
--user="${SERVICE_ACCOUNT_NAME}-${NAMESPACE}-${CLUSTER_NAME}" \
--namespace="${NAMESPACE}"
echo -n "Setting the current-context in the kubeconfig file..."
kubectl config use-context "${SERVICE_ACCOUNT_NAME}-${NAMESPACE}-${CLUSTER_NAME}" \
--kubeconfig="${KUBECFG_FILE_NAME}"
}
create_target_folder
# create_service_account
get_secret_name_from_service_account
extract_ca_crt_from_secret
get_user_token_from_secret
set_kube_config_values
echo -e "\\nAll done! Test with:"
echo "KUBECONFIG=${KUBECFG_FILE_NAME} kubectl get pods"
echo "you should not have any permissions by default - you have just created the authentication part"
echo "You will need to create RBAC permissions"
KUBECONFIG=${KUBECFG_FILE_NAME} kubectl get pods
这段脚本的出处:
因为原生的kubeconfig携带的集群名称比较长,脚本中截取了该集群名称 ${SERVICE_ACCOUNT_NAME}-${NAMESPACE}-${CLUSTER_NAME} 作为 credentials、user 和 context的配置名称,不方便我们切换上下文,如果有需要,该部分也可以自定义字符串,不会影响文件的使用。或者是在生成 config文件后手动去修改。
值得一提的是,serviceaccount 虽然有命名空间的指定,但是仍可为其绑定 clusterrole 赋予集群维度的权限,这是由于:
当创建SA时,TokenController 作为 kube-controller-manager
的一部分运行,并为其创建了相应的服务账号令牌 Secret 以允许访问 API。当创建 Pod 时,隐式地指定了 SA 为 default ,default 的元数据命名空间为 default ,这就是为什么当我们创建一个 Pod 实例而不去指定命名空间时,默认会创建到 default 命名空间下。而如果显式指定了 SA ,则会将该令牌绑定到 Pod 实例上,并将 kube-apiserver作为其受众,以该 SA 去创建 Pod 时,就会打到该 SA 的命名空间下。
执行该脚本,进行 SA 的创建和 kubeconfig 的生成
./kubernetes_add_service_account_kubeconfig.sh eks-admin default
如果操作系统为ubuntu,执行的时候遇到 set: Illegal option -o pipefail,原因和解决方案可以参考:
ubuntu”set Illegal option -o pipefail”
解决方法是将dash切换成bash
因为我做 kubconfig 导出仍然是作为系统管理员在本地自用,而不是做权限分发,所以我绑定了系统管理员权限的 clusterrole
SA=eks-admin
kubectl create clusterrolebinding ${SA}-binding --clusterrole=cluster-admin --serviceaccount=default:${SA}
KUBECONFIG="/tmp/kube/k8s-eks-default-conf"
kubectl get pods
如果要和本地config进行合并,进行多集群管理,参考我的另一篇文章
通过将多个 kubeconfig 文件合并为一个来进行K8S多集群统一管理
至此,我们通过创建 SA 的方式生成了包含其 Secret TOKEN的 kubeconfig 的文件,并且通过该文件访问 kube- APIserver 能够有效避开 IAM 鉴权,能够加强我们敲 kuebctl命令的体验,并且通过这种方式,也能够根据不同的 clusterrole & role 去生成不同的 kubeconfig 文件做权限的分发。
参考资料:
Installing or updating the latest version of the AWS CLI