직장 내 안전 감지 시스템 보안

1. 소개

이 Codelab에서는 규정 준수 제한이 있는 프로젝트에 있는 서비스를 위해 다양한 Google Cloud 서비스를 통합하는 방법을 보여주는 기본 데모 애플리케이션을 구성합니다. 이 프로젝트는 다음 보안 기능을 사용합니다.

이 Codelab은 초보자를 포함한 모든 수준의 개발자를 대상으로 합니다. Google Cloud Shell의 명령줄 인터페이스와 Python 코드를 사용합니다. Python 전문가가 아니어도 되지만 코드를 읽는 방법을 기본적으로 이해하면 개념을 이해하는 데 도움이 됩니다.

참고: 이는 프로덕션 애플리케이션이 아닌 간단한 개념 증명입니다. 실제 시나리오에서 인증되고 보안이 유지되는 외부 액세스와 같은 추가 예방 조치를 이 서비스에 적용합니다.

2. 시작하기 전에

프로젝트 설정

../shared/_project-setup.md

Cloud Shell 시작

Cloud Shell은 Google Cloud에서 실행되는 명령줄 환경으로, 필요한 도구가 미리 로드되어 제공됩니다.

  1. Google Cloud 콘솔 상단에서 Cloud Shell 활성화를 클릭합니다.

404e4cce0f23e5c5.png

  1. Cloud Shell에 연결되면 다음 명령어를 실행하여 Cloud Shell에서 인증을 확인합니다.
gcloud auth list
  1. 다음 명령어를 실행하여 프로젝트가 gcloud와 함께 사용하도록 구성되어 있는지 확인합니다.
gcloud config get project
  1. 프로젝트가 예상대로인지 확인한 후 아래 명령어를 실행하여 프로젝트 ID를 설정합니다.
export PROJECT_ID=$(gcloud config get project)

필수 IAM 권한

이 Codelab에 사용하는 계정에는 다음 IAM 역할이 있어야 합니다. 이러한 권한은 필수 Google Cloud 리소스 (프로젝트, 폴더, GKE 클러스터, KMS 키, 서비스 계정 등)를 만들고 Assured Workload를 구성하는 데 필요합니다.

조직 수준:

  • roles/assuredworkloads.admin(Assured Workloads 관리자): 규정 준수 구성을 보장하면서 Assured Workloads 리소스 자체를 만들고 관리합니다.

결제 계정 수준 (결제 계정):

  • roles/billing.accountUser(결제 계정 사용자): 지정된 결제 계정을 연결합니다.

Codelab 변수 구성

필요한 인프라를 만들려면 다음 환경 변수를 제공해야 합니다.

# The ID of the Billing Account in the format (XXXXXX-XXXXXX-XXXXXX).
# This value will be used to attach in the projects created using Assured Workloads
export BILLING_ACCOUNT=

# The ID of a Google Cloud Platform organization
# Run `gcloud organizations list` to check all your available organizations
export GCP_ORGANIZATION=

# The numeric ID of a folder where the Assured Workloads will create the resources.
export FOLDER_ID=

# Region where the application will be deployed.
# Since you are using Assured Workloads, you MUST use one of the valid locations as described here: <https://docs.cloud.google.com/assured-workloads/docs/locations>
export REGION="us-central1"

# The ID of an existing Google Cloud project to be used for API quota and billing purposes.
# This project will only be used to enable the Assured Workloads API and create an Assured Workload.
export QUOTA_PROJECT_ID=

# Random suffix used to avoid naming collisions when creating the GCP projects.
export RANDOM_SUFFIX=$(cat /dev/urandom | tr -dc 'a-z0-9' | head -c 5 ; echo)

# The ID of the projects that will be created using Assured Workloads.
# You can modify this value if you want a custom id.
export PROJECT_ID="il5-gemini-vision-aw-${RANDOM_SUFFIX}"
export KMS_PROJECT_ID="il5-gemini-vision-kms-${RANDOM_SUFFIX}"

3. Assured Workloads 기반 만들기

이제 규제 환경에서 애플리케이션의 기반을 마련할 수 있습니다. Assured Workloads를 사용하여 리소스의 통제된 환경을 만들어 규정 준수 요구사항을 적용하세요.

할당량 프로젝트 구성

할당량 프로젝트에서 Assured Workloads API를 사용 설정합니다. 이 API는 Assured Workloads를 만들고 관리하는 데 필요합니다.

gcloud services enable assuredworkloads.googleapis.com \
  --project="${QUOTA_PROJECT_ID}"

