이제 완성된 스크립트의 사용법을 설명드리겠습니다.
# 실행 권한 부여
chmod +x create-namespace-sa-permissions.sh
# 기본 사용 - 네임스페이스만 지정
./create-namespace-sa-permissions.sh -n backend-prod
# 실행 흐름:
# 1. backend-prod 네임스페이스 확인/생성
# 2. backend-prod-sa ServiceAccount 생성
# 3. 네임스페이스 내 전체 권한 부여
# 4. 추가 권한 대화형 입력
# 5. 만료일 설정
# 6. 권한 생성 및 확인
# 커스텀 SA 이름 사용
./create-namespace-sa-permissions.sh -n monitoring-system -s custom-monitoring-sa
# 네임스페이스 내 기본 권한 없이 추가 권한만
./create-namespace-sa-permissions.sh -n argocd --skip-namespace-perms
# 실제 적용하지 않고 확인만
./create-namespace-sa-permissions.sh -n test-env --dry-run
$ ./create-namespace-sa-permissions.sh -n argocd-system
=== 네임스페이스 기반 ServiceAccount 및 권한 생성 ===
🎯 대상 네임스페이스: argocd-system
👤 ServiceAccount: argocd-system-sa
[SUCCESS] 네임스페이스 'argocd-system' 확인됨
[INFO] ServiceAccount 'argocd-system-sa' 생성 중...
[SUCCESS] ServiceAccount 'argocd-system-sa' 생성 완료
=== 추가 권한 설정 ===
네임스페이스 내 기본 권한 외에 추가로 필요한 클러스터 레벨 권한을 설정합니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[QUESTION] 추가 권한 규칙을 입력하세요:
📋 API Groups 입력 (쉼표로 구분, 예: apps,extensions 또는 ""는 core group):
예시: apiextensions.k8s.io, rbac.authorization.k8s.io, admissionregistration.k8s.io
API Groups: apiextensions.k8s.io
📋 Resources 입력 (쉼표로 구분, 예: pods,services 또는 *는 모든 리소스):
예시: customresourcedefinitions, clusterroles, mutatingwebhookconfigurations
Resources: customresourcedefinitions
📋 Verbs 입력 (쉼표로 구분, 예: get,list,watch,create,update,patch 또는 *는 모든 동작):
일반적인 verbs: get, list, watch, create, update, patch, delete
Verbs: get,list,watch,create,update,patch
[SUCCESS] 추가된 권한 규칙:
API Groups: apiextensions.k8s.io
Resources: customresourcedefinitions
Verbs: get,list,watch,create,update,patch
추가 권한 규칙을 더 입력하시겠습니까? (y/N): y
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[QUESTION] 추가 권한 규칙을 입력하세요:
📋 API Groups 입력:
API Groups: admissionregistration.k8s.io
📋 Resources 입력:
Resources: mutatingwebhookconfigurations,validatingwebhookconfigurations
📋 Verbs 입력:
Verbs: get,list,watch,create,update,patch
[SUCCESS] 추가된 권한 규칙:
API Groups: admissionregistration.k8s.io
Resources: mutatingwebhookconfigurations,validatingwebhookconfigurations
Verbs: get,list,watch,create,update,patch
추가 권한 규칙을 더 입력하시겠습니까? (y/N): n
=== 만료일 설정 ===
권한의 만료일을 설정합니다.
📅 추천 옵션:
1. 1주일 후: 2025-07-03
2. 1개월 후: 2025-07-26
3. 3개월 후: 2025-09-26
4. 6개월 후: 2025-12-26
5. 직접 입력
선택하세요 (1-5): 3
=== 작업 요약 ===
🎯 네임스페이스: argocd-system
👤 ServiceAccount: argocd-system-sa
📅 만료일: 2025-09-26
🔑 네임스페이스 내 전체 권한: 부여됨
🔑 추가 클러스터 권한: 2개 규칙
위 설정으로 권한을 생성하시겠습니까? (y/N): y
=== 권한 생성 중 ===
[INFO] 네임스페이스 내 전체 권한 Role 'argocd-system-admin-role' 생성 중...
[SUCCESS] Role 'argocd-system-admin-role' 생성 완료
[INFO] RoleBinding 'argocd-system-admin-binding' 생성 중...
[SUCCESS] RoleBinding 'argocd-system-admin-binding' 생성 완료
[INFO] 추가 권한 ClusterRole 'argocd-system-additional-20250626151022' 생성 중...
[SUCCESS] ClusterRole 'argocd-system-additional-20250626151022' 생성 완료
[INFO] ClusterRoleBinding 'argocd-system-additional-binding-20250626151022' 생성 중...
[SUCCESS] ClusterRoleBinding 'argocd-system-additional-binding-20250626151022' 생성 완료
=== 권한 확인 ===
📋 네임스페이스 내 기본 권한 확인...
pods: ✅ 가능
services: ✅ 가능
deployments.apps: ✅ 가능
secrets: ✅ 가능
configmaps: ✅ 가능
📋 추가 권한 확인...
customresourcedefinitions.apiextensions.k8s.io (get): ✅ 가능
customresourcedefinitions.apiextensions.k8s.io (create): ✅ 가능
mutatingwebhookconfigurations.admissionregistration.k8s.io (get): ✅ 가능
validatingwebhookconfigurations.admissionregistration.k8s.io (create): ✅ 가능
[SUCCESS] 모든 권한이 올바르게 부여되었습니다!
--skip-namespace-perms로 건너뛰기 가능# 배치 모드 (대화형 입력 없이)
./create-namespace-sa-permissions.sh -n test-env --batch-mode
# 상세 출력 모드
./create-namespace-sa-permissions.sh -n backend-prod --verbose
# 권한 확인 생략
./create-namespace-sa-permissions.sh -n fast-deploy --skip-verification
# 조합 사용
./create-namespace-sa-permissions.sh -n monitoring \
-s prometheus-sa \
--skip-namespace-perms \
--dry-run \
--verbose
# CRD 관리
API Groups: apiextensions.k8s.io
Resources: customresourcedefinitions
Verbs: get,list,watch,create,update,patch
# RBAC 관리
API Groups: rbac.authorization.k8s.io
Resources: clusterroles,clusterrolebindings
Verbs: get,list,watch,create,update,patch
# Webhook 관리
API Groups: admissionregistration.k8s.io
Resources: mutatingwebhookconfigurations,validatingwebhookconfigurations
Verbs: get,list,watch,create,update,patch
# 만료 확인 스크립트를 cron에 등록
echo "0 2 * * * /path/to/cleanup-expired-permissions.sh" | crontab -
이제 dl-ops-wx-sa 대신 네임스페이스 기반으로 체계적으로 ServiceAccount와 권한을 관리할 수 있습니다!
#!/bin/bash
# 네임스페이스 기반 ServiceAccount 생성 및 권한 관리 스크립트
# 사용법: ./create-namespace-sa-permissions.sh -n NAMESPACE [OPTIONS]
set -e
# 색깔 출력
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_header() { echo -e "${PURPLE}=== $1 ===${NC}"; }
log_question() { echo -e "${CYAN}[QUESTION]${NC} $1"; }
# 기본 설정
SCRIPT_NAME=$(basename "$0")
AUDIT_LOG="/var/log/rbac-permissions.log"
show_help() {
cat << EOF
네임스페이스 기반 ServiceAccount 생성 및 권한 관리 스크립트
사용법:
$SCRIPT_NAME -n NAMESPACE [OPTIONS]
필수 옵션:
-n, --namespace NS 대상 네임스페이스 (ServiceAccount 이름은 NS-sa로 자동 생성)
선택 옵션:
-s, --service-account SA 커스텀 ServiceAccount 이름 (기본: NS-sa)
--skip-namespace-perms 네임스페이스 내 기본 권한 건너뛰기
--batch-mode 배치 모드 (모든 입력을 미리 제공)
--dry-run 실제 적용하지 않고 미리보기
--skip-verification 권한 확인 단계 생략
-v, --verbose 상세 출력
-h, --help 이 도움말 표시
기본 부여 권한 (네임스페이스 내):
• 모든 리소스에 대한 전체 권한 (apiGroups: ["*"], resources: ["*"], verbs: ["*"])
추가 권한 (대화형 입력):
• API 그룹, 리소스, 동작을 직접 지정
• 여러 개의 추가 권한 규칙 설정 가능
• 클러스터 레벨 권한 지원
예시:
$SCRIPT_NAME -n backend-prod
$SCRIPT_NAME -n monitoring-system -s custom-monitoring-sa
$SCRIPT_NAME -n argocd --skip-namespace-perms
$SCRIPT_NAME -n test-env --dry-run
실행 흐름:
1. 네임스페이스 확인/생성
2. ServiceAccount 생성 (NS-sa 형태)
3. 네임스페이스 내 기본 권한 부여 (선택)
4. 추가 권한 대화형 입력
5. 만료일 설정
6. 권한 생성 및 확인
EOF
}
# 감사 로그
audit_log() {
local action="$1"
local details="$2"
local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
echo "[$timestamp] USER=$USER ACTION=$action DETAILS=$details" >> "$AUDIT_LOG" 2>/dev/null || true
}
# 날짜 형식 검증
validate_date() {
local date_str="$1"
if ! date -d "$date_str" >/dev/null 2>&1; then
log_error "잘못된 날짜 형식: $date_str (YYYY-MM-DD 형식 사용)"
return 1
fi
local target_date=$(date -d "$date_str" +%s)
local current_date=$(date +%s)
if [[ "$target_date" -le "$current_date" ]]; then
log_error "만료일은 현재 날짜보다 미래여야 합니다: $date_str"
return 1
fi
return 0
}
# 네임스페이스 확인/생성
ensure_namespace() {
local namespace="$1"
if kubectl get namespace "$namespace" >/dev/null 2>&1; then
log_success "네임스페이스 '$namespace' 확인됨"
else
log_warn "네임스페이스 '$namespace'가 존재하지 않습니다"
read -p "생성하시겠습니까? (y/N): " create_ns
if [[ "$create_ns" == "y" || "$create_ns" == "Y" ]]; then
if [[ "$DRY_RUN" == "true" ]]; then
log_info "[DRY RUN] 네임스페이스 '$namespace' 생성 시뮬레이션"
else
kubectl create namespace "$namespace"
log_success "네임스페이스 '$namespace' 생성 완료"
fi
else
log_error "네임스페이스가 필요합니다"
exit 1
fi
fi
}
# ServiceAccount 생성
create_service_account() {
local sa_name="$1"
local namespace="$2"
if kubectl get serviceaccount "$sa_name" -n "$namespace" >/dev/null 2>&1; then
log_success "ServiceAccount '$sa_name' 이미 존재함 (네임스페이스: $namespace)"
else
log_info "ServiceAccount '$sa_name' 생성 중..."
local sa_yaml=$(cat << EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: $sa_name
namespace: $namespace
labels:
rbac.company.com/type: "managed-service-account"
rbac.company.com/managed-by: "script"
rbac.company.com/namespace: "$namespace"
annotations:
rbac.company.com/description: "Auto-generated ServiceAccount for $namespace namespace"
rbac.company.com/created-by: "$USER"
rbac.company.com/created-at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
EOF
)
if [[ "$DRY_RUN" == "true" ]]; then
echo "=== ServiceAccount YAML (DRY RUN) ==="
echo "$sa_yaml"
echo ""
else
echo "$sa_yaml" | kubectl apply -f -
log_success "ServiceAccount '$sa_name' 생성 완료"
fi
fi
}
# 네임스페이스 내 기본 권한 Role 생성
create_namespace_role() {
local role_name="$1"
local namespace="$2"
local expire_date="$3"
log_info "네임스페이스 내 전체 권한 Role '$role_name' 생성 중..."
local role_yaml=$(cat << EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: $role_name
namespace: $namespace
labels:
rbac.company.com/type: "namespace-admin"
rbac.company.com/managed-by: "script"
rbac.company.com/namespace: "$namespace"
annotations:
rbac.company.com/description: "Full admin access within $namespace namespace"
rbac.company.com/created-by: "$USER"
rbac.company.com/created-at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
rbac.company.com/expires: "$expire_date"
rules:
# 네임스페이스 내 모든 리소스에 대한 전체 권한
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
EOF
)
if [[ "$DRY_RUN" == "true" ]]; then
echo "=== Namespace Role YAML (DRY RUN) ==="
echo "$role_yaml"
echo ""
else
echo "$role_yaml" | kubectl apply -f -
log_success "Role '$role_name' 생성 완료"
fi
}
# RoleBinding 생성
create_role_binding() {
local binding_name="$1"
local role_name="$2"
local sa_name="$3"
local namespace="$4"
local expire_date="$5"
log_info "RoleBinding '$binding_name' 생성 중..."
local binding_yaml=$(cat << EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: $binding_name
namespace: $namespace
labels:
rbac.company.com/type: "namespace-admin"
rbac.company.com/managed-by: "script"
rbac.company.com/service-account: "$sa_name"
annotations:
rbac.company.com/description: "Binding for namespace admin access"
rbac.company.com/created-by: "$USER"
rbac.company.com/created-at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
rbac.company.com/expires: "$expire_date"
subjects:
- kind: ServiceAccount
name: $sa_name
namespace: $namespace
roleRef:
kind: Role
name: $role_name
apiGroup: rbac.authorization.k8s.io
EOF
)
if [[ "$DRY_RUN" == "true" ]]; then
echo "=== RoleBinding YAML (DRY RUN) ==="
echo "$binding_yaml"
echo ""
else
echo "$binding_yaml" | kubectl apply -f -
log_success "RoleBinding '$binding_name' 생성 완료"
fi
}
# ClusterRole 생성 (추가 권한용)
create_cluster_role() {
local role_name="$1"
local expire_date="$2"
local rules_json="$3"
log_info "추가 권한 ClusterRole '$role_name' 생성 중..."
# JSON에서 YAML 형태로 변환
local rules_yaml=""
echo "$rules_json" | jq -c '.[]' | while IFS= read -r rule; do
local api_groups=$(echo "$rule" | jq -r '.apiGroups[]' | sed 's/^/ - "/' | sed 's/$/"/')
local resources=$(echo "$rule" | jq -r '.resources[]' | sed 's/^/ - "/' | sed 's/$/"/')
local verbs=$(echo "$rule" | jq -r '.verbs[]' | sed 's/^/ - "/' | sed 's/$/"/')
rules_yaml+="- apiGroups:\n$api_groups\n resources:\n$resources\n verbs:\n$verbs\n\n"
done
local role_yaml=$(cat << EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: $role_name
labels:
rbac.company.com/type: "additional-permissions"
rbac.company.com/managed-by: "script"
annotations:
rbac.company.com/description: "Additional cluster-level permissions"
rbac.company.com/created-by: "$USER"
rbac.company.com/created-at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
rbac.company.com/expires: "$expire_date"
rules:
EOF
)
# rules를 동적으로 추가
local formatted_rules=""
echo "$rules_json" | jq -c '.[]' | while IFS= read -r rule; do
local api_groups=$(echo "$rule" | jq -r '.apiGroups')
local resources=$(echo "$rule" | jq -r '.resources')
local verbs=$(echo "$rule" | jq -r '.verbs')
formatted_rules+="- apiGroups: $api_groups\n resources: $resources\n verbs: $verbs\n"
done
# 임시 파일로 YAML 생성
local temp_file=$(mktemp)
echo "$role_yaml" > "$temp_file"
echo "$rules_json" | jq -r '.[] | "- apiGroups: " + (.apiGroups | tostring) + "\n resources: " + (.resources | tostring) + "\n verbs: " + (.verbs | tostring)' >> "$temp_file"
if [[ "$DRY_RUN" == "true" ]]; then
echo "=== Additional ClusterRole YAML (DRY RUN) ==="
cat "$temp_file"
echo ""
else
kubectl apply -f "$temp_file"
log_success "ClusterRole '$role_name' 생성 완료"
fi
rm -f "$temp_file"
}
# ClusterRoleBinding 생성
create_cluster_role_binding() {
local binding_name="$1"
local role_name="$2"
local sa_name="$3"
local namespace="$4"
local expire_date="$5"
log_info "ClusterRoleBinding '$binding_name' 생성 중..."
local binding_yaml=$(cat << EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: $binding_name
labels:
rbac.company.com/type: "additional-permissions"
rbac.company.com/managed-by: "script"
rbac.company.com/service-account: "$sa_name"
rbac.company.com/namespace: "$namespace"
annotations:
rbac.company.com/description: "Binding for additional cluster-level permissions"
rbac.company.com/created-by: "$USER"
rbac.company.com/created-at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
rbac.company.com/expires: "$expire_date"
subjects:
- kind: ServiceAccount
name: $sa_name
namespace: $namespace
roleRef:
kind: ClusterRole
name: $role_name
apiGroup: rbac.authorization.k8s.io
EOF
)
if [[ "$DRY_RUN" == "true" ]]; then
echo "=== ClusterRoleBinding YAML (DRY RUN) ==="
echo "$binding_yaml"
echo ""
else
echo "$binding_yaml" | kubectl apply -f -
log_success "ClusterRoleBinding '$binding_name' 생성 완료"
fi
}
# 추가 권한 수집
collect_additional_permissions() {
local permissions=()
log_header "추가 권한 설정"
echo "네임스페이스 내 기본 권한 외에 추가로 필요한 클러스터 레벨 권한을 설정합니다."
echo ""
while true; do
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
log_question "추가 권한 규칙을 입력하세요:"
# API Groups 입력
echo ""
echo "📋 API Groups 입력 (쉼표로 구분, 예: apps,extensions 또는 \"\"는 core group):"
echo " 예시: apiextensions.k8s.io, rbac.authorization.k8s.io, admissionregistration.k8s.io"
read -p "API Groups: " api_groups_input
# 입력 검증
if [[ -z "$api_groups_input" ]]; then
log_warn "API Groups는 필수입니다. 빈 값이면 \"\"를 입력하세요."
continue
fi
# API Groups 파싱
IFS=',' read -ra api_groups_array <<< "$api_groups_input"
local api_groups_json="["
for i in "${!api_groups_array[@]}"; do
local group=$(echo "${api_groups_array[$i]}" | xargs) # trim whitespace
if [[ "$group" == '""' || "$group" == "''" ]]; then
group=""
fi
api_groups_json+="\"$group\""
if [[ $i -lt $((${#api_groups_array[@]} - 1)) ]]; then
api_groups_json+=","
fi
done
api_groups_json+="]"
# Resources 입력
echo ""
echo "📋 Resources 입력 (쉼표로 구분, 예: pods,services 또는 *는 모든 리소스):"
echo " 예시: customresourcedefinitions, clusterroles, mutatingwebhookconfigurations"
read -p "Resources: " resources_input
if [[ -z "$resources_input" ]]; then
log_warn "Resources는 필수입니다."
continue
fi
# Resources 파싱
IFS=',' read -ra resources_array <<< "$resources_input"
local resources_json="["
for i in "${!resources_array[@]}"; do
local resource=$(echo "${resources_array[$i]}" | xargs)
resources_json+="\"$resource\""
if [[ $i -lt $((${#resources_array[@]} - 1)) ]]; then
resources_json+=","
fi
done
resources_json+="]"
# Verbs 입력
echo ""
echo "📋 Verbs 입력 (쉼표로 구분, 예: get,list,watch,create,update,patch 또는 *는 모든 동작):"
echo " 일반적인 verbs: get, list, watch, create, update, patch, delete"
read -p "Verbs: " verbs_input
if [[ -z "$verbs_input" ]]; then
log_warn "Verbs는 필수입니다."
continue
fi
# Verbs 파싱
IFS=',' read -ra verbs_array <<< "$verbs_input"
local verbs_json="["
for i in "${!verbs_array[@]}"; do
local verb=$(echo "${verbs_array[$i]}" | xargs)
verbs_json+="\"$verb\""
if [[ $i -lt $((${#verbs_array[@]} - 1)) ]]; then
verbs_json+=","
fi
done
verbs_json+="]"
# 권한 규칙 생성
local permission="{\"apiGroups\":$api_groups_json,\"resources\":$resources_json,\"verbs\":$verbs_json}"
permissions+=("$permission")
# 입력 내용 확인
echo ""
log_success "추가된 권한 규칙:"
echo " API Groups: $(echo "$api_groups_json" | jq -r '.[]' | tr '\n' ',' | sed 's/,$//')"
echo " Resources: $(echo "$resources_json" | jq -r '.[]' | tr '\n' ',' | sed 's/,$//')"
echo " Verbs: $(echo "$verbs_json" | jq -r '.[]' | tr '\n' ',' | sed 's/,$//')"
# 추가 권한 입력 여부 확인
echo ""
read -p "추가 권한 규칙을 더 입력하시겠습니까? (y/N): " add_more
if [[ "$add_more" != "y" && "$add_more" != "Y" ]]; then
break
fi
echo ""
done
if [[ ${#permissions[@]} -eq 0 ]]; then
echo "[]"
else
printf '%s\n' "${permissions[@]}" | jq -s '.'
fi
}
# 만료일 입력
get_expire_date() {
log_header "만료일 설정"
echo "권한의 만료일을 설정합니다."
echo ""
# 추천 옵션 제시
echo "📅 추천 옵션:"
echo " 1. 1주일 후: $(date -d '+7 days' +%Y-%m-%d)"
echo " 2. 1개월 후: $(date -d '+1 month' +%Y-%m-%d)"
echo " 3. 3개월 후: $(date -d '+3 months' +%Y-%m-%d)"
echo " 4. 6개월 후: $(date -d '+6 months' +%Y-%m-%d)"
echo " 5. 직접 입력"
echo ""
while true; do
read -p "선택하세요 (1-5): " choice
case $choice in
1)
echo "$(date -d '+7 days' +%Y-%m-%d)"
return 0
;;
2)
echo "$(date -d '+1 month' +%Y-%m-%d)"
return 0
;;
3)
echo "$(date -d '+3 months' +%Y-%m-%d)"
return 0
;;
4)
echo "$(date -d '+6 months' +%Y-%m-%d)"
return 0
;;
5)
while true; do
read -p "만료일을 입력하세요 (YYYY-MM-DD): " expire_date
if validate_date "$expire_date"; then
echo "$expire_date"
return 0
fi
done
;;
*)
log_warn "1-5 중에서 선택하세요."
;;
esac
done
}
# 권한 확인
verify_permissions() {
local sa_name="$1"
local namespace="$2"
local additional_permissions="$3"
log_header "권한 확인"
local sa_full="system:serviceaccount:$namespace:$sa_name"
local all_passed=true
echo "📋 네임스페이스 내 기본 권한 확인..."
# 네임스페이스 내 기본 권한 확인
local basic_resources=("pods" "services" "deployments.apps" "secrets" "configmaps")
for resource in "${basic_resources[@]}"; do
printf "%-50s" "$resource:"
if kubectl auth can-i create "$resource" --namespace="$namespace" --as="$sa_full" >/dev/null 2>&1; then
echo -e " ${GREEN}✅ 가능${NC}"
else
echo -e " ${RED}❌ 불가${NC}"
all_passed=false
fi
done
echo ""
echo "📋 추가 권한 확인..."
# 추가 권한 확인
if [[ "$additional_permissions" != "[]" ]]; then
echo "$additional_permissions" | jq -c '.[]' | while IFS= read -r rule; do
local api_groups=$(echo "$rule" | jq -r '.apiGroups[]')
local resources=$(echo "$rule" | jq -r '.resources[]')
local verbs=$(echo "$rule" | jq -r '.verbs[]')
for resource in $resources; do
for verb in $verbs; do
local full_resource="$resource"
if [[ "$api_groups" != "" ]]; then
full_resource="$resource.$api_groups"
fi
printf "%-50s" "$full_resource ($verb):"
if kubectl auth can-i "$verb" "$full_resource" --as="$sa_full" >/dev/null 2>&1; then
echo -e " ${GREEN}✅ 가능${NC}"
else
echo -e " ${RED}❌ 불가${NC}"
all_passed=false
fi
done
done
done
else
echo "추가 권한이 설정되지 않았습니다."
fi
echo ""
if [[ "$all_passed" == "true" ]]; then
log_success "모든 권한이 올바르게 부여되었습니다!"
else
log_warn "일부 권한이 제대로 부여되지 않았습니다. RBAC 전파를 기다리거나 설정을 확인하세요."
return 1
fi
}
# 최종 결과 요약
show_final_summary() {
local sa_name="$1"
local namespace="$2"
local role_name="$3"
local binding_name="$4"
local cluster_role_name="$5"
local cluster_binding_name="$6"
local expire_date="$7"
local additional_permissions="$8"
local skip_namespace_perms="$9"
log_header "🎉 최종 결과 요약"
echo "🎯 생성된 리소스:"
echo " 📦 네임스페이스: $namespace"
echo " 👤 ServiceAccount: $sa_name"
if [[ "$skip_namespace_perms" != "true" ]]; then
echo " 🔑 네임스페이스 Role: $role_name"
echo " 🔗 네임스페이스 RoleBinding: $binding_name"
fi
if [[ "$additional_permissions" != "[]" ]]; then
echo " 🔑 추가 권한 ClusterRole: $cluster_role_name"
echo " 🔗 추가 권한 ClusterRoleBinding: $cluster_binding_name"
fi
echo ""
echo "📅 만료일: $expire_date"
echo ""
echo "🔐 부여된 권한:"
if [[ "$skip_namespace_perms" != "true" ]]; then
echo " ✅ 네임스페이스 '$namespace' 내 모든 리소스에 대한 전체 권한"
fi
if [[ "$additional_permissions" != "[]" ]]; then
echo " ✅ 추가 클러스터 레벨 권한:"
echo "$additional_permissions" | jq -r '.[] | " • API Groups: " + (.apiGroups | join(", ")) + " | Resources: " + (.resources | join(", ")) + " | Verbs: " + (.verbs | join(", "))'
fi
echo ""
echo "🔍 권한 확인 명령어:"
echo "kubectl auth can-i --list --as=system:serviceaccount:$namespace:$sa_name"
echo "kubectl auth can-i create pods --namespace=$namespace --as=system:serviceaccount:$namespace:$sa_name"
echo ""
echo "🧹 권한 정리 명령어:"
if [[ "$skip_namespace_perms" != "true" ]]; then
echo "kubectl delete rolebinding $binding_name -n $namespace"
echo "kubectl delete role $role_name -n $namespace"
fi
if [[ "$additional_permissions" != "[]" ]]; then
echo "kubectl delete clusterrolebinding $cluster_binding_name"
echo "kubectl delete clusterrole $cluster_role_name"
fi
echo "kubectl delete serviceaccount $sa_name -n $namespace"
echo ""
echo "💡 ServiceAccount 사용 예시:"
echo "apiVersion: v1"
echo "kind: Pod"
echo "metadata:"
echo " name: example-pod"
echo " namespace: $namespace"
echo "spec:"
echo " serviceAccountName: $sa_name"
echo " containers:"
echo " - name: example"
echo " image: nginx"
}
# 메인 함수
main() {
NAMESPACE=""
SERVICE_ACCOUNT=""
SKIP_NAMESPACE_PERMS=false
BATCH_MODE=false
DRY_RUN=false
SKIP_VERIFICATION=false
VERBOSE=false
# 명령행 인수 파싱
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
show_help
exit 0
;;
-n|--namespace)
NAMESPACE="$2"
shift 2
;;
-s|--service-account)
SERVICE_ACCOUNT="$2"
shift 2
;;
--skip-namespace-perms)
SKIP_NAMESPACE_PERMS=true
shift
;;
--batch-mode)
BATCH_MODE=true
shift
;;
--dry-run)
DRY_RUN=true
shift
;;
--skip-verification)
SKIP_VERIFICATION=true
shift
;;
-v|--verbose)
VERBOSE=true
shift
;;
*)
log_error "알 수 없는 옵션: $1"
show_help
exit 1
;;
esac
done
# 필수 매개변수 확인
if [[ -z "$NAMESPACE" ]]; then
log_error "네임스페이스(-n)는 필수입니다"
show_help
exit 1
fi
# kubectl 설치 확인
if ! command -v kubectl &> /dev/null; then
log_error "kubectl이 설치되어 있지 않습니다"
exit 1
fi
# 클러스터 연결 확인
if ! kubectl cluster-info &> /dev/null; then
log_error "Kubernetes 클러스터에 연결할 수 없습니다"
exit 1
fi
# ServiceAccount 이름 기본값 설정
if [[ -z "$SERVICE_ACCOUNT" ]]; then
SERVICE_ACCOUNT="$NAMESPACE-sa"
fi
log_header "네임스페이스 기반 ServiceAccount 및 권한 생성"
echo "🎯 대상 네임스페이스: $NAMESPACE"
echo "👤 ServiceAccount: $SERVICE_ACCOUNT"
echo ""
# 1. 네임스페이스 확인/생성
ensure_namespace "$NAMESPACE"
# 2. ServiceAccount 생성
create_service_account "$SERVICE_ACCOUNT" "$NAMESPACE"
# 3. 추가 권한 수집
local additional_permissions
if [[ "$BATCH_MODE" == "true" ]]; then
log_info "배치 모드: 추가 권한 설정을 건너뜁니다"
additional_permissions="[]"
else
additional_permissions=$(collect_additional_permissions)
fi
# 4. 만료일 설정
local expire_date
if [[ "$BATCH_MODE" == "true" ]]; then
expire_date=$(date -d '+1 month' +%Y-%m-%d)
log_info "배치 모드: 만료일을 1개월 후로 설정 ($expire_date)"
else
expire_date=$(get_expire_date)
fi
# 리소스 이름 생성
local timestamp=$(date +%Y%m%d%H%M%S)
local role_name="$NAMESPACE-admin-role"
local binding_name="$NAMESPACE-admin-binding"
local cluster_role_name="$NAMESPACE-additional-$timestamp"
local cluster_binding_name="$NAMESPACE-additional-binding-$timestamp"
# 작업 요약 표시
log_header "작업 요약"
echo "🎯 네임스페이스: $NAMESPACE"
echo "👤 ServiceAccount: $SERVICE_ACCOUNT"
echo "📅 만료일: $expire_date"
if [[ "$SKIP_NAMESPACE_PERMS" != "true" ]]; then
echo "🔑 네임스페이스 내 전체 권한: 부여됨"
else
echo "🔑 네임스페이스 내 전체 권한: 건너뜀"
fi
if [[ "$additional_permissions" != "[]" ]]; then
echo "🔑 추가 클러스터 권한: $(echo "$additional_permissions" | jq length)개 규칙"
if [[ "$VERBOSE" == "true" ]]; then
echo "$additional_permissions" | jq -r '.[] | " • API Groups: " + (.apiGroups | join(", ")) + " | Resources: " + (.resources | join(", ")) + " | Verbs: " + (.verbs | join(", "))'
fi
else
echo "🔑 추가 클러스터 권한: 없음"
fi
if [[ "$DRY_RUN" == "true" ]]; then
log_info "=== DRY RUN 모드 ==="
# 네임스페이스 권한 시뮬레이션
if [[ "$SKIP_NAMESPACE_PERMS" != "true" ]]; then
create_namespace_role "$role_name" "$NAMESPACE" "$expire_date"
create_role_binding "$binding_name" "$role_name" "$SERVICE_ACCOUNT" "$NAMESPACE" "$expire_date"
fi
# 추가 권한 시뮬레이션
if [[ "$additional_permissions" != "[]" ]]; then
create_cluster_role "$cluster_role_name" "$expire_date" "$additional_permissions"
create_cluster_role_binding "$cluster_binding_name" "$cluster_role_name" "$SERVICE_ACCOUNT" "$NAMESPACE" "$expire_date"
fi
log_info "DRY RUN 완료 - 실제 리소스는 생성되지 않았습니다"
exit 0
fi
# 확인
echo ""
read -p "위 설정으로 권한을 생성하시겠습니까? (y/N): " confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
log_info "작업이 취소되었습니다"
exit 0
fi
# 5. 권한 생성
log_header "권한 생성 중"
# 네임스페이스 내 권한 생성
if [[ "$SKIP_NAMESPACE_PERMS" != "true" ]]; then
create_namespace_role "$role_name" "$NAMESPACE" "$expire_date"
create_role_binding "$binding_name" "$role_name" "$SERVICE_ACCOUNT" "$NAMESPACE" "$expire_date"
fi
# 추가 클러스터 권한 생성
if [[ "$additional_permissions" != "[]" ]]; then
# 임시 파일로 ClusterRole 생성
local temp_role_file=$(mktemp)
cat > "$temp_role_file" << EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: $cluster_role_name
labels:
rbac.company.com/type: "additional-permissions"
rbac.company.com/managed-by: "script"
rbac.company.com/namespace: "$NAMESPACE"
annotations:
rbac.company.com/description: "Additional cluster-level permissions for $NAMESPACE"
rbac.company.com/created-by: "$USER"
rbac.company.com/created-at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
rbac.company.com/expires: "$expire_date"
rules:
EOF
# rules 추가
echo "$additional_permissions" | jq -r '.[] | "- apiGroups: " + (.apiGroups | tostring) + "\n resources: " + (.resources | tostring) + "\n verbs: " + (.verbs | tostring)' >> "$temp_role_file"
log_info "추가 권한 ClusterRole '$cluster_role_name' 생성 중..."
kubectl apply -f "$temp_role_file"
log_success "ClusterRole '$cluster_role_name' 생성 완료"
rm -f "$temp_role_file"
# ClusterRoleBinding 생성
create_cluster_role_binding "$cluster_binding_name" "$cluster_role_name" "$SERVICE_ACCOUNT" "$NAMESPACE" "$expire_date"
fi
# 감사 로그
audit_log "CREATE_NAMESPACE_SA" "namespace=$NAMESPACE sa=$SERVICE_ACCOUNT expire=$expire_date additional_rules=$(echo "$additional_permissions" | jq length)"
# 6. 권한 확인
if [[ "$SKIP_VERIFICATION" != "true" ]]; then
echo ""
log_info "권한 전파 대기 중... (5초)"
sleep 5
verify_permissions "$SERVICE_ACCOUNT" "$NAMESPACE" "$additional_permissions"
fi
# 7. 최종 결과 표시
echo ""
show_final_summary "$SERVICE_ACCOUNT" "$NAMESPACE" "$role_name" "$binding_name" "$cluster_role_name" "$cluster_binding_name" "$expire_date" "$additional_permissions" "$SKIP_NAMESPACE_PERMS"
# 만료 알림 설정 정보
echo ""
log_header "📅 만료 관리"
echo "만료된 권한 자동 정리 스크립트:"
echo ""
cat << 'EOF'
#!/bin/bash
# cleanup-expired-permissions.sh
current_date=$(date +%Y-%m-%d)
echo "만료일이 $current_date 이전인 권한을 정리합니다..."
# 만료된 Role/RoleBinding 정리
kubectl get roles,rolebindings --all-namespaces \
-l rbac.company.com/managed-by=script \
-o json | \
jq -r ".items[] | select(.metadata.annotations.\"rbac.company.com/expires\" < \"$current_date\") | \
.kind + \"/\" + .metadata.name + \" -n \" + .metadata.namespace" | \
xargs -r -I {} sh -c 'echo "Deleting: {}"; kubectl delete {}'
# 만료된 ClusterRole/ClusterRoleBinding 정리
kubectl get clusterroles,clusterrolebindings \
-l rbac.company.com/managed-by=script \
-o json | \
jq -r ".items[] | select(.metadata.annotations.\"rbac.company.com/expires\" < \"$current_date\") | \
.kind + \"/\" + .metadata.name" | \
xargs -r -I {} sh -c 'echo "Deleting: {}"; kubectl delete {}'
echo "정리 완료!"
EOF
echo ""
log_success "✅ 네임스페이스 기반 ServiceAccount 및 권한 생성 완료!"
log_warn "⚠️ 만료일($expire_date)에 권한을 정리하는 것을 잊지 마세요!"
# 추가 유용한 명령어들
echo ""
log_info "💡 유용한 명령어들:"
echo ""
echo "현재 권한 상태 확인:"
echo "kubectl auth can-i --list --as=system:serviceaccount:$NAMESPACE:$SERVICE_ACCOUNT"
echo ""
echo "네임스페이스 내 Pod 생성 테스트:"
echo "kubectl auth can-i create pods --namespace=$NAMESPACE --as=system:serviceaccount:$NAMESPACE:$SERVICE_ACCOUNT"
echo ""
echo "ServiceAccount 토큰 확인:"
echo "kubectl get secret \$(kubectl get sa $SERVICE_ACCOUNT -n $NAMESPACE -o jsonpath='{.secrets[0].name}') -n $NAMESPACE -o jsonpath='{.data.token}' | base64 -d"
echo ""
echo "만료 예정 권한 목록:"
echo "kubectl get roles,rolebindings,clusterroles,clusterrolebindings -l rbac.company.com/managed-by=script -o custom-columns=TYPE:.kind,NAME:.metadata.name,NAMESPACE:.metadata.namespace,EXPIRES:.metadata.annotations.rbac\\.company\\.com/expires"
}
# 스크립트 실행
main "$@"```