Bitcoind Secure Connections


Bitcoind Secure Connections

When you point a wallet or script at bitcoind’s JSON-RPC from anywhere other than the same machine, you’re handling the keys to a vault.
By default, the RPC interface is plain HTTP using Basic authentication—which is just Base64 of user:pass, not encryption.
Anyone on-path can read your credentials, alter your requests, and replay them later. This post explains why you should
never expose RPC directly to the internet and shows four secure ways to wrap it in an encrypted, authenticated channel.

Contents

  1. Why an encrypted tunnel is mandatory
  2. Safe patterns (pick one)
    1. SSH local port-forward
    2. WireGuard (always-on)
    3. TLS reverse proxy with mTLS
    4. Tor onion service
  3. bitcoind RPC hardening (do this regardless)
  4. What not to do
  5. Quick decision guide
  6. Minimal secure setup in 3 commands (SSH)

Why an encrypted tunnel is mandatory

  • Basic auth isn’t encryption. It’s trivially reversible if intercepted.
  • No integrity on the wire. A man-in-the-middle can modify requests/responses (e.g., change address or amount).
  • Replay risk. Static credentials can be reused after capture.
  • High-value target. Attackers constantly scan for exposed :8332.
Bottom line: Keep RPC bound to localhost and reach it only through an encrypted, authenticated tunnel.

Safe patterns (pick one)

1) SSH local port-forward (simple, battle-tested)

On your client machine:

ssh -N -L 8332:127.0.0.1:8332 bitcoin@your-server.example

Then call RPC locally through the encrypted tunnel:

curl --user rpcuser:rpcpassword \
  -H 'content-type: text/plain' \
  --data-binary '{"jsonrpc":"1.0","id":"curl","method":"getblockchaininfo","params":[]}' \
  http://127.0.0.1:8332/

Pros: Zero extra services; strong crypto; easy to audit.
Cons: Requires an interactive SSH channel or a managed tunnel process.

2) WireGuard (always-on private network)

Create a WireGuard interface so your client reaches the node over a private subnet (e.g., 10.8.0.0/24). Keep bitcoind bound to 127.0.0.1 and expose it to WG with a loopback proxy.

Sketch:

  • Server: wg0 on 10.8.0.1
  • Client: wg0 on 10.8.0.2
  • Expose localhost RPC to WG only (server):
socat TCP-LISTEN:8332,bind=10.8.0.1,fork TCP:127.0.0.1:8332

Pros: Always-on, low-latency; great for fleets.
Cons: Slightly more setup; secure key distribution needed.

3) TLS reverse proxy with mutual TLS (mTLS)

Terminate TLS at nginx/haproxy/Caddy and proxy to 127.0.0.1:8332. Require a client certificate so only holders of your CA-issued certs can connect.

server {
  listen 8332 ssl;
  ssl_certificate     /etc/ssl/certs/server.crt;
  ssl_certificate_key /etc/ssl/private/server.key;

  # mTLS
  ssl_client_certificate /etc/ssl/certs/ca.crt;
  ssl_verify_client on;

  # Hardening (sample)
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_ciphers HIGH:!aNULL:!MD5;

  location / {
    proxy_pass http://127.0.0.1:8332;
    proxy_set_header Host $host;
    proxy_http_version 1.1;
  }
}

Client call:

curl --cert client.crt --key client.key --cacert ca.crt \
  --user rpcuser:rpcpassword \
  -H 'content-type: text/plain' \
  --data-binary '{"jsonrpc":"1.0","id":"curl","method":"getwalletinfo","params":[]}' \
  https://your-server.example:8332/

Pros: First-class TLS, revocable client creds, works through corporate egress.
Cons: You must operate a small PKI (CA, issuance, rotation).

4) Tor onion service (encrypted + optional endpoint auth)

Expose RPC as a Tor onion service and (optionally) require client authorization. Keep RPC on localhost; Tor provides encryption and reachability.

Pros: No public IP; encryption built-in; good for remote admin.
Cons: Higher latency; extra operational surface if new to Tor.

bitcoind RPC hardening (do this regardless)

In bitcoin.conf:

# 1) Never bind RPC to the public interface
rpcbind=127.0.0.1
rpcallowip=127.0.0.1

# 2) Use rpcauth (salted verifier) instead of plain rpcuser/rpcpassword
# Generate with contrib/rpcauth tool
rpcauth=alice:1c2f...$7b3c...

# 3) Least-privilege: turn off wallet RPC if not needed
disablewallet=1

# 4) Optional: keep logs minimal on production nodes
# debug=0
  • rpcauth protects the stored secret, but the wire still carries Basic auth → you still need encryption.
  • For same-host automation, prefer the random per-run .cookie file.
  • Host firewall: allow only SSH/WireGuard/TLS from trusted sources; drop everything else.

What not to do

  • Don’t expose :8332 to the public internet.
  • Don’t treat rpcallowip as “security”—it’s access control, not encryption.
  • Don’t reuse weak or shared credentials; rotate on any suspicion of exposure.
  • Don’t skip cert revocation when staff or machines churn (for mTLS).

Quick decision guide

  • Single admin, occasional access: SSH port-forward.
  • Team / multiple devices, always-on: WireGuard.
  • Enterprise with cert lifecycle: TLS + mTLS.
  • Privacy-sensitive remote ops: Tor onion service.

Minimal, secure setup in 3 commands (SSH method)

  1. Ensure RPC is local-only:
    # bitcoin.conf
    rpcbind=127.0.0.1
    rpcallowip=127.0.0.1
    rpcauth=alice:...
  2. Open a tunnel from your laptop:
    ssh -N -L 8332:127.0.0.1:8332 bitcoin@your-server.example
  3. Use RPC safely over the tunnel:
    curl --user alice:yourpassword \
      -H 'content-type: text/plain' \
      --data-binary '{"jsonrpc":"1.0","id":"curl","method":"getblockchaininfo","params":[]}' \
      http://127.0.0.1:8332/

You now have confidentiality, integrity, and strong endpoint authentication—exactly what the default RPC transport lacks.



Anuj holds professional certifications in Google Cloud, AWS as well as certifications in Docker and App Performance Tools such as New Relic. He specializes in Cloud Security, Data Encryption and Container Technologies.

Initial Consultation

Anuj Varma – who has written posts on Anuj Varma, Hands-On Technology Architect, Clean Air Activist.