Redirection (301) from within nginx based container (openresty lua-resty-openidc) to another container fails in browser on host machine because server (container name) is unknown

Hi,

I am a newbie experimenting with using lua-resty-openidc in a docker-compose environment that uses Keycloak Community 6.0.1 server as the OpenID Connect provider. A quick overview of the issue is illustrated here.

I am using keycloak with lua-resty-openidc (nginx based module) to provide OpenID connect authentication and authorisation flow.

When I make a browser request from the docker host to an endpoint exposed in the openresty container the request url fails to redirect and display on the browser since keycloak server is not recognised on the docker host machine. For example:

http://keycloak:8080/auth/realms/myclient/protocol/openid-connect/auth?response_type=code&client_id=myclient&state=dbd8f19e9fceb1b350cba24de40126b7&redirect_uri=http%3A%2F%2FX.X.X.X%3A4200&nonce=b3eb4bd24e829e02fd7510da90f808cd&scope=openid%20email%20profile

If I copy and paste the request url and use curl from within the openresty container then the login page html is returned from the keycloak container as expected.

I have appended the docker-compose file, Dockerfile for openresty service and nginx configuration files below.

Has anybody experienced and resolved a similar issue, i.e. how to redirect to and display html from a container within a docker-compose environment?

Update

If I add keycloak and angular to /etc/hosts redirection works for keycloak and angular containers. Is this the recommended solution or are there better alternatives?

nginx:

worker_processes 1;
events {
    worker_connections  128;
}

http {
  include mime.types;
  default_type application/octet-stream;
  sendfile on;
  keepalive_timeout 65;
  lua_code_cache off;
  lua_need_request_body on;
  gzip on;

  resolver 127.0.0.11 ipv6=off;
  real_ip_header X-Forwarded-For;
  real_ip_recursive on;

  lua_shared_dict discovery 1m;
  lua_shared_dict sessions 10m;
  lua_package_path '~/lua/?.lua;;';

  server {
    listen 8081;
    default_type plain/text;
    location / {
      content_by_lua '
        ngx.say("Hello ", ngx.req.get_headers()["X-USER"])
      ';
    }
  }

  server {
    listen 80;
    charset utf-8;
    default_type text/html;

    access_by_lua_block {
      local opts = {
        redirect_uri = "http://<ip addr>:4200",
        discovery = "http://keycloak:8080/auth/realms/myclient/.well-known/openid-configuration",
        client_id = "myclient",
        client_secret = "secret",
        ssl_verify = "no",
        accept_none_alg = false,
        accept_unsupported_alg = false,
        renew_access_token_on_expiry = true,
        access_token_expires_in = 3600,
        revoke_tokens_on_logout = true,
      }

      local res, err = require("resty.openidc").authenticate(opts)
      if err then
        ngx.status = 500
        ngx.say(err)
        ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
      end

      ngx.req.set_header("X-USER", res.id_token.sub)
    }

    expires           0;
    add_header        Cache-Control private;
    location / {
         default_type text/plain;
         index index.html index.htm;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
      root   html;
    }
  }
}

docker-compose:

version: "3.7"

networks:
  auth-network:
    ipam:
      config:
        - subnet: 172.22.0.0/16


services:
  postgres:
    container_name: postgres
    image: postgres
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: password
    ports:
      - "5433:5432"
    networks:
      - auth-network

  keycloak:
    container_name: keycloak
    image: jboss/keycloak
    ports:
      - "8080:8080"
      - "8443:8443"
      - "9990:9990"
    links:
      - api
      - postgres
    environment:
      DB_ADDR: postgres
      DB_DATABASE: keycloak
      DB_PORT: 5432
      DB_USER: keycloak
      DB_SCHEMA: public
      DB_PASSWORD: password
      DB_VENDOR: "POSTGRES"
      KEYCLOAK_USER: user
      KEYCLOAK_PASSWORD: password
      PROXY_ADDRESS_FORWARDING: "true"
    command:
      - "-b 0.0.0.0 \
      -Dkeycloak.migration.action=import \
      -Dkeycloak.migration.provider=dir \
      -Dkeycloak.migration.strategy=IGNORE_EXISTING \
      -Dkeycloak.migration.dir=/tmp"
    volumes:
      - ./data:/tmp
    networks:
      - auth-network

  openresty:
    build:
      context: ./nginx
      dockerfile: Dockerfile
    container_name: resty
    image: resty 
    links:
      - keycloak
    ports:
      - "8090:80"
      - "8081:8081"
      - "8082:8082"
    networks:
      - auth-network
    restart: always
    volumes:
      - "./nginx/conf/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf"