Assured Workloads 환경 만들기

다음 명령어는 데모를 위한 안전한 '시작 영역'을 만듭니다. 지정된 폴더와 결제 계정 아래에 두 개의 새 Google Cloud 프로젝트가 생성됩니다.

  • 한 프로젝트에서 GKE 클러스터와 애플리케이션을 호스팅합니다.
  • 다른 프로젝트에서 고객 관리 암호화 키 (CMEK)를 관리합니다.

프로젝트가 생성되는 순간부터 지정된 IL5 규정 준수 컨트롤이 두 프로젝트에 모두 자동으로 적용됩니다.

다음을 실행하여 워크로드 환경을 만듭니다.

export ASSURED_WORKLOAD_ID=$(gcloud assured workloads create \
    --project="${QUOTA_PROJECT_ID}" \
    --display-name="DoD IL5 Gemini Vision Demo" \
    --compliance-regime="IL5" \
    --billing-account="billingAccounts/${BILLING_ACCOUNT}" \
    --location="${REGION}" \
    --organization="${GCP_ORGANIZATION}" \
    --provisioned-resources-parent="folders/${FOLDER_ID}" \
    --resource-settings="consumer-project-id=${PROJECT_ID},consumer-project-name=DoD IL5 Workloads,encryption-keys-project-id=${KMS_PROJECT_ID},encryption-keys-project-name=DoD IL5 KMS" \
    --labels="codelab=gemini-vision-demo" \
    --format="value(name)")

echo "Assured Workload created: ${ASSURED_WORKLOAD_ID}"

export WORKLOAD_FOLDER_ID=$(gcloud assured workloads describe ${ASSURED_WORKLOAD_ID} \
    --location="${REGION}" \
    --project="${QUOTA_PROJECT_ID}" \
    --format="json" | grep -B 1 "CONSUMER_FOLDER" | grep -oE "[0-9]{10,}")

echo "Assured Workload folder created: ${WORKLOAD_FOLDER_ID}"

gcloud projects create "${PROJECT_ID}" \
    --folder="${WORKLOAD_FOLDER_ID}" \
    --name="DoD IL5 Workloads"

gcloud billing projects link "${PROJECT_ID}" \
    --billing-account="${BILLING_ACCOUNT}"

export PROJECT_NUMBER=$(gcloud projects describe "${PROJECT_ID}" --format="value(projectNumber)")

필수 Google Cloud API 사용 설정

빌드하기 전에 필요한 서비스에 필요한 API를 사용 설정합니다.

아래 명령어는 기본 워크로드 프로젝트 내에서 이 Codelab에 필요한 모든 서비스를 활성화합니다.

gcloud services enable \
    aiplatform.googleapis.com \
    artifactregistry.googleapis.com \
    cloudkms.googleapis.com \
    compute.googleapis.com \
    container.googleapis.com \
    iam.googleapis.com \
    logging.googleapis.com \
    monitoring.googleapis.com \
    --project="${PROJECT_ID}"

4. GKE 인프라 설정

GKE 클러스터를 지원하는 핵심 인프라를 빌드합니다. 여기에는 클러스터 전용 네트워크를 설정하고 노드의 데이터를 보호하기 위해 자체 암호화 키를 구성하는 작업이 포함됩니다.

VPC 네트워크 구성

커스텀 Virtual Private Cloud (VPC) 및 서브네트워크를 만듭니다. 이 접근 방식을 사용하면 IP 주소 범위를 완전히 제어할 수 있으며 클러스터가 의도한 대로 격리됩니다.

다음 명령어를 실행하여 지정된 리전 내에 VPC 네트워크와 서브네트워크를 만듭니다.

export GKE_NETWORK_NAME="il5-gke-network"
export GKE_SUBNETWORK_NAME="il5-gke-subnet"

gcloud compute networks create "${GKE_NETWORK_NAME}" \
    --description="VPC network for GKE cluster in DoD IL5 Assured Workload" \
    --subnet-mode="custom" \
    --project="${PROJECT_ID}"

gcloud compute networks subnets create "${GKE_SUBNETWORK_NAME}" \
    --network="${GKE_NETWORK_NAME}" \
    --range="10.10.0.0/20" \
    --region="${REGION}" \
    --description="Subnet for GKE cluster nodes in DoD IL5 Assured Workload" \
    --project="${PROJECT_ID}"

Cloud KMS로 암호화 구성

