๐ก 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




Leave a Reply