Kubernetes ReadOnly ServiceAccount & Limited

๐Ÿ’ก Kubernetes ReadOnly ServiceAccount & Limited โ€” Executive Summary

Technical architectural analysis, practical configuration manifests, and deployment instructions for Kubernetes ReadOnly ServiceAccount & Limited.

readonly SA, Secrets, Kubeconfig

Create permission
env
bash
export prefix=read-only
export ns=default
Create SA, token, CRB
bash
kubectl create serviceaccount ${prefix}-sa -n ${ns}

kubectl apply -f - << EOF
apiVersion: v1
kind: Secret
metadata:
  name: ${prefix}-sa-token
  namespace: ${ns}
  annotations:
    kubernetes.io/service-account.name: "${prefix}-sa"
type: kubernetes.io/service-account-token
EOF

kubectl apply -f - << EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: ${prefix}-clusterrolebinding
subjects:
  - kind: ServiceAccount
    name: ${prefix}-sa
    namespace: ${ns}
roleRef:
  kind: ClusterRole
  name: ${prefix}-clusterrole
  apiGroup: rbac.authorization.k8s.io
EOF

kubectl get secret ${prefix}-sa-token -n ${ns} -o jsonpath="{.data.token}" | base64 --decode
ClusterRole Definition
  • Apply Principle of Least Privilege (POLP)
bash
kubectl apply -f - << EOF
  apiVersion: rbac.authorization.k8s.io/v1
  kind: ClusterRole
  metadata:
    name: ${prefix}-clusterrole
  rules:
    - apiGroups: ["","apps","batch","autoscaling","app.k8s.io"]
      resources: ["pods","services","replicasets","deployments","daemonsets","statefulsets","replicationcontrollers","horizontalpodautoscalers","cronjobs","jobs","applications"]
      verbs: ["get", "list", "watch"]
    - apiGroups: ["apiextensions.k8s.io"]
      resources: ["customresourcedefinitions"]
      verbs: ["get", "list", "watch"]
  EOF
  • Exception management through OPA
bash
kubectl apply -f - << EOF
  apiVersion: rbac.authorization.k8s.io/v1
  kind: ClusterRole
  metadata:
    name: ${prefix}-clusterrole
  rules:
    - apiGroups: ["*"]
      resources: ["*"]
      verbs: ["get", "list", "watch"]
  EOF

  #ex) read-only๋กœ ์‹œ์ž‘ํ•˜๋Š” sa or clusterrole or role์„ ์ œํ•œ
  kubectl apply -f - << EOF
  apiVersion: templates.gatekeeper.sh/v1
  kind: ConstraintTemplate
  metadata:
    name: deny-read-only-access
  spec:
    crd:
      spec:
        names:
          kind: DenyReadOnlyAccess
    targets:
      - target: admission.k8s.gatekeeper.sh
    rego: |
      package denyreadonlyaccess

      violation[{"msg": msg}] {
        # ์š”์ฒญ๋œ ๋ฆฌ์†Œ์Šค๊ฐ€ Secret ๋˜๋Š” ServiceAccount์ธ์ง€ ํ™•์ธ
        input.review.object.kind == "Secret" || input.review.object.kind == "ServiceAccount"

        # ์š”์ฒญ๋œ ๋™์ž‘์ด get, list, watch ์ค‘ ํ•˜๋‚˜์ธ์ง€ ํ™•์ธ
        input.review.operation == "GET" || input.review.operation == "LIST" || input.review.operation == "WATCH"

        # ์š”์ฒญํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด ์ถ”์ถœ
        factor := "read-only"
        subject := input.review.userInfo.username

        # ServiceAccount, Role, ClusterRole ์ด๋ฆ„์ด "read-only"๋กœ ์‹œ์ž‘ํ•˜๋Š”์ง€ ํ™•์ธ
        startswith(subject, "system:serviceaccount:") {
          sa_name := split(subject, ":")[2]
          startswith(sa_name, factor)
        }
        startswith(subject, factor)

        msg := sprintf("'%s'๋กœ ์‹œ์ž‘ํ•˜๋Š” Role์ด๋‚˜ ServiceAccount์ธ '%s'๋Š” Secret ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.", [factor,subject])
      }
  EOF

  kubectl apply -f - << EOF
  apiVersion: constraints.gatekeeper.sh/v1beta1
  kind: DenySecretAccessConstraint
  metadata:
    name: deny-secret-access-for-users
  spec:
    match:
      kinds:
        - apiGroups: [""]
          kinds: ["Secret"]
      subjects:
        - kind: User
          name: "example-user"  # ํŠน์ • ์‚ฌ์šฉ์ž
        - kind: Group
          name: "example-group" # ํŠน์ • ๊ทธ๋ฃน
  EOF
Create access information
env
bash
export prefix=read-only
export ns=default
export clustername=kubernetes
export SERVER=
Plan 1: Create a kubeconfig containing certificate-authority-data
bash
cat <<EOF >> kubeconfig
apiVersion: v1
kind: Config
clusters:
- name: ${clustername}
  cluster:
    certificate-authority-data: $(kubectl config view --raw -o jsonpath="{.clusters[0].cluster.certificate-authority-data}")
    server: ${SERVER}
contexts:
- name: ${prefix}-context
  context:
    cluster: ${clustername}
    namespace: ${ns}
    user: ${prefix}-user
current-context: ${prefix}-context
users:
- name: ${prefix}-user
  user:
    token: $(kubectl get secret ${prefix}-sa-token -n ${ns} -o jsonpath="{.data.token}" | base64 --decode)
EOF
Option 2: enable insecure-skip-tls-verify and create kubeconfig
bash
cat <<EOF >> kubeconfig
apiVersion: v1
kind: Config
clusters:
- name: ${clustername}
  cluster:
    insecure-skip-tls-verify: true
    server: ${SERVER}
contexts:
- name: ${prefix}-context
  context:
    cluster: ${clustername}
    namespace: ${ns}
    user: ${prefix}-user
current-context: ${prefix}-context
users:
- name: ${prefix}-user
  user:
    token: $(kubectl get secret ${prefix}-sa-token -n ${ns} -o jsonpath="{.data.token}" | base64 --decode)
EOF
Client Settings
bash
export prefix=read-only
kubectl config use-context ${prefix}-context

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *