Skip to Content
CompliancePCI Verification Evidence

PCI DSS 4.0.1 Verification Evidence

This document provides control-by-control evidence for PCI DSS 4.0.1 compliance. Each requirement section includes two layers of verification:

  1. Source Evidence — Terraform resources and code that implement the control
  2. Deployed Verificationgcloud commands to confirm live infrastructure matches

Environment variables: All commands use $PCI_PROJECT and $CORE_PROJECT placeholders. Set these before running:

export PCI_PROJECT="gatelithix-pci" export CORE_PROJECT="gatelithix-core"

Requirement 1: Network Segmentation

Source Evidence

PCI ControlRequirementEvidence TypeEvidence LocationStatus
1.2.1Network segmentation controls installedTerraforminfra/terraform/modules/vpc/main.tfdeny_all_ingress (priority 65534, deny all protocols, source 0.0.0.0/0) and deny_all_egress (priority 65534, deny all protocols, dest 0.0.0.0/0)Verified
1.3.1Inbound CDE traffic restrictedTerraforminfra/terraform/pci/network.tfallow_core_to_pci (priority 900, TCP 443 only, source_ranges 10.0.0.0/20)Verified
1.3.2Outbound CDE traffic restrictedTerraforminfra/terraform/pci/network.tfallow_psp_egress (priority 900, TCP 443 only, target_tags psp-egress) and allow_restricted_apis_egress (TCP 443, dest 199.36.153.4/30)Verified
1.4.1Firewall between internet and CDETerraforminfra/terraform/pci/cloudrun.tf — vault service ingress = "INGRESS_TRAFFIC_INTERNAL_ONLY" (no direct internet access to CDE)Verified

VPC peering: Core and PCI VPCs are connected via bidirectional peering (core_to_pci / pci_to_core in infra/terraform/core/network.tf). Custom route import/export is disabled — only private IP traffic crosses the peering link.

Deployed Verification

List all PCI VPC firewall rules:

gcloud compute firewall-rules list \ --project=$PCI_PROJECT \ --filter="network:pci-vpc" \ --format="table(name, direction, priority, allowed[].map().firewall_rule().list():label=ALLOWED, sourceRanges, destinationRanges)"

Expected output: pci-vpc-deny-all-ingress and pci-vpc-deny-all-egress at priority 65534 (deny all). pci-vpc-allow-core-ingress, pci-vpc-allow-psp-egress, pci-vpc-allow-restricted-apis at priority 900 (TCP 443 only).

Verify vault service ingress restriction:

gcloud run services describe vault \ --project=$PCI_PROJECT \ --region=us-central1 \ --format="value(spec.template.metadata.annotations['run.googleapis.com/ingress'])"

Expected output: internal

Verify VPC peering exists:

gcloud compute networks peerings list \ --project=$CORE_PROJECT \ --network=core-vpc \ --format="table(name, network, peerNetwork, state)"

Expected output: core-to-pci peering in ACTIVE state.

Migration Pipeline CDE Isolation

The automated database migration pipeline enforces CDE isolation at every layer:

  • Image isolation: Core and PCI migration images are built and stored in separate Artifact Registry repositories. PCI Cloud Run Jobs pull exclusively from the PCI registry — no cross-CDE image dependencies.
  • VPC-internal execution: Migration Cloud Run Jobs run inside the VPC via VPC connectors (core-vpc-connector for core, pci-vpc-connector for PCI). No public network access to databases.
  • Credential handling: Database passwords are injected at runtime from Secret Manager (core-db-password, pci-db-password). No credentials are baked into container images.
  • Schema-only migrations: Migration files contain only DDL statements (CREATE TABLE, ALTER TABLE). No SELECT, INSERT, UPDATE, or DELETE of cardholder data. Verified by CI migration validation job.
  • Least-privilege execution: Core migrations run as gateway-sa, PCI migrations run as vault-sa — each with only the permissions needed for their respective database.
  • Audit trail: Every migration execution is logged in Cloud Run Job execution history and Cloud Logging with full traceability to the triggering commit SHA.

Deployed Verification:

# Verify migration job uses VPC connector (no public access) gcloud run jobs describe migrate-vault --project=$PCI_PROJECT --region=us-central1 --format="value(template.vpcAccess.connector)" # Verify migration image comes from PCI registry (not core) gcloud run jobs describe migrate-vault --project=$PCI_PROJECT --region=us-central1 --format="value(template.containers[0].image)" # Verify no secrets baked into image gcloud run jobs describe migrate-vault --project=$PCI_PROJECT --region=us-central1 --format="yaml(template.containers[0].env)"

Expected output: VPC connector should be pci-vpc-connector. Image URI should reference the PCI Artifact Registry (gatelithix-pci). Environment variables should reference Secret Manager secret references, not plaintext values.


Requirement 3: Protect Stored Account Data

Source Evidence

PCI ControlRequirementEvidence TypeEvidence LocationStatus
3.5.1PAN encrypted with strong cryptographyTerraforminfra/terraform/pci/kms.tfpan_encryption_key with protection_level = "HSM" and algorithm = "GOOGLE_SYMMETRIC_ENCRYPTION"Verified
3.5.1.1PAN fingerprint uses one-way hashTerraforminfra/terraform/pci/kms.tfpan_fingerprint_key with purpose = "MAC", algorithm = "HMAC_SHA256", protection_level = "HSM"Verified
3.6.1Key management procedures documentedTerraforminfra/terraform/pci/kms.tfrotation_period = "7776000s" (90 days automatic rotation), prevent_destroy = true lifecycleVerified

PAN data flow: PAN enters the system only through the vault service. The vault encrypts PAN at ingress using Cloud KMS HSM-backed keys. The gateway and all other services receive only token references (UUIDs) — PAN never leaves the CDE unencrypted.

Webhook signing secrets: Connector webhook signing secrets (Stripe, NMI, FluidPay) are stored in Google Secret Manager and injected into Cloud Run services at deployment time. This follows the same Secret Manager pattern as other sensitive credentials, satisfying PCI Req 3-4 cryptographic key management requirements. No webhook signing secrets are hardcoded or stored in source control.

Deployed Verification

Verify PAN encryption key configuration:

gcloud kms keys describe pan-encryption-key \ --keyring=gatelithix-vault \ --location=us-central1 \ --project=$PCI_PROJECT \ --format="yaml(purpose, versionTemplate.protectionLevel, versionTemplate.algorithm, rotationPeriod, nextRotationTime)"

Expected output: protectionLevel: HSM, algorithm: GOOGLE_SYMMETRIC_ENCRYPTION, rotationPeriod: 7776000s.

Verify PAN fingerprint key configuration:

gcloud kms keys describe pan-fingerprint-key \ --keyring=gatelithix-vault \ --location=us-central1 \ --project=$PCI_PROJECT \ --format="yaml(purpose, versionTemplate.protectionLevel, versionTemplate.algorithm)"

Expected output: purpose: MAC, protectionLevel: HSM, algorithm: HMAC_SHA256.

List all key versions (verify rotation is occurring):

gcloud kms keys versions list \ --key=pan-encryption-key \ --keyring=gatelithix-vault \ --location=us-central1 \ --project=$PCI_PROJECT \ --format="table(name, state, createTime)"

Requirement 4: Encrypt Transmission of Cardholder Data

Source Evidence

PCI ControlRequirementEvidence TypeEvidence LocationStatus
4.2.1PAN encrypted during transmission over open networksTerraform + ArchitectureVPC peering (private network, no internet transit) + firewall rules restricting to TCP 443 only. Gateway-to-vault communication is internal HTTPS over VPC peering.Verified
4.2.1.1Trusted certificates for PAN transmissionCloud RunCloud Run services use Google-managed TLS certificates. Internal service-to-service calls use Cloud Run identity tokens over HTTPS.Verified

Architecture note: PAN is transmitted only on two paths:

  1. Client to vault — via gateway proxy, HTTPS enforced by Cloud Load Balancer + Cloud Armor WAF
  2. Vault to KMS — internal Google API call over private network (restricted.googleapis.com at 199.36.153.4/30)

No PAN traverses the public internet in plaintext at any point.

Deployed Verification

Verify Cloud Run services use HTTPS:

gcloud run services describe vault \ --project=$PCI_PROJECT \ --region=us-central1 \ --format="value(status.url)"

Expected output: URL starting with https://.

Verify restricted Google API egress:

gcloud compute firewall-rules describe pci-vpc-allow-restricted-apis \ --project=$PCI_PROJECT \ --format="yaml(allowed, destinationRanges, direction)"

Expected output: destinationRanges: 199.36.153.4/30, allowed: tcp:443.


Requirement 7: Restrict Access to System Components

Source Evidence

PCI ControlRequirementEvidence TypeEvidence LocationStatus
7.2.1Access based on business need to knowTerraforminfra/terraform/pci/kms.tf — IAM binding vault_encrypter restricts roles/cloudkms.cryptoKeyEncrypterDecrypter to vault_sa_email only. IAM binding vault_mac_signer restricts roles/cloudkms.signerVerifier to vault_sa_email only.Verified
7.2.2Privileges assigned by job functionTerraformPer-service service accounts: gateway_sa_email (gateway), vault_sa_email (vault), connector_sa_email (all connectors). Each SA has only the roles needed for its function.Verified
7.2.3Default deny on all system componentsTerraforminfra/terraform/modules/vpc/main.tf — deny-all firewall baseline. Cloud Run IAM — only explicitly granted invokers can call services.Verified

Cross-service access matrix:

Source ServiceTarget ServiceIAM RoleEvidence
gateway_savaultroles/run.invokerpci/cloudrun.tfgateway_invokes_vault
gateway_sastripe-connectorroles/run.invokercore/cloudrun.tfgateway_invokes_stripe
gateway_sanmi-connectorroles/run.invokercore/cloudrun.tfgateway_invokes_nmi
gateway_safluidpay-connectorroles/run.invokercore/cloudrun.tfgateway_invokes_fluidpay
vault_saKMS pan-encryption-keyroles/cloudkms.cryptoKeyEncrypterDecrypterpci/kms.tfvault_encrypter
vault_saKMS pan-fingerprint-keyroles/cloudkms.signerVerifierpci/kms.tfvault_mac_signer

Deployed Verification

Verify KMS IAM policy (only vault SA has access):

gcloud kms keys get-iam-policy pan-encryption-key \ --keyring=gatelithix-vault \ --location=us-central1 \ --project=$PCI_PROJECT \ --format="yaml(bindings)"

Expected output: Single binding with role: roles/cloudkms.cryptoKeyEncrypterDecrypter and only vault_sa_email as member.

List service accounts in PCI project:

gcloud iam service-accounts list \ --project=$PCI_PROJECT \ --format="table(email, displayName, disabled)"

List service accounts in core project:

gcloud iam service-accounts list \ --project=$CORE_PROJECT \ --format="table(email, displayName, disabled)"

Verify Cloud Run service account assignments:

gcloud run services describe vault \ --project=$PCI_PROJECT \ --region=us-central1 \ --format="value(spec.template.spec.serviceAccountName)" gcloud run services describe api-gateway \ --project=$CORE_PROJECT \ --region=us-central1 \ --format="value(spec.template.spec.serviceAccountName)"

Requirement 8: Identify Users and Authenticate Access

Source Evidence

PCI ControlRequirementEvidence TypeEvidence LocationStatus
8.3.1MFA for all access into CDEConfigurationAuth0 tenant MFA policy (Guardian push + TOTP). All admin users must complete MFA to access the merchant portal.Verified
8.3.2Strong cryptography for authenticationCodeapps/gateway/ — Auth0 JWT validation using JWKS endpoint with RS256 algorithm. API keys use SHA-256 hashing.Verified
8.4.2MFA for remote network access to CDEConfigurationGCP IAM — Organization policy requires MFA for all console and API access. Cloud Run admin operations require authenticated IAM principals.Verified
8.6.1Service accounts managed with least privilegeTerraformPer-service SAs with specific role bindings. No wildcard roles. No service account key files (workload identity for CI/CD).Verified

Deployed Verification

Verify GCP IAM policy on PCI project (no allUsers/allAuthenticatedUsers):

gcloud projects get-iam-policy $PCI_PROJECT \ --format="yaml(bindings)" | grep -E "allUsers|allAuthenticatedUsers" || echo "PASS: No public access"

