I need to run, from docker compose, three containers: a fastapi server, a keycloack server and a postgres database.
This works well if I run the uvicorn command from my local bash instead of from docker-compose service. I also noted that if I run the code from outside docker-compose, I get the authorization option OpenIdConnect (OAuth2, authorization_code)
and from docker-compose: OpenIdConnect (OAuth2, authorization_code with PKCE)
.
My docker-compose.yaml:
version: '3.9'
services:
web:
build: ./foo
command: uvicorn main:app --reload --workers 1 --host 0.0.0.0 --port 8000
volumes:
- ./foo:/usr/src
ports:
- 8000:8000
depends_on:
- db
- kc
environment:
BAR_ENV: local
LOGGER_NAME: local
BAR_DB_LOCAL_USERPASS: bar:bar
BAR_DB_LOCAL_DB_NAME: bar
BAR_DB_LOCAL_HOST: localhost:5438
BAR_HOSTNAME: bar.local
BAR_AUTH_URL: http://auth.bar.local:8087
BAR_FRONT_URL: bar.local:3000
kc:
image: quay.io/keycloak/keycloak-x:latest
command: start-dev --db=postgres --db-url-host=$$DB_HOST --db-url-database=$$DB_DATABASE --db-username=$$DB_USER --db-password=$$DB_PASS --http-port=8087
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
DB_HOST: db
DB_DATABASE: &KC_DB_DB keycloak
DB_USER: &KC_DB_USER keycloak
DB_PASS: &KC_DB_PASS keycloak
domainname: auth.bar.local
ports:
- 8087:8087
depends_on:
- db
volumes:
- ./resources/keycloak-themes:/opt/keycloak/themes/theme
db:
image: postgres:14
environment:
POSTGRES_DB: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
KC_DB_DB: *KC_DB_DB
KC_DB_USER: *KC_DB_USER
KC_DB_PASS: *KC_DB_PASS
BAR_DB_DB: bar
BAR_DB_USER: bar
BAR_DB_PASS: bar
ports:
- 5438:5432
volumes:
- ./data/pg-data:/var/lib/postgresql/data
- ./resources/init-kc-db.sh:/docker-entrypoint-initdb.d/init-kc-db.sh
- ./resources/init-bar-db.sh:/docker-entrypoint-initdb.d/init-bar-db.sh
I'm able to access http://<realm>.bar.local:8000/docs
from the browser and to authenticate on OpenIdConnect (OAuth2, authorization_code with PKCE)
. It redirects me to keycloak login page and, then, back to swagger. But, if I try one of my endpoints in swagger, for example, /whoami
, I get a 500 internal server error.
Logs from web_1
service:
web_1 | keycloak.exceptions.KeycloakConnectionError: Can't connect to server (HTTPConnectionPool(host='auth.bar.local', port=8087): Max retries exceeded with url: /realms/<realm>/protocol/openid-connect/userinfo (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x7fd38041a6b0>: Failed to establish a new connection: [Errno 111] Connection refused')))
web_1 | {"asctime": "2022-04-26 11:31:54,929", "threadName": "MainThread", "filename": "httptools_impl.py", "lineno": 437, "message": "172.18.0.1:63454 - \"GET /api/v1_0/whoami HTTP/1.1\" 500", "severity": "INFO"}
the error above occurs in my keycloak_auth.py, when it tries to fetch user info from self.kc_clients[org]
:
class OpenIdConnectMultipleViaKeycloak(SecurityBase):
def __init__(
self, *, internal_well_known_url: str, server_url: str,
client_template: str, realm_template: str):
self.model = OpenIdConnectModel(
openIdConnectUrl=internal_well_known_url)
self.scheme_name = 'OpenIdConnect'
self.auto_error = True
self.server_url = server_url
self.client_template = client_template
self.realm_template = realm_template
self.kc_clients = {}
async def __call__(self, request: Request) -> Optional[str]:
org = get_org_from_host(request.base_url.hostname)
if org not in self.kc_clients:
self.kc_clients[org] = KeycloakOpenID(
server_url=self.server_url,
client_id=self.client_template.format(org=org),
realm_name=self.realm_template.format(org=org))
authorization: str = request.headers.get("Authorization")
if not authorization:
raise HTTPException(
status_code=HTTP_403_FORBIDDEN, detail="Not authenticated")
try:
userinfo = self.kc_clients[org].userinfo(
authorization.replace('Bearer ', ''))
userinfo['keycloak_realm'] = org
except KeycloakGetError as e:
raise HTTPException(
status_code=HTTP_403_FORBIDDEN, detail=str(e))
return userinfo
Inspecting kc_1
service from inside container:
[root@e3e5d33ce08b /]# nmap -O localhost
Starting Nmap 7.70 ( https://nmap.org ) at 2022-04-26 17:01 UTC
Nmap scan report for localhost (127.0.0.1)
Host is up (0.000080s latency).
Other addresses for localhost (not scanned): ::1
Not shown: 999 closed ports
PORT STATE SERVICE
8087/tcp open simplifymedia
Device type: general purpose
Running: Linux 2.6.X
OS CPE: cpe:/o:linux:linux_kernel:2.6.32
OS details: Linux 2.6.32
Network Distance: 0 hops
OS detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 4.21 seconds
and
root@e3e5d33ce08b /]# netstat -nlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:8087 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.11:41567 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:37927 0.0.0.0:* LISTEN -
udp 0 0 127.0.0.11:57222 0.0.0.0:* -
Active UNIX domain sockets (only servers)
Proto RefCnt Flags Type State I-Node PID/Program name Path
Active Bluetooth connections (only servers)
Proto Destination Source State PSM DCID SCID IMTU OMTU Security
Proto Destination Source State Channel
[root@e3e5d33ce08b /]#
Inspecting domain auth.bar.local
from web_1
container:
root@0cf70e1cef7f:/usr/src/barz# nmap -p 8087 auth.bar.local
Starting Nmap 7.80 ( https://nmap.org ) at 2022-04-26 17:02 UTC
Nmap scan report for auth.bar.local (127.0.0.1)
Host is up (0.000068s latency).
rDNS record for 127.0.0.1: localhost
PORT STATE SERVICE
8087/tcp closed simplifymedia
Nmap done: 1 IP address (1 host up) scanned in 15.06 seconds
It seems that domainname
is reachable from other containers and from outside, but requests made to port 8087 from outside don't work. I've tried to ps aux | grep start-dev
and it is running under PID 1. I can even wget
it inside kc_1
container and receive a response. I also tried code proposed in https://stackoverflow.com/a/50355857/6328506 , but the behavior did not change.
What am I supposed to do to successfully get http://auth.bar.local:8087/realms/<realm>/protocol/openid-connect/userinfo
using docker compose?