kubernetes创建用户和ServiceAccount并设置RoleBinding

本文在 Kubernetes-Dashboard 一文基础上,深入学习了 Kubernetes 的RBAC 权限策略,然后在集群创建用户和ServiceAccount,设置Role 和 RoleBinding, 实现指定权限用户的授权。

kubernetes创建用户和ServiceAccount并设置RoleBinding

update @2018/04/25
update @2018/12/21

简介

kubernetes中, 安全登录kubernetes-dashboard有3种方式:

  • 基本的用户名/密码方式: 被Kubernetes API默认禁用
  • 使用kubeconfig文件: dashboard目录尚不支持
  • 使用Token: 仅限ServiceAccount账户的token

综上,访问kubernetes-dashboard目前我们能选的只有第三种方式。

在命令行下,我们还可以通过设置用户的context文件对kubectl操作进行授权。

因此,本文会结合两种方式,达到既可以访问dashboard,又可以在命令行操作的目的。

创建SA账户并添加Role绑定

  • 生成SA文件
cat > developer-sa.yaml << EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: developer-sa
  namespace: default
EOF
  • 生成Role文件
cat > developer-role.yaml << EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: developer-role
rules:
- apiGroups: ["", "extensions", "apps", "batch"]
  resources: ["services", "deployments", "statefulsets", "replicasets", "replicationcontrollers", "pods", "pods/log", "configmaps", "jobs", "events", "persistentvolumeclaims", "secrets", "cronjobs", "daemonsets", "ingresses"]
  #verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # You can also use ["*"]
  verbs: ["get", "list", "watch"]
EOF
  • 生成RoleBinding文件
cat > developer-rolebinding.yaml << EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: developer-rolebinding
  namespace: default
subjects:
- kind: ServiceAccount
  name: developer-sa
  namespace: default
roleRef:
  kind: Role
  name: developer-role
  apiGroup: ""
EOF
  • 使用kubectl创建相应资源
kubectl create -f developer-sa.yaml
kubectl create -f developer-role.yaml
kubectl create -f developer-rolebinding.yaml
  • 获取satoken
kubectl describe secret $(kubectl get secret | grep developer-sa | awk '{print $1}') | awk '$1=="token:"{print $2}'

复制这个token, 到dashboard登录就可以了:

image

但是, 我们不可能总是使用dashboard来查看应用的状态和做相关的操作, 并且dashboard并不支持应用的实时日志, 这一点就不能满足目前阶段测试的需求了(后面会上日志收集工具), 那么就需要使用到命令行下的操作。

且看下面↓

创建用户在命令行下的kubeconfig文件

命令行下只能通过设置用户context方式来授权,我们需要创建用户、私钥、证书请求文件,并使用Kubernetes的CA文件给用户签发证书(certificate),然后通过kubectl创建许可(credentials)和上下文(context), 最后通过Rolebinding为用户绑定角色,实现授权访问。

  • 新建用户及相应目录

    useradd developer
    passwd developer
    mkdir -pv /home/developer/{.kube,.certs}
    
  • 使用openssl生成证书对

    cd /home/developer/.certs/
    
    # 本条视情况而定
    kubectl create namespace default
    
    openssl genrsa -out developer.key 2048
    
    openssl req -new -key developer.key -out developer.csr -subj "/CN=developer/OU=Development"
    
    openssl x509 -req -in developer.csr -CA /etc/kubernetes/ssl/ca.pem -CAkey /etc/kubernetes/ssl/ca-key.pem -CAcreateserial -out developer.crt -days 3660
    
  • 使用kubectl创建用户的context

    kubectl config set-credentials developer --client-certificate=./developer.crt --client-key=./developer.key
    kubectl config set-context developer-context --cluster=cluster.local --namespace=default --user=developer
    

    此时使用新增的context是无法获取pods的, 因为没有绑定角色:

    kubectl --context=developer-context get pods
    
  • 修改rolebinding yaml文件
    subjects下面增加一个User

    cat > developer-rolebinding.yaml << EOF
    apiVersion: rbac.authorization.k8s.io/v1a
    kind: RoleBinding
    metadata:
      name: developer-rolebinding
      namespace: default
    subjects:
    - kind: ServiceAccount
      name: developer-sa
      namespace: default
    - kind: User
      name: developer
      namespace: default
    roleRef:
      kind: Role
      name: developer-role
      apiGroup: ""
    EOF
    
  • 重新应用一下配置

    kubectl apply -f developer-rolebinding.yaml

  • 使用指定context进行查询

    kubectl --context=developer-context get pods

  • 设置默认context

    kubectl config use-context developer-context

  • 提取kubeconfig文件并放到用户developer主目录下

    cp /root/.kune/config /home/developer/.kube
    chown -R developer.developer /home/developer
    
  • 最后还原默认context

    kubectl config use-context admin-cluster.local
    

自动创建脚本

使用上面的步骤总是很不方便,我写了一个通用的脚本,传入usernamepasswordnamespace,即可一键完成授权,目前授予的是只读权限,其它权限可自行增减,也可以在创建完成后按需添加。

从 Kubernetes 1.11 开始,kubespray 将默认的 context 名字由原来的 admin-cluster.local 改成了 [email protected], 这里添加了逻辑判断并实现了兼容。

脚本内容:
vim create_user_sa.sh

#!/bin/env bash
# Description:
#     package chart
#   @Date:    2018-02-27
#   @Author:  Tima Ops
#   @Version:
#
# funtion
Usage() {
    echo "Usage:"
    echo " $0 [username] [password] [namespace] [project]"
    echo " You must specify the user's username, password and the namespace he/she belongs to."
}

[[ -z "$1" ]] || [[ -z "$2" ]] || [[ -z "$3" ]] || [[ -z "$4" ]] && Usage && exit 1
USER_NAME="$1"
PASS_WORD="$2"
NAME_SPACE="$3"
PROJECT="$4"

CA_HOME="/etc/kubernetes/ssl"
USER_HOME="/home/${USER_NAME}"

ADMIN_CONTEXT="admin-cluster.local"
CA_FILE="ca.pem"
CA_KEY_FILE="ca-key.pem"

kubectl config get-contexts | grep "admin-cluster.local" 2>/dev/null 1>&2
if [[ $? != 0 ]]; then
  ADMIN_CONTEXT="[email protected]"
  CA_FILE="ca.crt"
  CA_KEY_FILE="ca.key"
fi

export PATH=$PATH:/usr/local/bin/
kubectl config use-context ${ADMIN_CONTEXT}

# Create namespace
echo -ne "\n"
echo "[Info] Check if namespace ${NAME_SPACE} exsits."
COUNT=$(kubectl get namespace | grep -w "${NAME_SPACE}" | wc -l)
if [[ ${COUNT} = 1 ]]; then
  echo "[Info] Namespace ${NAME_SPACE} exsits, will be used instead of created."
else
  kubectl create namespace ${NAME_SPACE}
fi

# Create user
echo -ne "\n"
echo "[Info] Check if user ${USER_NAME} exsits."
id -u ${USER_NAME} > /dev/null 2>&1
if [[ $? = 0 ]]; then
  echo "[Info] User ${USER_NAME} exsits."
else
  echo "[Info] Creating user ${USER_NAME}"
  useradd ${USER_NAME}
fi

# Set password
echo "[Info] Setting password now..."
echo -e "${PASS_WORD}\n${PASS_WORD}" | passwd ${USER_NAME}
[[ -d ${USER_HOME}/.kube ]] && rm -rf ${USER_HOME}/.kube
[[ -d ${USER_HOME}/.certs ]] && rm -rf ${USER_HOME}/.certs
mkdir -pv ${USER_HOME}/{.kube,.certs} > /dev/null 2>&1

# Generate user certs
echo -ne "\n"
echo "[Info] Generate user certs now..."
cd ${USER_HOME}/.certs
openssl genrsa -out ${USER_NAME}.key 2048
openssl req -new -key ${USER_NAME}.key -out ${USER_NAME}.csr -subj "/CN=${USER_NAME}/OU=${PROJECT}"
openssl x509 -req -in ${USER_NAME}.csr -CA ${CA_HOME}/${CA_FILE} -CAkey ${CA_HOME}/${CA_KEY_FILE} -CAcreateserial -out ${USER_NAME}.crt -days 3660

# Create user context
echo -ne "\n"
echo "[Info] Create user context now..."
kubectl config set-credentials ${USER_NAME} --client-certificate=./${USER_NAME}.crt --client-key=./${USER_NAME}.key
kubectl config set-context ${USER_NAME}-context --cluster=cluster.local --namespace=${NAME_SPACE} --user=${USER_NAME}

# Generate service account YAML
echo -ne "\n"
echo "[Info] Generate ServiceAccount for ${USER_NAME} in ${NAME_SPACE} now..."
cat > ${USER_NAME}-sa.yaml << EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: ${USER_NAME}-sa
  namespace: ${NAME_SPACE}
EOF

# Generate role YAML
echo -ne "\n"
echo "[Info] Generate Role for ${USER_NAME} in ${NAME_SPACE} now..."
cat > ${USER_NAME}-role.yaml << EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: ${USER_NAME}-role
  namespace: ${NAME_SPACE}
rules:
- apiGroups: ["", "extensions", "apps", "batch"]
  resources: ["services", "deployments", "statefulsets", "replicasets", "replicationcontrollers", "pods", "pods/log", "configmaps", "jobs", "events", "persistentvolumeclaims", "secrets", "cronjobs", "daemonsets", "ingresses"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # You can also use ["*"]
  #verbs: ["get", "list", "watch"]
EOF

# Generate rolebinding YAML
echo -ne "\n"
echo "[Info] Generate RoleBinding for ${USER_NAME} in ${NAME_SPACE} now..."
cat > ${USER_NAME}-rolebinding.yaml << EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ${USER_NAME}-rolebinding
  namespace: ${NAME_SPACE}
subjects:
- kind: ServiceAccount
  name: ${USER_NAME}-sa
  namespace: ${NAME_SPACE}
- kind: User
  name: ${USER_NAME}
  namespace: ${NAME_SPACE}
roleRef:
  kind: Role
  name: ${USER_NAME}-role
  apiGroup: ""
EOF

# Use kubectl to create resources
echo -ne "\n"
kubectl get sa ${USER_NAME}-sa --namespace ${NAME_SPACE} > /dev/null 2>&1
if [[ $? = 0 ]]; then
  echo "[Info] ${USER_NAME}-sa exsits, will delete it and regenerate it."
  kubectl delete sa ${USER_NAME}-sa --namespace ${NAME_SPACE}
fi
kubectl create -f ${USER_NAME}-sa.yaml

kubectl get role ${USER_NAME}-role --namespace ${NAME_SPACE} > /dev/null 2>&1
if [[ $? = 0 ]]; then
  echo "[Info] ${USER_NAME}-role exsits, will delete it and regenerate it."
  kubectl delete role ${USER_NAME}-role --namespace ${NAME_SPACE}
fi
kubectl create -f ${USER_NAME}-role.yaml

kubectl get rolebinding ${USER_NAME}-rolebinding --namespace ${NAME_SPACE} > /dev/null 2>&1
if [[ $? = 0 ]]; then
  echo "[Info] ${USER_NAME}-rolebinding exsits, will delete it and regenerate it."
  kubectl delete rolebinding ${USER_NAME}-rolebinding --namespace ${NAME_SPACE}
fi
kubectl create -f ${USER_NAME}-rolebinding.yaml

# Fetch user token
echo -ne "\n"
echo "[Info] The token of ${USER_NAME} is:"
kubectl describe secret --namespace ${NAME_SPACE} $(kubectl get secret --namespace ${NAME_SPACE} | grep ${USER_NAME}-sa | awk '{print $1}') | awk '$1=="token:"{print $2}'
echo -ne "\n"
echo "[Info] You can use the token above to login kubernetes-dashboard."

# Test user context
echo -ne "\n"
echo "[Info] Testing user context ${USER_NAME}-context"
kubectl --context=${USER_NAME}-context get pods > /dev/null 2>&1
if [[ $? != 0 ]]; then
  echo "[Info] context tested failed, please check the certs and YAML files" && exit 1
else
  echo "[Info] context tested successfully! Set it for user ${USER_NAME} now..."
  kubectl config use-context ${USER_NAME}-context
  cp /root/.kube/config ${USER_HOME}/.kube
  chown -R ${USER_NAME}.${USER_NAME} ${USER_HOME}
  echo -ne "\n"
  echo "[Info] The context of ${USER_NAME} has been set successfully, you can use it like below:"
  echo " su - ${USER_NAME}"
  echo " kubectl get pods"
  echo -ne "\n"
fi

# Reset context to admin
kubectl config use-context ${ADMIN_CONTEXT}