create-namespace-sa-permissions.sh

진웅·2025년 6월 26일

K8S Basics

목록 보기
12/40

이제 완성된 스크립트의 사용법을 설명드리겠습니다.

🚀 스크립트 사용법

1. 기본 사용법

# 실행 권한 부여
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. 권한 생성 및 확인

2. 커스텀 ServiceAccount 이름

# 커스텀 SA 이름 사용
./create-namespace-sa-permissions.sh -n monitoring-system -s custom-monitoring-sa

3. 네임스페이스 권한 건너뛰기

# 네임스페이스 내 기본 권한 없이 추가 권한만
./create-namespace-sa-permissions.sh -n argocd --skip-namespace-perms

4. 미리보기 모드

# 실제 적용하지 않고 확인만
./create-namespace-sa-permissions.sh -n test-env --dry-run

📋 실행 시나리오 예시

시나리오: ArgoCD용 ServiceAccount 생성

$ ./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] 모든 권한이 올바르게 부여되었습니다!

🎯 주요 기능들

1. 자동 ServiceAccount 생성

  • 네임스페이스명-sa 형태로 자동 생성
  • 커스텀 이름도 지원

2. 네임스페이스 내 전체 권한

  • 기본적으로 해당 네임스페이스 내 모든 리소스에 대한 전체 권한
  • --skip-namespace-perms로 건너뛰기 가능

3. 대화형 추가 권한 설정

  • API Groups, Resources, Verbs를 직접 입력
  • 여러 개의 권한 규칙 추가 가능
  • 각 입력 후 확인 및 계속 여부 선택

4. 유연한 만료일 설정

  • 추천 옵션 (1주일, 1개월, 3개월, 6개월)
  • 직접 입력 가능
  • 날짜 형식 자동 검증

5. 포괄적인 권한 확인

  • 네임스페이스 내 기본 권한 확인
  • 추가 권한별 개별 확인
  • 권한 전파 대기 시간 포함

6. 완전한 정리 지원

  • 생성된 모든 리소스 정리 명령어 제공
  • 만료된 권한 자동 정리 스크립트 제공
  • 추적을 위한 라벨 및 어노테이션

🛠️ 고급 옵션들

# 배치 모드 (대화형 입력 없이)
./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

💡 사용 팁

1. 일반적인 추가 권한 조합

# 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

2. 권한 정리 자동화

# 만료 확인 스크립트를 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 "$@"```
profile
bytebliss

0개의 댓글