My background is a 36+ year Software Developer and Architect and I recently took an interest in WLED. I’ve posted a few times in the reddit /r/wled subreddit. I really love what has been done, but without HTTPS support, then there’s no way I’d ever expose my controller to the outside world and that is something I VERY MUCH WANTED! So I took on a few goals, and I’ll post what I’ve done.
Create a Reverse HTTPS Proxy to the WLED Controller
This was an absolute MUST HAVE in order to even remotely consider exposing the controller to the outside world. I have now installed NGINX in a Docker Container on a Raspberry Pi 3b and configured it to act as a HTTPS Reverse Proxy to the WLED Controller. This seemed simple at first but had a few hiccups, notably that, at least on my internet router, WSS (Secure Web Sockets) are very flakey and timeouts occur rather frequently. That being said, the reverse proxy worked perfectly and I will happily share the NGINX configuration file that is working once I trim a little of the unnecessary stuff out of it. Further, this also allowed for multiple WLED controllers to be exposed on different URL Paths. So, since I have two WLED Controllers, I have mapped TWO endpoints in my NGINX Configuration File which allow me to access both of the controllers from a different URL:
a. https://myserver.com/wled-bar
b. https://myserver.com/wled-test
Modify the WLED Firmware to support HTTPS
As of now, I’ve completed this step and have tested both the HTTP interface and the HTTPS interface from the web page of the firmware from the mobile app. This required about 20 file changes and the vast majority of those changes were in the HTML and Javascript Files. The issue was that the code was designed to use absolute URLs instead of relative URLs. I modified all of the necessary files to use relative URLs. So if I hit /settings URL it will go to https://myserver.com/wled-bar/settings and not https://myserver.com/settings which would not work.
Update the Mobile App
To begin, the mobile app was developed on a very old version of Visual Studio which I did not have access to, so the first thing I did was update it to Visual Studio 2022 and .NET6, which required some refactoring of the code. The mobile app is fairly “thin” in that most of the functionality of the app is supplied by the WLED Controller’s Web Pages. Basically the app is mostly a wrapper. I had never used Xamarin Forms so I had to learn that (pretty simple, actually) and then update the code to use the latest version of Xamarin Forms. Xamarin Forms is actually VERY nice and will likely use it again in the near future for other projects.
Added Security
Although “Basic Authentication” is NOT considered secure, it is still acceptable to me if done over HTTPS because everything is still encrypted. Granted, I wouldn’t put this in a SCIF, but for controlling LEDs, I felt confident enough that I’m ok with it. I updated the “Add Device” screen to allow the user to enter the username and password to associate with the device so that you do not always have to enter a username and password to access the WLED Controller.
So, in conclusion, I’m very close to having completed this and it has been a real journey. I’m still fairly new to WLED (about 3 or 4 months or so tops). There are a few issues I still need to resolve but as of now, I can access my two WLED controllers from both a web browser as well as the app from HTTP or HTTPS with or without username & password security via basic authentication.
There are still a few outstanding issues, mostly small things like the WLED Device List Page shows the HTTPS devices showing as offline, but you can still click on the power button to turn them on or click on the device and control it that way.
Let me know of the interest level so I can determine if it’s even worth (somehow) merging the code. I don’t want to step on the original author’s (or anyone else’s) toes. My biggest change would definitely be in the WLED App Code since I restructured it to fit into the Visual Studio 2022 and .NET6 solution structure.
Keep in mind the following is NOT a criticism of your efforts, what you’ve provided is very valuable.
The immediate issue(s) that occur to me:
What does this do to the code size especially on an ESP8266 device that can end up being limited for space.
How is memory (flash, free heap, and FS) usage affected?
How much extra processor power are we asking for?
I’m sure there are more factors (affect on fps?) that others will think of but in the mean time, it would be good to know what the various .bin sizes look like for ESP32 and/or ESP8266 devices.
I like the info on reverse proxy usage, I’m doing something similar with CaddyServer for other sites.
What does this do to the code size especially on an ESP8266 device that can end up being limited for space.
Pretty much none. The only changes in the firmware are largely the Javascript and/or HTML files, which get compressed. After looking at the Javascript, it would not be hard to reduce that size even further.
How is memory (flash, free heap, and FS) usage affected?
Again, little if any. I added 2 new fields ot the WLEDDevice object, two strings, which only affect the App’s storage, not the flash memory.
How much extra processor power are we asking for?
Again, none. The processor power is consumed on the mobile device which can easily handle HTTPS traffic, and the HTTPS is proxied to HTTP to the WLED Controller by the Raspberry Pi running NGINX which it can easily handle, so there should be absolutely NO impact on the WLED controller at all.
I’m sure there are more factors (affect on fps?) that others will think of but in the mean time, it would be good to know what the various .bin sizes look like for ESP32 and/or ESP8266 devices.
The current size of the firmware.bin file is 1,186,064 bytes. The 1.13.3 version is around 1.3MB so it’s less, though I haven’t figured out why the difference in size. As to the impact on FPS, etc., I would think no impact at all. The only thing changed in the firmware was almost 100% changing from absolute URLs to relative URLs, which affects nothing. Nothing I’ve changed should affect the actual control of the LEDs. Pretty sure they use the FastLED library which handles all of that behind the scenes.
I love the idea of having more secure access to WLED instances out of network and would be interested in utilizing this, provided you can explain how to do it on a windows machine vs a pie when complete.
You also may want to check out the WLED Discord if you are not already on there. Lots of helpful people/info there.
Let me get this straight: You did not add HTTPS support for WLED web server, you just updated links used in UI to work with HTTPS. Right? You use reverse proxy (with translation from HTTPS to HTTP) to access WLED device from the ouside world. Right?
Correct, the ESP32 could not handle HTTPS because of the limited space and it’s just not fast enough. The title of the post was
Making WLED Addressable via HTTPS
The relative links were absolutely necessary for me to allow for the WLED HTTP Server to be mapped to something other than the root URL (so something like /presets.json becomes presets.json). It’s still fully functional at the address like: http://192.168.1.69 but outside of the local network, it can also be accessed at an address like: https://someserver.com/wled-bar or https://someserver.com/wled-gameroom. These changes were almost all done in the HTML (.htm) files and index.js. While I have developed in C++ in the past, no changes to those were necessary. I suppose if you didn’t mind having the WLED Controller be your root device, you might not need any changes. You might be able to map it via DNS if you had your own domain so mapping something like wled-bar.mydomain.com to your router and somehow redirect just that domain to your WLED Device, not sure though.
The WLED Android App required the most changes, and again, most of those changes were restructuring due to upgrading to the latest .NET Framework, Visual Studio 2022, and the latest version of Xamarin Forms. I made a few changes to the C# code and added two fields to the “Add Device” page to allow for providing the username and password for basic auth so the user wouldn’t have to enter them every time they wanted to access the device from outside of their internal network. Also, apparently the device discovery is crashing in Android 13 so while I don’t have Android 13 yet, I wanted to be prepared when I did so I had to be able to compile the App. The App I compiled in the latest .NET Framework worked in the Android 13 Emulator, but haven’t had the ability to test on an actual device yet.
I’m still working on a few minor issues.
If you wanna take a shot at it yourself, the NGINX Reverse Proxy configuration file looks like this and there are a lot of commented out lines (like the basic auth part) because I was having a few issues with the WebSockets (which still are flakey from time to time but it’s probably my shitty router). I had to run the NGINX Server in a Docker container because the version of NGINX on the Raspberry Pi 3 seemed to have issues with WebSockets so I had to get version 1.23 of NGINX running and just felt like trying out Docker on the Pi and it performs perfectly fine. This all being said, it seemed that the issues I was having with WebSockets was when I was using HTTPS on my internal network. When I VPNed outside of my local network I rarely saw any WebSocket errors. The HTTP address to the internal network worked without issues.
server {
listen 80;
listen [::]:80;
server_name localhost;
access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
#
# Support for websockets... hopefully.
#
#upstream 192.168.1.210 {
# # enable sticky session based on IP
# ip_hash;
#
# server 192.168.1.210:80;
#}
server {
listen 443 http2 ssl;
listen [::]:443 http2 ssl;
server_name localhost myserver.com www.myserver.com;
access_log /var/log/nginx/host.access.log main;
ssl_certificate /usr/share/nginx/ssl/*********.crt;
ssl_certificate_key /usr/share/nginx/ssl/*********.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/.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";
}
}
As a software developer myself, this sounds like great work!
I currently use HomeAssistant via SSL/HTTPS as my proxy to WLED… but all your changes make complete sense and I would imagine a very valid feature to merge in!
I saw that reverse proxy support has been added in 14.0b3 but there are absolute uri yet in the code.
In this way, sub-folder forwarding is still not supported.
Have you got the chance to review the “base url” handling?