0

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?

Kfcaio
  • 442
  • 1
  • 8
  • 20
  • 1
    BTW `quay.io/keycloak/keycloak-x` image was only a preview of Quarkus Keycloak. Now it is released from Keycloak 17+, so I would use `quay.io/keycloak/keycloak` instead (minor conf changes may be needed). – Jan Garaj Apr 27 '22 at 08:14
  • That same seems to occur using keycloak 18 – Kfcaio Apr 28 '22 at 21:45
  • Note that the `domainname` setting doesn't have anything to do with hostnames or urls; it sets the [NIS](https://en.wikipedia.org/wiki/Network_Information_Service) domainname, which was relevant 20 years ago but not so much since then. – larsks Apr 29 '22 at 11:36

1 Answers1

0

Changing localhost for host.docker.internal and adopting solution proposed in https://stackoverflow.com/a/60026589/6328506 for service kc solved the problem. It worth mention that ping/nmap <service_name>, localhost and <network_alias> has different effects.

Kfcaio
  • 442
  • 1
  • 8
  • 20