-
Notifications
You must be signed in to change notification settings - Fork 12
uip-draft: userspace permissions #83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,215 @@ | ||
| --- | ||
| title: Userspace Permissions | ||
| description: Limiting capabilities of gall agents, and related work. | ||
| author: ~palfun-foslup | ||
| status: Draft | ||
| type: Standards Track | ||
| category: Kernel | ||
| created: 2026-01-27 | ||
| --- | ||
|
|
||
|
|
||
| ## Abstract | ||
|
|
||
| Gall agents have always had access to the entirety of the kernel API, as well as all other gall agents. Here we propose a permissions system, restricting the capabilities of agents on a given desk until a user grants corresponding permissions. We distinguish between static permissions, required by the desk prior to installation, and dynamic permissions, requested by a desk's agents at runtime. | ||
|
|
||
|
|
||
| ## Status | ||
|
|
||
| xx working group, subject to model changes there | ||
| xx draft, more detail forthcoming after discussion and gall model solidification | ||
| xx no specifics about agent effects, subject to gall model changes | ||
|
|
||
|
|
||
| ## Motivation | ||
|
|
||
| Gall implements a userspace model wherein "agents" can emit effects when they are evoked. While the shape and scope of these effects is dictated by gall, they map very closely onto the kernel API, with some additions for agent-to-agent interactions. Gall makes no effort to control, restrict or even monitor the effects emitted by the agents it runs. | ||
|
|
||
| In 2021, "software distribution" made it easy to install third-party userspace software. That work punted on most security-related questions, including ways to protect both kernel- and userspace against malicious third-party software. | ||
|
|
||
| This particular known security hole has been a big factor in preventing development and adoption of "high-stakes" software like secrets storage and cryptocurrency wallets. | ||
|
|
||
|
|
||
| ## Specification | ||
|
|
||
| We first give an overview of the behavior of permissions. Then we describe the requirements made of the API, the interplay between permissions and desk and lifecycles, we make recommendations about permission presentation and management, and describe best practices for userspace developers regarding permission requirements and requests. | ||
|
|
||
| This is not a capabilities-based model. This does not aim to solve the "confused deputy" problem. Local provenance is utilized to realize this model. This does not aim to extend provenance over the network in any way. | ||
|
|
||
| The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. | ||
|
|
||
| ### Overview | ||
|
|
||
| All `%pass` cards emitted by agents and all `.^` namespace reads performed by agents MUST be subject to permission control by gall. Permissions MUST be granted at the desk level in clay. Permissions on a desk MUST apply to all agents running from that desk. | ||
|
|
||
| Agents on the `%base` desk MUST be exempted from this permission control ("run as root"), and any agent MUST always be allowed to issue cards which would delete/revoke one of its created resources/subscriptions. Outside of that, there are no implicit permissions. Even interactions or reads to an agent running on the same desk MUST require a corresponding permission. | ||
|
|
||
| Desks MAY statically specify required permissions, in a `/desk.seal` file. The desk MUST NOT run (install, revive) until the specified permissions are granted. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if a the permissions in this file are revoked for a running agent, does this automatically suspend the agent?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reason I chose to phrase as-is: The operative requirement here is not being able to revoke required permissions. Needing to suspend falls out of that, not the other way around. |
||
|
|
||
| Agents MAY dynamically request optional permissions for their desk at runtime. These permissions can be granted and revoked at any time. Clay MUST track requested permissions and notify about additions (see below). | ||
|
|
||
| The `$bowl:gall` MUST include the permissions available to the agent at the time of its invocation. The standard library SHOULD provide helpers for checking a card or scry path against those permissions. | ||
|
|
||
| When an agent emits a `%pass` or performs a `.^` scry for which it doesn't have a corresponding permission, the agent invocation MUST be treated as having crashed. During `+on-init` and `+on-load`, this crashes the whole event. During all other invocations, this calls the agent's `+on-fail` with `%not-permitted`. | ||
|
|
||
| Both gall and clay need to know the current permission state (required, requested, granted) for a desk. Because permissions apply at the desk level and exist even when no agents are running, clay MUST be treated as the canonical store, with gall subscribing to clay for permission updates. | ||
|
|
||
| ### Interface and Types | ||
|
|
||
| Clay MUST support granting and revoking permissions through a `%seal` task and requesting permissions through a `%pine` task. | ||
| To support userspace reactivity (and gall's syncing), clay MUST expose a kernel-style subscription endpoint for notifications about permission requests and updates through a `%ward` task, and closing of that subscription through a `%wink` task. Agents that want to use this MUST be granted the corresponding permission. | ||
|
|
||
| The type that describes a permission MUST allow for "scoping" if possible. That is to say, for example, a permission for a resource at a path must apply to resources at _and below_ the given path. xx example of (unit resource-id) | ||
|
|
||
| xx concrete type description once gall api settles | ||
| xx include difference between local vs remote vs both comms | ||
|
|
||
| xx (request) notification type/api. should we differentiate between runtime-requested (optional) and update-requested (required)? | ||
|
|
||
| ### Permissions and Desk Lifecycles | ||
|
|
||
| A desk MUST be granted its required permissions before being set to live. (That is, before clay signals to gall that the agents on that desk should run.) | ||
| Once live, a desk's required permissions MUST NOT be able to be revoked. To do so, the desk must first be suspended. | ||
| A commit to a live desk MUST fail to apply if doing so would add required permissions that have not yet been granted. | ||
|
|
||
| xx this failure MUST register the to-be-required permissions as requested-for-upgrade and send a perm request notification | ||
|
|
||
| Desks that are installed as part of the boot sequence MUST have all their required permissions granted automatically. | ||
|
|
||
| Desks that are installed at the time of the upgrade which introduces userspace permissions MUST have all their required permissions granted automatically. | ||
|
|
||
| xx possibility to mark desk as "trusted", auto-granting all perms requests? | ||
|
|
||
| ### Permission Type Upgrades | ||
|
|
||
| To ensure it is always possible to present and reason about permissions, permission type definitions and usage is structured such that an "appetizer" update is always required. | ||
|
|
||
| There MUST be two versions of the permission type: one representing the latest supported permissions (A), and one representing the permissions that will be supported in the next kelvin upgrade (B). Any kelvin upgrade MUST replace type A with that of B, and MAY change type B to be any new permission type. | ||
|
|
||
| Type A MUST be used for all instances of permissions described in this specification, with the following exceptions: | ||
| - The `%seal` task MUST support _granting_ permissions of both type A and B in clay. | ||
| - But only type A permissions should _apply_ in gall. | ||
| - Permission requests originating from a desk upgrade attempt (see Lifecycles above) MAY use type B permissions. | ||
| - Permission management interfaces MUST support rendering both type A and B permissions. | ||
|
|
||
| To allow for manual "step-wise" kelvin upgrading where necessary, authoritative base desk sources SHOULD use clay labels to aid discovery and downloading of specific kelvin releases. | ||
|
|
||
| ### Presentation and Management | ||
|
|
||
| The `%base` desk SHOULD ship with a permission manager for each interface modality. | ||
| - Generators SHOULD be available for granting and revoking permissions on a given desk, either case-by-case or "all requested". | ||
| - `|install` SHOULD support granting whatever permissions are required. | ||
| - Eyre SHOULD serve a primitive permission management web interface. | ||
|
|
||
| When presenting permissions to the user, if the agent affected by a permission is locally known, it SHOULD be presented as or alongside the name or description of the desk it is running from. A "poweruser" SHOULD be empowered to view the "raw" underlying permission. | ||
|
|
||
| When an agent affected by a permission is not locally known, a permission manager MAY prevent the granting of the permission, even if this would prevent installation. | ||
|
|
||
| The recommendations around "known" agents SHOULD only apply to local agent interactions. Interactions that go over the network SHOULD be presented generically as "sharing data" or "reading". | ||
| xx does that imply "poke over the network" and "watch over the network" perms shouldn't specify agent name? can we meaningfully narrow by mark? | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you meaningfully enforce anything over the network? Once I allow a read, that kinda let's the data go in any real sense even if it was to some ostensible approved agent and the recipient was malicious and did something with the data after that approved read.. Same seems true in effect w/r/t poke over the network; counterparty could claim any sort of stuff about where it is coming from assuming it is properly formed. The limitation here seems like it is confirming that it came from a specific urbit, unless your doing something with a ZKP?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As discussed during core architecture today: We indeed cannot meaningfully enforce anything over the network. The only "enforcing" you can do is, on the receiver side, recognize what ship a read or write came from, and dispatch based on that as appropriate. That's the status quo for dealing with ingress, and that won't change. From the UIP:
Additionally, re "Once I allow a read", I should emphasize (and do so in the UIP too) that this model restricts what agents on your ship can do. It does not restrict what can be done to agents on your ship. Agents continue receiving all interactions sent their way, and continue to be responsible for checking the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a note (IIRC the topic of updating the software source was lightly touched on during core arch), my gut says there is a potential concern around a permissions escalation attack here if a desk can update it's source and then inherit the already granted permissions. This would mean that an attacker, instead of getting the user to grant some 'scary' perms directly to them, would just need to hijack the already granted perms of another desk; You could imagine this along the lines of "Install this desk as a dependency". My point being the better UX pattern might be to re-grant permissions with clarity of who you are installing from.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, any desk source code modification is a vulnerable point in that sense. The "Desk Sources" section under "Security Considerations" already talks about this:
|
||
|
|
||
| When an agent affected by a permission is on the desk requesting the permission, a permission manager MAY collate it with similar permissions, or hide them completely. | ||
|
|
||
| Permissions that are functionally equivalent to root access (see Security Considerations below) SHOULD be presented as such. | ||
|
|
||
| ### Requesting Permissions | ||
|
|
||
| Userspace developers SHOULD require (through the `/desk.seal` file) permissions that are essential to the core functioning of their desks. | ||
| For permissions without which the desk can reasonably function, these should be requested and checked at runtime (on-init/load, or on-demand). Developers SHOULD use the standard library-provided utilities for checking permission availability. | ||
|
|
||
| Developers SHOULD request permissions at the smallest reasonable scope. For example, when subscribing at paths of the shape `/data/[some-id]`, request permissions for `/data`, not for `/` (too broad), and not for each individual `/data/whatever` (too narrow). | ||
|
|
||
|
|
||
| ## Rationale | ||
|
|
||
| Permissions apply at the desk level, not at the agent level, because desks map more closely to the concept of "apps" than agents do. A desk may contain many agents, but they're commonly intended to function together as a pre-packaged suite of software. | ||
| Desks are less volatile than agents. Agents within a desk may or may not be running, and an agent may move from running on one desk, to running on a different desk. | ||
| Lastly, desks are more closely tied to specific publishers than agents are. That tie is not absolute though (only "running foreign desks" would be), so switching sources for a desk remains a risk. See also security considerations below. | ||
|
|
||
| The `%base` desk is exempt from permission checking because it runs the kernel. Any code that makes it onto the `%base` desk is implicitly trusted. If the `%base` desk code cannot be trusted, neither can the permissions implementation. | ||
|
|
||
| There are no implicit permissions granted, even for interactions between agents from the same desk, because this complicates the model and implementation, and raises additional security concerns. Hiding or auto-granting self-referential permissions can be done at the display or userspace level if desired. | ||
|
|
||
| There is separation between required/static and optional/dynamic permissions for a couple of reasons. | ||
| - Strictly requiring permissions improves the ergonomics of writing agents. No matter how good the helpers are, a check is a check, necessitates code branching, increases complexity. | ||
| - Specifying permissions ahead-of-time helps users make more-informed decisions about whether to install any given software. | ||
| - Necessary permissions are not always knowable ahead of time. This is almost necessarily true for software that intends to interact with other apps in a generic way: it cannot know what apps the user will make it interact with. | ||
|
|
||
| Treating agent invocations from which the agent tries to do something it does not have permission for as crashes matches the behavior of `.^` on non-existent paths and makes it obvious that the entire invocation is null and void. The latter is important to avoid internal inconsistencies in agents. xx better phrasing | ||
| The alternative would be to inject a "permission nack" into `+on-agent` or `+on-arvo` to notify them that their effect was prevented from executing. This cannot be done for `.^`, and results in more "loose ends" for developers to handle. xx phrasing | ||
| Developers should be encouraged to check permissions prior to emitting effects so they can handle failure cases eagerly/synchronously, rather than firing them off indiscriminately and handling failures lazily/asynchronously. | ||
| (To improve developers' ability to handle resulting `+on-fail` calls appropriately, that interface should be expanded to provide more details about the failed invocation. However, doing so is out of scope for this UIP.) | ||
|
|
||
| ### Interface and Types | ||
|
|
||
| Considering the possibility of building "app managers" in userspace, it is important for the kernel to expose permission information and management capabilities. | ||
|
|
||
| xx kernel-style subscriptions follow established pattern, see examples | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The big unknown for user interfaces is going to be TUI apps and optional permissions. It would suck to force TUI apps to end up using the generic kernel web interface but I don't have an easy solution for how they would get user permissions. Maybe dojo has some affordances
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As discussed during core architecture today: Given the permission management situation as specified (with Eyre serving a "stock" manager), every UI capable of displaying text has the out of displaying a link to that manager. This is not ideal for non-browser UIs, because it takes you out of context, but it will work. For dill-based TUIs in particular, there are thorough solutions here, but they require a ton of out-of-scope work. You can imagine dill, or drum, or some alternative having a rendering affordance that's spiritually equivalent to an iframe, letting apps say "show the |
||
|
|
||
| xx scoping | ||
|
|
||
| ### Permissions and Desk Lifecycles | ||
|
|
||
| Clay already manages desk "liveness" status and transitions. Permissions, applying at the desk level, overlap with this nicely. | ||
| xx desk liveness requirements | ||
|
|
||
| Automatically granting required permissions for desks present/installed during the boot sequence ensures the immediate post-boot state is "complete" according to the sequence's intent, without requiring additional permission-granting events to be formalized into the boot sequence. | ||
|
|
||
| Automatically granting required permissions for desks present/installed during the permissions upgrade ensures the upgrade can happen without user intervention. | ||
|
|
||
| ### Permission Type Upgrades | ||
|
|
||
| xx kelvin upgrade / changed permission types discussion | ||
| xx step-wisdom isn't formal but that can't stop us, there is precedent for awkward "choke-point" kelvins | ||
| xx we don't expect to change the permission type very often | ||
|
|
||
| ### Presentation and Management | ||
|
|
||
| Permission managers are advised to prevent the granting of permissions affecting unknown agents. Disregarding this advice saddles the user with an unanswerable question: what does it mean to read from or write to an app I have no awareness of? | ||
| "I trust it will be fine" can be used as an answer, but doing so is not risk-free. It is possible to lower that risk by, when installing a new app, showing all permissions granted to other desks that affect the to-be-installed app. | ||
|
|
||
| All of that assumes the `/desk.bill` file is a complete list of all agents that will be running on a desk. For scenarios where there are "optional" agents on a desk, the above becomes more nuanced. Arguably, in those cases, the relevant permissions should be optional, requested at runtime only once the optional agent has been observed to be running. | ||
|
|
||
| Permissions concerning agents on other ships should be presented generically, because there can be no guarantee about what software is running under what name on remote ships. | ||
|
|
||
| ### Requesting Permissions | ||
|
|
||
| Requesting permissions ahead-of-time improves both developer and user ergonomics: the developer never has to check permission status, and the user does not need to be prompted for those permissions after installing the app. | ||
| Requesting permissions dynamically grants users meaningful control over the behavior of the software they run, in practice letting them en- or disable features according to their level of trust in the software. | ||
|
|
||
|
|
||
| ## Backwards Compatibility | ||
|
|
||
| Userspace developers will need to specify the permissions which their agents require. Failure to do so will result in runtime crashes. | ||
|
|
||
| xx summarize upgrade instructions for userspace devs | ||
|
|
||
|
|
||
| ## Security Considerations | ||
|
|
||
| ### Eyre Security | ||
|
|
||
| This UIP describes a permission system for gall-level interactions. Other vanes also interact with gall's userspace. In general, the kernel is trusted software, so does not need to be checked for permissions. Eyre is an exception to this: it allows outside callers (anything capable of making HTTP requests) to interact with gall. To make userspace permissions hold water in real-world scenarios, permission checks must be extended to these outside callers. | ||
|
|
||
| A detailed specification for that work is outside of the scope of this UIP. In summary: eyre will append a desk to its provenance path when interacting with gall. Gall will check for permission to perform that interaction, as if it had originated from that desk. Eyre determines that desk based on the scope of the authenticating cookie. Eyre safeguards scoped cookies against trivial theft by serving desks on their own subdomains. | ||
|
|
||
| At the time of writing, that line of work is being pursued, and a working prototype is [available on the `eswg/wip` branch of urbit/urbit](https://github.com/urbit/urbit/compare/develop...eswg/wip). Formal UIP to follow. | ||
|
|
||
| ### Implicit Root | ||
|
|
||
| There are many seemingly-narrow permissions that are functionally equivalent to root, usually by interacting with something on the base desk. Care should be taken to enumerate all of these and inform the user about them appropriately whenever possible. A non-exhaustive list of such permissions is given below. | ||
|
|
||
| - pass `%dill` a `%belt` | ||
| - pass `%clay` a `[%park %base ...]` | ||
| - poke `%hood` with `&helm-pass` | ||
| - poke `%herm` with `%herm-task` | ||
|
|
||
| ### Desk Sources | ||
|
|
||
| Changing the installation source of a desk for which permissions have been granted implies trusting the new source with all of the granted permissions. Revoking permissions when changing installation source may be excessive, but warning about this in installation UI could be sensible. | ||
| Updating clay to support running off source desks directly (that is, running agents from a `[=ship =desk]` instead of a necessarily-local `desk`) would mitigate this, but such a change is outside the scope of this UIP. | ||
|
|
||
|
|
||
| ## Copyright | ||
|
|
||
| Copyright and related rights waived via [CC0](../LICENSE.md). | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How should this account for
%spider? Should a thread from a desk%fooinherit the permissions of%fooor the permissions of the%basewhere%spiderlives? Is this implemented in%spideror in the%khanvane?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch.
Spider is essentially implementing a "userspace within userspace". As such, if it wants to restrict the capabilities of the threads it runs, it itself is responsible for doing so.
Open question on whether threads should be restricted by the desk that supplies them, or by the desk that invokes them. (I'd guess the latter but defer judgement.) Both are possible.
In the former case, the relevant desk is obvious. In the latter case, when spider gets invoked, the
sap.bowlshould reflect the agent trying to start the thread. Gall has a scry for getting the desk an agent is running from.Because spider runs from the base desk, it's free to read the permissions that have been granted to each desk from clay. It can then check the thread's effects against those permissions.
The only thing I can't immediately say with certainty is the
.^story. Right now spider runs threads with+mule. Surely it must be possible to replace that with (essentially) a typed+mockinstead? That way spider gets to control the scry resolution of threads too, can reject them based on perms if desired.I vaguely remember there being demons here, but maybe I'm conflating this with attempts at capturing
.^failures in userspace?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Khan just defers to spider for running its threads. It should be responsible for calling spider in such a way that the caller provenance remains legible.
(The existence of "inline threads" further confirms my suspicion that thread effects should be restricted by their caller, rather than the desk they are supplied by: inline threads have no source desk.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'd also want to have mandatory Spider-supplied parent TIDs for child threads rather than optional user-supplied parent TIDs to ensure correct permission inheritance.