How to enable HTTP authentication on haproxy

If you intend to make your OctoPrint instance reachable via the internet through something like a port forward, you should protect it one level above OctoPrint, so that really only you and those you trust can access it and your webcam (which is not served by OctoPrint, only embedded).

:spiral_notepad: Note

The safest way would be to forgo the port forward all together and instead setup and use a VPN. You could also utilize a cloud access plugin such as PolarCloud or maybe a messenger plugin like Telegram.

If all of that is not an option, feel free to read on.

If you are running haproxy in front of OctoPrint anyhow (as shipped on OctoPi and recommended), an easy way way to accomplish this is setting up HTTP Authentication.

This can be done by defining a custom user list and limiting access to your OctoPrint instance to accounts defined in this list:

  1. Open /etc/haproxy/haproxy.cfg in your favourite text editor. You'll need to use sudo here, otherwise you won't be able to write your changes to disk! Example: sudo nano /etc/haproxy/haproxy.cfg
  2. Define a new user list:
    userlist OctoPrintUsers
        user myuser insecure-password mypassword
    
    Note: Instead of providing the password in plain text, it would be safer to create a hashed version and use that. For that you'd need to sudo apt install whois, then hash your password via mkpasswd -m sha-512 and then use the result in haproxy.cfg as password instead of insecure-password:
    userlist OctoPrintUsers
        user myuser password mypasswordhash
    
  3. Define a new access control list (ACL) based on this user list in the frontend section:
    acl ValidOctoPrintUser http_auth(OctoPrintUsers)
    
  4. Tell haproxy to prompt for authentication if the ACL doesn't match:
    http-request auth realm OctoPrint if !ValidOctoPrintUser
    
  5. Verify that the userlist and frontend sections in your file should now look somewhat like this:
    userlist OctoPrintUsers
            user mypasswd insecure-password mypassword
    
    frontend public
            bind *:80
            bind 0.0.0.0:443 ssl crt /etc/ssl/snakeoil.pem
            option forwardfor except 127.0.0.1
            acl Auth_OctoPrintUsers http_auth(OctoPrintUsers)
            http-request auth realm OctoPrint if !Auth_OctoPrintUsers
            use_backend webcam if { path_beg /webcam/ }
            default_backend octoprint
            errorfile 503 /etc/haproxy/errors/503-no-octoprint.http
    
    Save it and exit the editor.
  6. Restart haproxy: sudo service haproxy restart. The next time you try to open OctoPrint, you'll be prompted to authenticate by your browser.

All this was also demonstrated in OctoPrint On Air #10:

A more sophisticated version of this could also only prompt for authentication if the request originates outside your trusted home network.

Starting with OctoPrint 1.3.7, you'll also have the option to log into your OctoPrint account through the Basic Authentication header, meaning that if you have the same username/password combos configured in both haproxy and OctoPrint, you won't have to login twice.

2 Likes

I would very much likte to know how to accomplish this "A more sophisticated version of this could also only prompt for authentication if the request originates outside your trusted home network."

I have tried to google it but come up short.

Edit:
I managed to do this via a workaround. By creating two separate front ends, one binds to 443 (with basic auth) and the other to 80 (without basic auth) I could expose 443 over internet and connect to 80 locally. Not the most elegant solution but it solves my problem.

@Markus_Carlbark, I found the following reference to make the basic auth only required when outside the local network https://printoid.net/access-octoprint-from-the-internet/ which gave me enough to cobble this together.

First, I moved the "acl ..." and the "http-request auth..." lines from the frontend to the default backend. Then, I made a complete copy of the original default backend with a different name. Finally, I added the following line to the fronend to use the new unsecure backend from the local network: "use_backend octroprint_unsecure if { hdr_beg(host) -i 192.168 }"

So, the relavent sections of my haproxy.cfg now looks like the following:

