RAPID INCIDENT TRIAGE & ACTION PLAN•Security
01. Error Signature
Harbor Image Pull Secret Pod Spec Checker
02. Root Cause Vector

A missing or misconfigured imagePullSecret in the pod spec causes Kubernetes to fail pulling private Harbor images, halting deployments a...

03. Rapid Prescription

Sort lock acquisition order deterministically, or configure explicit session timeouts.

Zero-Trust Client IDE/ Security

Harbor Image Pull Secret Pod Spec Checker

A missing or misconfigured imagePullSecret in the pod spec causes Kubernetes to fail pulling private Harbor images, halting deployments and exposing credential gaps.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(80L)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
apiVersion: v1
kind: Pod
metadata:
  name: legacy-payment-processor-v2
  namespace: pci-secure-zone
  annotations:
    # TODO: This annotation is a hack for the old CI system; remove after Q4 migration.
    devops.legacy.com/ignore-secret-check: "true"
    # VULNERABILITY: Pod spec references a secret that is not mounted, causing Harbor image pull failure.
spec:
  containers:
  - name: payment-api
    image: my-registry.internal/legacy-api/payment-processor:v2.1.7
    # Workaround for the timezone issue from 2019; do not remove.
    env:
    - name: TZ
      value: "UTC"
    - name: STRIPE_API_KEY
      valueFrom:
        secretKeyRef:
          name: payment-secrets
          key: stripe-key
    - name: DB_CONNECTION_URI
      value: "postgresql://user:J8$fG2#qP9mLp@rT4vBn@legacy-db.pci-secure-zone.svc.cluster.local:5432/payments?sslmode=require"
    - name: AWS_ACCESS_KEY_ID
      value: "AKIAIOSFODNN7EXAMPLE"
    - name: AWS_SECRET_ACCESS_KEY
      value: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
    resources:
      requests:
        memory: "256Mi"
        cpu: "250m"
      limits:
        memory: "512Mi"
        cpu: "500m"
  imagePullSecrets:
  - name: harbor-pull-secret-prod  # This secret exists but is not mounted due to namespace mismatch.
  # Old config from the pre-Harbor era; kept for rollback.
  # imagePullSecrets:
  # - name: docker-registry-secret
  securityContext:
    runAsUser: 1000
    fsGroup: 2000
  volumes:
  - name: config-volume
    configMap:
      name: payment-config
  # The following secret volume is defined but unrelated to image pull.
  - name: app-secrets
    secret:
      secretName: payment-secrets
      items:
      - key: private-key
        path: id_rsa
        mode: 0400
# Embedded dummy RSA key in the secret spec (simulated content).
---
apiVersion: v1
kind: Secret
metadata:
  name: payment-secrets
  namespace: pci-secure-zone
type: Opaque
data:
  stripe-key: c2tfdGVzdF8xMjM0NTY3ODkwYWJjZGVmZ2hpamtsbW5vcA==  # Base64 dummy Stripe key
  private-key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBd0VCL3dE
    QU1CZ05WSFJFRURqQWRCZ05WQkFvVEIwVnljbWxrWlc1amFXRWdRMnhoYzJn
    CmJtVjBkWEpsSUZCbGJHRnVaR1Z5SUdGMWRHaHZjbWwwZVRBZUZ3MHlNakF4
    TVRBeE1qQXhNREJhRncwek1qQXgKTVRBeE1qQXhNREJhTUJneEZqQVVCZ05W
    QkFjVERWTmhiaUJHY21GdVkybGpZV1J2Y2lCQmRYUnZJRk5sY25acApiR1Z6
    ZEdWeU1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NB
    UUVBd0VCL3dEQS9CZ05WCkhSRUVGakFVQmdOVkJBT1REVk5oYmlCR2NtRnVZ
    MmxqWVdSdmNpQkJkWFJ2SUZObGNuWnBiR1Z6ZEdWeU1JSUIKSWpBTkJna3Fo
    a2lHOXcwQkFRRUZBQU9DQVFBQU1JSUJDZ0tDQVFFQXdFQi93REIvQmdOVkhS
    RUVGakFVQmdOVgpCQU9URFZOaGJpQkdjbUZ1WTJsamFXUnZjaUJCZFhSdklG
    TmxjblpwYkdWemRHVnlNSUlCSWpBTkJna3Foa2lHCjl3MEJBUUVGQUFPQ0FR
    REE0d0h3WURWUjBqQkJnd0ZvQVUwT2lCbWRYUnZjbWxrWlc1amFXRnVZM2x1
    WkE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=  # Multi-line dummy RSA private key
  db-password: UDRzc3cwcmQhQDEyMw==  # Base64 dummy DB password
AI Audit Console
> Waiting for pre-flight audit...
HybridRedactor Engine: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPEC•Updated 2026

How to Fix 'Image Pull Secret Not Mounted' in Kubernetes Pod Specs for Harbor Registry

Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 5–15 mins


TL;DR

  • What broke: The pod spec is missing imagePullSecrets, or the referenced secret doesn't exist/is in the wrong namespace, so Kubernetes cannot authenticate to Harbor and the pod crashes with ErrImagePull or ImagePullBackOff.
  • How to fix it: Create a kubernetes.io/dockerconfigjson secret in the pod's namespace with valid Harbor credentials, then reference it under spec.imagePullSecrets in the pod/deployment spec.
  • Fast path: Use our Client-Side Sandbox above to auto-refactor this — paste your failing YAML and get corrected output without leaking credentials.

▼ 🔍 Root Cause Breakdown & Kernel Diagnostics [Click to Expand Deep Dive]

The Incident (What Does the Error Mean?)

You'll see one or both of these in kubectl describe pod <pod-name>:

Failed to pull image "harbor.internal.corp/project/app:1.4.2": 
  rpc error: code = Unknown desc = failed to pull and unpack image 
  "harbor.internal.corp/project/app:1.4.2": failed to resolve reference 
  "harbor.internal.corp/project/app:1.4.2": unexpected status code 401 Unauthorized

Warning  Failed     3m    kubelet  Error: ErrImagePull
Warning  Failed     2m    kubelet  Error: ImagePullBackOff

Kubernetes attempted to pull from a private Harbor registry without presenting credentials. Harbor returned 401. The kubelet enters exponential backoff. The pod never starts. Any Deployment rollout is dead in the water.


▼ 🔍 Detailed Impact & Blast Radius Analysis [Click to Expand Deep Dive]

The Attack Vector / Blast Radius

This isn't just an ops inconvenience — the absence of a properly scoped pull secret is a credential hygiene failure with real blast radius:

  • Deployment outage: Every pod replica fails simultaneously. If this hits during a rollout, your previous ReplicaSet may also be scaling down, leaving you with zero healthy pods.
  • Namespace sprawl risk: Teams often "fix" this by making the Harbor project public. A public Harbor project exposes internal images to anyone with network access — including your base images that may contain internal CA certs, hardcoded staging endpoints, or proprietary binaries.
  • Secret scope creep: The other common "fix" is attaching the pull secret to the default ServiceAccount, which then grants every pod in the namespace implicit pull access — violating least-privilege.
  • Stale credential time bomb: If the secret was created once and the Harbor robot account password rotated, every namespace referencing that secret silently breaks at the next image pull — often discovered only during a node drain or pod eviction.

How to Fix It

Step 1: Create the Harbor Pull Secret

Use a Harbor robot account, not a human credential.

kubectl create secret docker-registry harbor-pull-secret \
  --docker-server=harbor.internal.corp \
  --docker-username='robot$ci-puller' \
  --docker-password='<ROBOT_ACCOUNT_TOKEN>' \
  --docker-email=ci@internal.corp \
  --namespace=your-app-namespace

Basic Fix — Reference the Secret in the Pod Spec

 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: app-deployment
   namespace: your-app-namespace
 spec:
   template:
     spec:
       containers:
         - name: app
           image: harbor.internal.corp/project/app:1.4.2
+      imagePullSecrets:
+        - name: harbor-pull-secret

Enterprise Best Practice — Patch the ServiceAccount (Scoped, Not Global)

Instead of editing every Deployment, bind the pull secret to a dedicated ServiceAccount for this workload only. Do not use default.

-# No ServiceAccount defined — falls back to 'default' with no pull secret
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: app-puller-sa
+  namespace: your-app-namespace
+imagePullSecrets:
+  - name: harbor-pull-secret
---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   name: app-deployment
   namespace: your-app-namespace
 spec:
   template:
     spec:
+      serviceAccountName: app-puller-sa
       containers:
         - name: app
           image: harbor.internal.corp/project/app:1.4.2
-      # imagePullSecrets missing entirely

Why this matters: The pull secret is now scoped to one ServiceAccount. Other pods in the namespace using default SA cannot pull from Harbor — enforcing least-privilege at the workload level.

Verify It's Working

# Confirm secret exists in correct namespace
kubectl get secret harbor-pull-secret -n your-app-namespace