저장 데이터에 대한 엄격한 규정 준수 요구사항을 충족하려면 고객 관리 암호화 키 (CMEK)를 사용하세요. 이를 통해 GKE 노드의 부팅 디스크를 암호화하는 데 사용되는 키를 직접 제어할 수 있습니다.

export KMS_KEYRING_NAME="il5_gke_key_ring"
export KMS_KEY_NAME="il5_gke_key"

gcloud kms keyrings create "${KMS_KEYRING_NAME}" \
    --location="$REGION" \
    --project="${KMS_PROJECT_ID}"

gcloud kms keys create "${KMS_KEY_NAME}" \
    --keyring="${KMS_KEYRING_NAME}" \
    --location="${REGION}" \
    --purpose="encryption" \
    --project="${KMS_PROJECT_ID}"

암호화 키가 이를 사용해야 하는 서비스와 별도의 KMS 프로젝트에 있으므로 워크로드 프로젝트의 Google Compute Engine 서비스 에이전트가 키에 액세스해야 합니다.

워크로드 프로젝트의 GCE 서비스 에이전트에게 이 키를 사용할 권한을 명시적으로 부여해야 합니다. 아래 명령어는 키에 IAM 정책 바인딩을 추가하여 서비스 에이전트에 필요한 역할을 부여합니다.

gcloud kms keys add-iam-policy-binding "${KMS_KEY_NAME}" \
    --location="${REGION}" \
    --keyring="${KMS_KEYRING_NAME}" \
    --member="serviceAccount:service-${PROJECT_NUMBER}@compute-system.iam.gserviceaccount.com" \
    --role="roles/cloudkms.cryptoKeyEncrypterDecrypter" \
    --project="${KMS_PROJECT_ID}"

GKE 노드 서비스 계정 구성

export GKE_NODE_SA=gke-node-sa

gcloud iam service-accounts create "${GKE_NODE_SA}" \
    --display-name="GKE Node Service Account" \
    --project="${PROJECT_ID}"

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:${GKE_NODE_SA}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/logging.logWriter"

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:${GKE_NODE_SA}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/monitoring.metricWriter"

5. GKE 클러스터 생성 및 구성

이제 GKE 클러스터를 만들 수 있습니다. 다음 명령어는 여러 보안 기능이 사용 설정된 GKE 클러스터를 프로비저닝합니다.

Google Cloud에서 노드와 컨트롤 플레인을 프로비저닝하므로 완료하는 데 몇 분 정도 걸립니다.

export GKE_CLUSTER=ppe-app

gcloud beta container clusters create "${GKE_CLUSTER}" \
    --project="$PROJECT_ID" \
    --region="$REGION" \
    --service-account="${GKE_NODE_SA}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --release-channel="regular" \
    --machine-type="n2d-standard-4" \
    --image-type="COS_CONTAINERD" \
    --disk-type="pd-ssd" \
    --disk-size="50" \
    --boot-disk-kms-key="projects/${KMS_PROJECT_ID}/locations/${REGION}/keyRings/${KMS_KEYRING_NAME}/cryptoKeys/${KMS_KEY_NAME}" \
    --metadata disable-legacy-endpoints=true \
    --num-nodes="1" \
    --network="projects/${PROJECT_ID}/global/networks/${GKE_NETWORK_NAME}" \
    --subnetwork="projects/${PROJECT_ID}/regions/${REGION}/subnetworks/${GKE_SUBNETWORK_NAME}" \
    --security-posture="standard" \
    --workload-vulnerability-scanning="disabled" \
    --workload-pool="${PROJECT_ID}.svc.id.goog" \
    --workload-metadata=GKE_METADATA \
    --addons="HorizontalPodAutoscaling,HttpLoadBalancing,NodeLocalDNS,GcePersistentDiskCsiDriver" \
    --max-surge-upgrade=1 \
    --max-unavailable-upgrade=0 \
    --binauthz-evaluation-mode="DISABLED" \
    --no-enable-basic-auth \
    --enable-autoupgrade \
    --enable-autorepair \
    --enable-confidential-nodes \
    --confidential-node-type=sev \
    --enable-ip-access \
    --enable-ip-alias \
    --enable-managed-prometheus \
    --enable-dns-access \
    --enable-shielded-nodes \
    --shielded-integrity-monitoring \
    --shielded-secure-boot

새 클러스터에 연결

새 클러스터와 상호작용하려면 로컬 kubectl 명령줄 도구를 구성하세요.

이 명령어는 클러스터의 사용자 인증 정보와 엔드포인트를 가져오고 로컬 kubeconfig 파일을 자동으로 구성합니다. 이 명령어를 실행하면 실행하는 모든 kubectl 명령어가 새 GKE 클러스터로 전달됩니다.

