Skip to content

GCP Deployment

DeepIntShield Enterprise images for GCP customers are distributed through GCP Artifact Registry, enabling native Workload Identity for secure, keyless authentication.

flowchart LR
subgraph GCP[GCP Project]
subgraph GKE[GKE Cluster]
Pod[DeepIntShield Pod]
KSA[K8s ServiceAccount]
end
GSA[GCP Service Account]
AR[Artifact Registry<br/>DeepIntShield Images]
end
KSA -->|Workload Identity| GSA
Pod -->|Impersonates| GSA
GSA -->|Pull Permission| AR
AR -->|Image| Pod
  • GKE cluster (v1.24+) with Workload Identity enabled
  • gcloud CLI configured with appropriate permissions
  • kubectl configured for your GKE cluster
  • Your GCP project allowlisted by DeepIntShield team

Workload Identity provides the most secure authentication method for GKE deployments by eliminating the need for service account keys.

If not already enabled, enable Workload Identity on your cluster:

Terminal window
# For existing cluster
gcloud container clusters update YOUR_CLUSTER_NAME \
--region=YOUR_REGION \
--workload-pool=YOUR_PROJECT_ID.svc.id.goog
# Verify Workload Identity is enabled
gcloud container clusters describe YOUR_CLUSTER_NAME \
--region=YOUR_REGION \
--format="value(workloadIdentityConfig.workloadPool)"

Create a service account that will be used to pull images:

Terminal window
# Create service account
gcloud iam service-accounts create deepintshield-pull-sa \
--display-name="DeepIntShield Image Pull SA" \
--project=YOUR_PROJECT_ID

Step 3: Request Access from DeepIntShield Team

Section titled “Step 3: Request Access from DeepIntShield Team”

Provide the following to the DeepIntShield team:

  • Your GCP project ID
  • Service account email: deepintshield-pull-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com

The DeepIntShield team will grant the necessary permissions to pull images from the registry.

Step 4: Create Namespace and ServiceAccount

Section titled “Step 4: Create Namespace and ServiceAccount”
Terminal window
kubectl create namespace deepintshield
apiVersion: v1
kind: ServiceAccount
metadata:
name: deepintshield-sa
namespace: deepintshield
annotations:
iam.gke.io/gcp-service-account: deepintshield-pull-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com

Allow the Kubernetes ServiceAccount to impersonate the GCP Service Account:

Terminal window
gcloud iam service-accounts add-iam-policy-binding \
deepintshield-pull-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com \
--role=roles/iam.workloadIdentityUser \
--member="serviceAccount:YOUR_PROJECT_ID.svc.id.goog[deepintshield/deepintshield-sa]"

Step 6: Create Image Pull Secret with Token Refresh

Section titled “Step 6: Create Image Pull Secret with Token Refresh”

Artifact Registry tokens expire after 60 minutes. Use a CronJob to refresh the imagePullSecret:

apiVersion: batch/v1
kind: CronJob
metadata:
name: refresh-ar-secret
namespace: deepintshield
spec:
schedule: "*/30 * * * *" # Every 30 minutes
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 3
jobTemplate:
spec:
template:
spec:
serviceAccountName: deepintshield-sa
containers:
- name: token-refresh
image: google/cloud-sdk:slim
command: ["/bin/bash", "-c"]
args:
- |
set -e
# Get access token using Workload Identity
TOKEN=$(gcloud auth print-access-token)
# Delete existing secret if it exists
kubectl delete secret ar-pull-secret --ignore-not-found -n deepintshield
# Create new imagePullSecret
kubectl create secret docker-registry ar-pull-secret \
--docker-server=REGION-docker.pkg.dev \
--docker-username=oauth2accesstoken \
--docker-password="$TOKEN" \
-n deepintshield
echo "Secret refreshed at $(date)"
restartPolicy: OnFailure
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-manager
namespace: deepintshield
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "create", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: secret-manager-binding
namespace: deepintshield
subjects:
- kind: ServiceAccount
name: deepintshield-sa
namespace: deepintshield
roleRef:
kind: Role
name: secret-manager
apiGroup: rbac.authorization.k8s.io
apiVersion: apps/v1
kind: Deployment
metadata:
name: deepintshield
namespace: deepintshield
spec:
replicas: 2
selector:
matchLabels:
app: deepintshield
template:
metadata:
labels:
app: deepintshield
spec:
serviceAccountName: deepintshield-sa
imagePullSecrets:
- name: ar-pull-secret
containers:
- name: deepintshield
image: REGION-docker.pkg.dev/DEEPINTSHIELD_PROJECT/YOUR_HUB_SLUG/deepintshield:latest
ports:
- containerPort: 8080
name: http
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "2Gi"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
volumeMounts:
- name: config
mountPath: /app/data/config.json
subPath: config.json
volumes:
- name: config
secret:
secretName: deepintshield-config
---
apiVersion: v1
kind: Service
metadata:
name: deepintshield
namespace: deepintshield
spec:
selector:
app: deepintshield
ports:
- port: 80
targetPort: 8080
protocol: TCP
type: ClusterIP

