# nginx.conf
...
# if the ext auth server, like heimdall returns `401 not authorized`
# then forward the request to the error401 block
error_page 401 = @error401;
location @error401 {
# redirect to the IdP for login
return 302 https://your-idp-service/login;
# you usually want your IdP to redirect back upon successful authentication
# typically, you can achieve that by adding such query parameters like
# return_to set to the value of the current request
}
NGINX Integration
This guide explains how to integrate heimdall with NGINX as well as with the NGINX Ingress Controller.
NGINX is an HTTP and reverse proxy server which became famous as one of the fastest web servers out there, heimdall can be integrated with by making use of the ngx_http_auth_request_module. In such setup, NGINX delegates authentication and authorization to heimdall. If heimdall answers with a 2XX code, NGINX grants access and forwards the original request to the upstream service. If heimdall returns 401 or 403, the access is denied with the corresponding error code. Any other response code returned by heimdall is considered an error.
Prerequisites
Integration with NGINX requires heimdall being operated in Decision Operation Mode exposing its HTTP(s) endpoint.
Limitations
NGINX ngx_http_auth_request_module responsible for communication with external authentication & authorization services, like heimdall, has a few limitations. As written above, it only supports 200, 401 and 403 response codes. That means:
You’ll not be able to drive redirects from heimdall, as 3xx error codes will result in 500 error returned by NGINX. You can partially overcome that limitation by letting heimdall respond with a 401 or 403 error code and mapping that to a redirect in NGINX itself, e.g. like shown below. This definitely not DRY and will not allow you using multiple identity provider if you need to
If there is no matching rule on heimdall side, heimdall responds with
404 Not Found
, which, as said above will be treated by NGINX as error. To avoid such situations, you can define a default rule, which is anyway recommended to have secure defaults
Vanilla NGINX
Since NGINX is highly configurable and heimdall supports different integration options, you can use any of the configuration examples given below. All of these enable heimdall to build the URL of the protected backend server for rule matching purposes.
In most cases you must configure heimdall to trust your NGINX instances by setting trusted_proxies
. Exceptions are described in the sections below.
Forward only the path and query information
With this method you don’t set any headers. That means, you cannot rely on the used HTTP scheme, or the host and port in your rules. Here NGINX uses the same HTTP method, used in the original request to it and add the path and query to the path/query URL used for communication with heimdall. That integration method does not require the configuration of trusted_proxies
in heimdall.
# nginx.conf
...
location / {
auth_request /_auth; (1)
auth_request_set $auth_cookie $upstream_http_set_cookie; (2)
add_header Set-Cookie $auth_cookie;
auth_request_set $authHeader0 $upstream_http_authorization; (3)
proxy_set_header 'Authorization' $authHeader0;
# mitigate HTTPoxy Vulnerability
# https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
proxy_set_header Proxy "";
...
}
location = /_auth { (4)
internal;
access_log off;
proxy_method $request_method; (5)
proxy_pass https://heimdall:4456$request_uri; (6)
proxy_pass_request_body off; (7)
proxy_set_header Content-Length "";
proxy_set_header Host $http_host; (8)
}
1 | Configures NGINX to forward every request to the internal /_auth endpoint (this is where the actual heimdall integration happens - see below). |
2 | When the response from heimdall returns, this and the next line set the Cookies set by heimdall in the response (whether you need this depends on your Contextualizers and Finalizers configuration) |
3 | When the response from heimdall returns, this and the next line set the Authorization header set by heimdall in the response (which header to set depends again on your Contextualizers and Finalizers configuration) |
4 | This is where the "magic" happens |
5 | Configure NGINX to use the HTTP method used by its client. Without this setting the implementation of proxy_path will use the HTTP GET method. |
6 | Configures NGINX to pass the request to heimdall and sets the request path and queries from the original request |
7 | Disables sending of the request body. If your heimdall rules make use of the body, you should set this to on and remove the next line. |
8 | Lets the NGINX setting the Host header, so it is accessible to heimdall. |
Forward all information in X-Forwarded-*
headers
With this method you set the X-Forwarded-Method
, X-Forwarded-Proto
, X-Forwarded-Host
and X-Forwarded-Uri
to let heimdall know the host, respectively domain url and the used HTTP method.
Compared to the previous integration option, the configuration only differs in the definition of the internal /_auth
endpoint. So, the example configuration is limited to that part only.
Proper configuration of trusted_proxies is mandatory if using this option. |
# nginx.conf
...
location = /_auth {
internal;
proxy_pass https://heimdall:4456; (1)
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Forwarded-Method $request_method; (2)
proxy_set_header X-Forwarded-Proto $scheme; (3)
proxy_set_header X-Forwarded-Host $http_host; (4)
proxy_set_header X-Forwarded-Uri $request_uri; (5)
proxy_set_header X-Forwarded-For $remote_addr;
}
1 | Configures NGINX to pass the request to heimdall. |
2 | Let NGINX forward the used HTTP method to heimdall. |
3 | Let NGINX forward the used HTTP scheme to heimdall. |
4 | Let NGINX forward the used host to heimdall. |
5 | Let NGINX forward the used path and query parameter to heimdall. |
NGINX Ingress Controller
Global Configuration
Using X-Forwarded-*
headers
The configuration used in the example below requires proper configuration of trusted_proxies on heimdall side. |
Global configuration can be achieved by setting the following properties in controller ConfigMap
. If you install the NGINX controller via the helm chart, you can add these properties under the controller.config
property of your helm values.yaml
file.
global-auth-url: "https://<heimdall service name>.<namespace>.svc.cluster.local:<port>" (1)
global-auth-response-headers: Authorization (2)
global-auth-snippet: | (3)
proxy_set_header X-Forwarded-Method $request_method;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
1 | Configures the controller to use heimdall’s main service endpoint with <heimdall service name> , <namespace> and <port> depending on your configuration. | ||
2 | Let NGINX forward the Authorization header set by heimdall to the upstream service upon successful response. This configuration depends on
your Contextualizers and Finalizers configuration. If not configured, NGINX will only react on Set-Cookie headers in responses from heimdall by default. | ||
3 | Configures the required headers to pass the information about the used HTTP scheme, host and port, request path and used query parameters to be forwarded to heimdall.
|
With that in place, you can simply use the standard Ingress
resource, and the NGINX Ingress Controller will ensure, each request will be analyzed by heimdall first.
This will result in an NGINX configuration corresponding to the integration option, described in the Forward all information in X-Forwarded-*
headers section.
Alternative Configuration
Alternatively, if you don’t want configuring trusted_proxies
and do not rely on the used HTTP scheme, host and port in your rules, you can also use the location-snippet
and the server-snippet
to the ConfigMap
of the NGINX Ingress Controller with values shown below.
This example is an exact copy of the configuration used in the very first integration option described above.
location-snippet: |
auth_request /_auth;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
auth_request_set $auth_header $upstream_http_authorization;
proxy_set_header 'Authorization' $auth_header;
proxy_set_header Proxy "";
server-snippet: |
location = /_auth {
internal;
access_log off;
proxy_method $request_method;
proxy_pass https://<heimdall service name>.<namespace>.svc.cluster.local:<port>$request_uri;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header Host $http_host;
}
As with the previous integration option, you can add these properties under the controller.config
property of your helm values.yaml
file if you install the NGINX Ingress Controller via helm.
Integration on Ingress
Resource Level
Using X-Forwarded-*
headers
One option to integrate heimdall with the NGINX Ingress Controller on the Ingress
resource level is making use of the nginx.ingress.kubernetes.io/auth-url
, nginx.ingress.kubernetes.io/auth-response-headers
and the nginx.ingress.kubernetes.io/auth-snippet
annotation as shown in the example below. This approach requires proper configuration of trusted_proxies
on heimdall side. On NGINX Ingress Controller side you must allow the usage of nginx.ingress.kubernetes.io/auth-snippet
(See also here).
nginx.ingress.kubernetes.io/auth-url: "https://<heimdall service name>.<namespace>.svc.cluster.local:<port>"
nginx.ingress.kubernetes.io/auth-response-headers: Authorization
nginx.ingress.kubernetes.io/auth-snippet: |
proxy_set_header X-Forwarded-Method $request_method;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Uri $request_uri;
# other annotations required
Alternative Configuration
Alternatively, if you don’t want configuring trusted_proxies
and do not rely on the used HTTP scheme, host and port in your rules, you can also use the nginx.ingress.kubernetes.io/configuration-snippet
and nginx.ingress.kubernetes.io/server-snippet
annotations and use the configuration shown below.
This example is an exact copy of the configuration used in the very first integration option described above.
nginx.ingress.kubernetes.io/configuration-snippet: |
auth_request /_auth;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
auth_request_set $auth_header $upstream_http_authorization;
proxy_set_header 'Authorization' $auth_header;
proxy_set_header Proxy "";
nginx.ingress.kubernetes.io/server-snippet: |
location = /_auth {
internal;
access_log off;
proxy_method $request_method;
proxy_pass https://<heimdall service name>.<namespace>.svc.cluster.local:<port>$request_uri;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header Host $http_host;
}
# other annotations required
Additional Resources
Checkout the examples on GitHub for a working demo.
Last updated on Apr 8, 2025