Postgres: POSTGRES_PASSWORD changes authentication settings unexpectedly

Created on 14 May 2019  路  9Comments  路  Source: docker-library/postgres

I believe that current authentication configuration in docker-entrypoint.sh is less secure than it should be. I have a problem with $POSTGRES_PASSWORD changing the authentication settings and not adequately warning user about it.

Currently if the password is not supplied host all all all trust is added to pg_hba.conf , disabling the password checks for all users. I think this goes way too far:

1) I honestly believe that there should be no way for the line host all all all trust to appear in configuration unless user explicitly sets ACCEPT_ANY_PASSWORD_FOR_ANY_USER=true
2) Currently any mistake in configuration (for example misspelling POSTGRES_PASSWORD) results in disabled authentication
3) Disabled authentication is completely silent. User could believe that password authentication is working, since the DB accepts the password.
4) Warning displayed if POSTGRES_PASSWORD wasn't set does not convey that password checks were disabled altogether.

No password has been set for the database. This will allow anyone with access to the Postgres port to access your database.

Sounds like you can fix it by setting the password, but the password would be just ignored.

I had an unpleasant experience of discovering my development DB for a personal project consuming 100% of server's CPU because someone was mining some kind of crypto coin on it.
Initially I was using POSTGRES_PASSWORD, but later I decided to move password configuration for "postgres" user together with creating additional, less privileged users into an init SQL file in /docker-entrypoint-initdb.d/ (amazing feature, btw). As you might imagine, I did not believe that this would disable all password checks altogether, and since my server continued to grant access with my password I had no idea that I was completely insecure.

I did not expect (and didn't need) high security from DB with an exposed port and without SSL, but having literally no security is a really strange decision.

I've read issues about security of this image (#3 #29 #31), and it seems like "security is annoying and not our problem" argument is winning. I'd much rather have official images be more secure by default, but I feel like this fight is futile. So here are my suggestions, from less bothersome to more secure:

1) Change the warning to clearly state "all authentication is disabled, any password is valid for any user, change pg_hba.conf or set $POSTGRES_PASSWORD if this behavior is undesirable" or something like that. Add this warning to readme.
2) If POSTGRES_PASSWORD is not set (better yet, if ALLOW_EMPTY_PASSWORD is set), then set it to an empty string and keep the md5 authentication always enabled. That way developers can still easily connect to a DB with empty password, but changing the password would never be silently ignored, like it was in my case.
3) If POSTGRES_PASSWORD is not set (better yet, if GENERATE_RANDOM_PASSWORD is set), then set password to a random string and print it on initial launch.

Official MySQL image allows to get second or third behavior by setting MYSQL_ALLOW_EMPTY_PASSWORD or MYSQL_RANDOM_ROOT_PASSWORD environment variables. Alternatively, you can set MYSQL_ROOT_PASSWORD directly. One of the three variables must be set explicitly, or MySQL container fails to launch.

For backwards compatibility ACCEPT_ANY_PASSWORD_FOR_ANY_USER or similarly explicitly named environment variable could be added. If it is set to true then host all all all trust is added to the config.

I'm willing to write the necessary code, but I'd like to know beforehand which approach would be accepted. IMHO MySQL's approach is the correct one, it is both flexible and explicit, but it's up to the maintainers.

Request

Most helpful comment

I think that at minimum the image needs a big strong warning at the start of the readme, warning that the default configuration is:

  • not data-durable; and
  • applies no access control

then links to the sections that discuss how to set each up.

Plus clearly and explicitly documenting that not passing a POSTGRES_PASSWORD disables all authentication. Even if a password is set later. Experienced postgres users will understand why that is, but most people will be astonished that when they ALTER USER postgres SET PASSWORD 'foobar' that has no effect, and the same's true for creating other users.

All 9 comments

I was just about to report this same bug! It's rather surprising that when you don't set a password for the postgres user, no password will ever checked for any user.

I think that at minimum the image needs a big strong warning at the start of the readme, warning that the default configuration is:

  • not data-durable; and
  • applies no access control

then links to the sections that discuss how to set each up.

Plus clearly and explicitly documenting that not passing a POSTGRES_PASSWORD disables all authentication. Even if a password is set later. Experienced postgres users will understand why that is, but most people will be astonished that when they ALTER USER postgres SET PASSWORD 'foobar' that has no effect, and the same's true for creating other users.

I've just had my db (luckily a backed-up side-project db) targeted by ransomware. Imagine my surprise when, in trying to find out how they were able to access the db, I managed to connect to the db on my server with a simple psql -h <ip> -U postgres from my laptop and later I found the line host all all all trust at the end of the pg_hba file!