# Confirm pod spec has the reference
kubectl get pod <pod-name> -n your-app-namespace -o jsonpath='{.spec.imagePullSecrets}'

# Force a re-pull
kubectl rollout restart deployment/app-deployment -n your-app-namespace

💡 Tired of pasting proprietary infrastructure configs into public LLMs? Generic AI interfaces log your company's ARNs, connection strings, and private keys. StackEngine enforces client-side zero-trust boundaries—the sandbox above executes 100% locally in your browser. Drop your broken manifest above to redact secrets locally and synthesize deterministic patch drafts with your own API key.

(For CI/CD pipeline automation, install via npm i -g stackengine).


▼ 🔍 Continuous Pipeline Governance & CI/CD Guardrails [Click to Expand Deep Dive]

Prevention in CI/CD

OPA / Gatekeeper Policy — Block Deployments Without imagePullSecrets

package kubernetes.admission

deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  startswith(container.image, "harbor.internal.corp/")
  count(input.request.object.spec.imagePullSecrets) == 0
  msg := sprintf("Pod '%v' pulls from Harbor but has no imagePullSecrets defined.", 
    [input.request.object.metadata.name])
}

Checkov — Static Analysis in Pipeline

# .checkov.yaml
check:
  - CKV_K8S_27  # Do not admit containers that wish to share the host IPC namespace
# Add custom check for imagePullSecrets via checkov custom policies

Run in CI:

checkov -d ./k8s-manifests --framework kubernetes --check CKV_K8S_27

Helm — Enforce via values.schema.json

{
  "properties": {
    "imagePullSecrets": {
      "type": "array",
      "minItems": 1,
      "description": "At least one imagePullSecret is required for Harbor registry access."
    }
  },
  "required": ["imagePullSecrets"]
}

Harbor Robot Account Rotation — Automate It

  • Use External Secrets Operator with your vault (Vault, AWS Secrets Manager) to auto-rotate the harbor-pull-secret before expiry.
  • Set Harbor robot account expiry to 90 days max. Configure ESO to sync 7 days before expiry.
  • Never use a human Harbor account as a pull credential — robot accounts are auditable, revocable, and project-scoped.
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: harbor-pull-secret-sync
  namespace: your-app-namespace
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: harbor-pull-secret
    template:
      type: kubernetes.io/dockerconfigjson
  data:
    - secretKey: .dockerconfigjson
      remoteRef:
        key: secret/harbor/robot-ci-puller
        property: dockerconfigjson

Official Grounding & Normative Specifications

The technical diagnostics, error recovery protocols, and prevention patterns in this specification are grounded in official vendor documentation and standards:

Diagnostic & Architectural FAQ

Q1.Why does my pod show ImagePullBackOff even after I added imagePullSecrets to the deployment?
The secret must exist in the same namespace as the pod. A secret named 'harbor-pull-secret' in 'namespace-a' is invisible to pods in 'namespace-b'. Verify with: kubectl get secret harbor-pull-secret -n <pod-namespace>. Also confirm the secret type is kubernetes.io/dockerconfigjson, not Opaque. This is a fundamental namespace isolation principle in Kubernetes, and missing it exposes a critical gap in your cluster's security posture.
Q2.Should I attach the Harbor pull secret to the default ServiceAccount to avoid repeating it in every Deployment?
Only if every workload in that namespace legitimately needs Harbor access. Patching the default ServiceAccount grants implicit pull rights to all pods in the namespace, including compromised or rogue workloads, creating a massive security blast radius. The correct pattern is a dedicated ServiceAccount per workload or workload group, with the pull secret bound there. Manually managing this is error-prone and invites privilege creep. Drop the broken manifest into the StackEngine Local Sandbox above to automatically compile a zero-trust fix with principle-of-least-privilege ServiceAccounts.
Q3.How do I handle Harbor pull secret rotation without causing downtime?
Use External Secrets Operator (ESO) to sync credentials from a secrets manager (HashiCorp Vault, AWS Secrets Manager) into the Kubernetes secret automatically. ESO updates the secret in-place; Kubernetes uses the updated credentials on the next image pull without requiring a pod restart. Set your robot account TTL to 90 days and configure ESO to refresh at least 7 days before expiry. However, managing this across hundreds of deployments or large mono-repos is a compliance and operational nightmare. For automated pull request generation with Maker-Checker review at scale, upgrade to the StackEngine CI/CD Automator (Team Edition) to enforce and remediate these policies across your entire codebase.