Nico640 / docker-unms

All-in-one docker image for Ubiquiti UISP (formerly UNMS). Supports x86_64 and ARM (Raspberry Pi).
https://hub.docker.com/r/nico640/docker-unms
199 stars 25 forks source link

Running with reverse proxy #107

Open outofsight opened 1 month ago

outofsight commented 1 month ago

Just started this image, I run behind a reverse proxy, It works but apparently the controller doesn't trust the proxy because, for example, the session tokens table show to the proxy address and not the real client ip address.

There is a way to properly configure the controller to trust the proxy?

Nico640 commented 4 weeks ago

Yes, that should be possible. Basically, the nginx config is generated by /refresh-configuration.sh, which uses the template located at /usr/local/openresty/nginx/templates/nginx.conf.template as the base config. The relevant part of the template should be this:

set_real_ip_from  ${LOCAL_PUBLIC_NETWORK};
real_ip_header    X-Forwarded-For;
real_ip_recursive on;

LOCAL_PUBLIC_NETWORK is set by /refresh-configuration.sh to the network that the docker container is in (e.g. 172.16.0.0/16)

Is your reverse proxy in the same network as the docker container and does it set the X-Forwarded-For header? You can check which network is used by looking at set_real_ip_from in /usr/local/openresty/nginx/conf/nginx.conf.

Btw, an easy way to check if UISP uses the real IP is by logging into UISP and checking the system logs, it should show the IP address of the user that logged in.

outofsight commented 4 weeks ago

In my current setup uisp-controller container belogs to two docker networks:

Caddy send the X-Forwarded-For header (https://caddyserver.com/docs/caddyfile/directives/reverse_proxy), but caddy container access uisp-controller from an address of the caddy network (10.0...), while set_real_ip_from instruction refers to the basebridge network (172.20...). So in the login log I see the caddy address (10.0...).

By the way, I also tried to make caddy and uisp-controller containers members of the the same basebridge network, but this also doens't works. In this case, in the login log, I see the LAN address of the docker host (the same for caddy and uisp-controller), still not the address of my computer when accessing the uisp-controller from the internet.

The X-Fordwarded-For header sent by caddy to uisp-controller contains a list of more than one proxy because I use several proxy. I don't know how to log requests received by uisp-controller, but as an example, caddy send the following header to another service:

"x-forwarded-for": "<client-public-ip>, <lan-address-of-docker-host>"

My Docker host also host the cloudflare-tunnels, another proxy container used to route requests from outside world to the internal network without the need of a public ip and NAT rules, so its address appears in the list of proxies.

Likely uisp-controller parse the X-Forwarded-For in strict way from right to left and, not trusting this LAN address, consider it as the client.

I can confirm that, when accessing the uisp-controller from internal network, skipping cloudflare-tunnels, the real ip of client is logged if the container belong to only one network.

Something like this in docker-compose.yml can solve the issue for me:

entrypoint: bash -c "cp -n /usr/local/openresty/nginx/templates/nginx.conf.template /usr/local/openresty/nginx/templates/nginx.conf.template.bak && sed '/set_real_ip_from\\s*$${LOCAL_PUBLIC_NETWORK};/c\\set_real_ip_from 10.0.0.0/16;\\nset_real_ip_from 10.185.50.16/31;' /usr/local/openresty/nginx/templates/nginx.conf.template.bak > /usr/local/openresty/nginx/templates/nginx.conf.template && cat /usr/local/openresty/nginx/templates/nginx.conf.template | grep set_real_ip_from && /init"

But a more easy way to provide a list of networks to be trusted by environment variable would be useful.