Limit login of a specific users from whitelist IP address

Hi there,

My Keycloak system has many users from different companies and the mass market.
For example, users which have email domains @company-x.com, @apple.com, @gmail.com,…

The base-URL account console is public on the Internet. So all users in the realm can log in to the account console from anywhere.

https://my-keycloak.com/auth/realms/myrealm/account/ (*)

But one day, the company-x want their employees (users have email domain @company-x.com) can only log in to the URL (*) from the whitelist IP address of their company’s network.

Could you give me the solution or some advice, please.

Thank you so much.

Take a look at the Authentication SPI Server Developer Guide
You can build a custom Authenticator that blocks users for a given domain for IPs that are not in a configured set.

1 Like

Thank you for your reply!
How can I capture the public IP address user log into the “account console”?
I’m looking forward to hearing from you. Once again, thank you.

I’m not sure I understand. Where do you want to capture the IP? In your Authenticator code?

I mean:
The base-URL account console is public on the Internet. So all users in the realm can log in to the account console from anywhere.

https://my-keycloak.com/auth/realms/myrealm/account/ (*)

The company-x want their employees (users have email domain @company-x.com) can only log in to the URL (*) from the whitelist IP address of their company’s network. When they back home or go to other locations (with different public IP addresses), they will no longer be able to log in.

If you are trying to do that in a custom Authenticator you can use the AuthenticationFlowContext. Depending if you’re using a reverse proxy the IP can come from one of the following methods:

    // no reverse proxy
    authenticationFlowContext.getHttpRequest().getRemoteAddress();

    // with reverse proxy
    authenticationFlowContext.getHttpRequest().getHttpHeaders().getHeaderString("X-Forwarded-For");

Hi, I and @truongnh are in the same team, and I have some questions.
When I’m using the below code

the getRemoteAddress() method didn’t exist.
Is there another way to get user’s IP when they login while I’m not using a reverse proxy ?

Sorry, I was looking at the wrong Javadoc for Resteasy. It looks like getRemoteAddress() isn’t there until version 4. I’d enumerate all of the headers from getHttpHeaders() and see if any of them contain the IP when you’re not using a reverse proxy.

Also, it looks like they get it from the session in some of the Keycloak code:

KeycloakSession session...
KeycloakContext context = session.getContext();
String ipAddress = context.getConnection().getRemoteAddr();

I’ve tried this

but it only return keycloak server’s IP.

Are you running in docker or behind a reverse proxy? I just tested standalone and it works fine.

We’re running Keycloak on a K8s cluster with no reverse proxy.
I’ve tested standalone too, and it returned 127.0.0.1 which is localhost.
So when I test on a K8s cluster, using keycloak for single sign on from one of my website, it return an IP like 172.x.x.x, which is local IP of worker node running Keycloak on K8s cluster.
The problem is we want to get user’s public IP when user login to Keycloak. Is that posible ?

This my be a k8s thing. What is the value of authenticationFlowContext.getHttpRequest().getHttpHeaders().getHeaderString("X-Forwarded-For")? Same?

When using

authenticationFlowContext.getHttpRequest().getHttpHeaders().getHeaderString("X-Forwarded-For")

I got 10.233.105.0 which is the IP address of Keycloak pod.

I’m afraid I can’t reproduce this. Can I ask where you are issuing the request from?

Hi @xgp, thank you for your kindness.

When we implemented the following method in our custom Authenticator

We could retrieve the Public IP of Clients in logs of Ingress Nginx Controller.
But, when we checked logs on Keycloak pods, we only got the private IP of LoadBalancer.

Could you give us some advice, please.

I’m not familiar with ingress-nginx, but you need to make sure it properly sets the “X-Forwarded-For” header to the public IP of the client before sending the request to Keycloak

I had this same issue when using Keycloak in Docker Swarm. I don’t have any experience with K8s but maybe this might help.

For us, there were two parts. The docker ingress network uses IPVS and SNAT which masquerades the source IP as the docker node’s ingress IP, so we would see the private IP of the docker nodes on the ingress network instead of the true source IP of the client. We have a workaround in place using the docker-ingress-routing-daemon as it seems to just be built this way by design in Docker Swarm.

The other part was that for our container, we needed to ensure the following environment variable was set:
PROXY_ADDRESS_FORWARDING: “true”

With these two changes, we were able to see the true source IP address of the client.

From there we added IP restrictions using the undertow subsystem on certain paths so that, for example, our master realm was only accessible from our internal IPs. This can be done through the jboss cli located by default in the container at /opt/jboss/keycloak/bin/jboss-cli.sh. For us, we do this with JBoss CLI and extend the Keycloak Image because we do other customizing, but if you didn’t want to extend the keycloak image, I suppose you could just create a script that edits the standalone.xml file and just mount it at in the startup-scripts directory. Here’s a link explaining how this is done: keycloak-documentation/admin.adoc at master · keycloak/keycloak-documentation · GitHub

I know it’s not K8s but I hope this helps!

edit: added links

1 Like