| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
| |
Review finding 12, browsers: the session cookie had no Secure flag.
Behind a TLS proxy, the token it carries went over plain HTTP if
someone typed http://.
POST /auth/session now sets Secure when the request came over TLS, as
the proxy says (X-Forwarded-Proto, or Forwarded's proto, of the first
hop). A client saying so itself only keeps its own cookie off plain
HTTP. operations.md's nginx example sets the header.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Review finding 12, browsers: the dashboard holds a bearer token in
memory and edits the document, but had no content security policy and
could be framed by any page of the same site (another port on the same
host, anything sharing a domain behind a proxy), acting with its user's
token.
The dashboard and the login page now come with a policy that runs only
their own inline script (by its hash, computed when first served),
loads nothing from elsewhere, and lets no page frame them
(frame-ancestors 'none', X-Frame-Options DENY). Fetches stay open to
any origin, as the members' dashboards are wherever the member list
says.
The browser check (check.js) loads the page from a file, without the
header, so the policy wants one look in a browser.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Review 3.1, part of finding 12: a signed value's payload bound path,
user, time and prev, but not the swarm. A user with the same name and
key in two swarms could have an envelope replayed from one into the
other, wherever the path holds no envelope yet (prev "").
The payload now names the swarm (`swarm`), and a node takes an envelope
only for its own. /auth/whoami says the swarm, and continuum-signed and
the example in api.md sign it. Wire version 44: nodes before this
refuse such envelopes, and those after refuse the old ones.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Review finding 8, second part: opening another member's dashboard
handed it a token reading everything (READ_ALL). A member writes its
own dashboard URL, so a malicious one received a read-everything token
under the operator's identity: /admin/config, /admin/changes,
/admin/log and the rest, on every node the user reaches.
The token handed over now reads the member list only (READ_MEMBERS, as
sent to subscribed nodes), which is enough to draw the map; the user
logs in at the other dashboard to see more. With 1151 it lasts five
minutes and isn't refreshed past them.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Review finding 8, first part: a refreshed token's lifetime came from
the refresh request, and it kept the session's end. So the five-minute
read-all token a dashboard hands to another member's refreshed into a
12-hour one, renewable for seven days.
A token asked to live less (`lifetime`, at login or refresh) now ends
its session with it: nobody refreshes it past its end, its holder
included, and a session begun from it (POST /auth/session) ends then
too.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The API checks the path a request asks for; the rules judge everything
the change does. Where they disagreed, the honest node's change was
refused by every other node, and with signatures required the node was
refused as a whole. The plan had both gates run the same judgement.
Under a policy, each change a node makes (its own writes and those
through the API) is now judged by rules::judge as receivers will judge
it, before it is signed or sent; one every node would refuse is rolled
back (the document forked at the heads before it, same actor: the
change never left) and the request refused with 403 (StoreError::
Refused). rules::judge_held, judging a change the document holds,
moves from swarm::refusals to access::rules for both.
The simulations that play a member past its own checks write with a
helper that bypasses them (forge).
Review finding 3 (the same judgement at both gates).
|
| |
|
|
| |
Flashes are drawn beneath the nodes' labels.
|
| |
|
|
|
| |
messages: off / dots / flash: a message expands at its own speed and
leaves an afterglow fading out in the direction it went.
|
| |
|
|
|
|
|
|
| |
Opening another member's dashboard passes a token reading only, for
five minutes, in the URL's fragment; its login page begins a session
with it. Tokens may be asked for with a shorter lifetime.
Auth plan M3 (hand-off).
|
| |
|
|
|
|
|
|
|
| |
With signatures required, a member whose own change broke a rule is
refused as a whole: sent nothing, shown in /peers (refused), in stats
(sync.refusedPeers) and on the dashboard, until a change of its is
taken again or the epoch changes.
Auth plan M6 (diverged standing).
|
| |
|
|
|
|
|
|
| |
The cookie lets in GET, HEAD and the session's own requests; anything
changing something brings the token itself, which no page of another
site or port can make a browser send. The dashboard's writes send it.
Auth plan M3 (browsers).
|
| |
|
|
|
|
|
|
|
| |
GET /login without authentication: log in by password or with a token
(from a login by key). POST/DELETE /auth/session keep the token as an
HttpOnly, SameSite=Strict cookie; the dashboard sends a browser
without a user there and renews the session while open.
Auth plan M2/M3 (browsers).
|
| |
|
|
|
|
|
|
|
|
| |
cs1.<age file>: a value encrypted with age to its readers' SSH keys,
which no node decrypts. _continuum/access/sealed declares paths whose
values must be sealed, for named readers or anyone; nodes check the
recipients by age's key tags, at the API and on receipt.
Auth plan M8, reworked: sealed values instead of containers, readers
as users only (nodes hold no keys).
|
| |
|
|
|
|
|
|
| |
POST /auth/challenge and /auth/login/key: a node-signed challenge,
valid five minutes and taken once, signed with one of the user's SSH
keys (ssh-keygen -Y sign -n continuum-login), for a token.
Auth plan M7 (key login, M2b).
|
| |
|
|
|
|
|
|
|
| |
Inspection gives each operation's kind; under strict every node
refuses, at a signed place, anything but an envelope that checks and
replaces the one there (deleting first as replacing does), anything
within one, and above one anything replacing what holds one.
Auth plan M7.
|
| |
|
|
|
|
|
|
|
| |
PUT with application/continuum-signed writes an envelope where values
are signed (403/409 saying why); reads open envelopes and name the
signer (x-continuum-signed-by), ?raw=true shows them. Under strict,
unsigned writes reaching a signed value and the users API are 409.
Auth plan M7.
|
| |
|
|
|
|
|
|
| |
_continuum/logouts/<name> holds when a user's tokens were last revoked,
written by the user itself or by one who may manage users, so users'
entries can be signed by their managers alone.
Auth plan M7.
|
| |
|
|
|
|
|
|
| |
_continuum/access/policy ({"version": 1}) has every node screen every
change it receives, on every path changes come in. A policy a node
doesn't know stops its writes through the API. GET /admin/refused.
Auth plan M6.
|
| |
|
|
|
|
|
| |
Forget records and released quarantines name the user who made them,
so they are judged by its capabilities.
Auth plan M6.
|
| |
|
|
|
|
|
| |
GET /admin/changes shows whether a signature by its node vouches for
each change.
Auth plan M5.
|
| |
|
|
|
|
|
| |
Signatures no sync message took go on their own within a second, to
each peer not sent them yet (wire version bumped).
Auth plan M5.
|
| |
|
|
|
|
|
|
|
|
| |
Every node signs the changes it commits with its node key, an Ed25519
signature over the change's hash, kept beside the document. One
signature on an actor's latest change vouches for its earlier ones.
Protocol data and paths listed in _continuum/access/signed are signed,
and writes with ?signed=true.
Auth plan M5.
|
| |
|
|
|
|
|
| |
GET /admin/users no longer lists a manages flag per user: whether a
user may manage users follows from its capabilities.
Auth plan M1 (cleanup).
|
| |
|
|
|
|
|
| |
Subscribed nodes' data merged into the state as the page patches it,
not overwritten by the next patch.
Auth plan M3 (fix).
|
| |
|
|
|
|
|
|
|
| |
The dashboard reads every member's links from the members themselves;
LINKS and LINKS_REPLY, swarm::links and their wire tags go (wire
version bumped). Nodes the browser can't reach are drawn from what the
reachable ones measure.
Auth plan M3.
|
| |
|
|
|
|
|
|
| |
The page compares the clocks the reports on its map carry: this
node's peers', and those of the nodes it subscribes to; the node no
longer gathers them from LINKS (api/dashboard/clocks.rs goes).
Auth plan M3 (preparing LINKS' removal).
|
| |
|
|
|
|
|
|
| |
The tree drawn is computed as every node computes its own part of it:
from the member list, and whom it and its neighbours reach, as they
told it; no longer from the links members report over LINKS.
Auth plan M3 (preparing LINKS' removal).
|
| |
|
|
|
|
|
| |
Each tick's messages of a subscribed node, listed in the console with
this node's own, from its own stream.
Auth plan M3 (live data from every reachable node).
|
| |
|
|
|
|
|
| |
The page reads a subscribed node's message counts from it too, and
draws them as dots on the map, as this node's own.
Auth plan M3 (live data from every reachable node).
|
| |
|
|
|
|
|
|
| |
Double-click a node to subscribe: the page reads its own links from it
directly with a narrowed token (reading the member list only), drawn
over what this node knows; kept per swarm in the browser.
Auth plan M3.
|
| |
|
|
|
|
|
|
|
| |
GET /admin/dashboard/links/own and its event stream: a node's own links,
as LINKS_REPLY carried them, for other members' dashboards with a
token. CORS for the members' dashboard origins and --cors-origins,
without credentials.
Auth plan M3.
|
| |
|
|
|
|
|
|
|
| |
POST /auth/login (by htpasswd or the proxy), /auth/refresh,
/auth/logout, GET /auth/whoami; every request may bring
Authorization: Bearer instead of a password. --token-lifetime and
--session-lifetime.
Auth plan M2.
|
| |
|
|
|
|
|
|
| |
/admin/users: list, add (one or several), remove, disable, grant and
revoke capabilities; each one change, refused if it would lock its
writer out. Capabilities as the store's paths name them.
Auth plan M1.
|
| |
|
|
|
|
|
| |
The dashboard's document box is read through the store, as its user
may, and the page is told only of changes where its user may read.
Auth plan M1 (the dashboard).
|
| |
|
|
|
|
|
|
|
| |
A write that would leave its own writer unable to manage users
(deleting, disabling or narrowing its entry, removing the registry) is
refused; --protocol-writers can set a registry right that nobody may
manage any more.
Auth plan M1.
|
| |
|
|
|
|
|
|
|
| |
Every request is judged by the registry the node holds: store reads
pruned to what the user may read, writes refused where it may not,
admin endpoints by protocol capabilities. Without a registry, as
before (--protocol-writers for protocol data).
Auth plan M1.
|
| |
|
|
|
|
|
|
| |
--bootstrap-users gives a new swarm a registry from its first change,
each first user with everything; other users may do nothing until
added. Without it the swarm has no registry and works as before.
Auth plan M1.
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|
| | |
|