traefik-forward-auth
traefik-forward-auth copied to clipboard
Keycloak Documentation
Includes notes from #134 and instructions on how to enable email passthrough with X-Forwarded-User
header
Currently setting this label on my traefik-forward-auth
container:
traefik.http.middlewares.traefik-forward-auth.forwardauth.authResponseHeaders: "X-Forwarded-User"
Results in the correct users email being specified with a default keycloak OIDC setup:
"X-Forwarded-User":["[email protected]"]
Is there any other settings you need?
Here's a Keycloak script I rigged up that performs RBAC based on Client Roles in keycloak and integrates nicely with Traefik Forward Auth and OIDC.
It first verifies the user is in an overarching 'proxy_access' client role, then an application specific client role.
// Imports
FormMessage = Java.type('org.keycloak.models.utils.FormMessage');
KeycloakUriBuilder = Java.type('org.keycloak.common.util.KeycloakUriBuilder');
Matcher = Java.type('java.util.regex.Matcher');
Pattern = Java.type('java.util.regex.Pattern');
function authenticate(context) {
// Obtain the generic client
var client = session.getContext().getClient();
// Verify Proxy Access
var proxyName = "proxy_access";
var proxyClientRole = client.getRole(proxyName);
if (proxyClientRole === null) {
context.forkWithErrorMessage(new FormMessage('label', 'Proxy Access Role \'' + proxyName + '\' is missing?'));
return;
}
if (! user.hasRole(proxyClientRole)) {
context.forkWithErrorMessage(new FormMessage('label', 'User is not in the \'' + proxyClientRole.getDescription() + '\' role.'));
return;
}
// Verify Application Access
var referer = httpRequest.getHttpHeaders().getHeaderString("Referer");
var refererUri = KeycloakUriBuilder.fromUri(referer).build();
var query = refererUri.getQuery();
// We extract the 'source' application from the state query parameter
var appPattern = Pattern.compile("state=[0-9a-f]+:oidc:https://(.*)/");
var matcher = appPattern.matcher(query);
if (matcher.find()) {
var appName = matcher.group(1);
var appClientRole = client.getRole(appName);
if (appClientRole === null) {
context.forkWithErrorMessage(new FormMessage('label', 'App Access Role \'' + appName + '\' is missing?'));
return;
}
if (! user.hasRole(appClientRole)) {
context.forkWithErrorMessage(new FormMessage('label', 'User is not in the \'' + appClientRole.getDescription() + '\' role.'));
return;
}
} else {
context.forkWithErrorMessage(new FormMessage('label', "Couldn't find the App hostname in the referer?"));
return;
}
context.success();
}
Just wanted to say with the above script, this works great for a single application, but if you're protecting multiple services behind a single OIDC app, then this approach won't work, as your cookie for service X will let you go into service Y, as Keycloak doesn't do any auth with the cookies, only the login page.
I think instead we'd have to extract the group(s) out of the OIDC token(s) within traefik-forward-auth and then make decisions then based on the users (eg. groups)