i have a Problem Running Spring Boot and Keycloak both in docker containers.
I started with Keycloak with mysql as db running in docker.
services:
mysql:
image: mysql:5.7
container_name: mysql
volumes:
- mysql_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: keycloak
MYSQL_USER: keycloak
MYSQL_PASSWORD: password
networks:
- testNetwork
keycloak:
image: jboss/keycloak
container_name: keycloak
restart: on-failure
volumes:
- ./config:/config/
environment:
DB_VENDOR: MYSQL
DB_ADDR: mysql
DB_DATABASE: keycloak
DB_USER: keycloak
DB_PASSWORD: password
KEYCLOAK_USER: xxx
KEYCLOAK_PASSWORD: yyy
KEYCLOAK_IMPORT_REALM: /keycloak/import/realm-import.json
ports:
- 8180:8080
depends_on:
- mysql
networks:
- testNetwork
Then i added my realm (SpringBootKeycloak), my client (testclient), and a user with role 'user'. After that i added spring-security to my Spring-boot-application. And edited my application.yml
spring:
main:
banner-mode: 'off'
application:
name: testclient
version: @project.version@
jpa:
hibernate:
ddl-auto: create
datasource:
url: jdbc:h2:mem:testclient;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
username: xxx
password: xxx
keycloak:
auth-server-url: http://localhost:8180/auth
realm: SpringBootKeycloak
resource: testclient
public-client: true
principal-attribute: preferred_username
security-constraints:
- authRoles:
- user
securityCollections:
- patterns:
- /*
server:
port: ${port:8090}
rest:
path: testclient
accoring to that i added my SecurityConfig:
/**
* Secure appropriate endpoints
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
.antMatchers("/*").hasRole("user") // only user with role user are allowed to access
.anyRequest().permitAll();
}
Running my SpringBoot-Application locally is working fine. I have to login with keycloak and get redirected to localhost:8090. But when i add my SpringBoot-Application to my docker-compose and start it in a container i get still to keycloak for login, but when i should redirect i get a 403.
testclient:
image: testclient
container_name: testclient
environment:
JAVA_OPTS: "-agentlib:jdwp=transport=dt_socket,address=5005,server=y,suspend=n"
build:
context: testclient-application
ports:
- 8090:8090
- 5006:5005
networks:
- testNetwork
with following container log:
{"@timestamp":"2018-08-16T11:50:11.530+00:00","@version":"1","message":"failed to turn code into token","logger_name":"org.keycloak.adapters.OAuthRequestAuthenticator","thread_name":"http-nio-8090-exec-6","level":"ERROR","level_value":40000,"stack_trace":"java.net.ConnectException: Connection refused (Connection refused)\n\tat java.net.PlainSocketImpl.socketConnect(Native Method)\n\tat java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)\n\tat java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)\n\tat java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)\n\tat java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)\n\tat java.net.Socket.connect(Socket.java:589)\n\tat org.apache.http.conn.scheme.PlainSocketFactory.connectSocket(PlainSocketFactory.java:121)\n\tat org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:180)\n\tat org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:144)\n\tat org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:134)\n\tat org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:610)\n\tat org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:445)\n\tat org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:835)\n\tat org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)\n\tat org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)\n\tat org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)\n\tat org.keycloak.adapters.ServerRequest.invokeAccessCodeToToken(ServerRequest.java:111)\n\tat org.keycloak.adapters.OAuthRequestAuthenticator.resolveCode(OAuthRequestAuthenticator.java:336)\n\tat org.keycloak.adapters.OAuthRequestAuthenticator.authenticate(OAuthRequestAuthenticator.java:281)\n\tat org.keycloak.adapters.RequestAuthenticator.authenticate(RequestAuthenticator.java:139)\n\tat org.keycloak.adapters.tomcat.AbstractKeycloakAuthenticatorValve.authenticateInternal(AbstractKeycloakAuthenticatorValve.java:203)\n\tat org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve.authenticate(KeycloakAuthenticatorValve.java:50)\n\tat org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve.doAuthenticate(KeycloakAuthenticatorValve.java:57)\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:575)\n\tat org.keycloak.adapters.tomcat.AbstractKeycloakAuthenticatorValve.invoke(AbstractKeycloakAuthenticatorValve.java:181)\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800)\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800)\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.lang.Thread.run(Thread.java:748)\n","app":"testclient","version":"1.0.0-SNAPSHOT"}
I can't figure out how to solve this...
EDIT 1: One more information: I'm running docker on Windows.
EDIT 2: A SOLUTION
My Working solution contains following:
To make things work, you’ll need to make sure to add the following to your hosts file (/etc/hosts on Mac/Linux, c:\Windows\System32\Drivers\etc\hosts on Windows).
127.0.0.1 keycloak
This is because you will access your application with a browser on your machine (which name is localhost, or 127.0.0.1), but inside Docker it will run in its own container, which name is keycloak.
Inner Docker port and published port needs to be same:
services:
mysql:
image: mysql:5.7
container_name: mysql
volumes:
- mysql_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: keycloak
MYSQL_USER: keycloak
MYSQL_PASSWORD: password
networks:
- testNetwork
keycloak:
image: jboss/keycloak
container_name: keycloak
restart: on-failure
volumes:
- ./config:/config/
environment:
DB_VENDOR: MYSQL
DB_ADDR: mysql
DB_DATABASE: keycloak
DB_USER: keycloak
DB_PASSWORD: password
KEYCLOAK_USER: xxx
KEYCLOAK_PASSWORD: yyy
KEYCLOAK_IMPORT_REALM: /keycloak/import/realm-import.json
ports:
- 8080:8080 <--- edited
depends_on:
- mysql
networks:
- testNetwork
Step 3: keycloak definition in application.yml for Spring boot edited auth-server-url:
keycloak:
realm: SpringBootKeycloak
auth-server-url: http://keycloak:8080/auth <--- edited
resource: testclient
public-client: true
security-constraints:
- authRoles:
- user
securityCollections:
- patterns:
- /*
ssl-required: external
confidential-port: 0
The ugly thing coming with this solution: You cant map your Docker Port onto another port to access from url. ports: - 8080:8080 i spend a lot of time testing other combinations, with the result that the access url port has to be the same as inner docker port (8080 in my case).
EDIT 4:
Same thing is working with Thorntail.
To change the port for Keycloak add...
environment:
JAVA_OPTS: "-Djboss.socket.binding.port-offset=10 -Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m
-Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true"
... for keycloak in docker-compose. -Djboss.socket.binding.port-offset=10 sets default port (8080) + offset (10) The rest are default values for keycloak. Don't forget to edit "ports" and "auth-server-url"
I think your problem is auth-server-url: http://localhost:8180/auth
. That localhost
effectively has a different meaning when your app is running inside a docker container.
Inside the container it needs to be the name of the container i.e. keycloak
. This is a bit awkward as when you connect to keycloak from your host machine you'd want to use localhost
but the token issuer url needs to match to the url on which the token was requested (otherwise the token is rejected) so you end up having to put keycloak
into your etc/hosts file.
You are in good company with this problem - I've encountered this working with Activiti. And you can find the JHipster project dealing with it in the same way - they say:
To make things work, you’ll need to make sure to add the following to your hosts file (
/etc/hosts
on Mac/Linux,c:\Windows\System32\Drivers\etc\hosts
on Windows).
127.0.0.1 keycloak
This is because you will access your application with a browser on your machine (which name is
localhost
, or127.0.0.1
), but inside Docker it will run in its own container, which name iskeycloak
.