gcloud container clusters get-credentials "${GKE_CLUSTER}" \
    --region="${REGION}" \
    --project="${PROJECT_ID}" \
    --dns-endpoint

6. 애플리케이션 ID 구성

Kubernetes 서비스 계정을 Google Cloud IAM 서비스 계정에 연결하려면 워크로드 아이덴티티를 구성하세요.

서비스 계정 만들기

클러스터 내에 전용 Kubernetes 서비스 계정 (KSA)을 만듭니다.

export GKE_NAMESPACE=default
export GKE_SA=ppe-sa

kubectl create sa "${GKE_SA}" --namespace="${GKE_NAMESPACE}"

다음으로 애플리케이션에 Google Cloud 내의 ID가 필요합니다. 애플리케이션의 Google Cloud IAM 서비스 계정을 만듭니다. 서비스 계정을 만든 후 필요한 역할을 부여합니다.

# Create GCP service account
gcloud iam service-accounts create "${GKE_SA}" \
    --project="${PROJECT_ID}"
# Grant necessary roles
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:${GKE_SA}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/aiplatform.user"

Kubernetes 서비스 계정이 IAM을 가장하도록 허용

두 서비스 계정을 모두 만들었으면 마지막 단계는 두 계정 간의 링크를 만드는 것입니다. 이 프로세스는 두 부분으로 구성됩니다. 먼저 Google Cloud 서비스 계정에 IAM 정책을 추가합니다.

# Allow the Kubernetes service account to act as GCP service account by using Workload Identity
gcloud iam service-accounts add-iam-policy-binding "${GKE_SA}@${PROJECT_ID}.iam.gserviceaccount.com" \
    --project="${PROJECT_ID}" \
    --role="roles/iam.workloadIdentityUser" \
    --member="serviceAccount:${PROJECT_ID}.svc.id.goog[${GKE_NAMESPACE}/${GKE_SA}]"

두 번째로 Kubernetes 서비스 계정에 주석을 추가합니다.

kubectl annotate --namespace="${GKE_NAMESPACE}" serviceaccount "$GKE_SA" \
    iam.gke.io/gcp-service-account="${GKE_SA}@${PROJECT_ID}.iam.gserviceaccount.com"

이 단계를 완료하면 Kubernetes 서비스 계정으로 클러스터에서 실행되는 모든 포드가 이제 Vertex API에 액세스할 수 있습니다.

참고: 이 구성을 간소화하기 위해 서비스 계정 가장을 없앨 수 있습니다. 자세한 내용과 제한사항은 여기를 참고하세요.

7. 애플리케이션 빌드 및 배포

이제 애플리케이션을 패키징하고 저장한 후 GKE 클러스터에 배포할 차례입니다.

Artifact Registry 저장소 만들기

앱을 실행하려면 Docker 컨테이너로 패키징하고 Artifact Registry 저장소 내부에 저장해야 합니다. 아래 명령어를 사용하여 이 저장소를 만듭니다.

export REPOSITORY_ID=ppe-repo

gcloud artifacts repositories create "${REPOSITORY_ID}" \
  --repository-format=docker \
  --location="${REGION}" \
  --project="${PROJECT_ID}" \
  --description="Regional Docker repo for PPE App"

GKE 노드에서 사용하는 서비스 계정에 새 저장소에서 읽을 수 있는 권한을 명시적으로 부여해야 합니다.

gcloud artifacts repositories add-iam-policy-binding "${REPOSITORY_ID}" \
    --location="${REGION}" \
    --role="roles/artifactregistry.reader" \
    --project="${PROJECT_ID}" \
    --member="serviceAccount:${GKE_NODE_SA}@${PROJECT_ID}.iam.gserviceaccount.com"

Docker 이미지 빌드 및 푸시

Docker 이미지를 빌드하고 저장소에 푸시합니다. 먼저 소스 코드를 클론하고 컨테이너 이미지를 빌드합니다. 그런 다음 전체 Artifact Registry 경로로 태그를 지정하고 이전에 만든 저장소에 푸시합니다.

git clone https://github.com/GoogleCloudPlatform/next-26-sessions.git

cd BRK3-034-workplace-safety

cd ppe

export IMAGE_TAG="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY_ID}/ppe-app:v15"
docker build -t "${IMAGE_TAG}" .
docker push "${IMAGE_TAG}"

GKE에 배포

