Skip to content

Silent port conflict goes undetected #798

Description

@wchargin

I ran into a confusing scenario where I think Postgres.app should have reported "Port in use" and failed to start, but instead managed to bind and steal traffic from a process that was already listening on that port.

I have a Docker container running PostGIS and exposing container port 5432 on host port 5432. I was able to open a psql shell on port 5432, but was confused because the database didn't have the right data. Eventually I figured out that Postgres.app was also listening on port 5432:

$ sudo lsof -i :5432
COMMAND     PID     USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
com.docke  1029 wchargin  128u  IPv6 0x97e222976b6cf6c5      0t0  TCP *:postgresql (LISTEN)
postgres  45709 wchargin    7u  IPv6 0x91b1855dc1c99cac      0t0  TCP localhost:postgresql (LISTEN)
postgres  45709 wchargin    8u  IPv4 0xa012493f18604ea9      0t0  TCP localhost:postgresql (LISTEN)

$ ps -p 45709 -o comm
COMM
/Applications/Postgres.app/Contents/Versions/16/bin/postgres

I searched the source for SO_REUSEPORT / SO_REUSEADDR and found the comment leading to #676. I'm glad that that issue is resolved, but it seems like this issue is an unfortunate consequence of the fix.

I do think that it may be somewhat confusing to use this socket option, since it subverts the application that if an application is listening on a port then it will be able to receive packets sent to that port. Notably, this confusion only occurs because my Docker container is listening on all interfaces. If I expose 127.0.0.1:5432:5432 instead of just 5432:5432, then Postgres.app properly reports "Port in use" or the Docker container fails with "Ports are not available". So it feels a bit clumsy and unpredictable that Postgres.app behaves this way.

Might there be a different way of solving the original issue that doesn't cause this side-effect?

Steps to reproduce

  1. Make sure that Postgres.app is stopped.
  2. Run docker run --rm -e POSTGRES_PASSWORD=password -p 5432:5432 -it postgres, and leave that running in its own terminal.
  3. Run psql -h localhost -p 5432 -U postgres, and execute (say) CREATE TABLE tab(), SELECT count(1) FROM tab.
  4. Start Postgres.app. Note that it moves to the "Running" state without any error.
  5. Run psql -h localhost -p 5432 -U postgres again. Note that SELECT count(1) FROM tab now fails: no such relation.

If you have the psql shells from (3) and (5) open at the same time, you can note that their \conninfos report identical results:

You are connected to database "postgres" as user "postgres" on host "localhost" (address "::1") at port "5432".

…yet they are actually talking to different clusters!

(Then you can Ctrl-C in the terminal from step 2, which will destroy the container.)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions