Configuring an ingress manually#
Normally, you should create Kubernetes Ingress
resources for protected applications using the GafaelfawrIngress
custom resource.
This is simpler, allows Gafaelfawr to do a lot of the work for you, and avoids annoying-to-debug syntax errors.
This is documented in Configuring ingress with GafaelfawrIngress.
However, sometimes you can’t create the Ingress
resource via other means and instead need to manually configure it with annotations.
The most common example is third-party Helm charts that allow changing the ingress class and annotations, but not (easily) delegating it to a custom resource.
For those cases, follow this documentation to construct the ingress annotations manually.
This documentation only discusses how to create annotations that are equivalent to what GafaelfawrIngress
would have done, and does not describe in detail what those annotations do.
To use this documentation, find the configuration that you need in Configuring ingress with GafaelfawrIngress, read that section to understand the configuration options, and then find the corresponding section in this guide to see what annotations to add.
See How Gafaelfawr-protected ingresses work for an overview of how Gafaelfawr protects services.
Basic configuration#
The required minimum annotations for a web service that returns 401 if the user is not authenticated are:
annotations:
nginx.ingress.kubernetes.io/auth-method: "GET"
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization,Cookie,X-Auth-Request-Email,X-Auth-Request-User"
nginx.ingress.kubernetes.io/auth-url: "https://<hostname>/auth?scope=<scope>"
nginx.ingress.kubernetes.io/configuration-snippet: |
auth_request_set $auth_www_authenticate $upstream_http_www_authenticate;
auth_request_set $auth_status $upstream_http_x_error_status;
auth_request_set $auth_error_body $upstream_http_x_error_body;
error_page 403 = @autherror;
Replace <hostname>
with the hostname of the ingress on which the Gafaelfawr routes are configured, and <scope>
with the name of the scope that should be required in order to visit this site.
Multiple scopes may be requested by repeating the scope
parameter.
For example, to require both read:tap
and read:image
scopes, use:
annotations:
nginx.ingress.kubernetes.io/auth-method: "GET"
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization,Cookie,X-Auth-Request-Email,X-Auth-Request-User"
nginx.ingress.kubernetes.io/auth-url: "https://<hostname>/auth?scope=read:tap&scope=read:image"
nginx.ingress.kubernetes.io/configuration-snippet: |
auth_request_set $auth_www_authenticate $upstream_http_www_authenticate;
auth_request_set $auth_status $upstream_http_x_error_status;
auth_request_set $auth_error_body $upstream_http_x_error_body;
error_page 403 = @autherror;
To allow any one of the listed scopes to grant access, instead of requiring the user have all of the scopes, add satisfy=any
:
annotations:
nginx.ingress.kubernetes.io/auth-method: "GET"
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization,Cookie,X-Auth-Request-Email,X-Auth-Request-User"
nginx.ingress.kubernetes.io/auth-url: "https://<hostname>/auth?scope=read:tap&scope=read:image&satisfy=any"
nginx.ingress.kubernetes.io/configuration-snippet: |
auth_request_set $auth_www_authenticate $upstream_http_www_authenticate;
auth_request_set $auth_status $upstream_http_x_error_status;
auth_request_set $auth_error_body $upstream_http_x_error_body;
error_page 403 = @autherror;
Redirecting users to log in#
To redirect the user to the login page instead of returning a 401 error if the user is not authenticated, add an auth-signin
annotation as well:
annotations:
nginx.ingress.kubernetes.io/auth-method: "GET"
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization,Cookie,X-Auth-Request-Email,X-Auth-Request-User"
nginx.ingress.kubernetes.io/auth-signin: "https://<hostname>/login"
nginx.ingress.kubernetes.io/auth-url: "https://<hostname>/auth?scope=<scope>"
nginx.ingress.kubernetes.io/configuration-snippet: |
auth_request_set $auth_www_authenticate $upstream_http_www_authenticate;
auth_request_set $auth_status $upstream_http_x_error_status;
auth_request_set $auth_error_body $upstream_http_x_error_body;
error_page 403 = @autherror;
Requesting delegated tokens#
To request a delegated internal token, use these annotations:
annotations:
nginx.ingress.kubernetes.io/auth-method: "GET"
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization,Cookie,X-Auth-Request-Email,X-Auth-Request-User,X-Auth-Request-Token"
nginx.ingress.kubernetes.io/auth-signin: "https://<hostname>/login"
nginx.ingress.kubernetes.io/auth-url: "https://<hostname>/auth?scope=<scope>&delegate_to=<service>&delegate_scope=<delegate-scope>,<delegate-scope>"
nginx.ingress.kubernetes.io/configuration-snippet: |
auth_request_set $auth_www_authenticate $upstream_http_www_authenticate;
auth_request_set $auth_status $upstream_http_x_error_status;
auth_request_set $auth_error_body $upstream_http_x_error_body;
error_page 403 = @autherror;
<service>
should be replaced with an internal identifier for the service.
<delegate-scope>
is a comma-separated list of scopes requested for the internal token.
The token will be included in the request in an X-Auth-Request-Token
header, and thus must be added to the auth-response-headers
annotation.
For the special case of notebook tokens, instead use:
annotations:
nginx.ingress.kubernetes.io/auth-method: "GET"
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization,Cookie,X-Auth-Request-Email,X-Auth-Request-User,X-Auth-Request-Token"
nginx.ingress.kubernetes.io/auth-signin: "https://<hostname>/login"
nginx.ingress.kubernetes.io/auth-url: "https://<hostname>/auth?scope=<scope>¬ebook=true"
nginx.ingress.kubernetes.io/configuration-snippet: |
auth_request_set $auth_www_authenticate $upstream_http_www_authenticate;
auth_request_set $auth_status $upstream_http_x_error_status;
auth_request_set $auth_error_body $upstream_http_x_error_body;
error_page 403 = @autherror;
In both cases, services designed for API instead of browser access can omit the nginx.ingress.kubernetes.io/auth-signin
to return authentication challenges to the user instead of redirecting them to the login page.
To request that the delegated token also be passed in the Authorization
header as a bearer token, append &use_authorization=true
to the nginx.ingress.kubernetes.io/auth-url
annotation.
Header filtering for anonymous ingresses#
If an ingress shares a hostname with any authenticated service, it should still configure Gafaelfawr to perform header filtering even if it allows anonymous access. This prevents leakage of Gafaelfawr credentials to underlying services.
To do this with a manually-configured ingress, add the following annotations:
annotations:
nginx.ingress.kubernetes.io/auth-method: "GET"
nginx.ingress.kubernetes.io/auth-response-headers: "Authorization,Cookie"
nginx.ingress.kubernetes.io/auth-url: "https://<hostname>/auth/anonymous"
Note the different auth-url
route.
Configuring authentication#
The URL in the nginx.ingress.kubernetes.io/auth-url
annotation accepts several parameters to customize the authentication request.
Most but not all of these are discussed above.
scope
(required)The scope claim that the client JWT must have. May be given multiple times. If given multiple times, the meaning is govered by the
satisfy
parameter. Scopes are determined by mapping the group membership provided by the authentication provider, using theconfig.groupMapping
Helm chart value. See Scopes for more information.satisfy
(optional)How to interpret multiple
scope
parameters. If set toall
(or unset), the user’s token must have all of the given scopes. If set toany
, the user’s token must have one of the given scopes.auth_type
(optional)Controls the authentication type in the challenge returned in
WWW-Authenticate
if the user is not authenticated. By default, this isbearer
. Services that want to prompt for HTTP Basic Authentication should set this tobasic
instead.notebook
(optional)If set to a true value, requests a notebook token for the user be generated and passed to the service in the
X-Auth-Request-Token
header. This may not be set at the same time asdelegate_to
.delegate_to
(optional)If set, requests an internal token. The value of this parameter is an identifier for the service that will use this token to make additional requests on behalf of the user. That internal token will be generated if necessary and passed in the
X-Auth-Request-Token
header. This may not be set at the same time asnotebook
.delegate_scope
(optional)A comma-separated list of scopes that the internal token should have, if available from the authenticating token. Only meaningful when
delegate_to
is also set.By default, these scopes are optional. The delegated token will have each scope listed if the authenticating token has that scope, but if it does not, authentication will still succeed and a delegated token will still be passed down but some scopes will be missing. If the protected service wants to ensure that all requested scopes are present in the delegated token, every scope listed in
delegate_scopes
must also be listed inscope
, andsatisfy
must either be unset or set toall
.minimum_lifetime
(optional)The required minimum lifetime for a delegated token (internal or notebook). Since the maximum lifetime of a delegated token is the same as the remaining lifetime of the authenticating token, capped by the maximum token lifetime, this may also be used to set the minimum remaining lifetime of the user’s session.
If the presented authentication credentials don’t satisfy this required lifetime, a 401 error will be returned. If the
nginx.ingress.kubernetes.io/auth-signin
annotation is set in theIngress
, this will force a user reauthentication.use_authorization
(optional)If set to a true value, replace the
Authorization
header with one containing the delegated token as a bearer token. This option only makes sense in combination withnotebook
ordelegate_to
.
These parameters must be URL-encoded as GET parameters to the /auth
route.