Project | LXD |
Status | Implemented |
Author(s) | @monstermunchkin |
Approver(s) | @stgraber @tomp |
Release | 5.13 |
Internal ID | LX037 |
Abstract
This adds OpenID Connect as a new authentication method.
Rationale
LXD currently supports only tls
and candid
as authentication methods. The former uses certificates for authentication, and the latter requires a candid server which is a macaroon-based authentication service.
OpenID Connect (OIDC) is a simple identity layer on top of the OAuth 2.0 protocol. One of the advantages of OIDC is that it allows authentication without LXD having to store and manage users and passwords.
Specification
Design
The following new server configuration keys will be added:
oidc.issuer
oidc.client.id
oidc.url_params
The oidc.issuer
key contains the URL of the server issuing the tokens. This for example could point to a Keycloak server.
Clients are applications and services that can request authentication of a user, and communicate with the issuer using the oidc.client.id
. OAuth2 supports two different kinds of clients: confidential clients, and public clients. For confidential clients, a client secret is needed which also needs to be safe. Since we cannot ensure this (as our code is public), the LXD CLI will act as a public client.
The oidc.url_params
configuration key is optional, and tells the client which URL parameters it needs to add when logging in. Some OIDC Providers require setting the audience
URL parameter, otherwise they will issue an empty access token.
OIDC can be enabled by simply setting oidc.issuer
and oidc.client.id
.
If the client wants to communicate with the LXD server using OpenID, it sets the X-LXD-oidc
HTTP header for every request. That way the server knows what kind of authentication to use.
LXD advertises the authentication methods it supports. The oidc
value will be added to this list. oidc
should be favored over candid
which itself is favored over tls
.
Adding a new remote
The user can add a new remote using lxc remote add <foo> --auth-type=oidc
.
Authentication
If the client cannot be authenticated, the LXD server returns an error containing the issuer, client ID, and client secret. It also contains the type of error and can be one of the following:
authentication request
invalid token
The authentication request
error means that the user needs to log in. The invalid token
error most likely means that the token has expired, but it can also have a different reason. This error also contains the reason which can be one of these errors.
Using the issuer, client ID, and client secret, the user will be presented with a login page. On successful login, the client receives an access token, refresh token, and ID token. These are stored on disk.
If invalid token
was returns by the LXD server, and the refresh token is still valid, a new access token will be retrieved without user interaction. Otherwise the login page will be presented again.
Once the tokens have been retrieved, the only one used for authentication by the LXD server is the access token. The others stay with the user and client. The ID token is not used for anything beyond this point, and the refresh token is for refreshing the access token.
The access token will be sent with every request using the Authorization
HTTP header:
Authorization: Bearer <access_token>
Since the access token is a JSON Web Token (JWT), it can be decoded and validated offline (i.e. without having to call the OpenID Provider). The LXD server will do this on every request, and return an error if the validation fails.
Initial flow to get token
- User tries adding remote with
lxc remote add <remote> --auth-type=oidc
- LXD server returns OIDC information (issuer and client ID)
- User is asked to log in using the browser using the provided URL
- Once logged in, the client does a code exchange in order to get the tokens (access token, refresh token, and ID token)
- Client uses access token to access LXD
Valid access token
- Client sets
Authorization
HTTP header with access token to access LXD
Expired access token but valid refresh token
- Client sets
Authorization
HTTP header with access token to access LXD - LXD returns OIDC information together with the
invalid token
error - Client gets a new access token using the refresh token
- Client uses new access token to access LXD
Expired access token and expired refresh token
- Client sets
Authorization
HTTP header with access token to access LXD - LXD returns OIDC information together with the
invalid token
error - Client fails to get a new access token using the expired refresh token
- User is required to log in from the browser, and client uses OIDC information to get new tokens
- Client uses new access token to access LXD
CLI changes
The --auth-type
flag for lxc remote add
accepts oidc
.
Upgrade handling
If there’s a remote configured which uses candid, and the LXD server adds OIDC support, the client will continue using candid. OIDC should only then be used when adding a new remote.
Further information
OIDC clients use scope values to specify what access privileges are being requested for access tokens. The oidc
scope is required when using OIDC authentication. The offline_access
scope is used for getting refresh tokens. The LXD client will therefore use both of these scopes.