1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
# Trust model
Who can do what to a swarm, and what stands in the way. In short: the swarm key
decides who is a member, and members are trusted with everything.
## The boundaries
**The network.** Every transport authenticates the sender of every message, each by
its own key: Mycelium by the public key of the sending daemon, libp2p by the peer ID
of the connection, Tor by a challenge handshake on each connection, in which both sides
prove their node key ([`transports.md`](transports.md)). So a node always knows which address sent it something, though not
who wrote what that message carries (changes are relayed). On top, a node proves its
node ID at each address with a signature by its node key (WHO and ME, [`peers.md`](peers.md)), so a
node is known by its node ID whichever transport and address it uses. A private Mycelium
network (`--mycelium-network-key-file`, `--mycelium-network-name`) additionally keeps
other Mycelium nodes from reaching ours at all; on libp2p and Tor, anyone who knows an
address can connect, and the swarm key is what keeps them out.
**The swarm key.** With `--swarm-key-file`, a node proves the key in the handshake
before anything else it sends is acted on; until then it is a *stranger*. Without a
swarm key, every node that can reach a member can join: a joining node doesn't need to
know the swarm ID, and the swarm ID is no secret anyway. The key file should be readable
by its owner only; a node warns at startup if others may read it.
**Public infrastructure.** Nodes use none unless told to: Mycelium peers are those in
`--mycelium-peers`, and libp2p reaches only seeds, members and the relays of the swarm.
With `--libp2p-public`, a node joins the IPFS DHT and may use public relays: they see
whom it talks to and how much (the traffic itself is encrypted end to end), and the DHT
makes its peer ID and addresses public.
**The node key.** `--node-key-file` holds the node's own key, from which its node ID
(`continuum:<hex>`) derives: who the node is, whatever transport reaches it. It is a
secret like the swarm key: readable by its owner only (a node warns otherwise; a key
it creates is).
**The HTTP API.** Every endpoint but the API description, the login page (`/login`) and
logging in by key needs a user from `--htpasswd`, one a reverse proxy names
(`--user-header`), or a token a node of the swarm issued to one of those or to a user
signing a challenge with one of its keys ([`access.md`](access.md), Tokens), sent as a
header or as the browser session's cookie: the node believes the proxy's
header from `--trusted-proxies` alone, but from those blindly, so whoever can send
requests from there can be any user. With the default loopback addresses, that is every
user of the host; with only the API's Unix socket (`--trusted-proxies unix`), those its
file's permissions let in: the node's user and group ([`operations.md`](operations.md), Behind a
reverse proxy). It listens on `127.0.0.1:3000` by default (`--bind-addr`) and speaks
plain HTTP: to expose it, put a TLS proxy in front. What each user may do is the
swarm's: its user registry, in the document, gives each capabilities by path, to read
and to write, protocol data apart ([`access.md`](access.md)); a user the registry doesn't have
may do nothing. In a swarm without a registry (started without one), all users may read
and write user data and use the admin endpoints, and only those in `--protocol-writers`
write protocol data. Epoch metadata and the swarm ID are never writable. The `/debug` endpoints exist only with
`--debug-api`.
**The configuration.** Whoever can write a node's configuration file or its options can
run commands as the node's user: a `cmd:` (or `command`) value runs at startup, and a
`file:` value reads any file the node can ([`configuration.md`](configuration.md), "Importing values").
Protect the configuration like the swarm key.
## What members are trusted with
Everything in the document, unless the swarm says otherwise. What it can say comes in
three steps, each adding to the one before ([`access.md`](access.md)).
**Without a policy**, a member syncs changes to and from its peers, and every node acts
on what the document says:
* **User data**: any member can write or delete any path.
* **Who wrote what** (`GET /admin/changes`): each change names its node and API user
as the writing node says. Nodes sign their changes ([`api.md`](api.md), Signed
changes), so a change a signature vouches for shows `verified`: its node made it. One
that shows unverified may yet be vouched for by its node's next signature, or may be
another member's claiming that node. The user a change names is its node's claim,
signed or not. The names of the users who write are in the document, and so on every
node.
* **The member registry, the user registry, forget records, remakes and votes**: the
API lets only users given protocol data write them, but a member can write anything
into its own document, and every node acts on it: the topology, the electorate, who
may do what, which peers are forgotten, which remakes commit. Votes aren't counted
again by the nodes that switch.
So a member that misbehaves, by a bug or on purpose, can rewrite or delete data,
manipulate the registries and the electorate, forge votes, commit a remake without a
majority, and make the swarm forget peers.
**With a policy** (Changes judged on every node), every node judges every change it
receives, as the document stood when it was written, and refuses one breaking the
rules, with everything built on it: a member writes its own entry and votes only; epochs
and the swarm ID come from roots only; a remake's decision from its coordinator; the
rest, user data and protocol data alike, as the registry lets the user the change
names. A banned node gets nothing in, and no handshake answered. With signatures
required, a change counts only once its node's signature vouches for it, so a member
can no longer write as another member. What stays: the user a change names is its
node's word, so a member can write wherever any user may; and a node refusing a change
diverges from the member that wrote it without anyone being told more than the log and
`GET /admin/refused` say.
**Strict** (`"values": "signed"`, Values signed by their users): the registry, the
policy, the bans and the paths the swarm lists hold values their users signed, with
their SSH keys, checked by every node against the registry as the writer saw it. No
member can grant itself or anyone a capability, change the policy or lift a ban, or
write a listed path, but as a user who may signed it; there, the user is proven, not
claimed.
**Sealed values** (Values sealed for their readers) are read only by those they are
sealed for: no node holds their keys. Members still see that they exist, their sizes,
and when and by whom they were written, and keep what they read before a reader was
removed.
Whatever the steps: every member reads the whole document (but sealed values), any
member can propose a remake and coordinate it, and what a member says about itself is
believed:
* **Epochs it advertises**: a rejoining node follows the epoch ready members
advertise, corroborated by two of them once it was ready (one if only one is active,
and one before it was ever ready), and downloads the epoch's snapshot from them,
checked against its hash.
* **What it says about itself**: its keepalives and handshake replies say whether it is
ready, whether it would sync with the recipient, and how long it last heard from it
(which counts towards the recipient's stability tier). Nodes believe that: a member
claiming to be ready can become a joining node's catch-up source (though one with an
empty document can't make it ready: empty heads prove nothing), and one misreporting
absences can move a node between tiers.
* **A joining node's first sync**: an empty document has no policy, so what it gets
first is taken as the node it joins through judged it.
Keep the swarm key to nodes you trust as much as your data, and the policy, strict
where it matters, for the rest.
## What depends on clocks
Nodes don't agree on time; each judges by its own clock: voting periods, when a remake
expires and may be aborted by others, the replay window, alive stamps (and so the
electorate). Keep the clocks within a grace period (four keepalives) of each other,
e.g. with NTP. A node whose clock is further off can, together with an operator's
abort, make a remake contested ([`remakes.md`](remakes.md), Limits). A node compares its clock with its
peers' and warns when its own is off (`--clock-skew-warn`; [`measurements.md`](measurements.md), Clocks).
## What a stranger can do
Send messages. Before it proves the swarm key, a node ignores everything from it but
the handshake, and doesn't sync with it. A stranger can still cost a node the work of
reading and dropping its messages, but no more than 256 KiB of each: a node that hasn't
proved itself a member (its HELLO, or HELLO_REPLY, with the key) can only usefully send
a handshake, WHO or ME, or PING or PONG, and libp2p and Tor refuse a larger frame from
it before reading it, by the length it announces (`transport::Admission`). Mycelium's
daemon has taken a message whole before the node sees it; a stranger's larger one is
dropped undecoded. A member's messages may be large (a sync message up to 64 MiB).
## What protects against mistakes
Not against a hostile member, but against honest ones getting in each other's way:
* Majority voting with one vote per member and epoch: two remakes can't both commit
([`remakes.md`](remakes.md)).
* A snapshot every node builds itself and checks against the committed epoch, and
downloads checked against their hashes.
* Protocol data writable through the API only by users given it ([`access.md`](access.md)), or
in a swarm without a registry by `--protocol-writers`, and logged.
## Not yet
What is left of protecting the swarm against its members, and of keys in browsers, is in
[`future-work.md`](future-work.md): Write protection by checking changes, Read access
beyond sealed values, Keys in browsers.
|