frontend public

    bind :::80 v4v6
    bind :::443 v4v6 ssl crt /etc/ssl/snakeoil.pem
    option forwardfor except 127.0.0.1
    use_backend webcam if { path_beg /webcam/ }
    use_backend octoprint_unsecure if { hdr_beg(host) -i 192.168 }
    default_backend octoprint

backend octoprint

    acl needs_scheme req.hdr_cnt(X-Scheme) eq 0
    reqrep ^([^\ :]*)\ /(.*) \1\ /\2
    reqadd X-Scheme:\ https if needs_scheme { ssl_fc }
    reqadd X-Scheme:\ http if needs_scheme !{ ssl_fc }
    option forwardfor
    server octoprint1 127.0.0.1:5000
    errorfile 503 /etc/haproxy/errors/503-no-octoprint.http
    acl ValidOctoPrintUser http_auth(OctoPrintUsers)
    http-request auth realm OctoPrint if !ValidOctoPrintUser

backend octoprint_unsecure

    acl needs_scheme req.hdr_cnt(X-Scheme) eq 0
    reqrep ^([^\ :]*)\ /(.*) \1\ /\2
    reqadd X-Scheme:\ https if needs_scheme { ssl_fc }
    reqadd X-Scheme:\ http if needs_scheme !{ ssl_fc }
    option forwardfor
    server octoprint1 127.0.0.1:5000
    errorfile 503 /etc/haproxy/errors/503-no-octoprint.http

backend webcam

    reqrep ^([^\ :]*)\ /webcam/(.*)     \1\ /\2
    server webcam1  127.0.0.1:8080
    errorfile 503 /etc/haproxy/errors/503-no-webcam.http

userlist OctoPrintUsers

    user USERNAME insecure-password PASSWORD

And for whatever it's worth, I've actually changed the default ports and only forwarded the SSL through my firewall (i.e. not on 443, 80, 88, 8080, etc.).

1 Like

Starting with OctoPrint 1.3.7, you'll also have the option to log into your OctoPrint account through the Basic Authentication header, meaning that if you have the same username/password combos configured in both haproxy and OctoPrint, you won't have to login twice.

Is this still true? I've configured haproxy as described, and have the same user/password configured, but I'm still getting a second login prompt.

Have you also enabled the corresponding settings in config.yaml to trust those headers? It doesn't do so by default.

I had not. I did, and now it works, thanks!

i try to follow this but i have no success:

@acovar but you didn't secure your backend webcam, or did you?

1 Like

To accomplish a HTTP-Authentication only for connections from outside your LAN, I really would not suggest to use the solution with two different backends which you can find here (https://printoid.net/access-octoprint-from-the-internet/) or in the post by @akovar above.

As far as I understand, this solution would only protect the backend octoprint while backend webcam stays unprotected from inside and outside your LAN.

In other words, everybody that knows the IP address and port of your OctoPrint could see your webcam's stream on http://IP:Port/webcam/.

A safer as well as much easier solution to prompt for authentication only for requests from outside your network would be something like

[...]

frontend public
    bind *:80
    bind 0.0.0.0:443 ssl crt /etc/ssl/snakeoil.pem
    option forwardfor except 127.0.0.1

    # Authentication
    acl Auth_OctoPrintUsers http_auth(OctoPrintUsers)
    
    # Exclude internal IPs from Authentication
    acl InternalIP src -i 192.168.0.0/24

    # check acl InternalIP AND acl Auth_OctoPrintUsers
    http-request auth realm octoprint if !InternalIP !Auth_OctoPrintUsers
    
    use_backend webcam if { path_beg /webcam/ }
    default_backend octoprint
    errorfile 503 /etc/haproxy/errors/503-no-octoprint.http

The key is in 192.168.0.0/24 which defines all IP addresses from 192.168.0.0 to 192.168.0.255 - find more about it here: https://en.wikipedia.org/wiki/Subnetwork

Further Readings:

1 Like