Obviously my fault for not checking deployment and default options, I'm not trying to shift blame which obviously belongs to me and noone else. But I incorrectly assumed that, being a super popular docker image of the best relational database, it had a sensible pseudo-secure default, something like "trust all connections coming from localhost, and from anywhere if using the correct password".

"Trust anything from anywhere" is in my opinion an option that shouldn't even exist, much less be the default when some tangentially-related environment variable is unset! At the very least, if it really needs to exist, it should be under some very uncomfortable and scary-looking parameter, along the lines of ALLOW_ANYONE_IN_THE_WORLD_TO_MANAGE_YOUR_DATABASE_WITHOUT_ANY_KIND_OF_AUTHENTICATION

The problem is that localhost doesn't mean anything in a Docker context. If you're running your database with localhost being the only thing accessible inside Docker, you might as well not be running it at all (since localhost is only accessible from directly within the PostgreSQL container itself).

One idea we've had to help combat this problem is to enforce an explicit POSTGRES_PASSWORD being set, which isn't great from a usability perspective but would be better from a default-security perspective.

Operationally as a side note, I would say there are very few good reasons for exposing the PostgreSQL port externally from the cluster (-p XXX:NNN), and would recommend instead utilizing Docker's (or Kubernetes') built-in networking and service discovery.

Hurray! Thanks for your response, @tianon.

In the past 20 days I鈥檝e experimented with postgres and... For some strange reason you can鈥檛 supply empty string as a password. You can easily set empty sting as a password, but then you just can鈥檛 login with it: psql: fe_sendauth: no password supplied. 馃く馃が. At least now I understand why trust thing happened.

The problem is that localhost...

There's no problems with localhost, It's absolutely clear (for me, at least).

My problem is that insanely permissive host all all all trust could be set in pg_hba without user's explicit wish.

there are very few good reasons for exposing the PostgreSQL port externally

Yes there are. But if your image is intended only for usage in trusted networks, then please, let鈥檚 reflect this in the documentation ~preferably in bold and at the very top~. Otherwise your default configuration shouldn鈥檛 disable authentication without user鈥檚 consent.

My proposals, updated given the weird empty-password behavior:
1) Have md5 auth turned on by default and set "postgres" as a default password if no POSTGRES_PASSWORD is specified. Mention it in the logs.
2) If setting a default password is unacceptable then refuse to start without password supplied. It could be overridden by setting DISABLE_ALL_AUTHENTICATION_FOR_ALL_USERS or similarly _descriptive_ variable. This option would add host all all all trust to pg_hba. This should be clearly documented, something like:

Postgres doesn鈥檛 allow empty passwords, so the only way to access db without password is to disable all authentication completely. Setting a password later would not re-enable authentication. This option should not ever be used if the postgres port is exposed to the internet.
3) If having developers specify one environment variable is too much of an inconvenience, then at least improve the messaging and documentation. Right now documentation for POSTGRES_PASSWORD is 3 paragraphs long, contains 2 notes, but fails to mention that not specifying it disables all authentication. Warning in entry point also doesn鈥檛 mention it.

I'm not talking about making postgres image secure by default, this will hurt usability and as we all know, usability is much more important than security when it comes to databases ~SQL Injections: going strong for 20 years!~. I'm only asking for _understandable_ security model without backstabbing.

@tianon ^^^^^

Is somebody playing Curb Your Enthusiasm theme or is it in my head?

Currently any mistake in configuration (for example misspelling POSTGRES_PASSWORD) results in disabled authentication

This misconfiguration vulnerability (or, as you put it "Request") is open for 8 months with no actionable feedback.

Try searching github for POSTRES_PASSWORD, POSTGERS_PASSWORD and other possible misspellings. Not all of the results use db with exposed port, but all of those people have no authentication on their database and do not know it. And those are only the open source projects, normally people do not publish their production db config files.

Maintainers, please. Choose 1, 2, or 3 and I'll implement it. Propose 4th alternative and I'll implement that. Say "we don't care" and close the issue. Do something concrete.

This really needs to change. It's easy for users to expose their whole DB to the world by default, silently, but that doesn't make it user friendly. It just sets them up with a time bomb that will explode on them later.

Yes, it'd be better to use proper service mapping and discovery, but even then you should preferably define some model of authentication beyond "0.0.0.0/0 trust". To mitigate the issues around the lack of a true "localhost" you can expose a variable for allowed subnets, for example.

Opened #658. Let me know if that is sufficient or if you have any suggestions for improvement.

@yosifkit Thanks. Per comment on PR that looks sensible; making the user make an explicit decision is good. Better, it's BC so long as they're already doing the sensible thing and setting POSTGRES_PASSWORD so it only forces changes to be made by the people who really should be reviewing their setup anyway.

Was this page helpful?
0 / 5 - 0 ratings