Artifact Registry에서 컨테이너 이미지를 사용할 수 있으므로 마지막 단계는 GKE에 이미지를 가져와 실행하도록 지시하는 것입니다. 이는 Kubernetes 매니페스트 파일에서 애플리케이션의 리소스를 정의하고 이를 클러스터에 적용하여 달성됩니다. 이 명령어는 파일에 정의된 Deployment 및 Service 객체를 만듭니다.

export GKE_DEPLOY=ppe-detector

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ${GKE_DEPLOY}
spec:
  replicas: 2
  selector:
    matchLabels:
      app: ${GKE_DEPLOY}
  template:
    metadata:
      labels:
        app: ${GKE_DEPLOY}
    spec:
      serviceAccountName: ${GKE_SA}
      containers:
      - name: ${GKE_DEPLOY}
        image: ${IMAGE_TAG}
        env:
        - name: PROJECT_ID
          value: ${PROJECT_ID}
        - name: LOCATION
          value: ${REGION}
        ports:
        - containerPort: 8080
        resources:
          requests:
            cpu: "250m"
            memory: "512Mi"
          limits:
            cpu: "500m"
            memory: "1Gi"
---
apiVersion: v1
kind: Service
metadata:
  name: ${GKE_DEPLOY}
spec:
  type: LoadBalancer
  selector:
    app: ${GKE_DEPLOY}
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
EOF

8. 애플리케이션 테스트

마지막 단계는 데모 애플리케이션에 액세스하여 테스트하는 것입니다. 여기에는 서비스에 할당된 외부 IP 주소를 가져오고 간단한 프런트엔드를 통해 상호작용하는 작업이 포함됩니다.

서비스의 외부 IP 가져오기

노출된 서비스의 외부 IP를 가져옵니다 (프로비저닝하는 데 약 30초 소요).

export IP_ADDRESS=$(kubectl get service "${GKE_DEPLOY}" -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $IP_ADDRESS

로컬에서 프런트엔드 실행

이 데모에서 프런트엔드는 GKE 배포에 포함되지 않는 간단한 HTML/JavaScript 페이지입니다. 로컬 머신에서 실행되도록 설계되었습니다.

프런트엔드는 프로덕션 애플리케이션에서 GCP를 통해 제공되어야 합니다. 로컬 머신에서 다음을 실행합니다.

# Update the index.html file with the server IP address
cd frontend

# For Linux
sed -i "s#\(const BACKEND_URL = \"http://\)[^/]\+\(\/analyze\";\)#\1${IP_ADDRESS}\2#g" "index.html"

# For MacOS
#sed -i '' "s#\(const BACKEND_URL = #\"http://\)[^/]*\(\/analyze\";\)#\1${IP_ADDRESS}\2#g" "index.html"

python3 -m http.server 8001

Chrome에서 http://localhost:8001/index.html을 엽니다.

9. 삭제

요금이 계속 청구되지 않도록 이 Codelab에서 만든 리소스를 삭제합니다.

GKE 클러스터 삭제

전체 애플리케이션을 삭제하려면 GKE 클러스터를 삭제하면 됩니다. 이렇게 하려면 다음을 실행합니다.

gcloud container clusters delete "${GKE_CLUSTER}" \
    --region="$REGION" \
    --project="${PROJECT_ID}"

Assured Workloads 삭제

다음 명령어를 실행하여 모든 Assured Workloads 관련 리소스를 삭제합니다.

# Workload project deletion
gcloud billing projects unlink "${PROJECT_ID}"
gcloud projects delete "${PROJECT_ID}"

# KMS project deletion
gcloud billing projects unlink "${KMS_PROJECT_ID}"
gcloud projects delete "${KMS_PROJECT_ID}"

# Assured Workload folder deletion
gcloud resource-manager folders delete ${WORKLOAD_FOLDER_ID} --quiet

# Assured Workload deletion
gcloud assured workloads delete "${ASSURED_WORKLOAD_ID}" \
    --location="${REGION}" \
    --organization="${GCP_ORGANIZATION}" \
    --project="${QUOTA_PROJECT_ID}"

10. 축하합니다

미션 완료! Gemini를 사용하여 안전모를 감지하는 규제 산업용 작업장 안전 감지 시스템을 빌드했습니다.

달성한 내용:

  • 데이터 보호 및 개인 정보 보호: CMEK로 컨피덴셜 GKE 노드를 프로비저닝했습니다.
  • 데이터 경계: 규제 대상 환경에 플랫폼 규정 준수 컨트롤을 사용 설정했습니다.

참조 문서