How to use Docker and Nginx to get started with reverse proxy authentication for services that don't natively support OAuth.
Joey Miller • Last updated July 06, 2023
This guide is the second part in a multi-part series of guides:
In the first part of this guide, we covered setting up Keycloak. This gives us single sign-on (SSO) for services that can be configured to authenticate with Keycloak/OAuth2/SAML, etc. For services that don't support this, we need to additionally set up reverse proxy authentication.
An example of a service that may require this is a single-user service such as Pihole. In this case, we would disable the Pihole login page and rely on having the reverse proxy (Nginx) prevent unauthenticated users from accessing the service.
We will be configuring OAuth2 Proxy with Keycloak to accomplish this.
OAuth2-proxy
worksWhen a user attempts to access a service, Nginx can be configured to call an endpoint to check if the user is authenticated. In this scenario that endpoint is provided by OAuth2 Proxy.
This guide assumes you are using Docker + Nginx Proxy Manager (NPM) as the reverse proxy. This guide will be easy to adapt to bare Nginx.
Within Nginx Proxy Manager, I will be assuming you have set up SSL and are enforcing HTTPS for each proxy host. Otherwise, additional setup may be required - such as setting the environment variable OAUTH2_PROXY_COOKIE_SECURE=false
for OAuth2 Proxy.
OAuth2-proxy
First, we need to create a client in Keycloak. This will be used to allow OAuth2 Proxy to validate user authentication with Keycloak.
Go to the Keycloak Administration Console
Create a new client by going to Clients > Create client
.
Client type
as OpenID Connect
Client ID
to oauth2-proxy
Client authentication
to On
Authentication flow
to only Standard flow
Save
From the Clients > oauth2-proxy > Credentials
page, copy the Client secret
(we will be using this below)
From the Clients > oauth2-proxy > Settings
page:
Set Valid redirect URIs
to https://auth.example.com/oauth2/callback
Set Front-channel logout URL
to https://auth.example.com/oauth2/sign_out
OAuth2-proxy
single sign-out works. OAuth2-proxy
will log itself out when a logout request is sent to our realm.Add the following to your docker-compose.yml
(in addition to the keycloak
and npm
services we already added earlier on):
oauth2proxy:
# internal: oauth2proxy on port 4180
image: quay.io/oauth2-proxy/oauth2-proxy:latest
environment:
OAUTH2_PROXY_HTTP_ADDRESS: '0.0.0.0:4180'
OAUTH2_PROXY_COOKIE_SECRET: '< COOKIE SECRET >'
OAUTH2_PROXY_COOKIE_DOMAINS: '.example.com' # Required so cookie can be read on all subdomains.
OAUTH2_PROXY_WHITELIST_DOMAINS: '.example.com' # Required to allow redirection back to original requested target.
# Configure to use Keycloak
OAUTH2_PROXY_PROVIDER: 'oidc'
OAUTH2_PROXY_CLIENT_ID: 'oauth2-proxy'
OAUTH2_PROXY_CLIENT_SECRET: '< CLIENT SECRET >'
OAUTH2_PROXY_EMAIL_DOMAINS: '*'
OAUTH2_PROXY_OIDC_ISSUER_URL: 'https://auth.example.com/realms/master'
OAUTH2_PROXY_REDIRECT_URL: 'https://auth.example.com/oauth2/callback'
depends_on:
- keycloak
restart: unless-stopped
Make sure to:
OAUTH2_PROXY_CLIENT_SECRET
to the Client secret
value you copied from the Keycloak Administration Console
OAUTH2_PROXY_COOKIE_SECRET
to a strong cookie secret you generated. See the OAuth2 Proxy docs for further instructions to help accomplish this.Note: By default,
OAuth2 Proxy
requires that all users have theirUsers
page for the realm. If you would like to remove this requirement fromOAuth2 Proxy
, make sure you set the environment variablesOAUTH2_PROXY_INSECURE_OIDC_ALLOW_UNVERIFIED_EMAIL=true
andOAUTH2_PROXY_OIDC_EMAIL_CLAIM=sub
.
Then, let's configure OAuth2 Proxy in the NPM web interface. As mentioned earlier in this guide, we will be hosting it at auth.example.com/oauth2
. Replace example.com
with your domain name.
From the NPM dashboard, click Edit
for our auth.example.com
Proxy Host.
In the Custom Locations
tab for the auth.example.com
entry, add a location:
Define location
to ~ ^/oauth2/.+$
Scheme
, Forward Hostname / IP
, and Forward Port
to http
, oauth2proxy
, 4180
(respectively)Now that we have configured OAuth2 Proxy we are ready to use it to provide authentication to our services. I will assume such services are already being reverse proxied by NPM.
To configure a service to use reverse proxy authentication, we need need to make some changes to the service in the NPM dashboard.
Add location
, set Define location
to /
.
Scheme
, Forward Hostname / IP
, and Forward Port
for your serviceauth_request /oauth2/auth;
error_page 401 = @error401;
Add location
Define location
to /oauth/auth
Scheme
, Forward Hostname / IP
, and Forward Port
to http
, oauth2proxy
, 4180
(respectively)Under the Advanced
tab, set:
location @error401 {
return 302 https://auth.example.com/oauth2/start?rd=https://$host$uri;
}
This tells Nginx to use OAuth2 Proxy to check if we are authenticated. If we are not, we should be redirected to the login page.
Some multi-user services support expect the reverse proxy to pass the authenticated username/email in an HTTP header.
In addition to completing the above steps, add the following to the environment:
section of oauth2proxy
in your docker-compose.yml
:
OAUTH2_PROXY_SET_XAUTHREQUEST: true
Then, let's configure Nginx to pass the service a header to inform it of the logged-in user.
From the NPM dashboard, click Edit
for the service.
In the Custom Locations
tab, find the location/endpoint that corresponds to our service (such as /
):
auth_request
lines we added earlier) :auth_request_set $user $upstream_http_x_auth_request_preferred_username;
proxy_set_header REMOTE-USER $user;
(where REMOTE-USER
is the HTTP header that the service will look for to determine the logged-in user)
In the first part of this series, we covered some approaches to prevent user(s) from accessing an entire client. Since a single OAuth2 Proxy instance/client can provide authentication for many services - we need a different approach to restrict users on a per-service basis.
OAuth2 Proxy can support restricting members by role or group.
Keycloak already provides the necessary information (client scope) to OAuth2 Proxy for restricting users by role, but some additional configuration is needed for groups:
Client scopes
> Create client scope
and create a new Client Scope with the name groups
.Mappers
tab and click Configure a new mapper
. Click Group Membership
give it a name and set Token Claim Name
to groups
and add it. Tick Add to access token
and Add to userinfo
.Clients > oauth2-proxy
and in the Client scopes
tab click Add client scope
and add groups
as Default
.Globally enforcing a Realm role
required by all users that attempt to authenticate through OAuth2 Proxy can be done by setting the environment variable OAUTH2_PROXY_ALLOWED_ROLES
or OAUTH2_PROXY_ALLOWED GROUPS
respectively.
For example. To require all users to be part of the oauth2proxy
realm role, add the following to the environment
section of the oauth2proxy
service in your Docker compose file:
OAUTH2_PROXY_ALLOWED_ROLES: 'oauth2proxy'
OAuth2 Proxy supports enforcing groups on a per-service basis by adding a query parameter to the /oauth2/auth
location we set up earlier when "Configuring a service for reverse proxy auth".
Because Nginx/NPM does not support query parameters in the auth_request
URI, we need to use a redirect
instead.
To require users accessing a service to be a part of the group mygroup
: click the gears icon for the service's /oauth2/auth
location and set the custom configuration to:
rewrite ^ /oauth2/auth?allowed_groups=mygroup break;
We've now successfully set up an SSO implementation that will work with the majority of our services.
Some multi-user services such as Jellyfin that don't have OAuth or Header Auth support can still cause us headaches. In this case, I recommend adding LDAP to your implementation.
Tags
If you found this post helpful, please share it around: