How to use Docker and Nginx to get started with self-hosting single sign-on with Keycloak.
Joey Miller • Last updated July 06, 2023
This guide is the first part in a multi-part series of guides:
There are plenty of great services to self-host, including Nextcloud, and Tandoor Recipes. If you've ever tried self-hosting more than a few services you'll understand the frustration of remembering many different passwords and continuously having to log in. After doing some research, I realized my homelab needed "single sign-on" (SSO). SSO is an authentication method (typically viewed as an enterprise feature) that allows secure authentication with many services using "just one set of credentials". Using SSO I would be able to achieve my dream of needing only a single-login event to access all of my services.
There are many tools that we can use for SSO, such as Authelia, Authentik, or Keycloak.
Although some of the aforementioned SSO tools may be easier to set up, I decided to go with Keycloak. Keycloak is an enterprise-level tool that is supported by Redhat. Using Keycloak will give us a lot of flexibility, and ticks the boxes for acceptable memory usage, theme-ability, and multi-factor authentication support.
Keycloak supports:
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 (NPM), I will be assuming you have set up SSL and are enforcing HTTPS for each proxy host. Otherwise, additional setup may be required - especially when dealing with OAuth2 Proxy in part 2 of this guide series.
To get started with Keycloak, add the following to the services:
section of your docker-compose.yml
file:
npm:
image: 'jc21/nginx-proxy-manager:latest'
ports:
- '80:80'
- '81:81'
- '443:443'
volumes:
- ./data/nginx_proxy_manager/data:/data
- ./data/nginx_proxy_manager/letsencrypt:/etc/letsencrypt
restart: unless-stopped
keycloak:
# internal: keycloak on port 8080
image: quay.io/keycloak/keycloak:latest
command: start
environment:
KC_HOSTNAME: 'auth.example.com'
KC_PROXY: 'edge'
KEYCLOAK_ADMIN: 'admin'
KEYCLOAK_ADMIN_PASSWORD: 'admin'
volumes:
- ./data/keycloak:/opt/keycloak/data
restart: unless-stopped
Note: The above example will have Keycloak running with the built-in H2 database. It is recommended in production to use an external database (i.e. MySQL, PostgreSQL, etc).
Then, let's configure Keycloak on a subdomain in the NPM web interface. We will host Keycloak at https://auth.example.com/auth
and the OAuth proxy (explained later in this guide) at https://auth.example.com/oauth2
. Replace example.com
with your domain name.
Info: The default NPM username is
[email protected]
, and the password ischangeme
. Make sure to change these.
From the NPM dashboard, click Add Proxy Host
In the Details
tab:
Domain Names
to auth.example.com
Scheme
, Forward Hostname / IP
, and Forward Port
to http
, keycloak
, 8080
(respectively)Lets hide the default dashboard, and redirect the root path to the administrative console. In the Custom locations
tab, add a location.
Define location
to = /
Scheme
, Forward Hostname / IP
, and Forward Port
to http
, keycloak
, 8080
(respectively)return 301 /admin;
In the Advanced
tab:
Custom Nginx Configuration
field to:proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
We don't always want every user to be able to access every service. For example, you may want to give a friend access to your recipe application but not to the Pihole admin console.
If you have a compliant OIDC Client Application, setting Client authentication
and Authorization
to ON
will be enough to access the Authorization
tab for the client. Then you will be able to create a Group-based policy
from the Policies
sub-tab.
Unfortunately, this isn't always suitable - such as if the client uses SAML for authentication/authorization. In this case, another way to handle this in Keycloak (without needing any extensions) is to duplicate the default browser flow and add a condition that denies access if the user doesn't have the required role.
Note: This method requires a separate browser flow for each unique set of roles you would like to mandate for users of clients/services.
For more details see the answers by @Stuck and @heilerich on StackOverflow.
To increase the security of your authentication process, Keycloak allows enabling two-factor authentication (2FA) for users. This requires users to provide a valid one-time-pass (OTP) from an authenticator app on their smartphone.
Go to the Keycloak Administration Console
Go to Authentication > Required actions
and for Configure OTP
toggle Set as default action
to On
.
Go to the Users
page. By clicking on each existing user, add Configure OTP
to Required user actions
from the User details > Details
tab.
After these steps - on the next login users will be required to set up their authenticator for 2FA.
We've successfully set up a basic SSO implementation with Keycloak.
If all the services you are using are capable of authentication via OAuth, SAML, or Keycloak - the journey ends here for you. This implementation will be sufficient.
If you have other services that expect HTTP Header Auth or manage their own login flow (i.e. via LDAP) - continue reading. This guide continues in:
Tags
If you found this post helpful, please share it around: