The Problem
A third-party email API key was scattered across 24+ repositories, environment variables, and developer machines. This created several issues:
- Anyone with repo access could see the key
- Key rotation required updating dozens of places manually
- No audit trail of API usage
- Security risk if any single repo was compromised
Initial Exploration
The discussion started with Azure Key Vault as a potential solution for secret management. However, fetching keys from Key Vault at runtime would require code changes across all 24 services — not feasible without significant disruption.
The constraint was clear: no code changes, no environment variable changes.
The Solution: API Proxy
Instead of distributing the API key, centralize it. Create a proxy that:
- Receives requests from internal services
- Appends the real API key
- Forwards to the third-party API
- Returns the response
Services continue calling the same API endpoint. They don't know they're hitting a proxy.
Implementation
Phase 1: Azure Infrastructure
Internal Certificate Authority
Created a private Root CA to issue certificates for internal traffic. This allows services to trust the proxy without relying on public certificate authorities.
rootCA.key → Keep secret, never share
rootCA.crt → Distribute to all services (safe to share publicly)
Proxy VM Setup
Provisioned an Azure VM running nginx as a reverse proxy:
- Listens on port 443 for
api.example.com - Uses internal CA-signed certificate
- Strips incoming auth headers
- Adds the real API key
- Forwards to actual third-party API using public DNS (8.8.8.8)
Private DNS Zone
Configured Azure Private DNS to resolve api.example.com to the proxy VM's private IP. All services in the VNet automatically route through the proxy — no configuration needed on their end.
Base Container Images
Created custom base images (Python, Node) with the Root CA pre-installed. Services using these base images automatically trust the proxy's certificate.
For Python, this required adding the Root CA to both the system store and the certifi package store.
Phase 2: GCP Infrastructure
GCP services needed to route through the same proxy, but cross-cloud traffic required additional security.
Public Endpoint with Authentication
- Created a public DNS record pointing to Azure VM
- Obtained Let's Encrypt certificate for the public domain
- Added token-based authentication for external traffic
GCP Proxy VM
Rather than modifying GCP services to add auth headers, deployed a GCP proxy VM that:
- Receives internal GCP traffic via Private DNS
- Adds the authentication token
- Forwards to Azure proxy's public endpoint
GCP Private DNS
Configured Cloud DNS private zone to route api.example.com to GCP proxy VM. All GCP VMs automatically use this.
Cloud Run Integration
Cloud Run services don't use VPC by default. Required:
- Serverless VPC Access Connector
- Configuring each Cloud Run service to route all traffic through VPC
- Scripted bulk update for all services
VM Certificate Trust
Uploaded Root CA certificate to a public Cloud Storage bucket. Any VM can install trust with:
curl -o /tmp/rootCA.crt https://storage.googleapis.com/bucket/rootCA.crt
sudo cp /tmp/rootCA.crt /usr/local/share/ca-certificates/internal-ca.crt
sudo update-ca-certificates
Architecture Summary
Azure Services
→ Private DNS (api.example.com → Azure Proxy private IP)
→ Azure Proxy (adds API key, uses public DNS 8.8.8.8 to resolve real API)
→ Third-party API
GCP Services (VMs)
→ Private DNS (api.example.com → GCP Proxy private IP)
→ GCP Proxy (adds auth token)
→ Azure Proxy public endpoint (validates token, adds API key, uses public DNS 8.8.8.8)
→ Third-party API
GCP Services (Cloud Run)
→ VPC Connector
→ Private DNS
→ Same flow as GCP VMs
Note: The Azure proxy uses public DNS resolvers (8.8.8.8) to reach the real third-party API. This bypasses the Private DNS override that would otherwise create a routing loop.
Security Layers
| Layer | Protection | |-------|------------| | Private DNS | Internal traffic never leaves VNet/VPC | | Internal CA | Only trusted services can connect to proxy | | Token Auth | External traffic must have valid token | | IP Allowlist | Azure NSG allows only GCP proxy IP | | Single Location | API key exists only on Azure proxy VM |
Benefits Achieved
- Key Rotation: Change one config file on one VM
- No Code Changes: Services work exactly as before
- Audit Trail: All API calls logged in one place
- Reduced Exposure: Key removed from 24 repos and dev machines
- Cross-Cloud: Works across Azure and GCP seamlessly
Key Learnings
-
nginx can route by SNI — Multiple server blocks on port 443, each with different certs and auth rules
-
Python uses certifi, not system certs — Must add Root CA to both stores for Python services
-
Cloud Run needs VPC connector — Serverless services don't use Private DNS by default
-
DNS-level routing enables zero-change migrations — Services don't know they're being proxied