Locking down REST and/or JSON-RPC APIs in a generic way, with an object capability security model.
Given some sort of priviliged access to a backend, this proxy would expose the service with no changes to the API apart from additional requirement for macaroon authorization.
A second mode would also be able to expose such a service to an unrestricted port, by statically configuring a macaroon to be submitted to an upstream endpoint, allowing hardened APIs to be exposed with no macaroon required.
These two modes should normally be used in separate processes, but could also be deployed in a single processes space without actual macaroons validation (i.e. just a static list of 1st party caveats to apply to some proxied connections).
- time expiry (stateless)
- rate limiting (in memory state)
- use limits (persistent state, e.g. up to n requests)
- socket metadata (client address, etc)
Note that HTTP and JSON-RPC are orthogonal, either or both might be appropriate for a given service. JSON-RPC should support REST as well as stream (UNIX/TCP) based JSON-RPC, including bitcoind's nonstandard implementation ("in band" errors reporting).
- HTTP
- method & path validation
- header requirements
- request body constraints
- formal validity (JSON, multipart form, etc)
- response status types (e.g. to avoid leaking internal errors)
- JSON-RPC
- constraining method sets
- JSON schema (for params or REST JSON bodies) - care must be taken to not load external references unless explicitly allowed in server configuration, and to parse caveats containing schemas conservatively or to provide a server whitelist of schema based caveats. a reasonable option is to trust upstream schema definitions (e.g. swagger APIs) but allow caveats to constrain them
- what about more semantic validation later on? perhaps simple JSON parsing preprocessors, e.g. to split strings, or extract numerical values, to make schema based validation more useful?
- response binding - this would be too complicated for the initial version, but in principle responses can be used to mint macaroons with caveats (e.g. issuing a successful request grants you access to call some other method, but only with e.g. a parameter from the response)
What about GRPC? generic Protobuf? cap'n'proto? these could be added later using some custom build tool to compile a server binary with the schema definitions if the internal APIs are sufficiently generic, but this is substantially more complex than dynamic validation
Server configuration requires a graph of 3 layers for defining service routing:
- list of named upstream services of various types, with optional access macaroons
- named services can be encumbered by associating with a MAC secret for macaroon validation (static or generated on each startup), or with a static list of 1st party caveats, with composition
- internally encumbered services can then be exposed to listeners
the configuration itself should conceptually be a list of operations to be replayed at startup, constructing the capability graph, binding sockets, etc, so that an administrative RPC api can also be added later (though I'm dubious about use cases, apart from having an administrative API that can mint new macaroons, but it might be a good dogfooding exercise)
Priviliged macaroons would be exported as files on startup, and a separate utility can restrict macaroons by adding caveats known to be understood by the server.
For easy hardening of well known APIs, a reviewed library of stock caveats could be maintained outside of the main server code base.