I have surfed through google without finding any concrete answers or examples, so again trying my luck here (often get lucky).
The problem
I have a single spring boot RESTful service running behind an apache
reverse proxy. This RESTful service is running HTTP only. Say it's running on
local ip 172.s port 8080.
I have also configured an apache reverse proxy. Say it's running on
local ip 172.a and public ip 55.a. This proxy responds to both port 80, but all the HTTP traffic is automatically redirected to 443.
I have another server running a standalone Keycloak server. Also
this server is configured to be public accessible through the
reverse proxy. Say it's running on local ip 172.k. This Keycloak server is running on HTTP-only. The HTTP requests are handled using SSL over the reverse proxy.
Last, I have another frontend-webapp running on local ip 172.f. This frontend-webapp is running under Nodejs, and is also configured through the reverse proxy. It's also running only HTTP, but client(browser) is using SSL through the reverse proxy, just as for the Keycloak and RESTful service. This frontend is consuming the RESTful service, and is also configured to authenticate using the keycloak javascript adapter.
The RESTful service is configured as bearer-only using Spring Boot Keycloak adapter, while the frontend app is configured with access type public.
The RESTful service server, Keycloak server, and the frontend server are not public accessible; they are accessible only through the reverse proxy. But they can communicate with each other (since they are in the same private network).
In the frontend keycloak.json file, the auth-server-url
is set to the proxy url https://example.com/auth
, and the frontend is able to successfully get a valid token. Now when I try to consume the RESTful service, I get a error in RESTful adapter that the token issuer is invalid. In the http-header I am, of course, sending the Authorization: Bearer <token>
. The reason I am getting this error is that in RESTful keycloak configuation, I have configured the auth-server-url
to use the local url http://172.k:9080/auth
, so this url is different from the one in the token (which is https://example.com/auth
).
Question
I cannot include the same auth-server-url
in the RESTful service as for the frontend, because that will require me to also setup HTTPs on the RESTful service (because that url is https), and that will complicate stuff a lot, including the need to setup certificates and stuff like that. Also I think it's inefficient and not practical to setup SSL on local only servers.
So my question is how I can make the adapter talk to the Keycloak without going through the reverse proxy. I want the RESTful adapter to talk to the Keyclok server for token verification through auth-server-url: http://172.k:9080/auth
.
Earlier there was a different url for backend, that got removed: https://issues.jboss.org/browse/KEYCLOAK-2623
I'm using Keycloak for a project in docker containers. I had the same problem but in a local network (so maybe this will not be the solution, in this case i'm sorry). So this was the situation:
- REST java webapp running on wildfly, in a single Docker container
- Keycloak running in a single Docker container in the same network of the previous
Apache running locally on my machine outside Docker, serving an angular 2 app, with the properly config
The angular 2 app's adapter was pointing the url http://aaa.auth.com (i modified the local file hosts with the entry 127.0.0.1 aaa.auth.com)
- I added a link between Wildfly Docker and Keycloak Docker by the hostname http://aaa.auth.com and i used this hostname in the Java webapp adapter.
- Both the adapters were pointing to the same address, as far as i know this is a Keycloak requirement, see https://issues.jboss.org/browse/KEYCLOAK-2067
There are a lot of differences from your case (Docker, HTTP vs HTTPS etc), but, to avoid the communication REST-Keycloak via Web, have you tried modifying the file hosts of your server (hosting the RESTful service) inserting an entry with the local IP of your reverse proxy (172.a) and "example.com"?
Or, maybe you can solve it with a private DNS?
I tried different things, but could not solve the problem. To me it seems like there is not way to specify auth-server-url: http://172.k:9080/auth
in the backend adapter while the frontend adapter is putting auth-server-url:https://example.com/auth
in the token. So my solution was to configure all the backend services to also the auth-server-url: https://example.com/auth
.
The only disadvantage of this is that my backend service adapter communicates with keycloak over web, which probably is not so good performance wise, but at least everything works as it should. It should have been possible to somehow specify a local keycloak endpoint within the same local network, or the same VPN in AWS.
I've had a similar problem in a docker swarm environment. My Keycloak and my spring boot container were both behind the same reverse proxy.
For Tomcat:
The matter is to configure the http(s) connector correctly. Let's say the host name and port of our reverse proxy are http://${EXTERNAL_HOSTNAME}:${EXTERNAL_PORT}.
Then the http(s) connector in tomcat.xml should have those two additional attributes:
<Connector [...] proxyName="${EXTERNAL_HOSTNAME}" proxyPort="${EXTERNAL_PORT}"/>
This will make all calls to servletRequest.getServerName() and servletRequest.getServerPort() respond with the values of our reverse proxy. The keycloak adapter certainly uses these functions to determine the redirect url.
For Spring Boot: Drop this class in your classpath:
@Component
public class TomcatReverseProxyCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>, TomcatConnectorCustomizer {
@Value("${server.tomcat.proxy-name}")
private String proxyName;
@Value("${server.tomcat.proxy-port}")
private int proxyPort;
@Override
public void customize(final TomcatServletWebServerFactory factory) {
factory.addConnectorCustomizers(this);
}
@Override
public void customize(final Connector connector) {
connector.setProxyName(this.proxyName);
connector.setProxyPort(this.proxyPort);
}
}
and then setting this in application.properties:
server.tomcat.proxy-name=${EXTERNAL_HOSTNAME}
server.tomcat.proxy-port=${EXTERNAL_PORT}
Additional Configuration of the keycloak adapter (Examples are for Spring Boot):
My keycloak is also behind the same reverse proxy. So I also had to set the auth server url of the keycloak adapter to the hostname of the reverse proxy. Then I abused the proxy setting of the keycloak adapter to make it use the services on the internal leg:
keycloak.auth-server-url=http://${EXTERNAL_HOSTNAME}:${EXTERNAL_PORT}/auth
keycloak.proxy-url=http://${INTERNAL_KEYCLOAK_HOSTNAME}:${INTERNAL_KEYCLOAK_PORT}/auth
Also these settings might make some sense:
server.servlet.session.cookie.domain=${EXTERNAL_HOSTNAME}
server.use-forward-headers=true
server.tomcat.remote-ip-header=x-forwarded-for
server.tomcat.protocol-header=x-forwarded-proto
server.tomcat.protocol-header is important for those who terminate SSL on their reverse proxy.
One of the solutions is to upgrade the adapter to 8.0.0. This problem seems to be fixed in version 8.0.0.
https://issues.redhat.com/browse/KEYCLOAK-6073