Security for operators
What protects a node on a server, and what is up to you: who on the machine counts as the owner, how the node is fenced in, what it shows the internet, and how much a peer can cost it.
Dassie is young, and its code changes every week. Run it on a server that holds nothing else you care about, and if you find a weakness, please report it privately.
On this page
The owner on the machine
The dassie command talks to the node over a local socket,
/run/dassie.sock. Whoever can open that socket is the
node's owner: every call on it may do what you can do in the wallet,
with no login.
systemd makes the socket with mode 0660, owned by root
and the group dassie-users, so root and that group's
members can use it. The installer creates the group and adds the
user who ran it. A member of the group can:
-
print the setup link of a node not set up yet, and with it claim
the node (
dassie setup-url); -
pair an app that then acts as the owner from anywhere, until you
remove it (
dassie pair); -
move the node's money between its hubs and its XRP Ledger account
(
dassie hub), and change its settings.
So add to dassie-users only people you'd give your
wallet. To see who is in it, and to take someone out:
$getent group dassie-users
$sudo gpasswd -d <user> dassie-users
dassie status names no secret, so you can paste its
output when you ask for help. The setup link has a command of its own
for that reason, and the node never writes it to its log: the journal
is often readable by people who don't run the node. Open the link soon
after you install: until someone does, whoever opens it first claims
the node.
The sandbox
The node runs as dassie.service, fenced in by systemd.
It doesn't start as root and never becomes root: systemd opens its
ports, including the privileged ones, and hands them over.
| Setting | What it means |
|---|---|
DynamicUser=yes |
The node runs as a user systemd makes for it, which owns nothing else on the machine. |
CapabilityBoundingSet= (empty), NoNewPrivileges=yes |
It holds no privilege, and can't gain any. |
ProtectSystem=strict, ProtectHome=yes |
It can write only its own data directory, and can't see home directories at all. |
StateDirectoryMode=0700, UMask=0077 |
What it writes, only it can read. |
PrivateTmp, PrivateDevices, PrivateUsers, ProtectProc=invisible |
It has its own /tmp, no devices, and sees no other
users or their processes.
|
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 |
It can open only local sockets and internet connections. |
SystemCallFilter=@system-service, MemoryDenyWriteExecute=yes |
It can make only the system calls an ordinary service needs, and can't write memory it then runs. |
The unit file, /etc/systemd/system/dassie.service, lists
the rest. To see how systemd rates it:
$systemd-analyze security dassie.service
Put your own changes in a drop-in
(sudo systemctl edit dassie.service), which updates leave
alone, and don't loosen these settings: the node needs none of what
they take away.
The updater, dassie-update.service, runs as root, since
it installs units and restarts the node, but it too may write only
/opt/dassie, the units and the node's data. It installs
only a release signed with the release key built into the binary, and
checks it before it unpacks anything
(Releases you can trust).
What the internet sees
| Port | What answers there |
|---|---|
| TCP 80 | Let's Encrypt's checks, and a redirect to HTTPS. |
| TCP 443 | The wallet, Open Payments, BTP for ILP clients, and a WebSocket that carries QUIC for nodes without a domain name whose UDP is blocked. |
| UDP 443 | Other nodes, over QUIC. The node doesn't start without this port. |
| UDP 7768 | Requests for the node's snapshot: the quotes it swaps at, signed with its key. Only a core node that offers swaps answers, with one datagram no larger than the request, and only as many per second as its budget allows (Read a node's snapshot). |
The wallet is on the public port, behind a login that only your recovery phrase can make. Your phrase never leaves your browser: the browser sends a proof derived from it, never the phrase. The node limits how fast any one client may try setup and login. Owner requests are taken only from pages the node served itself, so another site can't act for you in your browser.
Apps that pay with Open Payments spend only what the grant you approved allows, within the limits it names (Connect apps). A BTP client spends without limit unless you give its token a cap in the wallet (API keys), so give each token one.
A few environment variables exist for development and testing:
DASSIE_PEER_ALLOW_PRIVATE, DASSIE_DEV_TIMINGS
and DASSIE_DEV_LIVE_REALM. Never set them on a node that
faces the internet. If you set either of the first two while the node
listens beyond localhost, it warns in its log at startup.
The third works only in a development build: a released node refuses
to start with it set.
What a peer can cost you
Your node keeps its own books of what each peer owes it, and decides everything from them: whether to take a payment, when to ask for settlement, when to end a peering. Nothing a peer says is trusted past your node's own limits, so however a peer behaves, by mistake or on purpose, what it can cost you is bounded.
Peers settle through settlement hubs. Over a hub, the credit your node gives a peer is its own hold toward that peer: money set aside in advance, from which the peer takes only what your node owes it. What the peer owes your node is backed the other way round, by the peer's hold toward yours, and your node is paid from that hold whether the peer cooperates or not. So what a peer can cost you is about:
- what is on its way between you at that moment, which the node caps at two and a half of the largest packets on each hub, each way;
- the fees of claiming what you're owed from a hold, which the node books as a loss on that peer.
A swap with a peer is a payment through it like any other, within the same bounds. A peer that claims what it rejected loses its peering. If you want a node kept away for good, ban it in the wallet's Network page: only you ban, the node never does it by itself.
Being in a hub has costs of its own: its reserve, its fees, and the ledger's fees to move money in and out. You also trust the hub's validators with what your node keeps in the hub. Settlement hubs explains what they can and can't do, and Peers and holds how credit follows the hold.
Why it works this way, and why peers settle only over hubs.
What you look after
- Your recovery phrase. Everything secret derives from it. Keep it offline.
- The node's data and its backups. The database holds the node key unencrypted (The data holds the node key).
-
Root and
dassie-users. Both are the owner. - Updates. Leave the nightly update on, so fixes reach your node (Updates and rollback).
A wallet you can trust without trusting the node that serves it is not there yet: today your browser runs the wallet your node sends it. What's next