-
@ Girino Vey!
2025-06-15 03:38:59Securely Expose Your LND Node via Cloudflared and a VPS
Introduction & Motivation
Exposing your Lightning Network Daemon (LND) node to the public internet is essential for full participation in the Lightning Network. However, doing so introduces critical challenges in terms of security, privacy, and resilience against attacks.
This guide presents a solution using Cloudflare Tunnel and Cloudflare Access—a modern, secure method to expose services to the internet while maintaining strict control and minimizing exposure. We also compare this approach with more traditional methods such as SSH tunnels and VPNs (WireGuard/OpenVPN + socat/iptables) to highlight the trade-offs.
Traditional Exposure Methods: Risks and Limitations
SSH Reverse Tunnels (
ssh -R
,sshtunnel
)- ✅ Outbound-only: The connection is initiated from the LND host. The VPS cannot reach the internal network on its own.
- ⚠️ Metadata exposure: The LND host's IP address, SSH key, and connection metadata are visible to the VPS.
- ⚠️ Access control is coarse: Without extra measures, any process on the VPS can access the forwarded service.
- ⚠️ No service-layer authentication: Anyone reaching the exposed port can interact with LND, assuming the protocol is known.
VPNs (WireGuard/OpenVPN + iptables or socat)
- ⚠️ Blurs the security boundary: VPNs extend the network perimeter to remote clients. Misconfigured rules can inadvertently expose internal services.
- ❌ Allows lateral movement: If the VPS is compromised, it may gain full access to the LND host’s network.
- ✅ Traffic is encrypted, but requires correct firewalling and routing to be safe.
- ⚠️ Complexity: These setups often involve manual iptables, port forwarding, and tight coupling of services.
Cloudflare Tunnel + Access (This Guide)
This solution uses Cloudflared on the LND host to initiate an outbound connection to Cloudflare, which then routes traffic through a secured, authenticated TCP tunnel via a VPS.
- ✅ No exposed ports on the LND host.
- ✅ VPS cannot discover or access the LND IP—only forwards TCP packets to Cloudflare.
- ✅ Service Token authentication ensures only authorized clients can connect.
- ⚠️ Trust in Cloudflare is required. While your LND host is protected from the internet, Cloudflare can see metadata, and potentially, inspect unencrypted traffic.
- ❌ Cloudflare is proprietary infrastructure. You’re protected from attackers, but not necessarily from Cloudflare or governments.
Security & Privacy Comparison
| Feature | SSH Tunnel | VPN + socat/iptables | Cloudflare Tunnel + Access | | --------------------------------- | ------------------ | -------------------- | -------------------------- | | Encrypts traffic | ✅ | ✅ | ✅ | | Initiated from LND host | ✅ | ❌ | ✅ | | Prevents VPS from accessing LAN | ✅ | ❌ | ✅ | | Hides LND IP from VPS | ❌ | ❌ | ✅ | | Hides LND IP from tunnel provider | ❌ | ✅ | ❌ | | Proprietary infrastructure | ❌ | ❌ | ⚠️ (Cloudflare) | | Fine-grained access control | ⚠️ (manual) | ⚠️ (manual) | ✅ (Service Token) | | Susceptible to VPS compromise | ⚠️ (metadata only) | ❌ (full access) | ✅ (isolated forwarding) |
Tutorial: Step-by-Step Setup
1. Set Up the Cloudflare Tunnel
- In your Cloudflare Zero Trust dashboard, navigate to Access → Tunnels.
- Create a new tunnel (e.g.,
lnd-tunnel
). - Choose Use Token Authentication and copy the token.
-
Add a Public Hostname:
-
Hostname:
lnd-external-access.mydomain.com
- Service Type: TCP
- URL:
tcp://lnd:9735
(This is the internal Docker hostname and port for LND.)
2. Run Cloudflared on the LND Host
Assuming your LND node runs in Docker inside the
bitcoin-net
network:.env
env TOKEN=your-cloudflare-tunnel-token
docker-compose.yml
```yaml version: "3.8"
services: cloudflared: image: cloudflare/cloudflared:latest command: tunnel --no-autoupdate run --token ${TOKEN} restart: unless-stopped env_file: - .env networks: - bitcoin-net
networks: bitcoin-net: external: true name: bitcoin-net ```
3. Create the Cloudflare Service Token
- Go to Access → Service Auth.
- Click Create Service Token.
- Name it (e.g.,
lnd-access-client
) and store both the Token ID and Token Secret—you’ll need these on the VPS and to configure access.
4. Define the Cloudflare Access Application
- Navigate to Access → Applications → Add an Application.
- Choose Self-hosted.
-
Set:
-
Application Name:
lnd-external-access
- Domain:
lnd-external-access.mydomain.com
-
In Policies:
-
Add a policy:
- Name:
Allow via Service Token
- Action:
Service Auth
- Include → Service Token: select the token you just created
- Name:
5. Deploy the Forwarding VPS
a. Configure firewall:
bash sudo ufw allow ssh sudo ufw allow 9735/tcp sudo ufw enable
b. Set environment variables:
.env
env TOKEN_ID=your-service-token-id TOKEN_SECRET=your-service-token-secret HOSTNAME=lnd-external-access.mydomain.com LISTEN=tcp://0.0.0.0:9735
c. Run Cloudflared:
docker-compose.yml
```yaml version: "3.8"
services: cloudflared: image: cloudflare/cloudflared:latest ports: - 9735:9735 command: > access tcp --service-token-id ${TOKEN_ID} --service-token-secret ${TOKEN_SECRET} --hostname ${HOSTNAME} --url ${LISTEN} --loglevel debug env_file: - .env restart: unless-stopped ```
6. Configure DNS
In Cloudflare DNS:
-
Create an A record:
-
Name:
lnd
- Type: A
- Value:
VPS_IP
- Proxy status: DNS only
7. Configure LND
Edit
lnd.conf
to include:ini externalip=lnd.mydomain.com
Restart LND to apply changes.
8. Test the Connection
From another LND node or client:
bash lncli connect <pubkey>@lnd.mydomain.com:9735
Check logs in LND and Cloudflared to confirm successful connection.
Final Notes
- This method preserves your network perimeter, even if the VPS is compromised.
- All traffic is authenticated with service tokens, and no direct access is possible to your LND host.
- You do trust Cloudflare, so weigh that risk in high-privacy environments.