So let's talk about sandboxing plugins and plugin APIs (mostly ignoring managed lifecycle for now, as that's fairly orthogonal). I'll first sketch an approach that gets around an explicit representation of plugin APIs, and then argue why I think that might make sense.
Sandboxing works through capabilities. By default, a plugin has no capabilities other than allocating memory and maintaining its connection to the server. The protocol defines a set of capabilities, including but not limited to file io, socket io, reading the ssb database, writing to the database, telling the server to establish a connection to another server, talking to remote plugins (possibly filtered by pubkey and/or name), and so on. A plugin specifies which of those capabilities it needs, and if the server grants them (as ultimately instructed by the user), then the plugin is started in a sandbox that only allows those os io actions for which it has capabilities (and the server disallows rpcs for accessing the ssb db etc if those capabilities were not given).
A plugin a
may only communicate with another other plugin b
if b
has no capability that a does not already have as well. And peeking into managed lifecycles: The same condition holds for plugin a
starting up plugin b
.
That's basically it. The big difference to what I read between the lines of @Dominic and @dinosaur comments/proposals/drafts is that the whole system is agnostic to how plugins actually talk to each other. They don't specify an rpc api, and they don't specify capabilities needed per rpc. A short list of nice things about that:
- fewer stuff the server needs to know about, fewer security-critical stuff the server could get wrong
- does not impose any specifically structured protocols upon plugins
- no need to declaratively specify an API (that needs to be manually implemented anyways, so there's no simplicity gained through declarativity)
- inter-plugin communication capabilities do not have to be specified explicitly, but emerge naturally
In an alternative setting with explicitly named and tracked rpcs, you would get the additional expressive power of assigning different capabilities to different rpcs of the same plugin. That is honestly the only advantage of such a system I can come up with (ignoring introspection, which is orthogonal and which we can discuss at another time). Well, and upon closer examination that advantage disappears, because you can not reliably do this. The plugin as a whole is granted a single set of capabilities, so to fully function, it needs to request the union of the capabilities of all its rpcs. But then, if we don't trust the plugin (and that's the whole reason for the sandboxing in the first place), we can not trust its claim that a certain rpc won't make use of undeclared capabilities. So we end up with a single set of capabilities for the whole plugin, just as in the system I sketched above.
There are a few possible ways of extending the system: Rather than binarily either granting all capabilities or not starting the plugin at all, it could be possible to inform it of the subset of capabilities it was granted. The plugin can then decide whether it wants to shut down or whether it can operate in a restricted mode. Note that this is does not fix the issue about rpc-level capabilities not working: If a plugin is granted all its capabilities, than less privileged plugins still can not be allowed to call methods for which the plugins claims that it does not need some powerful capabilities, because we still can't trust that claim.
As a final note: It could be possible to do dynamic capability (re)assignment, and to define capabilities for talking to specific plugins on the fly. I mention these for completeness' sake, but I think these are bad ideas. The security enforcing parts should stay as simple as possible.
@Dominic I'd love to hear your thoughts on this approach for dealing with inter-plugin communication restrictions, even if it is just a simple "ack".
I'm not fundamentally opposed to enforcing some sort of introspective rpc protocol for plugins (although I would prefer to not do that). There are some good arguments for that (consistency, introspection, stability of the ecosystem), my main point is that capability-based security is not among those reasons.
This post was powered by Felix Mendelssohn Bartholdy. I should listen to music more often =)