Security hardening¶
Minimum production posture for a node that touches real funds.
RPC authentication¶
Never ship rpcuser=user, rpcpassword=password. Two good options:
Random password¶
Generate and rotate:
rpcauth hash¶
Store the hashed credential in navio.conf instead of the plaintext password. Generate with the helper script:
# from the navio-core source tree
./share/rpcauth/rpcauth.py alice
# outputs:
# String to be appended to navio.conf:
# rpcauth=alice:abc123...$def456...
# Your password: 3SRr...
Keep the plaintext password in your client's config only. The daemon never has it on disk.
Bind RPC to loopback only¶
If remote RPC access is genuinely needed, front naviod with an authenticated reverse proxy (nginx + TLS + client cert, WireGuard, or SSH tunnel). Do not expose port 33677 to the public internet.
Firewall¶
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp comment 'SSH'
sudo ufw allow 33670/tcp comment 'Navio P2P'
# DO NOT open 33677 unless you have a very specific reason
sudo ufw enable
Run as non-root user¶
Create a dedicated navio user to run the daemon — never root:
Run your supervisor with User=navio / Group=navio equivalents.
Process isolation¶
Navio docs do not prescribe systemd. The same hardening intent translates across supervisors — apply whichever of the following your chosen init supports:
| Hardening intent | How to achieve it |
|---|---|
| Drop privileges | runit chpst -u navio; OpenRC command_user="navio"; Docker USER navio; setpriv in wrapper scripts |
| Read-only rootfs | Docker read_only: true; bubblewrap --ro-bind /; nsjail |
/tmp isolation |
Docker tmpfs: /tmp; bubblewrap --tmpfs /tmp; custom namespace |
Block /home access |
bubblewrap --bind $DATADIR /home/navio/.navio --ro-bind / /; Docker volume-only mount |
| Prevent new privileges | setpriv --no-new-privs in wrapper; Docker security_opt: [no-new-privileges:true] |
| Restrict syscalls | seccomp-bpf profile via prctl; Docker --security-opt seccomp=profile.json |
| Restrict network | Firewall rules (see below); network namespace; Docker network isolation |
| File-descriptor limits | ulimit -n 65536; Docker ulimits:; wrapper script |
| Mount noexec volumes | mount -o noexec; Docker volume driver opts |
Container-based deployments get most of this from Docker / Podman defaults. For bare-metal deployments that want a systemd-free equivalent, bubblewrap + runit is a solid combination.
Tor (.onion) connectivity¶
Run Tor on the same host, configure the hidden service for port 33670, copy the resulting onion address into externalip=. See Bitcoin Core Tor guide — the pattern is identical.
I2P / CJDNS¶
- I2P:
i2psam=127.0.0.1:7656(requires a local I2P router). - CJDNS:
cjdnsreachable=1once CJDNS is active on the host.
Disk / data protection¶
- Full-disk encryption on the VPS. On Linux: LUKS2.
- Enable wallet encryption (
encryptwallet <password>or via SDKkeyManager.setPassword). - Periodic encrypted wallet backups (see Backup & restore). Store the encrypted blob off-host.
OS baseline¶
- Automatic security updates:
sudo apt-get install unattended-upgrades && sudo dpkg-reconfigure unattended-upgrades. - SSH: key-auth only,
PermitRootLogin no,PasswordAuthentication no. - Fail2ban on SSH and, optionally, on the RPC port if you must expose it.
- Time sync:
chrony,openntpd, orntpd— whatever matches your distro. Clock drift causes handshake failures with peers.
Separation of duties¶
Larger deployments separate:
- Cold signer — holds the spending key, permanently offline or in HSM.
- Hot staker — sees outputs through an audit key or restricted wallet, produces coinstake transactions.
- Ingest / exchange node — audit-only, indexes deposits, triggers deposit crediting workflow.
The SDK's audit-key restore supports this pattern out of the box. See Build a watch-only audit wallet and Exchange integration.
Incident response¶
If you suspect compromise:
- Stop
naviodandnavio-staker. - Move funds to a fresh wallet on a known-good device — from the offline mnemonic, not the possibly-compromised wallet file.
- Reinstall the host OS. Never try to "clean up" after a root compromise.
- Restore the wallet on the new host from mnemonic.