Skip to content

Reverse-Connect Architecture

The reverse-connect model eliminates inbound network access to the vault. The vault server initiates an outbound connection to the cluster, not the other way around. Nothing is publicly exposed except the cluster's broker endpoint.


The Problem with Direct Connect

In the direct-connect model, the wrapper inside each pod connects directly to the vault endpoint:

┌─────────────────────┐         ┌──────────────────────┐
│  Kubernetes Cluster  │         │  EU Vault Server     │
│                      │         │  (Hetzner / OVH)     │
│  ┌────────────────┐  │  HTTPS  │                      │
│  │ wrapper (pod)   │──────────>│  :8200 (public)      │
│  └────────────────┘  │         │                      │
│                      │         └──────────────────────┘
└─────────────────────┘

This requires the vault to accept inbound connections. Even with mTLS and IP allowlists, the vault has a public attack surface: port 8200 is reachable from the internet. For enterprises running vaults on EU-sovereign infrastructure (Hetzner, OVH, Scaleway), exposing any port to inbound traffic from cloud provider networks is a security and compliance concern.

Direct connect is appropriate when:

  • Vault and cluster are in the same VPC or private network
  • Private peering or VPN connects the vault network to the cluster network
  • The vault endpoint is not exposed to the public internet

Reverse-Connect Model

In the reverse-connect model, the connection direction is inverted. The vault server runs a bridge component that connects outbound to a broker running inside the cluster. The wrapper fetches secrets through the broker, which proxies the request back through the bridge to the vault.

┌──────────────────────────────────────────────────┐
│  Kubernetes Cluster                               │
│                                                   │
│  ┌────────────────┐      ┌─────────────────────┐  │
│  │ wrapper (pod)   │─────>│ broker (in-cluster)  │  │
│  └────────────────┘      │ cloudtaser-broker    │  │
│                          │ svc :8443            │  │
│  ┌────────────────┐      │                      │  │
│  │ wrapper (pod)   │─────>│                      │  │
│  └────────────────┘      └──────────┬──────────┘  │
│                                     │ gRPC/mTLS   │
└─────────────────────────────────────┼─────────────┘
                              (outbound from vault)
┌─────────────────────────────────────┼─────────────┐
│  EU Vault Server (Hetzner / OVH)    │              │
│                                     │              │
│  ┌────────────────────┐   ┌────────┴────────────┐  │
│  │ OpenBao / Vault     │<──│ vault-bridge         │  │
│  │ :8200 (localhost)   │   │ (outbound only)      │  │
│  └────────────────────┘   └─────────────────────┘  │
│                                                     │
│  Firewall: DENY ALL INBOUND                         │
│            ALLOW OUTBOUND to broker endpoint        │
└─────────────────────────────────────────────────────┘

Connection Flow

  1. vault-bridge starts on the vault server and connects outbound to the cluster's broker endpoint (a LoadBalancer or Ingress with a stable DNS name)
  2. broker accepts the bridge connection and holds it open as a persistent gRPC stream with mTLS
  3. When a wrapper needs to fetch secrets, it sends the request to the broker's in-cluster service (cloudtaser-broker.cloudtaser-system.svc:8443)
  4. The broker proxies the vault API request through the bridge connection to the vault server
  5. vault-bridge forwards the request to the local vault instance (localhost:8200)
  6. The vault response travels back through the same path: vault -> bridge -> broker -> wrapper

What Is Exposed

Component Network Exposure
Vault server None. No inbound ports. Egress-only firewall.
vault-bridge None. Outbound connection only. No listening ports.
Broker (in-cluster) Cluster-internal service on port 8443, plus a public endpoint for the bridge to connect to (LoadBalancer or Ingress)
Wrapper None. Connects to in-cluster broker service only.

The only publicly reachable component is the broker's external endpoint, which accepts connections exclusively from the vault-bridge using mTLS with pinned certificates.


Security Properties

Zero inbound attack surface on the vault

The vault server's firewall denies all inbound traffic. There is no port to scan, no TLS handshake to probe, no endpoint to DDoS. The vault is invisible from the internet.

Vault controls the connection

The vault-bridge initiates and maintains the connection. If the vault operator wants to disconnect, they stop the bridge. The cluster cannot force a connection to the vault. This gives the vault operator (the EU data controller) full control over when and whether secrets are accessible.

mTLS with certificate pinning

The bridge-to-broker connection uses mutual TLS with pinned certificates. The broker only accepts connections from bridges presenting a known client certificate. The bridge only connects to brokers presenting a known server certificate. Certificate rotation is handled by the CloudTaser operator.

No secret data in the broker

The broker is a transparent proxy. It forwards encrypted vault API requests and responses without inspecting or caching them. The broker never holds secret values. If the broker is compromised, the attacker sees only TLS-encrypted vault API traffic and cannot decrypt it without the wrapper's vault token.

Connection resilience

The bridge maintains a persistent connection with automatic reconnection. If the connection drops (network interruption, broker pod restart), the bridge reconnects within seconds. Wrappers that need secrets during a disconnection retry with exponential backoff until the bridge reconnects.


Comparison: Direct vs Reverse Connect

Property Direct Connect Reverse Connect
Vault network exposure Inbound port 8200 required No inbound ports
Firewall rules Allow inbound from cluster IPs Allow outbound to broker only
Connection initiator Wrapper (in cluster) Bridge (on vault server)
Network topology VPC peering, VPN, or public endpoint Any -- vault just needs outbound HTTPS
Latency Direct TLS connection One additional hop through broker
Vault control Vault accepts connections passively Vault actively controls the connection
Cross-cloud Requires network peering or VPN Works out of the box
Air-gap compatible No (requires runtime connectivity) No (requires runtime connectivity)
Recommended for Same-VPC or peered-VPC deployments Cross-cloud, EU-sovereign vault hosting

When to Use Reverse Connect

Reverse connect is recommended when:

  • The vault is hosted on EU-sovereign infrastructure (Hetzner, OVH, Scaleway) outside the cloud provider's network
  • Security policy prohibits any inbound traffic to the vault server
  • The vault serves clusters across multiple cloud providers (GKE + EKS + AKS)
  • There is no VPC peering or VPN between the vault network and the cluster network

Direct connect is sufficient when:

  • The vault and cluster are in the same VPC or privately peered VPCs
  • A VPN or WireGuard tunnel connects the vault network to the cluster
  • The vault is hosted on the same cloud provider with private service connectivity

Deployment

Broker (in-cluster)

The broker is deployed by the CloudTaser Helm chart when reverse-connect mode is enabled:

helm install cloudtaser oci://europe-west4-docker.pkg.dev/skipopsmain/cloudtaser/cloudtaser \
  --namespace cloudtaser-system \
  --create-namespace \
  --set operator.vaultAddress=broker://cloudtaser-broker.cloudtaser-system.svc:8443 \
  --set broker.enabled=true \
  --set broker.externalHost=broker.prod.example.com

The broker creates:

  • A Kubernetes Service (cloudtaser-broker) on port 8443 for in-cluster wrapper traffic
  • A LoadBalancer or Ingress for the bridge to connect to from outside the cluster
  • TLS certificates for mTLS authentication

Bridge (on vault server)

The bridge runs alongside the vault as a standalone binary or container:

vault-bridge \
  --broker-address broker.prod.example.com:443 \
  --vault-address http://127.0.0.1:8200 \
  --client-cert /etc/bridge/tls.crt \
  --client-key /etc/bridge/tls.key \
  --ca-cert /etc/bridge/ca.crt

Or as a systemd service:

[Unit]
Description=CloudTaser Vault Bridge
After=network-online.target vault.service
Wants=network-online.target

[Service]
ExecStart=/usr/local/bin/vault-bridge \
  --broker-address broker.prod.example.com:443 \
  --vault-address http://127.0.0.1:8200 \
  --client-cert /etc/bridge/tls.crt \
  --client-key /etc/bridge/tls.key \
  --ca-cert /etc/bridge/ca.crt
Restart=always
RestartSec=5
User=vault

[Install]
WantedBy=multi-user.target

Vault firewall

With reverse connect, the vault server's firewall can be maximally restrictive:

# Deny all inbound
iptables -P INPUT DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow outbound to broker only
iptables -P OUTPUT DROP
iptables -A OUTPUT -o lo -j ACCEPT
iptables -A OUTPUT -d <broker-ip> -p tcp --dport 443 -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT  # DNS resolution
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT