aboutsummaryrefslogtreecommitdiffstats
path: root/src/access
Commit message (Collapse)AuthorAgeFilesLines
* screen: maps made, and votes, judged with what came before them intv3 days1-8/+30
| | | | | | | | | | | | | | | Review 2, N6: finding 2's class survived for two reads the frontier didn't cover: whether a map a change makes is there (maps made on the way, 1137; holders made where none, 1147, for any path), and _continuum/votes, which `relevant` didn't list. A crafted pair, the second making a map the first made, was taken by nodes receiving both in one batch (judged at the frontier, where the map isn't yet) and refused by nodes receiving them apart. The screen now keeps the paths the accepted, not applied yet, touch; a change making a map at one of them is judged after they go in. Votes join what `relevant` lists. A crafted pair needs hand-made changes, which the tests don't build.
* rules: under strict, a user's own entry is its own to signtv3 days1-0/+28
| | | | | | | | | | | | | | | Review 2, N5: under a strict policy a user's signed PUT of its own entry, keys changed (continuum-signed key-add), passed the API and envelope::check's own_keys_only, and was then refused by the rules' path check: _continuum/users/<name> wanted write on the registry, so the node judged it as every node would and answered 403. 1168 worked only without strict. Under a strict policy the rules now take a write of a user's own entry from that user, leaving what it may be to signed_values, which runs envelope::check: an envelope by the user, with only its keys changed, one left at least. Strict only: under another policy the entry isn't an envelope, and the user would rewrite its own capabilities.
* remake: turning a running swarm strict, keeping what an operator signedtv3 days2-0/+225
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Until a swarm is strict, the user a change names is its node's word, so any member could have planted registry entries, policy, bans or user-signed values as any user, plain or signed by a key it put there itself; writing the strict policy trusted all of it as it was. A remake of a new kind, `strict`, turns a swarm strict instead: * Its content is a manifest (access::strict): the swarm, the strict policy, and each value kept at the places values are signed, by the SHA-256 of its JSON, signed by an operator (ssh-keygen -Y sign -n continuum-strict). * Every node builds the next epoch as compaction does, keeping at the signed places only the values the manifest names, as they were at the base, and writing its policy (RemakeKind::protocol, the one kind that changes protocol data). Writes after the base reach the new epoch only by replays, judged by the strict policy. * A node votes for one only with its manifest signed by one of --strict-keys, keys given out of the document; without any, for none (logged once, `Not voting for a strict remake`), unless --follow-strict-remakes. A coordinator proposes only what it would vote for (403). Once a majority commits one, every node switches, as with any remake: refusing to switch would only cut a node off. * GET /admin/strict-manifest gives the manifest keeping everything held now, the values for review and the heads they are at; continuum-signed strict shows them, signs once confirmed and proposes at those heads. The sim a_swarm_turning_strict_through_a_remake: a member plants a registry entry as a manager; the operator's manifest leaves it out; a and b, given the key, commit; c, without, follows; the entry is gone and the policy strict everywhere.
* users: a user can't remove its own last keytv3 days2-26/+94
| | | | | | | | A user managing its own keys (1168) could remove the last, and then neither sign nor log in by key. It can't now: DELETE /auth/keys/{id} of the last answers 409, the rules take a user's change of its own keys only with one left after it, and under a strict policy its own entry signed only with one key at least. One who may manage users still may.
* refusals: a drop taking much waits for a second member, and showstv3 days1-0/+27
| | | | | | | | | | | | | | | | | | A node told (REFUSALS) that it holds a change the swarm's rules refuse, and whose own rules refuse it too, dropped it and everything built on it on one peer's word, however much that was; its own writes among them went to the quarantine, the others' nowhere. * A drop taking more than 100 changes, or more than ten and a tenth of the document, now waits until a second member names the same change: the node's rules and one peer's word could both be off. Logged as an error, counted in /admin/stats (sync.dropsHeld). * The others' changes dropped are listed with the refused (GET /admin/refused, "taken unjudged, dropped"), for operators to see. The cheap half of asking the others (future-work.md, Telling a malicious change from a wrong policy).
* users: a user manages its own keystv3 days2-1/+93
| | | | | | | | | | | | | | | | | Only a user who may manage users could change a user's keys: a user couldn't add a new laptop's key, or remove a lost one. A user now manages its own keys, whatever else it may do: * GET /auth/keys: its keys, its entry, and the envelope holding it under a strict policy (`signed`), to sign against. * PUT /auth/keys/{id} with an authorized_keys line, DELETE /auth/keys/{id}: one change, as the user. The rules take a write of _continuum/users/<name>/keys/… from the user it names. * Under a strict policy, the user signs its entry with only its keys changed, with a key it holds already (envelope::check takes that from the user itself), PUT to its own entry even without write on the registry; continuum-signed key-add and key-remove do so.
* login: logins by key capped per usertv3 days1-23/+91
| | | | | | | | | | | | Review 6: a node remembers the challenges used until they expire, 10 000 at most, and refused every login by key once full. One user holding a key could make 10 000 logins within five minutes and keep everyone else out. A user may now log in by key at most --key-logins-per-user times (100) on a node within a challenge's lifetime, past which that user waits and others don't; the 10 000 stay as the cap for all users together. The lifetime, five minutes until now, is --challenge-lifetime.
* screen: waiting changes building on a refused one refused at oncetv3 days1-25/+90
| | | | | | | | | | | A change waiting for a dependency that hadn't come stayed waiting when another of its dependencies was refused meanwhile, until the bound pushed it out. Changes building on one refused before were refused one generation per batch. Building on a refused change is now followed through at once, before judging and again after (refuse_building): whatever waits on a change refused in the batch, or on one of those, is refused then and there.
* screen: a batch Automerge refuses names its changestv3 days1-2/+41
| | | | | | | | | | | | Review section 7: Automerge takes the changes judged in a batch all or none, so one it won't take (a fork of an actor's changes, under a policy without signatures) fails the whole batch, and every batch carrying it. The log said who sent the message (`Failed to process sync message`, `Delta download failed`, with the peer) but not what failed. The error now names the changes it was given, the first eight, each with its seq and the node it says made it, so whoever sends the offending one shows in the log beside what it sent.
* envelope: a signed value names its swarmtv3 days4-3/+27
| | | | | | | | | | | | 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.
* token: a banned node's tokens aren't takentv3 days2-3/+19
| | | | | | | | | Review finding 11: token::verify checked only that the issuer is a member, and a ban leaves the member entry. A compromised node's tokens stayed valid after it was banned, until it was forgotten too. A token issued by a banned node is now refused (BannedIssuer), as soon as the ban reaches the node judging it.
* screen, signatures: the oldest waiting go, not the newesttv3 days1-3/+10
| | | | | | | | | | | | | | Review finding 9: once 4096 signatures waited for their changes, new ones were dropped, and the waiting changes were cut at 10 000 keeping the oldest. A member could send junk once (signatures of changes that never come, changes no signature will vouch for) and fill both for good. Every honest signature arriving before its change was then lost, so under signatures required its changes waited for the actor's next signature: a stall for quiet actors. Both now keep the newest: the oldest go. A share per sender, so a member sending junk without pause can't push out what others sent either, is in future-work.md.
* rules: an expired remake aborted by any membertv3 days2-1/+105
| | | | | | | | | | | | | | | | Under a policy, a remake's decision was taken from its coordinator only. A node aborting an expired remake whose coordinator is gone (DELETE /admin/remakes, after abortableAt) was refused by its own judgement (403), so such a remake could never be decided. The decision is now also taken from a user who may write it (the protocol writers), and an abort from any member once the remake expired a grace period ago (abortableAt, by the coordinator's grace period in the proposal). The rules have no clock of their own, so that is judged by the time the change says it was made: the writer's word, so a member could abort a remake early, as it could ask its coordinator to, but never commit one. Before then, an abort stays a request the coordinator applies.
* rules: a carried remake record taken from the coordinator its snapshot namestv3 days1-22/+75
| | | | | | | | | | | | | | | | | Review finding 7, second part: a record carried into a new epoch whole (proposal and decision in one change) was taken from any member, for any remake ID. A member could write a forged record, decision included, for a fresh ID in a single change. The snapshot's epoch metadata now names the remake's coordinator, as the quorum built it. The carried-record exception holds only in a later epoch, only for the remake that made it, and only from the coordinator its snapshot names, where there is no record yet. Wire version 43: the snapshot, and so the epoch ID, changes. The review suggested carrying the record inside the snapshot instead. The snapshot can't hold the decision, which names the snapshot's own hash. So the snapshot vouches for who may carry the record instead.
* rules: protocol maps judged by what is done to themtv3 days1-2/+46
| | | | | | | | | | | | | Review finding 7, first part: the protocol-map rules looked at paths, not actions. Any member could delete _continuum/members, /votes or /remakes whole, or rewrite another remake's proposal (its coordinator) and then write its decision. What holds the members' entries, votes and remake records, and a remake's record itself, are now made by a member only where there is none; a proposal is written only where there is none. Replacing or removing any of them is left to a user who may write there (the protocol writers). An abort asked for stays any member's.
* screen: a signature vouches for its change and what it builds ontv3 days1-8/+84
| | | | | | | | | | | | | | | Review finding 5: vouching by (actor, seq) isn't bound to a hash. A member can make a change under another member's actor at a seq that member's signature covers. The change is then taken as vouched, and if it breaks a rule, the honest member is refused as a whole and logged as the one whose own change broke the rules. A signature now vouches for its change and the changes of its actor it builds on: in the document, as before; among received changes, those it reaches through the batch's dependencies. A received change under an actor at a seq whose change the document holds (a signature verified up to it) is a fork: refused as forged, never as its actor's node's, not told back in REFUSALS, and logged with the peer that brought it.
* sync: a member refused as a whole, synced with again once it changed documenttv3 days1-0/+8
| | | | | | | | | | | | A node refusing a member as a whole sent it nothing, and lifted that only when a change of the member's was taken again. But a member that dropped what was refused (REFUSALS) starts its sync sessions over, and a fresh session sends no changes until the other side answers: the two waited for each other, and the member stayed refused. The refusal now notes the member's run and epoch_seq when it began; a member with another document since (it dropped what was refused, or rejoined) is synced with again.
* access: a node judges its own changes before they leavetv3 days1-0/+53
| | | | | | | | | | | | | | | | | | | | 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).
* access: forgetting and revoking as the API letstv3 days1-4/+46
| | | | | | | | | | | | | | Two writes the API lets a user make were refused by the rules: * DELETE /peers/{pk}, with write on _continuum/forgotten alone, writes the forget record and removes the member's entry in one change; the rules wanted write on the entry. Removing an entry is now also taken in a change forgetting that member, from a user who may forget it. * DELETE /admin/users/{name}/tokens by a user who manages users writes _continuum/logouts/<name>; the rules wanted write there. One who may write _continuum/users may now write anyone's logouts entry. Review finding 3 (DELETE /peers, revoking tokens).
* access: maps made on the way judged by what they holdtv3 days1-0/+84
| | | | | | | | | | | | A write makes the maps on its way where there are none (store::write_path); the rules judged each made map as a write at its own path, so a user whose capability lies below a map not made yet (write on servers/eu, a first PUT at servers/eu/0) had its first write taken by its node and refused by every other. A map made where there was none, in a change writing within it, is now judged by what is written within it. Review finding 3 (parent maps created on the way).
* access: signed values in before the next is judgedtv3 days1-2/+73
| | | | | | | | | | | Under a strict policy the rules read user data: the value at a signed place (prev) and what holds one. A batch judged at its frontier missed a signed write earlier in the same batch, so two successive signed writes at one path received together were refused on some nodes and taken on others. A change at, within or above a signed path now goes in before the next is judged. Review finding 2.
* access: judged by the policy written undertv3 days2-2/+40
| | | | | | | | | | The rules read the policy at the change's dependencies only for strict mode, so history from before the policy was judged by today's rules when a late node caught up, or when REFUSALS had it judged again. A change written while the document had no policy, or one this node doesn't know, is now judged by none. Review finding 6.
* access: a forgotten node cancels its own recordtv3 days1-1/+8
| | | | | | | | | | A node forgotten swarm-wide while alive cancels its forget record with a change naming no user; the rules judged _continuum/forgotten by the registry alone, so every node refused the cancellation and everything the node wrote after it. A node may now write its own _continuum/forgotten/<id>, as it may its member entry. Review finding 4.
* access: an epoch's root is never droppedtv3 days1-2/+8
| | | | | | | | | | | | | | | A REFUSALS naming the swarm's first change made the node receiving it judge that change again in a document that isn't empty, refuse it as "a change without dependencies that isn't the epoch's root", and drop it with everything built on it: its whole document. The first epoch's root is now the first change a document holds, as it comes to an empty one or as it is in one; a change judged again on a peer's word is never refused for having no dependencies; and drop_taken refuses to drop an epoch's root, or anything that would leave nothing kept, logging an error instead. Review finding 1 (critical).
* swarm: changes taken unjudged, dropped (REFUSALS)tv4 days1-18/+41
| | | | | | | | | A node refusing changes a peer brought tells it which broke a rule; the peer judges them itself, and if its rules refuse them too, drops them with all built on them (its document forked without them, a fresh actor), quarantining its own writes among them. Wire version 42. Auth plan M6 (rejoin on taint).
* access: members refused as a wholetv4 days1-0/+80
| | | | | | | | | 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).
* access: refused oncetv4 days1-10/+147
| | | | | | | | A refused change is remembered by its hash: offered again it is refused without decoding, judging or logging, so is one built on it, and a peer offering nothing else is answered later each time, up to a minute. Auth plan M6 (re-sending).
* access: values sealed for their readerstv4 days4-1/+473
| | | | | | | | | | 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).
* access: --bootstrap-keystv4 days1-10/+62
| | | | | | | The first users' SSH keys, recorded at genesis with their entries, so a swarm can be strict from its start. Auth plan M7.
* auth: logging in by keytv4 days2-0/+206
| | | | | | | | 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).
* access: signed values judged on receipttv4 days2-7/+219
| | | | | | | | | 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.
* api: signed values at the storetv4 days3-12/+39
| | | | | | | | | 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.
* access: strict policy, signed pathstv4 days4-25/+97
| | | | | | | | | "values": "signed" (strict, requiring signatures too): the registry, _continuum/access, bans and the paths in _continuum/access/userSigned (* for any key) hold values their users signed. Envelopes read as the values they hold where the node reads protocol data. Auth plan M7.
* access: logouts apart from users' entriestv4 days3-19/+24
| | | | | | | | _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.
* access: values signed by their userstv4 days4-2/+288
| | | | | | | | | access::envelope: cv1.<payload>.<signature>, the payload naming the path, value, user, time and the envelope it replaces (prev), signed with one of the user's SSH keys from the registry (keys). Checked against the registry as the writer saw it. Auth plan M7.
* access: SSH signaturestv4 days2-0/+260
| | | | | | | access::sshsig: OpenSSH's SSHSIG, verified and made natively (Ed25519, sha512/sha256), checked against OpenSSH's own test vector. Auth plan M7.
* access: banstv4 days3-1/+52
| | | | | | | | _continuum/banned/<node ID>: every node refuses the banned node's changes from then on (judged as the ban stood) and leaves its handshakes unanswered. Auth plan M6.
* access: signatures requiredtv4 days2-10/+140
| | | | | | | | "signatures": "required": a change counts only once a signature by its committer vouches for it, and waits until then; every node then signs every change it makes. Auth plan M6.
* access: the policy enforcedtv4 days3-4/+169
| | | | | | | | _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.
* access: screening received changestv4 days3-1/+389
| | | | | | | | | access::screen: a batch of received changes judged change by change, those that change what rules read applied before the next is judged; a refused change stays out with whatever builds on it, kept aside for operators; changes missing dependencies wait. Auth plan M6.
* access: rules by pathtv4 days2-0/+407
| | | | | | | | | | access::rules::judge: a change judged as its writer saw the document (at its dependencies): committer a member or registering itself; members' entries and votes their own; epochs and swarm ID from roots; a remake's decision by its coordinator; the rest by the registry for the user the change names. Auth plan M6.
* docs: tokens through a proxy; token formats latertv4 days1-1/+2
| | | | | | | | operations.md: an nginx proxy passing requests with a token on to the node and asking the others for its own login. future-work: other token formats. Auth plan M2.
* store: keys_at tells no map from an empty onetv4 days1-2/+5
| | | | | | | | registry::exists counted any value at _continuum/users as a registry; keys_at answers None where there is no map, and its users (records, members, forget records, remakes) say what they make of it. Auth plan M1 (fix).
* access: tokenstv4 days3-7/+354
| | | | | | | | | ct1 tokens: who, how they logged in, a session, when they expire, and optionally a narrower limit; signed by the issuing node's key and taken by every member (a node ID is its key). Revocation by the registry as it stands when a request comes. Auth plan M2.
* access: the users endpointstv4 days1-34/+20
| | | | | | | | /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.
* access: bootstrap users onlytv4 days1-26/+9
| | | | | | | The first users come only from --bootstrap-users; the genesis node warns of htpasswd users that aren't among them. Auth plan M1.
* access: genesis of swarms from before the registrytv4 days1-34/+19
| | | | | | | | Swarms started before the registry keep working without one; the genesis node's options for a registry apply only to new swarms. What tests and migration leave is noted in future-work. Auth plan M1, bootstrap and migration (2.5).
* access: keeping the swarm manageabletv4 days1-0/+26
| | | | | | | | | 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.
* access: first users at genesistv4 days1-0/+87
| | | | | | | | --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.
* access: the user registrytv4 days2-0/+414
_continuum/users/<name>: one entry per user, with capabilities by path (read, write) granted by whom and when, and a disabled flag. Read leniently: a damaged entry gives less, never more. Protocol data stays apart: a capability outside _continuum never covers it. Auth plan M1.