mirror of
https://github.com/google/nomulus
synced 2026-03-27 12:55:28 +00:00
This doesn't update everything -- it leaves out some of the more complicated changes (architecture, code-structure, configuration, install, and proxy-setup). Those will require more complete rewrites, so I'm punting them to a future PR.
197 lines
9.3 KiB
Markdown
197 lines
9.3 KiB
Markdown
# Authentication framework
|
|
|
|
Nomulus performs authentication and authorization on a per-request basis. Each
|
|
endpoint action defined has an `@Action` annotation with an `auth` attribute
|
|
which determines the ways a request can authenticate itself, as well as which
|
|
requests will be authorized to invoke the action.
|
|
|
|
## Authentication and authorization properties
|
|
|
|
The `auth` attribute is an enumeration. Each value of the enumeration
|
|
corresponds to a pair of properties:
|
|
|
|
* the *minimum authentication level* which is authorized to run the action
|
|
* the *user policy* for the action
|
|
|
|
### Authentication Levels
|
|
|
|
There exist three levels of authentication level:
|
|
|
|
* `NONE`: no authentication was found
|
|
* `APP`: the request was authenticated, but no user was present
|
|
* `USER`: the request was authenticated with a specific user
|
|
|
|
`NONE` and `USER` are fairly straightforward results (either no authentication
|
|
was present, or a user was present), but `APP` is a bit of a special case. It
|
|
exists for requests coming from service accounts, Cloud Scheduler, or the
|
|
proxy -- requests which are authenticated but don't necessarily come from any
|
|
one particular "user" per se. That being said, authorized users *can* manually
|
|
run these tasks; it's just that service accounts can too.
|
|
|
|
Each action has a minimum request authentication level. Some actions (e.g. RDAP)
|
|
are completely open to the public, and have a minimum level of `NONE`. Some
|
|
require authentication but not necessarily a user, and have a minimum level of
|
|
`APP`. And some cannot function properly without knowing the exact user, and
|
|
have a minimum level of `USER`.
|
|
|
|
### User policy
|
|
|
|
The user policy indicates what kind of user is authorized to execute the action.
|
|
There are two possible values:
|
|
|
|
* `PUBLIC`: an authenticated user is required, but any user will do
|
|
(authorization is done at a later state)
|
|
* `ADMIN`: there must be an authenticated user with admin privileges (this
|
|
includes service accounts)
|
|
|
|
Note that the user policy applies only to the automatic checking done by the
|
|
framework before invoking the action. The action itself may do more checking.
|
|
For instance, the registrar console's main page has no authentication at all,
|
|
and all requests are permitted. However, the first thing the code does is check
|
|
whether a user was found. If not, it issues a redirect to the login page.
|
|
|
|
Likewise, other pages of the registrar console have a user policy of `PUBLIC`,
|
|
meaning that any logged-in user can access the page. However, the code then
|
|
looks up the user to make sure he or she is associated with a registrar.
|
|
|
|
Also note that the user policy only applies when there is actually a user. Some
|
|
actions can be executed either by an admin user or by an internal request coming
|
|
from a task queue, which will not have a defined user at all. So rather than
|
|
determining the minimum user level, this setting should be thought of as
|
|
determining the minimum level a user must have *if there is a user at all*. To
|
|
require that there be a user, set the minimum authentication level to `USER`.
|
|
|
|
### Allowed authentication and authorization values
|
|
|
|
There are three pairs of authentication level + user policy that are used in
|
|
Nomulus (or even make sense). These are:
|
|
|
|
* `AUTH_PUBLIC`: Allow all access and don't attempt to authenticate. This is
|
|
used for completely public endpoints such as RDAP.
|
|
* `AUTH_PUBLIC_LOGGED_IN`: Allow access only by users authenticated with some
|
|
type of OAuth token. This allows all users (`UserPolicy.PUBLIC`) but
|
|
requires that a particular user exists and is logged in (`AuthLevel.USER`).
|
|
This is used primarily for the registrar console.
|
|
* `AUTH_ADMIN`: Allow access only by admin users or internal requests
|
|
(including Cloud Scheduler tasks). This is appropriate for actions that
|
|
should only be accessed by someone trusted (as opposed to anyone with a
|
|
Google login). This permits app-internal authentication (`AuthLevel.APP`)
|
|
but if a user is present, it must be an admin (`UserPolicy.ADMIN`). This is
|
|
used by many automated requests, as well as the proxy.
|
|
|
|
### Action setting golden files
|
|
|
|
To make sure that the authentication and authorization settings are correct and
|
|
expected for all actions, a unit test uses reflection to compare all defined
|
|
actions for a specific service to a
|
|
[golden file](https://github.com/google/nomulus/blob/master/core/src/test/resources/google/registry/module/routing.txt)
|
|
containing the correct settings.
|
|
|
|
Each line in the file lists a path, the class that handles that path, the
|
|
allowable HTTP methods (meaning GET and POST, as opposed to the authentication
|
|
methods described above), the value of the `automaticallyPrintOk` attribute (not
|
|
relevant for purposes of this document), and the two authentication and
|
|
authorization settings described above. Whenever actions are added, or their
|
|
attributes are modified, the golden file needs to be updated.
|
|
|
|
The golden file also serves as a convenient place to check out how things are
|
|
set up. For instance, the backend actions are accessible to admins and internal
|
|
requests only, the pubapi requests are open to the public, and console requests
|
|
require an authenticated user.
|
|
|
|
### Example
|
|
|
|
The `EppTlsAction` class handles EPP commands which arrive from the proxy via
|
|
HTTP. Only admin users and internal requests should be allowed to execute this
|
|
action, to avoid anyone on the Internet sending us random EPP commands. Further,
|
|
the HTTP method needs to be `POST`, so that the EPP command is contained in the
|
|
body rather than the URL itself (which could be logged). Therefore, the class
|
|
definition looks like:
|
|
|
|
```java
|
|
|
|
@Action(
|
|
service = Action.Service.FRONTEND,
|
|
path = "/_dr/epp",
|
|
method = Method.POST,
|
|
auth = Auth.AUTH_ADMIN)
|
|
public class EppTlsAction implements Runnable {
|
|
...
|
|
```
|
|
|
|
and the corresponding line in frontend_routing.txt (including the header line)
|
|
is:
|
|
|
|
```shell
|
|
SERVICE PATH CLASS METHODS OK MIN USER_POLICY
|
|
FRONTEND /_dr/epp EppTlsAction POST n APP ADMIN
|
|
```
|
|
|
|
## Implementation
|
|
|
|
The code implementing the authentication and authorization framework is
|
|
contained in the `google.registry.request.auth` package. The main method is
|
|
`authorize()`, in `RequestAuthenticator`. This method takes the auth settings
|
|
and an HTTP request, and tries to authenticate and authorize the request,
|
|
returning the result of its attempts. Note that failed authorization (in which
|
|
case `authorize()` returns `Optional.absent()`) is different from the case where
|
|
nothing can be authenticated, but the action does not require any; in that case,
|
|
`authorize()` succeeds, returning the special result
|
|
AuthResult.NOT_AUTHENTICATED.
|
|
|
|
The ultimate caller of `authorize()` is
|
|
`google.registry.request.RequestHandler`, which is responsible for routing
|
|
incoming HTTP requests to the appropriate action. After determining the
|
|
appropriate action, and making sure that the incoming HTTP method is appropriate
|
|
for the action, it calls `authorize()`, and rejects the request if authorization
|
|
fails.
|
|
|
|
### Authentication methods
|
|
|
|
Nomulus requests are authenticated via OIDC token authentication, though these
|
|
tokens can be created and validated in two ways. In each case, the
|
|
authentication mechanism converts an HTTP request to an authentication result,
|
|
which consists of an authentication level, a possible user object, and a
|
|
possible service account email.
|
|
|
|
#### `IapOidcAuthenticationMechanism`
|
|
|
|
Most requests, e.g. the registrar console or Nomulus CLI requests) are routed
|
|
through GCP's
|
|
[Identity-Aware Proxy](https://docs.cloud.google.com/iap/docs/concepts-overview).
|
|
This forces the user to log in to some GAIA account (specifically, one that is
|
|
given access to the project). We attempt to validate a provided IAP OIDC token
|
|
with the IAP issuer URL (`https://cloud.google.com/iap`) and the proper IAP
|
|
audience (`/projects/{projectId}/global/backendServices/{serviceId}`), where
|
|
`projectId` refers to the GCP project, and `serviceId` refers to the service ID
|
|
retrievable from the
|
|
[IAP configuration page](https://pantheon.corp.google.com/security/iap).
|
|
Ideally, this service ID corresponds to the HTTPS load balancer that distributes
|
|
requests to the GKE pods.
|
|
|
|
Note: the local Nomulus CLI's
|
|
[LoginCommand](https://github.com/google/nomulus/blob/master/core/src/main/java/google/registry/tools/LoginCommand.java)
|
|
uses a special-case form of this where it saves long-lived IAP credentials
|
|
locally.
|
|
|
|
#### `RegularOidcAuthenticationMechanism`
|
|
|
|
Service account requests ( e.g.
|
|
[Cloud Scheduler jobs](https://docs.cloud.google.com/scheduler/docs/schedule-run-cron-job))
|
|
or requests coming through the proxy use a non-IAP OIDC token provided by the
|
|
caller. These requests have a different issuer URL (
|
|
`https://accounts.google.com`) and use the fairly standard OAuth bearer token
|
|
architecture -- an `Authorization` HTTP header of the form "Bearer: XXXX".
|
|
|
|
### Configuration
|
|
|
|
The `auth` block of the configuration requires two fields: *
|
|
`allowedServiceAccountEmails` is the list of service accounts that should be
|
|
allowed to run tasks when internally authenticated. This will likely include
|
|
whatever service account runs Nomulus in Google Kubernetes Engine, as well as
|
|
the Cloud Scheduler service account. * `oauthClientId` is the OAuth client ID
|
|
associated with IAP. This is retrievable from the
|
|
[Clients page](https://pantheon.corp.google.com/auth/clients) of GCP after
|
|
enabling the Identity-Aware Proxy. It should look something like
|
|
`someNumbers-someNumbersAndLetters.apps.googleusercontent.com`
|