portainer: Proxy BasicAuth not compatible with Portainer default authentication

Bug description When one has basic auth enabled on their webserver (such as caddyserver with me) portainer fails to login at all

Expected behavior A proper and safe logn

Portainer Logs Provide the logs of your Portainer container or Service. You can see how here

USER: Webserver Logs + Config Server Logs

04/Jun/2020:23:20:53 +0000 [ERROR 401 /api/status] BasicAuth: user "" was not found or password was incorrect. 108.162.246.180 admin.redacted /api/status HTTP/1.1
04/Jun/2020:23:22:10 +0000 [ERROR 401 /api/users/1] BasicAuth: user "" was not found or password was incorrect. 108.162.246.180 admin.redacted /api/users/1 HTTP/1.1
04/Jun/2020:23:22:10 +0000 [ERROR 401 /api/auth] BasicAuth: user "" was not found or password was incorrect. 108.162.246.180 admin.redacted /api/auth HTTP/1.1
04/Jun/2020:23:22:10 +0000 [ERROR 401 /api/settings/public] BasicAuth: user "" was not found or password was incorrect. 108.162.246.180 admin.redacted /api/settings/public HTTP/1.1

user was welcometohell

server Config

https://admin.redacted {

        tls /opt/caddy/cert/pub /opt/caddy/cert/priv
        
        basicauth / welcometohell redacted
        proxy / 192.168.0.101:9080 {
			transparent
			websocket
		}
    log /opt/caddy/log/admin.log
    errors /opt/caddy/log/admin.err
}

Steps to reproduce the issue:

  1. use any portainer server
  2. Put Portainer behind reverse proxy with basic auth configured
  3. attempt to login through reverse proxy

image image

Technical details:

  • Portainer version: 1.24
  • Docker version (managed by Portainer):
Client: Docker Engine - Community
 Version:           19.03.8
 API version:       1.40
 Go version:        go1.12.17
 Git commit:        afacb8b7f0
 Built:             Wed Mar 11 01:25:46 2020
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.8
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.17
  Git commit:       afacb8b7f0
  Built:            Wed Mar 11 01:24:19 2020
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.2.13
  GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc:
  Version:          1.0.0-rc10
  GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683
  • Platform (windows/linux): Linux
  • Command used to start Portainer : docker run -d -p 8000:8000 -p 9080:9000 --name="Portainer" -v /var/run/docker.sock:/var/run/docker.sock -v /opt/docker/portainer:/data portainer/portainer

image

  • Browser: Firefox Dev Edition

Additional Context The only way i can get it to work via the URL is by disabling basic auth in my config, with is what provides and extra boost to my security should a login vulnerability be found in portainer

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 15
  • Comments: 40 (4 by maintainers)

Most upvoted comments

I alos agree with https://github.com/portainer/portainer/issues/2809#issuecomment-638323529 I know oAuth is now a standard and can be a secured solution; but as @toussa says with a BASIC_AUTH method, “You cannot even see and access to the door to try to open it.” I don’t want either to let portainer app (login page or whatever) be viewable/accessible from anyone without been first authenticated. I prefer the auth been set before reaching the app (Nginx BASIC_AUTH). Accessing Portainer is too sensitive and everyone know that no app can be 100% secure 😉

Been three years… guys, do something.

I’ve just spent many hours trying to understand the issue well enough to be able to search and end up here.

As it stands it seems that Portainer seems stuck due to past coding choices in a situation where it can not be integrated into a wider environment. Reverse proxies are not going away, they provide central control points for access, monitoring, administration and responding for all deployed systems. So products that can not integrate with them are a problem and something that the product writers must address.

What I can not understand is why the writers of Portainer decided to remove the --no-auth which allowed integration to take place without providing a long term solution.

For my situation, it means I most likely comment out Portainer from my deployment process and maybe revisit the issue at some point in the future. I lose a nice tool from my stack and Portainer loses a potential customer, but that’s life.

Because Portainer provides access to your deployment environment, it does need to provide a basic level of security. It would be irresponsible if users can inadvertently open that up to the rest of the world.

We are talking about the same product? You know the one that is by default 100% open to any connected network user for 5 mins unless someone connects to it to set up an admin account. Also, the same product that is mostly installed within environments that auto restarts any application that shuts down. It is this particular feature that caused me to start looking at using the reverse proxy in the first place as it means that the ce release current ships with no default security, while being one of the most powerful tools deployed.

Perhaps consider using OAuth, which should work with a reverse proxy solution.

That is missing the point - the aim of any security group and the supporting infrastructure group is to deploy a common environment, that meets the ‘general’ security policies of the business - placing every deployed application behind reverse proxies makes meeting such policies far easier and far easier to prove at audit time. The security group should not have to validate that every installation of every deployed application has been correctly secured by whichever team has deployed it.

Using your first comment as an example - if Portainer worked behind a reverse proxy the business security group could be certain that no security hole is created by a miss selecting of a command-line option - or due to no manual configuration work having been carried out. The security group would also have access to common logs showing access to all applications within the business, while also being able to deploy pro-active tools to manage/protect access.

Just to be clear - application-level security is great, but it does not solve the wider issues of many teams given responsibility for business-level issues. Reverse proxies like Traefik are a foundation of the ‘Infrastructure as code’ direction the IT world is taking, and the current Portainer design is impacting such work.

why the writers of Portainer decided to remove the --no-auth

Because Portainer provides access to your deployment environment, it does need to provide a basic level of security. It would be irresponsible if users can inadvertently open that up to the rest of the world.

Reverse proxies are not going away, they provide central control points for access

