We need a way to defined REST endpoints that are restricted for use by trusted clients under the control of the wiki maintainer, typically inside the local network. The driving use case for this is the need for a mechanism to execute jobs through HTTP requests from changeprop-jobrunner, see T175146.
Proposal
REST modules represent a collection of related endpoints covered by a versioned spec (see T362480). Each module should be able to declare an "audience designation" (see T366567: REST: introduce audience designations (proposal)). If should add a way to define a mechanism for protecting modules against unwanted access based on its audience designation. That would provide a way to flag mdodules as "private", so they can implement functionality that would be unsafe to offer to the general public.
Multiple protection mechanisms can be made availabel that can be applied separately or in combination. Three mechanisms should probably be supported from the start:
- user-group: This will deny access to any user not in any of the listed groups. This is a natural way of restricting access, but may be impactical or cumbersome, depending on the available authentication mechanism. (TBD: we could also require user rights rather than groups).
- network: This will allow access only for requests coming from an address matchign one of the network ranges provided. This is a convenient way to allow access to services on the local network, but can easily be misconfigured to allow access to anyone, when requests are routed through a proxy.
- signed: Require requests to be signed according to RFC 9421, based on a shared secret, sich as $wgSecretKey. Libraries for request signing are available for a wide range of languages (through mostly based on earlier draft versions of the RFC, which is fairly young). This is useful e.g. in the context of end-to-end testing using a framework like Mocha: a key can easily be shared between the test code and the server under test, and the same configuration will work on test systems and local development setups without having to worry about changing network addresses.
- allow: Can be set to either true (no protection) or false (the module is disabled). This could be used to allow access during testing, or prevent access on servers that are serving public traffic. This would be false per default, so private modules are not accessible on vanilla installs. It could be set to true in DevelopmentSettings.php.
Example:
// Enable HMAC authorization for private modules $wgRestAPIProtection['private'] = [ 'network' => [ '192.168.0.0/24' ], 'signed' => [ 'hmac-keys' => [ 'default' => $wgSecretKey ], ], ];
Use at WMF
The deriver for this proposal is T175146: JobQueue: Unify JobRunner entry points. To address that task, we could define a jobqueue module and designate it as private. We can use the trivial allow protection to disable private modules on hosts that serve page views, and allow it on the jobrunner cluster:
Example:
$wgRestAPIProtection['private'] = [ 'allow' => true, ];
This would be equivalent to the way we currently restrict access to the RunSignleJob.php script (T362480). It would be sufficient for our own use in production. Other environments would have to use different protection mechanisms.
Rationale for using audience designations
All this could be achieved by having a was to flag modules as "private", and then configuring what protection should apply for private modules.
However, it seems useful to generalize the concept of "protection" for "audience designations" to allow different levels of protection for different sets of modules. For example, beyond the very struct "private" designation, we may also have a "bots" deswignation that would limit access to certain APIs to users in the "bots" group. Or we could define very weak protection based on the User-Agent, to (nominally) restrict access to certain modules to the Wikipedia App.
$wgRestModuleDesignations= [ 'edit.v2' => [ 'bots' ], 'pci.v2 => [ 'apps' ], ]; $wgRestAPIProtection['bots'] = [ 'user-group' => [ 'bot' ], ]; $wgRestAPIProtection['apps'] = [ 'user-agent' => '/^Wikipedia-App/', ];
For more information on audience designations, see https://docs.google.com/document/d/1yarF_xQkFzQJUOvP3rMooFTFL6tKgK3C-Bf8zV00QR4/edit