職場の安全検出システムの保護

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 Workload 環境を作成する

次のコマンドは、デモ用の安全なランディング ゾーンを作成します。指定したフォルダと請求先アカウントの下に 2 つの新しい Google Cloud プロジェクトが作成されます。

  • 1 つのプロジェクトで 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 APIs を有効にする

ビルドする前に、必要なサービスに必要な 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 サービス アカウントに接続するには、Workload Identity を構成します。

サービス アカウントの作成

クラスタ内に専用の 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 Service Account が IAM Service Account の権限を借用できるようにする

両方のサービス アカウントが作成されたら、最後に両者の間にリンクを作成します。このプロセスは 2 つの部分で構成されています。まず、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 サービス アカウントを使用してクラスタで実行されている Pod は、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 イメージのビルドと push

Docker イメージをビルドしてリポジトリに push します。まず、ソースコードのクローンを作成してコンテナ イメージをビルドします。次に、完全な Artifact Registry パスでタグ付けし、以前に作成したリポジトリに push します。

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 にイメージを pull して実行するように指示します。これは、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 Workload 関連のすべてのリソースを削除します。

# 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 を使用して Confidential GKE ノードをプロビジョニングしました
  • データ境界: 規制対象環境でプラットフォーム コンプライアンス制御を有効にしました

リファレンス ドキュメント