Verify Cloud Run services require authentication:

gcloud run services get-iam-policy vault \ --project=$PCI_PROJECT \ --region=us-central1 \ --format="yaml(bindings)"

Expected output: No allUsers binding — only specific service accounts.


Requirement 10: Log and Monitor All Access

Source Evidence

PCI ControlRequirementEvidence TypeEvidence LocationStatus
10.2.1Audit logs enabled for all CDE componentsTerraforminfra/terraform/pci/logging.tfpci_audit_sink with filter logName:"cloudaudit.googleapis.com" routes all Cloud Audit Logs to locked bucketVerified
10.3.3Audit logs protected from modificationTerraforminfra/terraform/pci/logging.tflocked = true on pci_audit_logs bucket, prevent_destroy = true lifecycleVerified
10.5.1Audit log retention >= 12 monthsTerraforminfra/terraform/pci/logging.tf — Cloud Logging bucket retention_days = 365. Cloud Storage archive bucket retention_period = 31536000 (365 days), is_locked = true (cannot be shortened)Verified
10.6.3Archival of audit logsTerraforminfra/terraform/pci/logging.tfpci_audit_archive Cloud Storage bucket with NEARLINE storage class, versioning enabled, locked retention policy. pci_audit_archive_sink exports to storage.Verified

Deployed Verification

Verify locked logging bucket:

gcloud logging buckets describe pci-audit-logs \ --location=us-central1 \ --project=$PCI_PROJECT \ --format="yaml(retentionDays, locked, lifecycleState)"

Expected output: retentionDays: 365, locked: true, lifecycleState: ACTIVE.

Verify log sink is routing audit logs:

gcloud logging sinks describe pci-audit-sink \ --project=$PCI_PROJECT \ --format="yaml(destination, filter)"

Expected output: filter: logName:"cloudaudit.googleapis.com", destination pointing to pci-audit-logs bucket.

Verify Cloud Storage archive bucket retention:

gcloud storage buckets describe gs://${PCI_PROJECT}-pci-audit-archive \ --format="yaml(retention_policy)"

Expected output: retentionPeriod: 31536000, isLocked: true.


Requirement 11: Test Security of Systems and Networks

Source Evidence

PCI ControlRequirementEvidence TypeEvidence LocationStatus
11.3.1Internal vulnerability scans performedCI/CD.github/workflows/ci.ymlsecurity job runs gosec ./... on every PR and push to develop. Static analysis for Go security vulnerabilities (SQL injection, hardcoded credentials, weak crypto).Verified
11.3.2External vulnerability scans performedCI/CD.github/workflows/ci.ymlscan job runs Trivy container scan (aquasecurity/trivy-action) with severity HIGH,CRITICAL and exit-code: 1 (blocks merge). Scans against CVE databases.Verified
11.6.1Change detection on payment pagesN/ANot applicable — Gatelithix Gateway is API-only. No hosted payment pages. Merchants integrate via API and SDKs.N/A

CI scanning pipeline:

Scan TypeToolTriggerSeverity ThresholdEvidence
Static analysisgosecPR + push to developAll findings.github/workflows/ci.ymlsecurity job
Container scanTrivy (image mode)Push to developHIGH, CRITICAL.github/workflows/ci.ymlscan job
Dependency auditGo module verificationPR + pushBuild failure on vulngo test -race catches incompatible deps

Deployed Verification

Verify CI workflow exists and contains scan jobs:

# Review the CI workflow file for scanning steps cat .github/workflows/ci.yml | grep -A 5 "gosec\|trivy\|vulnerability"

Verify recent CI runs include security scans:

Review GitHub Actions workflow runs at: https://github.com/<org>/gatelithix-gateway/actions/workflows/ci.yml

Each successful run should show Security Scan and Container Scan jobs completed.


Requirement 9: Physical Security (Skipped)

Not in scope. Physical security of data centers is the responsibility of Google Cloud Platform. GCP data centers undergo independent SOC 2 Type II and PCI DSS audits. Reference: Google Cloud Compliance .

Gatelithix Gateway runs entirely on Cloud Run (serverless) with no customer-managed physical infrastructure.