Perhaps consider using OAuth, which should work with a reverse proxy solution.

This is basically required for Portainer to be usable in my opinion. I’d like to handle authentication with something like oauth2-proxy, and either have portainer have no authentication, or accept custom authentication headers (preferred).

I would let the user configure the header to use, and put a section in the documentation.

I’m not an expert by any means, but something like JWT_HEADER=“Authorization” in a config file or an environment variable somewhere would do.

Add a section in your documentation saying “change that value to JWT_HEADER=JWTAuthorization if you wish to combine JWT and basic auth. Make sure there’s no issue with your setup if you do so”.

If i break my setup by changing the header, i’m then fully responsible for it, and i can fix it by modifying my setup to reflect my own changes.

Again, i’m not an expert, but i’d be really surprised to learn that some web servers see the “Authorization” header as JWT first. Do you have any example ?

I wouldn’t recommend exposing Portainer directly to the internet. And even with HTTPS now being enabled by default, sending your credentials with every request isn’t really best practise. Maybe consider using a VPN and OAuth instead.

hello darkness my old friend…

Any updates on this? I really like the idea of @Tetedeiench because if would change nothing for all the users wanting to use just portainers auth functionality and enables a much more broader range of additional auth scenarios via revers proxys.

I’m not an expert by any means, but something like JWT_HEADER=“Authorization” in a config file or an environment variable somewhere would do.

Add a section in your documentation saying “change that value to JWT_HEADER=JWTAuthorization if you wish to combine JWT and basic auth. Make sure there’s no issue with your setup if you do so”.

I vote for this too. Putting sites behind a basic auth protected reverse proxy not only hides the back-end pages completely but also enables protecting the site with tools like fail2ban.

It also provides protection in case critical vulnerabilities are discovered in the authentication mechanisms of the back-end site. It gives admins time to update w/o worrying about vulnerabilities being exploited immediately after being published.

@andreas-wolf Thank you dear friend, you helped me solve the problem, thank you from the bottom of my heart ❤️

Sadly I don’t think they will do something for BasicAuth 😦 Personally I continue to use the 1.22.2 docker version that works with the BasicAuth method.

Any new about this? I’m stick with putting portainer behind a reverse proxy with basic Auth, and I can’t seem to find a way to pass both basic auth and jwt at the same time. Any help?

Adding onto the previous reply

It also makes it so people cannot see what is hidden so they cannot enact an exploit

On Sat, Aug 15, 2020, 3:11 AM Laurens Blanckenborg notifications@github.com wrote:

I vote for this too. Putting sites behind a basic auth protected reverse proxy not only hides the back-end pages completely but also enables protecting the site with tools like fail2ban.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/portainer/portainer/issues/3893#issuecomment-674378448, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACPQOXXFCHYOGQO7E3MFNTLSAZNN5ANCNFSM4NTCHAAQ .

I’m all for it 😃 That would be a great solution to a problem discussed many times here, according to {insert your favorite search engine} !

I concur - I do not want to rely solely on a login page’s security, and implement basic auth as a added layer of security on top on every critical management app i have. It’s easy to implement, fail2ban is closely monitoring it as well, it just makes things secure in a very practical and easy way.

Portainer is the sole app i know not supporting this.

Please reconsider your decision and support Basic Auth. The --no-auth isn’t an option in my case.

To resolve this issue I had to do the following

Portainer’s GUI now sits behind an instance of Teleport, which in turn sits behind Traefik.

It’s a lot more work, but Teleport does have a range of features that makes the work worthwhile.

The key thing is that it is not possible for a total insecure copy of Portainer CE to be published to the internet - Portainer CE by default asks for the admin user to be setup via it’s GUI interface on first deployment. EE is not much better as it asks for a licence key and you can now get such a key for free. As such an unconfigured instance can be taken over by anyone who finds it on the internet. in comparison Teleport fails safe if not full configured.

Problem is portainer is disallowing some security measures by its OAuth implementation.

While I would never use the “–no-auth” switch myself, it makes sense to use an implementation that allows other security features to work.

Just allow us to configure the header used, and it’s the end of the story.

this does not solve the problem tho this solution basically turns off your own basic auth for portainer and you are back to sqaure one with portainers auth

what is needed is a solution to run your own authentication in front of portainer, so you don’t have to trust the portainer authentication mechanism (since portainer has a lot of privileges)

Here is a nginx configuration for @Virsacer proposal.

location / {
      if ($http_authorization = "") {
        set $auth_basic "your_domain";
      }
      if ($http_authorization != "") {
        set $auth_basic off;
      }
      auth_basic $auth_basic;
      auth_basic_user_file /etc/nginx/conf.d/.htpasswd;
      proxy_http_version 1.1;
      proxy_set_header Connection "";
      proxy_pass https://192.168.1.1:9443/;
    }

I’m not sure how this helps. So if the request has an Authorization header, basic auth is off and the header is used. This leads to the effect that only for the portainer login page basic auth is needed. This basic auth is not propagated to portainer, you still need to log in separately to portainer. This hack can easily been circumvented by sending any Authorization header which leads directly to the login page.

Terminology aside, the issue here is that you wish to use Portainer’s default authentication option with basic auth from a proxy provider, which doesn’t work because the proxy is overriding Portainer’s JWT. Use of the Authorization header for JWT authentication is the default standard as explained here, which we wanted to respect the last time this was discussed.

With the upcoming release of Portainer enterprise edition, maybe this is an architecture change we want to consider now, to better support use of proxies?

@deviantony @ncresswell thoughts on this?