Dockerfile:

FROM openresty/openresty:xenial

# install dependencies
RUN ["luarocks", "install", "lua-resty-session"]
RUN ["luarocks", "install", "lua-resty-http"]
RUN ["luarocks", "install", "lua-resty-jwt"]

ADD lib/resty/openidc.lua /usr/local/openresty/lualib/resty/openidc.lua

EXPOSE 80
1 Like

Have you found the solution yet?..I am facing the same problem as u. If solved out, let me know the solution. Thanks

What makes you think that container hostnames would be resovable any where outside the container network?

A redirect takes place in the browser process, thus will not work without name resolution as your browser must be able to directly call the target adress. You either run a dns server in your lab and add the entries to it OR you modify each hosts /etc/hosts (like you did). The redirect needs to point to either a fqdn for a reverse proxy or directly to the node where the container port is published - pointing it to the container hostname is not the right approach.

Thanks @meyay for responding. Much appreciated. Yes, it seems to be working if I add container names to /etc/hosts bound to 127.0.0.1 I have created a playground repository at gitlab. I am only running this on one docker host machine, it is not being used on a swarm across many nodes. Regarding the other mechanism of using DNS server, could dnsmasq be used for this to resolve to localhost, 127.0.0.1?

I am not sure that I understand the last sentence regarding redirect… needs to point to either a fully qualified domain name for a reverse proxy or directly to the node where the container port is published - pointing it to the container hostname is not the right approach… Does this mean that the redirection should point to the IP address and port of the docker host or alternatively an nginx reverse proxy running on docker host?

Hi @kyawthetkhine,

I have created a docker-compose environment on gitlab that is an example architecture. To get the redirection working I used /etc/hosts in my case. The answer from @meyay also suggests using a dns server to resolve container names…

Using /etc/hosts solved the problem for me with the host not being recognised in the redirect request, however when redirected to the keycloak login from the gateway container I am experiencing another problem with CORS cross origin requests, detailed here

1 Like

Only true, if your browser and the docker engine are on the same host. Every other host in your lan would require the ip of the docker host instead. This has nothing to do with ports.

yes and no: it means that the redirection should point to the hostname of the docker host AND host port of the publised container port. Forget that I even mentioned nginx.

I did not suggest to modify /etc/hosts AND have a dns server. If you have a dns server where you can freely edit entries, it is the prefered solution.

Keycloack works as most of the identity providers work: some sort of plugin checks whether you have a valid session, if not, your browser will be forwared to the keycloack login page. After a successfull login, the session token is returned to your browser and a redirect is send to your browser to forward to the target service, which checks for a valid session token and grants access. This does happen in your browser and NOT inside docker between Keycloak and your target service!

You have to use URLs that are valid from the browser perspective and not from the perspective of the containers.

Thanks for the help and suggestions @meyay, much appreciated. Ok, I think that I understand now. So as an alternative to /etc/hosts as implemented in my gitlab repository, I could configure docker to use a DNS server containing entries of container name and IP address of docker host that the container is running on.

After some quick reading I could maybe do this using suggestion here or via dns configuration option of docker-compose file.

Any URLs in the identity provider configuration should be set from the perspective of the browser, i.e. using the DNS name and host port of published container port.

I am confused why you still try to solve a problem that exists in your LAN inside docker…

Lets assume your docker engine host is named docker0 and your container is named service0 and you would publish port 80 from the host to port 8080 of the container. The URL you need to configure is http://docker0:80 and NOT service0:8080.

Seems like I am terrible at explaining it in a way you understand.
I am afraid someone else needs to pitch in.

Good luck!

@dcs3spp i was going through the repo https://gitlab.com/dcs3spp/angular_keycloak/-/tree/master i came across a lua file, does that need any modification , did you write it on your own or its taken from some other source. If it needs modification i need to understand the file … Any help here will be great :wink: