#

lnd

(30 articles)

Securely Expose Your LND Node via Cloudflared and a VPS

# **Securely 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** 1. In your Cloudflare Zero Trust dashboard, navigate to **Access → Tunnels**. 2. Create a new tunnel (e.g., `lnd-tunnel`). 3. Choose **Use Token Authentication** and copy the token. 4. 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** 1. Go to **Access → Service Auth**. 2. Click **Create Service Token**. 3. 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** 1. Navigate to **Access → Applications → Add an Application**. 2. Choose **Self-hosted**. 3. Set: * **Application Name**: `lnd-external-access` * **Domain**: `lnd-external-access.mydomain.com` 4. In **Policies**: * Add a policy: * **Name**: `Allow via Service Token` * **Action**: `Service Auth` * **Include → Service Token**: select the token you just created --- ### **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.

BTCpayApi Update 03-28-2025

#btcpayserver #lightning #lnd #powershell <p style="text-align: center; font-size:160%;">BTCpayAPI now supports file upload</p> <p><img style="display: block; margin-left: auto; margin-right: auto; border-width: 10px; border-style: hidden;" src="https://cadayton.onrender.com/images/BTCpayApi800.png" alt="" width="800" height="300" /></p> I'm continuing to add functionality to BTCpay and BTCpayAPI which is using REST Api(s) to manage my BTCPAY server and LND cloud instance. It is nice to have this just running locally on my home Linux desktop. Here is the code that implements this functionality. ``` "Uploadfile" { $apislug = "api/v1/files" #$filepath = Split-Path $options $filename = Split-Path $options -Leaf # CONST $CODEPAGE = "iso-8859-1" # alternatives are ASCII, UTF-8 # Read file byte-by-byte $fileBin = [System.IO.File]::ReadAllBytes($options) # Convert byte-array to string $enc = [System.Text.Encoding]::GetEncoding($CODEPAGE) $fileEnc = $enc.GetString($fileBin) # We need a boundary (something random() will do best) $boundary = [System.Guid]::NewGuid().ToString() $LF = "`r`n" $bodyLines = ( "--$boundary", "Content-Disposition: form-data; name=`"file`"; filename=`"$fileName`"", "Content-Type: application/octet-stream$LF", $fileEnc, "--$boundary--$LF" ) -join $LF $URI = $BTCPayCfg.BTCpayApi.GreenApi.url + $apislug $apiKeyToken = 'token ' + $script:BTCPAY_API $headers = @{'Authorization'=$apiKeyToken} return Invoke-RestMethod -Uri $uri -Method Post -Headers $headers -ContentType "multipart/form-data; boundary=`"$boundary`"" -Body $bodyLines } ``` Here is the revision history since my last article. ``` Version Date Whom Notes ======= ========== ======== ===================================================== 0.1.4 03/28/2025 cadayton Added UploadFile method to upload a file to the BTCpay server 0.1.3 03/27/2025 cadayton Added GetFiles returns listing of files uploaded to BTCpay server 0.1.2 03/19/2025 cadayton ForwardingHistory new parameter "total_fees" tallys mfees for events returned 0.1.1 03/18/2025 cadayton ForwardingHistory now support additional parameters 0.1.0 03/15/2025 cadayton initial release. ``` The inspiration for this logic was adapted from [weipah](https://gist.github.com/weipah/19bfdb14aab253e3f109) . One wouldn't think uploading a single file wouldn't require this much coding. The Greenfield's REST Api documentation for this end point wasn't very helpful. In my book, good end-user documentation of one's code is just as important as the code itself. I believe documentation is usually lacking on most projects because the effort of doing so is almost equal to the effort of writing the code. It is also the least fun part about writing code. The job is not done until the paper work has been completed. :) https://btcpayserver.sytes.net