WLED
WLED copied to clipboard
Allow WLED access behind reverse proxy
These changes should allow for devices to be referenced from URL mappings to different devices as well as via HTTPS from a HTTPS Reverse Proxy.
So the WLED Controller can still be accessed by the internal IP. In addition, a web server can be setup to reverse proxy to the internal device allowing outside access. This can also include Basic Auth over the connection for added security. While not perfect, Basic Auto over HTTPS is very good, not perfect, but is a simple solution to protecting your WLED Devices.
I am using a Raspberry Pi 3 with NGINX running in a docker container on the Pi.
My NGINX Configuration File looks like this (some of it is commented out from debugging but I left it around). I've also removed some info from the config for my own privacy.
server {
listen 443 http2 ssl;
listen [::]:443 http2 ssl;
server_name localhost [removed for PR].com www.[removed for PR].com;
access_log /var/log/nginx/host.access.log main;
ssl_certificate /usr/share/nginx/ssl/[removed for PR].crt;
ssl_certificate_key /usr/share/nginx/ssl/[removed for PR].key;
location / {
try_files $uri $uri/ =404;
root /usr/share/nginx/html;
index index.html index.htm index.nginx-debian.html;
}
location /wled-bar/ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://192.168.1.210/;
proxy_buffering off;
proxy_read_timeout 90;
auth_basic "Username and Password Required";
auth_basic_user_file /etc/nginx/conf.d/.htpasswd;
# WebSocket Support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /wled-bar/ws {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://192.168.1.210/ws;
# include proxy_params;
proxy_buffering off;
proxy_read_timeout 90;
# WebSocket Support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
I would like to merge this as I believe such a proxy setup is a viable way to securely expose your WLED devices to public networks.
Since I don't have personal experience with proxies, for links to be added in the future, is this correct?
- Root-relative URLs (
/settings
) do not work - URLs relative to the current page (
settings
) work (and./settings
is the same thing?) - Relative go up 1/2 levels (
.
/..
) works - Absolute URLs (
http(s)://<IP>/settings
) work
One more point: In principle I like the sessionStorage['baseurl']
approach, but in the case the user directly navigates to a settings page without loading the UI first (which I often do), it could lead to an invalid URL (undefined/settings
). Would need something like sessionStorage['baseurl'] ? sessionStorage['baseurl'] : ''
but that is janky. Do you have another idea?
Since I don't have personal experience with proxies, for links to be added in the future, is this correct?
* Root-relative URLs (`/settings`) do **not** work * URLs relative to the current page (`settings`) work (and `./settings` is the same thing?) * Relative go up 1/2 levels (`.`/`..`) works * Absolute URLs (`http(s)://<IP>/settings`) work
Anything behind reverse proxy may have intermediate path inserted.
So a http://wled.local/settings/leds
may become http://www.public.com/device/settings/leds
as seen in browser but ESP will still see /settings/leds
request (from proxy).
As AsyncWebServer has no concept of folders within URL and UI does not know if it is dealing with reverse proxy some mechanism has to be employed in UI to deal with possible /settings/leds
vs. /somepath/settings/leds
and more important, calling JS and loading JSON files with intermediate path included or not.
Cool. I'll take a look at this in the next few days, the holidays have been very busy. I'll also make sure every use case for complex URLs works properly and will try to document it accordingly.
- I do like the baseurl approach. It just needs to be initialized if it doesn't exist. I thought I did that, but will double check.
- Root relative is another thing I'll have to check. I'm surprised they don't work.
I posted the HTTPS Nginx configuration. The HTTPS Reverse Proxy just forwards requests from a particular URL to a HTTP Server. What ever paths are specified are forwarded verbatim. The added benefit is having multiple WLED "Servers" running on different URL Paths. So if I have two, the external endpoints could be:
https://www.drbix.com/barled -> http://192.168.1.69 https://www.drbix.com/barled/settings/../settings -> http://192.168.1.69/settings/../settings
https://www.drbix.com/kitchen -> http://192.168.1.72
I tested this using Basic Auth over HTTPS which is much better than just regular basic auth over http https://security.stackexchange.com/questions/988/is-basic-auth-secure-if-done-over-https. It's not SCI type secure, but for this type of application, it's definitely a lot more secure. Also, I'll check absolute URLs through the proxy; they should work but I'll double check to be 100% sure. I would think that all of your bullet points below should work fine. The proxy just takes everything after the base URL and puts in the base URL of the WLED that was mapped in the Nginx Configuration.
On Fri, Nov 25, 2022 at 6:55 AM Blaž Kristan @.***> wrote:
Since I don't have personal experience with proxies, for links to be added in the future, is this correct?
-
Root-relative URLs (
/settings
) do not work This should work fine -
URLs relative to the current page (
settings
) work (and./settings
is the same thing?) * Relative go up 1/2 levels (.
/..
) works * Absolute URLs (http(s)://<IP>/settings
) work This should also work fine
Anything behind reverse proxy may have intermediate path inserted. So a http://wled.local/settings/leds may become http://www.public.com/device/settings/leds as seen in browser but ESP will still see /settings/leds request (from proxy). correct As AsyncWebServer has no concept of folders within URL and UI does not know if it is dealing with reverse proxy some mechanism has to be employed in UI to deal with possible /settings/leds vs. /somepath/settings/leds and more important, calling JS and loading JSON files with intermediate path included or not. I updated it so the AsyncWebServer works as expected. It had been a bit since I played with the WebSockets stuff, but it came back pretty quickly. I'll dig through the changes to document it better.
— Reply to this email directly, view it on GitHub https://github.com/Aircoookie/WLED/pull/2788#issuecomment-1327384781, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACDEFVW5HXJ4BCF6CUFYL3WKCSKNANCNFSM6AAAAAAQPUOYVE . You are receiving this because you authored the thread.Message ID: @.***>
On Fri, Nov 25, 2022 at 6:55 AM Blaž Kristan @.***> wrote:
Since I don't have personal experience with proxies, for links to be added in the future, is this correct?
- Root-relative URLs (
/settings
) do not work- URLs relative to the current page (
settings
) work (and./settings
is the same thing?)- Relative go up 1/2 levels (
.
/..
) works- Absolute URLs (
http(s)://<IP>/settings
) workAnything behind reverse proxy may have intermediate path inserted. So a http://wled.local/settings/leds may become http://www.public.com/device/settings/leds as seen in browser but ESP will still see /settings/leds request (from proxy). As AsyncWebServer has no concept of folders within URL and UI does not know if it is dealing with reverse proxy some mechanism has to be employed in UI to deal with possible /settings/leds vs. /somepath/settings/leds and more important, calling JS and loading JSON files with intermediate path included or not.
— Reply to this email directly, view it on GitHub https://github.com/Aircoookie/WLED/pull/2788#issuecomment-1327384781, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACDEFVW5HXJ4BCF6CUFYL3WKCSKNANCNFSM6AAAAAAQPUOYVE . You are receiving this because you authored the thread.Message ID: @.***>
My bad, I misread your question in bullet point 1. Root relative URLs *MIGHT *be an issue. There may also be methods to fix this such as what is pointed out in this article https://serverfault.com/questions/932628/how-to-handle-relative-urls-correctly-with-a-nginx-reverse-proxy .
On Fri, Nov 25, 2022 at 9:26 AM Dave Bixler @.***> wrote:
Cool. I'll take a look at this in the next few days, the holidays have been very busy. I'll also make sure every use case for complex URLs works properly and will try to document it accordingly.
- I do like the baseurl approach. It just needs to be initialized if it doesn't exist. I thought I did that, but will double check.
- Root relative is another thing I'll have to check. I'm surprised they don't work.
I posted the HTTPS Nginx configuration. The HTTPS Reverse Proxy just forwards requests from a particular URL to a HTTP Server. What ever paths are specified are forwarded verbatim. The added benefit is having multiple WLED "Servers" running on different URL Paths. So if I have two, the external endpoints could be:
https://www.drbix.com/barled -> http://192.168.1.69 https://www.drbix.com/barled/settings/../settings -> http://192.168.1.69/settings/../settings
https://www.drbix.com/kitchen -> http://192.168.1.72
I tested this using Basic Auth over HTTPS which is much better than just regular basic auth over http https://security.stackexchange.com/questions/988/is-basic-auth-secure-if-done-over-https. It's not SCI type secure, but for this type of application, it's definitely a lot more secure. Also, I'll check absolute URLs through the proxy; they should work but I'll double check to be 100% sure. I would think that all of your bullet points below should work fine. The proxy just takes everything after the base URL and puts in the base URL of the WLED that was mapped in the Nginx Configuration.
On Fri, Nov 25, 2022 at 6:55 AM Blaž Kristan @.***> wrote:
Since I don't have personal experience with proxies, for links to be added in the future, is this correct?
Root-relative URLs (
/settings
) do not work This should work fineURLs relative to the current page (
settings
) work (and./settings
is the same thing?) * Relative go up 1/2 levels (.
/..
) works * Absolute URLs (http(s)://<IP>/settings
) work This should also work fineAnything behind reverse proxy may have intermediate path inserted. So a http://wled.local/settings/leds may become http://www.public.com/device/settings/leds as seen in browser but ESP will still see /settings/leds request (from proxy). correct As AsyncWebServer has no concept of folders within URL and UI does not know if it is dealing with reverse proxy some mechanism has to be employed in UI to deal with possible /settings/leds vs. /somepath/settings/leds and more important, calling JS and loading JSON files with intermediate path included or not. I updated it so the AsyncWebServer works as expected. It had been a bit since I played with the WebSockets stuff, but it came back pretty quickly. I'll dig through the changes to document it better.
— Reply to this email directly, view it on GitHub https://github.com/Aircoookie/WLED/pull/2788#issuecomment-1327384781, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACDEFVW5HXJ4BCF6CUFYL3WKCSKNANCNFSM6AAAAAAQPUOYVE . You are receiving this because you authored the thread.Message ID: @.***>
On Fri, Nov 25, 2022 at 6:55 AM Blaž Kristan @.***> wrote:
Since I don't have personal experience with proxies, for links to be added in the future, is this correct?
- Root-relative URLs (
/settings
) do not work- URLs relative to the current page (
settings
) work (and./settings
is the same thing?)- Relative go up 1/2 levels (
.
/..
) works- Absolute URLs (
http(s)://<IP>/settings
) workAnything behind reverse proxy may have intermediate path inserted. So a http://wled.local/settings/leds may become http://www.public.com/device/settings/leds as seen in browser but ESP will still see /settings/leds request (from proxy). As AsyncWebServer has no concept of folders within URL and UI does not know if it is dealing with reverse proxy some mechanism has to be employed in UI to deal with possible /settings/leds vs. /somepath/settings/leds and more important, calling JS and loading JSON files with intermediate path included or not.
— Reply to this email directly, view it on GitHub https://github.com/Aircoookie/WLED/pull/2788#issuecomment-1327384781, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACDEFVW5HXJ4BCF6CUFYL3WKCSKNANCNFSM6AAAAAAQPUOYVE . You are receiving this because you authored the thread.Message ID: @.***>
Why are you trying to map different WLED devices by path? Would make much more sense to do based on hostname, that is normally how to access different hosts
@netmindz I can see the appeal of https://wled.myhome.com/kitchen/ and https://wled.myhome.com/porch/ as opposed to multiple hosts.
which is ...?
It's been a long time and I've been absent for the most part (real-life issues), but getting back into the groove of things. I'm hoping, if this feature is still desired, to go back in and either rewrite it in the latest version or fix this branch. I'm open to suggestions.
which is ...?
http://kitchen.wled.myhome.com Or http://porch.myhome.com
As you suggested. And which works as is BTW.
When proxying any web application, it is much more common to proxy pass the entire host rather than a single path. While some web apps can handle being mapped to a different path, you often hit other issues like the use of cookies, session storage etc as the hostname is used as the key for these. These might not be an issue for WLED right now, but I would bet that developers of future functionally would not consider the impact of multiple copies of the app differentiated by only path.
While this PR might end up containing all the right changes, as this is an a-typical use with non-trivial deployment and testing requirements it is likely to break in the future.
Given that for either host based or path based you are going to have a fair amount of manual setup for each node unless you use some funky regex or similar, it's not like you really save a lot on path-based approach, beyond adding an extra cname DNS record per node, unless wildcard is used.
BTW beta-3
branch has reverse proxy functionality implemented and it seems it is working correctly on my set-up.
I took a slightly different approach to allow independent workings of each and every page regardless of any previous visit.
It allows both ways - host based or path based reverse proxy.
That's great man, sorry this fell off my plate. I'll definitely test stuff.
On Mon, Jun 5, 2023 at 3:17 PM Blaž Kristan @.***> wrote:
BTW beta-3 branch has reverse proxy functionality implemented and it seems it is working correctly on my set-up. I took a slightly different approach to allow independent workings of each and every page regardless of any previous visit.
It allows both ways - host based or path based reverse proxy.
— Reply to this email directly, view it on GitHub https://github.com/Aircoookie/WLED/pull/2788#issuecomment-1577345663, or unsubscribe https://github.com/notifications/unsubscribe-auth/AACDEFWY2KZISJQS3NVVKRTXJYWGDANCNFSM6AAAAAAQPUOYVE . You are receiving this because you authored the thread.Message ID: @.***>
Superseded by #3238