← ~/blogs/

Outbound Service Tunneling - An Architectural Pattern for Remote Access

2025-11-15

Outbound Service Tunneling

This post describes a remote access architecture using outbound-initiated, TLS-wrapped tunnels. The pattern is useful for home labs behind CGNAT, development workstations, and distributed infrastructure where opening inbound ports isn't possible or desirable.

What This Is

Machines establish outbound connections to external endpoints, then expose internal services back through those connections.

Key properties:
- Outbound initiation only; no inbound ports required
- TLS wraps inner protocols (SSH, database connections)
- Services become accessible via external endpoint

Common scenarios: Home labs behind CGNAT, development workstations on corporate networks, distributed infrastructure.

The Problem

You have services running on a machine that can't accept inbound connections:
- Behind carrier-grade NAT (no public IP)
- Corporate firewall blocking inbound
- Network policy restricting port exposure

Traditional solutions like VPNs or port forwarding don't work in these environments.

How It Works

The architecture uses layered protocols:

Internal Network                          External Endpoint (VPS)
┌─────────────────┐                       ┌─────────────────┐
│ Internal        │                       │ Exposed         │
│ Services        │                       │ Endpoints       │
│ (DBs, APIs)     │                       │ (now reachable) │
└────────┬────────┘                       └────────▲────────┘
         │                                         │
┌────────▼────────┐                       ┌────────┴────────┐
│ SSH Client      │◄─────Reverse──────────│ SSH Daemon      │
│ Reverse Tunnels │      Tunnels          │ Tunnel Endpoint │
└────────┬────────┘                       └────────▲────────┘
         │                                         │
┌────────▼────────┐                       ┌────────┴────────┐
│ TLS Wrapper     │                       │ TLS Termination │
│ (stunnel)       │                       │ (stunnel)       │
└────────┬────────┘                       └────────▲────────┘
         │                                         │
         └──────────TLS on 443─────────────────────┘
                (looks like HTTPS)

The Mechanism

  1. Internal machine opens outbound TLS connection (port 443)
  2. TLS handshake with optional mutual certificate authentication
  3. SSH session authenticates inside TLS wrapper
  4. SSH reverse tunnels bind external ports to internal services

Services on the internal network become reachable via the external endpoint. The outbound TLS connection looks indistinguishable from normal HTTPS traffic.

Why TLS Wrapping?

Many corporate networks only allow outbound traffic on specific ports (80, 443). By wrapping SSH in TLS on port 443:

This isn't about evading security—it's about working within network constraints while maintaining proper authentication and audit trails.

Security Layers

The architecture provides multiple independent security layers:

Layer Function Scope
TLS encryption Protects data in transit Transport
Mutual TLS (optional) Verifies both endpoints have valid certificates Endpoint authentication
SSH key auth Requires possession of private key User authentication
Explicit tunnels Only specified services are exposed Service exposure

Trust Model

Trust is enforced at the application layer rather than network perimeter:

What's Visible Where

Network layer (corporate firewall):
- Standard TLS on port 443
- Connection metadata (destination IP, SNI hostname)
- Cannot see payload content

Tunnel endpoint (your VPS):
- Full visibility into exposed services
- Authentication logs for tunnel establishment
- Access logs for tunneled services

Implementation with stunnel

On the internal machine (client):

# /etc/stunnel/stunnel.conf
[ssh-out]
client = yes
accept = 127.0.0.1:2222
connect = vps.example.com:443

On the VPS (server):

# /etc/stunnel/stunnel.conf
[ssh-in]
accept = 443
connect = 127.0.0.1:22
cert = /etc/stunnel/server.pem

Then establish the SSH reverse tunnel:

# From internal machine
ssh -p 2222 -R 5432:localhost:5432 user@127.0.0.1

This exposes the internal PostgreSQL (port 5432) on the VPS.

Making It Robust

For production use:

# autossh for automatic reconnection
autossh -M 0 \
    -o "ServerAliveInterval 30" \
    -o "ServerAliveCountMax 3" \
    -p 2222 \
    -R 5432:localhost:5432 \
    -N user@127.0.0.1

Run as a systemd service for persistence across reboots.

Comparison with Alternatives

Approach Trust Model Network Visibility
Traditional VPN Network-level access VPN protocol traffic
Outbound TLS tunneling Service-level access Standard HTTPS
Zero-trust proxies (Cloudflare, Tailscale) Identity-based access Provider infrastructure
Direct port exposure IP-based access Inbound connections

The outbound tunneling approach gives you service-level access control without depending on third-party infrastructure.

Detection and Monitoring

This architecture produces observable patterns:

Traffic characteristics:
- Persistent connections with consistent duration (hours to days)
- Predictable traffic patterns distinct from interactive browsing
- Connections to infrastructure hosting providers
- Regular connection re-establishment schedules

Analysis opportunities:
- Statistical modeling of connection behavior
- Anomaly detection based on duration baselines
- Infrastructure correlation via DNS and certificate data

Network security teams can detect these patterns. The goal isn't stealth—it's working within policy constraints while maintaining proper security controls.

Use Cases

Home Lab

Development Workstations

Distributed Infrastructure

Summary

Outbound TLS tunneling is useful when you need external service access without inbound connections. The pattern provides:

For the Docker networking complications that can arise when combining this pattern with containerized services, see my post on Docker bridge network isolation.