Implementing MFA and RBAC on Server Consoles
Server security is the most essential task for every business. MFA (Multi-Factor Authentication) and RBAC (Role-Based Access Control) are security tools that can help you protect your server from unauthorized access, data leaks, and cyberattacks. This guide will show you a complete MFA and RBAC Server Setup.
In this tutorial, we will use Keycloak as the identity provider (IdP) to manage users, enforce MFA via TOTP or WebAuthn, and implement RBAC using roles and groups. Also, PostgreSQL will serve as Keycloak’s database. To protect a web application, we will deploy OAuth2-Proxy in combination with NGINX.
Optionally, Teleport can be used to enforce single sign-on (SSO), MFA, and RBAC for SSH and Kubernetes access.
For best performance and security, we will show the setup on PerLod’s Linux dedicated server, which provides optimized infrastructure for identity and access management systems like Keycloak.
Table of Contents
Prerequisites for MFA and RBAC Server Setup
The first step is to prepare your server. You must install the required packages and set up Docker on your server.
First, run the system update and install required packages with the following commands:
sudo apt update
sudo apt install ca-certificates curl gnupg jq -y
Then, set up Docker on your server with the following commands:
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) stable" \
| sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
sudo apt update && sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y
sudo usermod -aG docker $USER
Also, you need three domain names that point to your server’s IP address. In this guide, we use:
- auth.example.com for Keycloak behind a reverse proxy.
- app.example.com for a sample web app.
- teleport.example.com, which is optional.
Keycloak Authentication Setup with OAuth2 Proxy
At this point, you must create an environment variable file that acts as the central hub for configuring all components of the authentication stack, including Keycloak, PostgreSQL, and OAuth2 Proxy.
Remember to edit the passwords and domains with your values.
First, create a working directory and navigate to it with the command below:
mkdir -p ~/mfa-rbac && cd ~/mfa-rbac
Then, create the Env file with the command below:
cat > .env <<'EOF'
# ---- General
DOMAIN_BASE=example.com
KEYCLOAK_HOSTNAME=auth.example.com
# ---- Postgres
POSTGRES_DB=keycloak
POSTGRES_USER=keycloak
POSTGRES_PASSWORD=SuperStrongPgPass
# ---- Keycloak bootstrap admin
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=AdminPass
# ---- OIDC client (we'll create it in Keycloak, values mirrored here)
OAUTH2_PROXY_CLIENT_ID=nginx-oauth2
OAUTH2_PROXY_CLIENT_SECRET=OIDC_Client_Secret
OAUTH2_PROXY_COOKIE_SECRET=$(openssl rand -base64 32 | tr -d '\n')
# ---- Web
APP_FQDN=app.example.com
EOF
Docker Compose Stack for Keycloak Authentication
Now you must create a Docker compose file for Keycloak authentication. This stack will provide a ready-to-run development environment where you can access the demo app at http://app.example.com:8081 and it will redirect you to Keycloak for login before granting access.
sudo nano compose.yml
Add the following content to the file:
services:
postgres:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 10
keycloak:
image: quay.io/keycloak/keycloak:24.0
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
environment:
KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN}
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/${POSTGRES_DB}
KC_DB_USERNAME: ${POSTGRES_USER}
KC_DB_PASSWORD: ${POSTGRES_PASSWORD}
KC_PROXY: edge
KC_HOSTNAME: ${KEYCLOAK_HOSTNAME}
KC_HTTP_ENABLED: "true"
KC_HTTP_PORT: "8080"
command:
- start
- --hostname=${KEYCLOAK_HOSTNAME}
- --proxy=edge
- --http-enabled=true
- --http-port=8080
ports:
- "8080:8080"
# Demo app to protect (static site)
demo:
image: nginx:1.27
restart: unless-stopped
volumes:
- ./site:/usr/share/nginx/html:ro
- ./nginx/demo.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- oauth2-proxy
ports:
- "8081:80"
oauth2-proxy:
image: quay.io/oauth2-proxy/oauth2-proxy:v7.6.0
restart: unless-stopped
environment:
OAUTH2_PROXY_PROVIDER: oidc
OAUTH2_PROXY_OIDC_ISSUER_URL: http://keycloak:8080/realms/Perlod
OAUTH2_PROXY_CLIENT_ID: ${OAUTH2_PROXY_CLIENT_ID}
OAUTH2_PROXY_CLIENT_SECRET: ${OAUTH2_PROXY_CLIENT_SECRET}
OAUTH2_PROXY_COOKIE_SECRET: ${OAUTH2_PROXY_COOKIE_SECRET}
OAUTH2_PROXY_EMAIL_DOMAINS: "*"
OAUTH2_PROXY_COOKIE_SECURE: "false" # set "true" when behind HTTPS
OAUTH2_PROXY_SET_XAUTHREQUEST: "true"
OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER: "true"
OAUTH2_PROXY_PASS_ACCESS_TOKEN: "true"
OAUTH2_PROXY_UPSTREAMS: "http://demo"
OAUTH2_PROXY_REDIRECT_URL: http://${APP_FQDN}/oauth2/callback
OAUTH2_PROXY_WHITELIST_DOMAIN: ".${DOMAIN_BASE}"
OAUTH2_PROXY_SKIP_PROVIDER_BUTTON: "true"
# We'll authorize by Keycloak 'groups' claim:
# Example: only users in group "/web/viewers" may access
OAUTH2_PROXY_ALLOWED_GROUPS: "/web/viewers"
OAUTH2_PROXY_SCOPE: "openid email profile groups"
# Bind only locally; NGINX will talk to it
ports:
- "4180:4180"
command:
- --http-address=0.0.0.0:4180
volumes:
pgdata:
Create a Demo Web Application
At this point, you can complete the setup by creating a minimal demo application and the proper Nginx configuration that integrates it with the OAuth2 Proxy for authentication.
Create the minimal site and Nginx config with the following commands. Create the folders:
mkdir -p site nginx
Set up the minila site with the command below:
cat > site/index.html <<'EOF'
<!doctype html><html><body>
<h1>Protected App</h1>
<p>If you see this, OAuth2 login worked and your RBAC allowed it.</p>
</body></html>
EOF
Then, set up the Nginx configuration with the command below:
cat > nginx/demo.conf <<'EOF'
server {
listen 80;
server_name _;
# All requests must go through oauth2-proxy
location /oauth2/ {
proxy_pass http://oauth2-proxy:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Uri $request_uri;
}
# Unauthenticated requests get bounced to /oauth2/sign_in
location / {
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
proxy_pass http://localhost:8081; # upstream is itself since this is demo container
proxy_set_header Host $host;
}
location = /oauth2/auth {
proxy_pass http://oauth2-proxy:4180/oauth2/auth;
proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Uri $request_uri;
}
}
EOF
When you are done with the setup, run the Docker Compose file and verify it with:
docker compose up -d
docker compose ps
Once running, you can access the two main interfaces:
- Keycloak Admin Console:
http://server-ip:8080 - Demo Application:
http://server-ip:8081
Configuring Keycloak: Realms, RBAC, and MFA
At this point, you must configure essential settings for Keycloak to establish a robust security foundation for your applications.
- Initial login and realm:
First, you must visit the Keycloak Admin Console and navigate to the Administration Console. From there, log in with the admin and AdminPass you have defined.
Then, create a Realm with your organization’s name.
- Create RBAC building blocks:
We will use groups to organize users and roles for more specific permissions.
Groups are like categories for users. For example:
- /web/viewers: Users who can only view the web app.
- /web/admins: Users with admin privileges.
You can add groups later for other services.
Roles define specific permissions and are assigned to groups. Example roles:
- role_web_view for viewers.
- role_web_admin for admins.
Assign Roles to Groups:
- /web/viewers: add role_web_view.
- /web/admins: add role_web_view and role_web_admin.
Users: Create a test user, set a temporary password, and require the user to change it at first login. Add it to /web/viewers.
- MFA policies (TOTP with WebAuthn):
MFA adds an extra layer of security. Users will need to provide a second factor when logging in.
You must go to Realm Settings and then Authentication. Ensure the browser flow is set to Browser. Enable required actions for users to enroll MFA: Configure OTP and WebAuthn Register.
Edit the Browser Flow to make OTP required after username and password login.
OTP Settings:
- Type: TOTP.
- Algorithm: SHA1.
- Digits: 6.
- Look ahead: 1–3.
WebAuthn Settings:
- Relying Party ID = your Keycloak hostname.
- User verification = preferred or required.
After setup, users will be prompted to enroll TOTP like an authenticator app and WebAuthn (security keys) on their first login.
Connect Web App To Keycloak Using OIDC
In this step, we will connect our web app to Keycloak using OpenID Connect (OIDC). This lets users securely sign in through Keycloak before accessing the app. We will create an OIDC client in Keycloak for oauth2-proxy, which will handle authentication between Keycloak and the web app.
1. Create the client with the following steps:
- Go to Clients and then Create.
- Client ID: Set nginx-oauth2.
- Client type: Set OpenID Connect.
- Authentication: Set to On.
- Valid redirect URIs: Set to
http://app.example.com/oauth2/callback. - Web origins: Set to
http://app.example.com. - Click Save.
2. Client secret setup:
- Open the Credentials tab and copy the Client Secret.
- Add it to your .env file as:
OAUTH2_PROXY_CLIENT_SECRET=your-client-secret
3. Include user group info in tokens:
We want the user groups like /web/viewers or /web/admins to be included in their login token. To do this, Go to Client Scopes, Add Mapper, and Group Membership.
- Name: groups.
- Token Claim Name: groups.
- Full group path: ON.
Add this scope to the Assigned Default Client Scopes of nginx-oauth2.
4. Check assigned scopes:
Go to Clients, nginx-oauth2, and Client Scopes. Make sure these are added: roles, profile, email, and your new group’s scope.
5. Set token settings:
In the Advanced tab, set token lifetimes, for example:
- Access Token Lifespan: 10 minutes.
- Refresh Token: adjust as needed.
6. Apply the changes: Restart oauth2-proxy to load the new settings:
docker compose restart oauth2-proxy demo
Set Up DNS and TLS for Keycloak and Web App
Now that Keycloak and the app are working, you can set up DNS records, HTTPS encryption, and a reverse proxy to secure them. You can use a reverse proxy like Caddy, Traefik, or NGINX to handle HTTPS with Let’s Encrypt certificates.
The proxy will handle TLS (HTTPS) at the front, and it will forward traffic to your internal containers:
- https://auth.example.com: forwards to Keycloak.
- https://app.example.com: forwards to your demo app.
- Keep OAuth2 requests going to oauth2-proxy:4180 as configured in NGINX.
Once everything runs on HTTPS, you must set:
OAUTH2_PROXY_COOKIE_SECURE=true
This makes cookies secure and only sends them over HTTPS. If you already use a global proxy or Cloudflare, just adapt your configuration accordingly.
Test The Full Login and Authorization Process
At this point, you can test the login and authorization process to verify it works as expected.
Open the http://app.example.com/, and you must be redirected to the Keycloak login page. Log in as your test user you have created, Keycloak will ask the user to set a new password and complete MFA.
After a successful login, Keycloak sends the user back through the oauth2-proxy and NGINX to the app, and the site should load.
For the authorization test, remove the user from /web/viewers or change your allowed groups in OAUTH2_PROXY_ALLOWED_GROUPS to /web/admins. Restart the oauth2-proxy with:
docker compose restart oauth2-proxy
Now the user should get a 401 Unauthorized and be redirected to /oauth2/sign_in.
If something fails, check the logs with the commands below:
docker logs -f $(docker compose ps -q keycloak)
docker logs -f $(docker compose ps -q oauth2-proxy)
docker logs -f $(docker compose ps -q demo)
After login, you can decode the access token to see group info with:
TOKEN="paste_access_token_here"
jq -R 'split(".")|.[1]|@base64d|fromjson' <<< "$TOKEN"
You should see your group claim like /web/viewers.
SSH and Kubernetes SSO with Teleport (Optional)
If you want to use the same Keycloak login and MFA for SSH or Kubernetes access, you can connect Teleport via OIDC. To do this, you must install Teleport on another server and expose it at https://teleport.example.com.
In Keycloak, you must create a new OIDC client:
- Client ID: teleport.
- Redirect URI:
https://teleport.example.com/v1/webapi/oidc/callback. - Scopes: openid, email, profile, groups.
- Add a group’s claim like before.
Then, you can configure the Teleport /etc/teleport.yaml file with your Keycloak details:
auth_service:
enabled: true
authentication:
type: oidc
oidc:
issuer_url: https://auth.example.com/realms/Perlod
client_id: teleport
client_secret: <KC_CLIENT_SECRET>
redirect_url: https://teleport.example.com/v1/webapi/oidc/callback
scope: ["openid", "email", "profile", "groups"]
claims_to_roles:
- roles: ["devops"] # Teleport role name
claim: "groups"
value: "/ssh/devops" # Keycloak group path
In Teleport, create a devops role defining what users can do. In Keycloak, add users to /ssh/devops. They’ll automatically get Teleport access through OIDC.
FAQs
Why use Keycloak for MFA and RBAC?
Keycloak is an open-source Identity and Access Management solution that supports TOTP and WebAuthn, Role and group management, Standards like OIDC, OAuth2, and SAML. It also has Integration with almost any app or service.
How is RBAC managed in Keycloak?
Keycloak uses realms, roles, and groups to implement RBAC.
How does Keycloak support auditing and logging for security compliance?
Keycloak provides event logging for both admin and user activities. You can view, export, or send logs to external monitoring systems for compliance and auditing purposes. This helps track MFA usage, failed login attempts, and role modifications.
Conclusion
At this point, you have a centralized IdP Keycloak that enforces MFA and issues tokens with RBAC roles and groups. Also, you have a production pattern to protect web apps via OAuth2-Proxy with NGINX.
To host your solution on a reliable infrastructure, PerLod Hosting offers secure and scalable Linux dedicated servers.
We hope you enjoy this guide on MFA and RBAC Server Setup. Subscribe to our X and Facebook channels to get the latest articles and updates.
For further reading:
Secure your Server with OSSEC and Fail2Ban