Compare commits
No commits in common. "main" and "v0.0.24" have entirely different histories.
2 changed files with 0 additions and 359 deletions
|
|
@ -61,7 +61,6 @@ top left corner of the documents.
|
|||
* [Configuration and maintenance](docs/configuration.md)
|
||||
* [Using services](docs/services.md)
|
||||
* [FAQ](docs/faq.md)
|
||||
* [Security model](docs/security-model.md)
|
||||
|
||||
Features
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,358 +0,0 @@
|
|||
# Security Model
|
||||
|
||||
This document explains how nix-bitcoin protects your node and funds. It is intended
|
||||
for operators who may not be familiar with NixOS security primitives.
|
||||
|
||||
For vulnerability reporting and the security fund, see [SECURITY.md](../SECURITY.md).
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Secrets Management](#secrets-management)
|
||||
- [What is stored](#what-is-stored)
|
||||
- [When secrets are generated](#when-secrets-are-generated)
|
||||
- [File permissions](#file-permissions)
|
||||
- [What is NOT in the secrets directory](#what-is-not-in-the-secrets-directory)
|
||||
- [Wallet Key Material](#wallet-key-material)
|
||||
- [LND](#lnd)
|
||||
- [bitcoind](#bitcoind)
|
||||
- [Service Isolation](#service-isolation)
|
||||
- [Unix users and groups](#unix-users-and-groups)
|
||||
- [systemd hardening](#systemd-hardening)
|
||||
- [Network namespace isolation](#network-namespace-isolation)
|
||||
- [RPC whitelisting](#rpc-whitelisting)
|
||||
- [Operator user](#operator-user)
|
||||
- [Network Security](#network-security)
|
||||
- [RPC binding](#rpc-binding)
|
||||
- [Tor](#tor)
|
||||
- [WireGuard](#wireguard)
|
||||
- [Firewall and D-Bus](#firewall-and-d-bus)
|
||||
- [Hardening Presets](#hardening-presets)
|
||||
- [Threat Model Summary](#threat-model-summary)
|
||||
- [Operator Responsibilities](#operator-responsibilities)
|
||||
- [Further Reading](#further-reading)
|
||||
|
||||
---
|
||||
|
||||
## Secrets Management
|
||||
|
||||
nix-bitcoin has its own secrets system for managing service credentials. Secrets
|
||||
are files stored in a dedicated directory on the node, protected by unix
|
||||
permissions and never written to the
|
||||
[Nix store](https://nixos.org/manual/nix/stable/store/) (which is world-readable).
|
||||
|
||||
The secrets directory location depends on the deployment method:
|
||||
- **Krops / NixOps:** `/var/src/secrets`
|
||||
- **Flakes / Containers:** `/etc/nix-bitcoin-secrets`
|
||||
|
||||
### What is stored
|
||||
|
||||
Each enabled service registers the secret files it needs. The secrets fall into
|
||||
these categories:
|
||||
|
||||
**Passwords** (random 20-character strings):
|
||||
- `bitcoin-rpcpassword-privileged`, `bitcoin-rpcpassword-public` — bitcoind RPC authentication
|
||||
- `lnd-wallet-password` — encrypts LND's `wallet.db` on disk
|
||||
- `rtl-password`, `joinmarket-password`, `btcpayserver-password`, `lightning-loop-password` — web UI and service passwords
|
||||
- `backup-encryption-env` — backup encryption passphrase
|
||||
|
||||
**Derived credentials** (computed from passwords):
|
||||
- `bitcoin-HMAC-privileged`, `bitcoin-HMAC-public` — HMAC hashes used in bitcoind's `rpcauth=` config
|
||||
- Same pattern for Liquid (`liquid-rpcpassword-*`, `liquid-HMAC-*`)
|
||||
|
||||
**TLS keys and certificates:**
|
||||
- `lnd-key` — EC private key (prime256v1) for LND's gRPC/REST API
|
||||
- `lnd-cert` — self-signed x509 certificate (10-year validity)
|
||||
|
||||
**Other keys:**
|
||||
- `clightning-replication-ssh-key` — for database replication
|
||||
- WireGuard server/peer private and public keys (when the WireGuard preset is enabled)
|
||||
|
||||
### When secrets are generated
|
||||
|
||||
Secrets generation depends on the deployment method:
|
||||
|
||||
| Method | Where generated | Mechanism |
|
||||
|--------|----------------|-----------|
|
||||
| **Krops** | Locally (your machine) | `generate-secrets` shell command runs before rsync to target |
|
||||
| **NixOps** | Locally (your machine) | `generate-secrets` shell command, transferred via NixOps keys |
|
||||
| **Flakes** | On the target node | `setup-secrets.service` at boot with `nix-bitcoin.generateSecrets = true` |
|
||||
| **Containers** | Inside the container | Same as Flakes |
|
||||
| **Manual** | You create them yourself | Set `nix-bitcoin.secretsSetupMethod = "manual"` |
|
||||
|
||||
For Krops and NixOps, secrets are generated once locally and then synced to the
|
||||
target. They are idempotent: existing files are never overwritten. If you delete
|
||||
a secret file and redeploy, it will be regenerated.
|
||||
|
||||
### File permissions
|
||||
|
||||
The `setup-secrets` systemd service enforces permissions on every boot or deploy:
|
||||
|
||||
1. The secrets directory is set to `root:root 0700` during setup
|
||||
2. Each secret file is assigned ownership and permissions as declared by its
|
||||
module (e.g. `lnd-wallet-password` is owned by `lnd:lnd 0440`)
|
||||
3. Any file in the directory not claimed by a module is locked to `root:root 0440`
|
||||
4. The directory is opened to `0751` after setup completes, allowing services
|
||||
to access their specific files
|
||||
|
||||
All nix-bitcoin services declare a systemd dependency on `nix-bitcoin-secrets.target`,
|
||||
which is only reached after `setup-secrets` completes successfully.
|
||||
|
||||
### What is NOT in the secrets directory
|
||||
|
||||
The secrets system manages **service-to-service credentials**. The following are
|
||||
explicitly outside its scope:
|
||||
|
||||
- **LND seed mnemonic** (`/var/lib/lnd/lnd-seed-mnemonic`) — see [Wallet Key Material](#wallet-key-material)
|
||||
- **LND wallet database** (`/var/lib/lnd/chain/bitcoin/<network>/wallet.db`)
|
||||
- **LND macaroons** (`/var/lib/lnd/chain/bitcoin/<network>/*.macaroon`)
|
||||
- **LND channel backup** (`/var/lib/lnd/chain/bitcoin/<network>/channel.backup`)
|
||||
- **bitcoind wallet** (`wallet.dat` or descriptors, if created by the operator)
|
||||
|
||||
---
|
||||
|
||||
## Wallet Key Material
|
||||
|
||||
### LND
|
||||
|
||||
LND uses the [aezeed cipher seed](https://github.com/lightningnetwork/lnd/tree/master/aezeed)
|
||||
scheme — a 24-word mnemonic that encodes the wallet's master entropy and a
|
||||
birthday timestamp.
|
||||
|
||||
**How the wallet is created on first boot:**
|
||||
|
||||
The LND module's `preStart` script checks if `wallet.db` exists. If not:
|
||||
|
||||
1. `lndinit gen-seed` generates a fresh 24-word aezeed mnemonic and writes it
|
||||
to `/var/lib/lnd/lnd-seed-mnemonic`
|
||||
2. `lndinit init-wallet` creates `wallet.db` using the seed and the
|
||||
`lnd-wallet-password` from the secrets directory
|
||||
3. LND starts and auto-unlocks using the wallet password file
|
||||
|
||||
**Important details:**
|
||||
|
||||
- **No cipher seed passphrase:** nix-bitcoin does not set an aezeed passphrase.
|
||||
The mnemonic is encrypted with the default passphrase `"aezeed"`, which offers
|
||||
no real protection. Anyone with the 24 words can derive all keys.
|
||||
- **Auto-unlock tradeoff:** The wallet password is stored on disk in the secrets
|
||||
directory so LND can start unattended. This means root access to the node
|
||||
grants access to the wallet.
|
||||
- **The seed file is only used once:** After `wallet.db` is created, LND never
|
||||
reads the seed file again. It can and should be deleted from disk after backup.
|
||||
- **Static Channel Backups (SCB):** LND maintains `channel.backup` which is
|
||||
updated atomically every time a channel is opened or closed. It is encrypted
|
||||
with a key derived from the seed. If you re-seed, old SCBs become invalid.
|
||||
|
||||
### bitcoind
|
||||
|
||||
nix-bitcoin does **not** create or manage a bitcoind wallet. The `bitcoind.nix`
|
||||
module configures the daemon and RPC authentication only. If you create a wallet
|
||||
via `bitcoin-cli createwallet`, its key material is entirely your responsibility
|
||||
to manage and back up.
|
||||
|
||||
---
|
||||
|
||||
## Service Isolation
|
||||
|
||||
### Unix users and groups
|
||||
|
||||
Each nix-bitcoin service runs as its own dedicated system user (e.g. `bitcoind`,
|
||||
`lnd`, `clightning`). Services cannot read each other's data directories unless
|
||||
explicitly granted access through group memberships.
|
||||
|
||||
### systemd hardening
|
||||
|
||||
All nix-bitcoin services apply a strict systemd security profile
|
||||
([`pkgs/lib.nix`](../pkgs/lib.nix)), which includes:
|
||||
|
||||
| Setting | Effect |
|
||||
|---------|--------|
|
||||
| `ProtectSystem = "strict"` | Filesystem is read-only except for explicitly allowed paths |
|
||||
| `ProtectHome = true` | No access to home directories |
|
||||
| `PrivateTmp = true` | Isolated `/tmp` per service |
|
||||
| `PrivateDevices = true` | No access to physical devices |
|
||||
| `NoNewPrivileges = true` | Cannot gain new privileges via setuid/setgid |
|
||||
| `MemoryDenyWriteExecute = true` | Prevents writable+executable memory (JIT disabled) |
|
||||
| `ProtectKernelTunables = true` | Cannot modify kernel parameters |
|
||||
| `ProtectKernelModules = true` | Cannot load kernel modules |
|
||||
| `ProtectProc = "invisible"` | Other processes hidden in `/proc` |
|
||||
| `PrivateUsers = true` | Cannot see other users |
|
||||
| `IPAddressDeny = "any"` | All network denied by default (services opt in to what they need) |
|
||||
| `CapabilityBoundingSet = ""` | No Linux capabilities |
|
||||
| `SystemCallFilter` | Restricted to `@system-service` syscall set |
|
||||
| `RestrictAddressFamilies` | Only `AF_UNIX`, `AF_INET`, `AF_INET6` |
|
||||
|
||||
For the full list, see `man systemd.exec` and `man systemd.resource-control`.
|
||||
|
||||
### Network namespace isolation
|
||||
|
||||
The optional [`netns-isolation`](../modules/netns-isolation.nix) module places
|
||||
each service in its own
|
||||
[network namespace](https://man7.org/linux/man-pages/man7/network_namespaces.7.html).
|
||||
Services can only communicate with other services through explicitly allowed
|
||||
paths. For example, LND can reach bitcoind's RPC, but RTL cannot directly reach
|
||||
bitcoind.
|
||||
|
||||
Enable with:
|
||||
```nix
|
||||
nix-bitcoin.netns-isolation.enable = true;
|
||||
```
|
||||
|
||||
Note: This is not compatible with the WireGuard preset.
|
||||
|
||||
### RPC whitelisting
|
||||
|
||||
The [`bitcoind-rpc-public-whitelist`](../modules/bitcoind-rpc-public-whitelist.nix)
|
||||
module restricts which RPC methods the `public` RPC user can call. Services that
|
||||
only need read access (like electrs or mempool) use the `public` user, which
|
||||
cannot call wallet or administrative RPCs. Only the `privileged` user has full
|
||||
RPC access.
|
||||
|
||||
### Operator user
|
||||
|
||||
The [`operator`](../modules/operator.nix) module creates a non-root user with
|
||||
group memberships for each enabled service. This lets you run `bitcoin-cli`,
|
||||
`lncli`, `lightning-cli`, etc. without being root. The operator user has read
|
||||
access to service data but does not have write access or the ability to modify
|
||||
service configuration.
|
||||
|
||||
---
|
||||
|
||||
## Network Security
|
||||
|
||||
### RPC binding
|
||||
|
||||
bitcoind and LND bind their RPC/API interfaces to `127.0.0.1` by default. They
|
||||
are not reachable from outside the machine unless you explicitly change the bind
|
||||
address.
|
||||
|
||||
### Tor
|
||||
|
||||
The [`secure-node`](../modules/presets/secure-node.nix) preset imports
|
||||
[`enable-tor`](../modules/presets/enable-tor.nix), which routes all outbound
|
||||
traffic from nix-bitcoin services through Tor. Services that support it can also
|
||||
accept inbound connections via
|
||||
[onion services](https://community.torproject.org/onion-services/overview/).
|
||||
|
||||
When Tor enforcement is active (`tor.enforce = true`), a service's systemd
|
||||
`IPAddressAllow` is restricted to localhost and link-local addresses only,
|
||||
preventing any clearnet communication.
|
||||
|
||||
### WireGuard
|
||||
|
||||
The [`wireguard`](../modules/presets/wireguard.nix) preset creates an encrypted
|
||||
VPN tunnel for connecting a mobile wallet (e.g. Zeus) to your node. It sets up a
|
||||
single-peer WireGuard interface with:
|
||||
|
||||
- The node as server (`10.10.0.1`)
|
||||
- Your device as peer (`10.10.0.2`)
|
||||
- Firewall rules restricting the peer to only reach the node's address (no
|
||||
routing to the broader network)
|
||||
- Helper commands (`nix-bitcoin-wg-connect`, `lndconnect-wg`) that generate QR
|
||||
codes for one-scan setup
|
||||
|
||||
This is an alternative to connecting over Tor, offering lower latency at the
|
||||
cost of requiring a reachable IP and port forwarding for NAT.
|
||||
|
||||
### Firewall and D-Bus
|
||||
|
||||
The `secure-node` preset enables the NixOS firewall and the
|
||||
[`security`](../modules/security.nix) module's D-Bus process information hiding.
|
||||
The D-Bus restriction prevents unprivileged services from discovering other
|
||||
services' process information (command lines, cgroup paths) via systemd's D-Bus
|
||||
interface.
|
||||
|
||||
---
|
||||
|
||||
## Hardening Presets
|
||||
|
||||
nix-bitcoin provides three presets that can be combined:
|
||||
|
||||
**[`secure-node.nix`](../modules/presets/secure-node.nix)** — Opinionated base
|
||||
configuration:
|
||||
- Enables firewall
|
||||
- Routes all traffic through Tor
|
||||
- Replaces `sudo` with `doas`
|
||||
- Enables D-Bus process information hiding
|
||||
- Creates an SSH onion service
|
||||
- Enables the operator user
|
||||
- Sets up daily backups
|
||||
- Enables `nodeinfo` command
|
||||
|
||||
**[`hardened.nix`](../modules/presets/hardened.nix)** — Imports the
|
||||
[NixOS hardened kernel profile](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/hardened.nix).
|
||||
This enables kernel-level hardening (address space layout randomization, kernel
|
||||
module restrictions, etc.) at a ~50% performance cost. Resets `allowUserNamespaces`
|
||||
to `true` (needed for Nix sandboxing) and uses the standard `libc` allocator.
|
||||
|
||||
**[`hardened-extended.nix`](../modules/presets/hardened-extended.nix)** — Builds
|
||||
on `hardened.nix` with additional restrictions:
|
||||
- Disables kernel log leaks, SysRq, debugfs
|
||||
- Restricts ptrace, TTY line disciplines, core dumps
|
||||
- Disables TCP SACK and timestamps
|
||||
- Blacklists obscure network protocols, rare filesystems, Bluetooth, webcam,
|
||||
Thunderbolt/FireWire (DMA attack prevention)
|
||||
- Enables USBGuard
|
||||
- Enforces signed kernel modules and kernel lockdown
|
||||
|
||||
See [madaidan's Linux Hardening Guide](https://madaidans-insecurities.github.io/guides/linux-hardening.html)
|
||||
for background on these settings.
|
||||
|
||||
None of these presets affect secrets generation — that is controlled separately
|
||||
by the deployment method and `nix-bitcoin.generateSecrets`.
|
||||
|
||||
---
|
||||
|
||||
## Threat Model Summary
|
||||
|
||||
| Scenario | Impact | Notes |
|
||||
|----------|--------|-------|
|
||||
| **Attacker has root on the node** | Full compromise. All funds at risk. | `lnd-wallet-password` is on disk, allowing wallet decryption. All secrets are readable. |
|
||||
| **Secrets directory leaked, no network access** | No direct fund theft. | Secrets contain RPC passwords and TLS keys but not wallet key material. Without network access to the RPC port, passwords are not exploitable. |
|
||||
| **Secrets directory leaked, with RPC network access** | bitcoind wallet funds at risk (if a wallet exists). | The `privileged` RPC password allows calling any bitcoind RPC, including spending. LND funds are safer: the attacker also needs a macaroon (not in secrets dir) to call LND RPCs. |
|
||||
| **LND seed mnemonic leaked** | All LND on-chain funds compromised. | The attacker can derive all keys. Channel funds are at risk if the attacker broadcasts old commitment transactions. Immediate action required: sweep funds, close channels, re-seed. |
|
||||
| **Nix store accessed** | No secrets exposed. | Secrets are never written to the Nix store. Configuration files reference secret file paths, not secret values. |
|
||||
| **Deploy machine compromised** | Full compromise. | For Krops/NixOps, the deploy machine holds plaintext secrets and controls what code is deployed to the node. |
|
||||
|
||||
---
|
||||
|
||||
## Operator Responsibilities
|
||||
|
||||
These items are not automated by nix-bitcoin and require manual action:
|
||||
|
||||
- [ ] **Back up the LND seed mnemonic.** After first boot, copy
|
||||
`/var/lib/lnd/lnd-seed-mnemonic` to secure offline storage. Then delete the
|
||||
file from the node. This is the only way to recover on-chain funds.
|
||||
|
||||
- [ ] **Back up channel state.** Copy
|
||||
`/var/lib/lnd/chain/bitcoin/mainnet/channel.backup` after opening new channels.
|
||||
This allows off-chain fund recovery via the
|
||||
[Data Loss Protection protocol](https://github.com/lightningnetwork/lnd/blob/master/docs/recovery.md).
|
||||
Alternatively, enable `services.backups` to automate this.
|
||||
|
||||
- [ ] **Secure the deploy machine.** For Krops and NixOps deployments, your local
|
||||
`secrets/` directory contains plaintext credentials. If your local machine is
|
||||
compromised, the node is compromised.
|
||||
|
||||
- [ ] **Review enabled presets.** Consider enabling `secure-node.nix` (Tor,
|
||||
firewall, operator user) and `netns-isolation` (network namespace separation
|
||||
between services). These are not enabled by default.
|
||||
|
||||
- [ ] **Understand the auto-unlock tradeoff.** LND's wallet password is stored
|
||||
on disk so the service can start unattended. This means anyone with root access
|
||||
to the node can unlock the wallet. There is no way to require manual unlock at
|
||||
boot within the current nix-bitcoin design.
|
||||
|
||||
---
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [NixOS Hardened Profile](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/hardened.nix) — kernel and userspace hardening applied by `hardened.nix`
|
||||
- [systemd.exec(5)](https://www.freedesktop.org/software/systemd/man/systemd.exec.html) — reference for the systemd sandboxing options used in `pkgs/lib.nix`
|
||||
- [systemd.resource-control(5)](https://www.freedesktop.org/software/systemd/man/systemd.resource-control.html) — resource limiting and IP address filtering
|
||||
- [Linux network namespaces](https://man7.org/linux/man-pages/man7/network_namespaces.7.html) — the kernel feature behind `netns-isolation`
|
||||
- [aezeed cipher seed](https://github.com/lightningnetwork/lnd/tree/master/aezeed) — LND's seed scheme and how it differs from BIP39
|
||||
- [LND fund recovery](https://github.com/lightningnetwork/lnd/blob/master/docs/recovery.md) — on-chain and off-chain recovery procedures
|
||||
- [madaidan's Linux Hardening Guide](https://madaidans-insecurities.github.io/guides/linux-hardening.html) — background for `hardened-extended.nix` settings
|
||||
- [Nix Store security](https://nixos.org/manual/nix/stable/store/) — why secrets must not be written to the store
|
||||
Loading…
Add table
Add a link
Reference in a new issue