HTTP Basic Authorization Header Support

Hi,

is it possible to add HTTP Basic Authorization [RFC 7617] support to Pi-hole?

The new embedded webserver civetweb supports HTTP Basic, we would just need a way to configure it.

This would enable people who already have an IAM Solution to configure Pi-hole to use existing users (e.g. Authentik + nginx allows for proxy auth with HTTP Basic).

Thank you for considering my feature request.

Let's explore this possibility but I don't really know what needs to be done for supporting your exact use case. We will not add basic auth as an alternative to our session-based authentication system.

The reason is simple: We don't have the password in clear (that'd be totally unsafe) but only a BALLOON password hash. Anytime we want to check whether the password is correct, we have to do the (intentionally!) hard hashing which can easily take up to one second on low-end devices such as Raspberry Pi 1. Having to do this for each and every independent API request (there can easily be > 10 requests on the dashboard), would just make the API completely unusable and would only be possible by sacrificing security by making the password hashing parameters weaker than they are now.

However, what I could image would be that we check on the login page whether suitable headers are provided and use the password transmitted there to try a login. If this works, the user would automatically be redirected to the page they wanted to visit. It'd basically some form of auto-login, not completely invisible but quick.

What do you think?

Ready for you to try on a custom branch, details see over there.

Hello DL6ER,

thank you for picking up my feature request that quickly.
HTTP Basic Auth against /api/auth works, but this unfortunately does not work for my usecase, as the Basic Auth would need to happen on /admin (basically replacing the login page).

If it is possible I would suggest implementing an option in pihole.toml like this:

[webserver]
  # Login method for the web interface
  # Possible values are:
  #    "Basic", for HTTP Basic Authorization (Browser Popup)
  #    "Form", Login Page
  authorization = "Basic" 

The "Form" option would be the current behavior and should be set as the default, whilst the Basic option would use an .htpasswd file that is handled by civetweb. Both would use the session-based authentication system as they would both only be two possible options of 'entering' the credentials.

Another option would be to combine the two and just check for the "Authorization" header, and if it is not present send the user to the login page (or if it is easier to implement show the login page anyway and auto-login like you suggested).

I unfortunately do not know enough C to implement this myself.

Thank you again for your time and effort.

Are you sure this does not work? How did you test it?

I did two things:

  1. Firstly, I tested using curl as shown in the PR test.
  2. Secondly, I opened a new Firefox Private Window, and checked that I was not logged it (automatic redirect to login page). Then, I opened https://pi-hole:ABC@pi.hole/admin/ where pi-hole is the hard-coded username and ABC is the password I am trying here.
    Firefox kept me asking a few times it I am really sure I want to send my password to this page and - boom - I was logged in. Yes, I did saw the login page for a split second but I didn't had to do anything.
    The nasty Firefox tabs shouldn't be something you'd have to be too worried about if they are handled by your nginx).

So I did a third test now and set up a very basic nginx reverse proxy taking care about adding authentication:

events {
  worker_connections  4096;  ## Default: 1024
}

http {
 server {
  location / {
    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_pass http://192.168.2.11:80; # This is where my Pi-hole lives
    proxy_set_header Authorization "Basic cGktaG9sZTpBQkM=";
  }
 }
}

As soon as I browse to http://127.0.0.1/admin, I see a redirection to the login page and immediately back to the web interface where I can work with Pi-hole without login (even if directly browsing to http://192.168.2.11:80 does need a login with said password ABC).

So I am not sure why it should not be working for you.


On another note: Reading a bit on the Internet about "IAM" and "nginx" typically leads to a solution where no authentication is used on the target. Now, I am not actually sure we need such a feature in Pi-hole at all and I am more inclined to a "No" right now, cfm. the following schema:


(source)

This makes a lot of sense. You authorize on the reverse proxy level and only those that are authorized make their way through to your Pi-hole. Making your nginx the central IAM node allowing you to have an arbitrarily complicated user management.

Hello DL6ER,

I tested as follows:

version: "3"

# More info at https://github.com/pi-hole/docker-pi-hole/ and https://docs.pi-hole.net/
services:
  pihole:
    container_name: pihole
    image: pihole/pihole:basic_auth
    # For DHCP it is recommended to remove these ports and instead add: network_mode: "host"
    ports:
      - "1153:53/tcp"
      - "1153:53/udp"
      - "1167:67/udp" # Only required if you are using Pi-hole as your DHCP server
      - "80:80/tcp"
      - "443:443/tcp" # By default, FTL will generate a self-signed certificate
    environment:
      TZ: 'America/Chicago'
      FTLCONF_webserver_api_password: 'ABC'
    # Volumes store your data between container upgrades
    volumes:
      - './etc-pihole:/etc/pihole'
     # - './etc-dnsmasq.d:/etc/dnsmasq.' # Only needed if you have some custom configs for dnsmasq
    # https://github.com/pi-hole/docker-pi-hole#note-on-capabilities
    cap_add:
      - NET_ADMIN # Required if you are using Pi-hole as your DHCP server, else not needed
    restart: unless-stopped
  • docker compose up

With this setup I get the following results:

  1. Simple test against api

    curl -k -L -sv https://pi-hole:ABC@localhost:443/api/auth
    

    works: {"session":{"valid":true,"totp":false,"sid":"...

  2. Manual Header Test

    curl -k -L -sv -H "Authorization: Basic cGktaG9sZTpBQkM=" https://localhost:443/api/auth
    

    woks: {"session":{"valid":true,"totp":false,"sid":"...

  3. Browser test against /auth/api
    https://pi-hole:ABC@localhost/api/auth
    Does not work. I get a popup:

    You are about to log in to the site “localhost” with the username “pi-hole”, but the website does not require authentication. This may be an attempt to trick you.
    Is “localhost” the site you want to visit?
    No Yes

    Answering yes shows: {"session":{"valid":false,"totp":false,"...

  4. Repeating the same test as in 1.-3. with the path /admin/, as well as / results in:

    1. The HTML code of the login page
    2. The HTML code of the login page
    3. Same result as Number 3.

Regarding your note abount IAM and nginx: Yes that would be a typical solution in corporate networks when you only allow the proxy-server to connect to the application. In a simple home setup, where pihole is typically used, this is not the case. For example my setup has a pihole docker container deployed on my host on the br0 network, making it visible to the network on a static ip different from my hosts ip.
That makes it impossible to not expose ports 80/443 of the container to the network and I do not know of a way to implement a firewall between my network and the container running on br0 without completely remodeling the network - which would make it very different from a 'normal'-pi-hole-users network.
That is the reason I want to retain the need for authentication when accessing the pi-hole directly, but being able to "inject" the Basic Auth header when going through my reverse proxy, which forces authentication.

1 Like