반응형
default 네임스페이스에 K8s Cluster 전역 조회 권한이 있는 ServiceAccount 생성 ( Secret 리소스 읽기 제외 )
apiVersion: v1
kind: ServiceAccount
metadata:
name: read-only-no-secrets
namespace: default
---
# Kubernetes RBAC는 명시적 허용만 가능(제외 불가)하므로
# core API group에서 secrets를 의도적으로 제외하고 나머지 리소스만 나열한다.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: read-only-no-secrets
rules:
- apiGroups: [""]
resources:
- pods
- pods/log
- pods/status
- services
- endpoints
- configmaps
- persistentvolumeclaims
- persistentvolumes
- namespaces
- nodes
- events
- replicationcontrollers
- resourcequotas
- limitranges
- serviceaccounts
- bindings
- componentstatuses
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["autoscaling"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["policy"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["storage.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["discovery.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apiextensions.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["scheduling.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["coordination.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["events.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["metrics.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["admissionregistration.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["certificates.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["node.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["flowcontrol.apiserver.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: read-only-no-secrets
subjects:
- kind: ServiceAccount
name: read-only-no-secrets
namespace: default
roleRef:
kind: ClusterRole
name: read-only-no-secrets
apiGroup: rbac.authorization.k8s.io
생성한 SA의 장기(만료 없음) 토큰 Secret 생성하기
apiVersion: v1
kind: Secret
metadata:
name: read-only-no-secrets-token
namespace: default
annotations:
kubernetes.io/service-account.name: read-only-no-secrets
type: kubernetes.io/service-account-token
kubeconfig 파일 생성하기
#!/usr/bin/env bash
set -euo pipefail
NAMESPACE="${NAMESPACE:-default}"
SECRET_NAME="${SECRET_NAME:-read-only-no-secrets-token}"
SA_NAME="${SA_NAME:-read-only-no-secrets}"
KUBECONFIG_OUT="${KUBECONFIG_OUT:-./readonly-kubeconfig.yaml}"
# 토큰 컨트롤러가 비동기로 채우므로 최대 ~10초 대기
TOKEN_B64=""
for i in 1 2 3 4 5; do
TOKEN_B64=$(kubectl get secret "$SECRET_NAME" -n "$NAMESPACE" \
-o jsonpath='{.data.token}' 2>/dev/null || true)
if [ -n "$TOKEN_B64" ]; then break; fi
echo "토큰이 아직 채워지지 않음 ($i/5), 2초 대기..." >&2
sleep 2
done
if [ -z "$TOKEN_B64" ]; then
echo "ERROR: $NAMESPACE/$SECRET_NAME 에 토큰이 없습니다." >&2
echo " - Secret이 존재하는지: kubectl get secret $SECRET_NAME -n $NAMESPACE" >&2
echo " - 어노테이션이 SA를 가리키는지 확인하세요." >&2
exit 1
fi
TOKEN=$(printf '%s' "$TOKEN_B64" | base64 -d)
CA=$(kubectl get secret "$SECRET_NAME" -n "$NAMESPACE" -o jsonpath='{.data.ca\.crt}')
SERVER=$(kubectl config view --minify --raw -o jsonpath='{.clusters[0].cluster.server}')
CLUSTER=$(kubectl config view --minify --raw -o jsonpath='{.clusters[0].name}')
if [ -z "$CA" ] || [ -z "$SERVER" ] || [ -z "$CLUSTER" ]; then
echo "ERROR: CA / server / cluster 중 일부를 가져오지 못했습니다." >&2
echo " CA len=${#CA} SERVER=$SERVER CLUSTER=$CLUSTER" >&2
exit 1
fi
umask 077
cat > "$KUBECONFIG_OUT" <<EOF
apiVersion: v1
kind: Config
clusters:
- name: ${CLUSTER}
cluster:
server: ${SERVER}
certificate-authority-data: ${CA}
users:
- name: ${SA_NAME}
user:
token: ${TOKEN}
contexts:
- name: read-only
context:
cluster: ${CLUSTER}
user: ${SA_NAME}
namespace: ${NAMESPACE}
current-context: read-only
EOF
chmod 600 "$KUBECONFIG_OUT"
echo "✓ kubeconfig 생성: $KUBECONFIG_OUT"
echo ""
echo "테스트:"
echo " KUBECONFIG=$KUBECONFIG_OUT kubectl get pods -A # OK"
echo " KUBECONFIG=$KUBECONFIG_OUT kubectl get secrets # Forbidden 정상"반응형
'Ops > Kubernetes' 카테고리의 다른 글
| Graceful하게 Pod 종료하기 (0) | 2026.06.05 |
|---|---|
| k8s Secret을 Git으로 관리하는 방법 (2) | 2026.04.30 |
| 영구적인 Service Account Token 만들기 (0) | 2026.02.04 |
| node-core-dns 란? (0) | 2025.12.31 |
| [이슈] Network Policy - failed to get status by proxying to the pod, you might lack permissions to get pods/proxy (0) | 2025.12.08 |