Before the first deployment, manually create the initial imagePullSecret:

Terminal window
# Authenticate gcloud
gcloud auth login
# Create initial secret
kubectl create secret docker-registry ar-pull-secret \
--docker-server=REGION-docker.pkg.dev \
--docker-username=oauth2accesstoken \
--docker-password="$(gcloud auth print-access-token)" \
-n deepintshield

For cross-project deployments or when you need to use an existing service account:

Terminal window
# Grant impersonation permission
gcloud iam service-accounts add-iam-policy-binding \
DEEPINTSHIELD_PROVIDED_SA@DEEPINTSHIELD_PROJECT.iam.gserviceaccount.com \
--role=roles/iam.serviceAccountTokenCreator \
--member="serviceAccount:deepintshield-pull-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com"

Update the CronJob to use impersonation:

args:
- |
set -e
# Get access token by impersonating the DeepIntShield SA
TOKEN=$(gcloud auth print-access-token \
--impersonate-service-account=DEEPINTSHIELD_PROVIDED_SA@DEEPINTSHIELD_PROJECT.iam.gserviceaccount.com)
kubectl delete secret ar-pull-secret --ignore-not-found -n deepintshield
kubectl create secret docker-registry ar-pull-secret \
--docker-server=REGION-docker.pkg.dev \
--docker-username=oauth2accesstoken \
--docker-password="$TOKEN" \
-n deepintshield

For environments that cannot use Workload Identity:

Terminal window
# Create key (provided by DeepIntShield team)
# Store key securely
# Create imagePullSecret
kubectl create secret docker-registry ar-pull-secret \
--docker-server=REGION-docker.pkg.dev \
--docker-username=_json_key \
--docker-password="$(cat sa-key.json)" \
-n deepintshield
Terminal window
# Configure docker for Artifact Registry
gcloud auth configure-docker REGION-docker.pkg.dev
# Pull test (requires impersonation or direct access)
docker pull REGION-docker.pkg.dev/DEEPINTSHIELD_PROJECT/YOUR_HUB_SLUG/deepintshield:latest
Terminal window
# Check ServiceAccount annotation
kubectl get sa deepintshield-sa -n deepintshield -o yaml
# Verify pod can authenticate
kubectl exec -it deployment/deepintshield -n deepintshield -- \
gcloud auth print-access-token
# Check token refresh CronJob
kubectl get cronjob refresh-ar-secret -n deepintshield
kubectl get jobs -n deepintshield
  1. Check imagePullSecret exists: kubectl get secret ar-pull-secret -n deepintshield
  2. Verify token is valid: Check if CronJob ran successfully
  3. Check Workload Identity binding: Ensure GCP SA is bound to K8s SA
Terminal window
# Check pod events
kubectl describe pod -l app=deepintshield -n deepintshield
# Manually refresh token
kubectl create job --from=cronjob/refresh-ar-secret manual-refresh -n deepintshield
Terminal window
# Verify Workload Identity pool
gcloud container clusters describe YOUR_CLUSTER_NAME \
--region=YOUR_REGION \
--format="value(workloadIdentityConfig.workloadPool)"
# Check IAM binding
gcloud iam service-accounts get-iam-policy \
deepintshield-pull-sa@YOUR_PROJECT_ID.iam.gserviceaccount.com

If pods fail to pull images after 60 minutes:

  1. Verify CronJob is running: kubectl get cronjob -n deepintshield
  2. Check CronJob logs: kubectl logs -l job-name=refresh-ar-secret -n deepintshield
  3. Manually trigger refresh: kubectl create job --from=cronjob/refresh-ar-secret manual-refresh -n deepintshield