Cannot connect to PostgreSQL unix domain socket

I'm trying to connect to PostgreSQL Unix domain socket from a PHP web application. Relevant system components:

  • CentOS 7 x64 (SELinux enforced)
  • postgresql93 9.3.5-2PGDG.rhel7
  • nginx 1.6.2-1.el7.ngx
  • php-common 5.4.16-23.el7_0.3
  • php-fpm 5.4.16-23.el7_0.3
  • php-pdo 5.4.16-23.el7_0.3
  • php-pgsql 5.4.16-23.el7_0.3

PostgreSQL is listening on standard port 5432 and I have no problems to use it via TCP/IP at 127.0.0.1:5432, but when I try to connect to its Unix domain socket, I have a following error:

Cannot connect to database: SQLSTATE[08006] [7] could not connect to server:
No such file or directory
    Is the server running locally and accepting
    connections on Unix domain socket "/tmp/.s.PGSQL.5432"?

File /tmp/.s.PGSQL.5432 exists and I can connect using psql:

$ psql -Uusername db_name
psql (9.3.5)
Type "help" for help.

db_name=>

So there should be no problems from the PostgreSQL side. Or are there?

Relevant line from /var/lib/pgsql/9.3/data/pg_hba.conf:

local    all    username        trust

Change of location for Unix domain socket file in /var/lib/pgsql/9.3/data/postgresql.conf did not help:

unix_socket_directories = '/var/run/pgsql'

There was nothing in /var/log/audit/audit.log, but I've tried to disable SELinux, just to be sure:

# setenforce 0

This did not help, so it is not SELinux.

Relevant lines from strace of php-fpm:

[pid   882] socket(PF_LOCAL, SOCK_STREAM, 0) = 5
[pid   882] fcntl(5, F_SETFL, O_RDONLY|O_NONBLOCK) = 0
[pid   882] fcntl(5, F_SETFD, FD_CLOEXEC) = 0
[pid   882] connect(5, {sa_family=AF_LOCAL, sun_path="/tmp/.s.PGSQL.5432"}, 110) = -1 ENOENT (No such file or directory)
[pid   882] close(5)                    = 0

The file name is correct and, again, file /tmp/.s.PGSQL.5432 exists:

$ ls -l /tmp/.s.PGSQL.5432
srwxrwxrwx. 1 postgres postgres 0 Nov  1 09:47 /tmp/.s.PGSQL.5432

It looks like php-fpm process is not chroot'ed:

# ls -l /proc/882/root
lrwxrwxrwx. 1 apache apache 0 Oct 31 19:54 /proc/882/root -> /

At this point I have no more ideas where the problem can be and I would appreciate any help.


Solution 1:

I've resolved the problem. The initial cause of it was in systemd service file for php-fpm /usr/lib/systemd/system/php-fpm.service:

[Service]
PrivateTmp=true

This means that php-fpm could not see /tmp/.s.PGSQL.5432 nor anything else located in /tmp. To resolve the issue I've changed the location for PostgreSQL Unix domain socket file with following steps:

  • Create a new directory /var/pgsql (note changing SELinux file context):

    # mkdir /var/pgsql
    # chown posgres:postgres /var/pgsql
    # chmod 0755 /var/pgsql
    # semanage fcontext -a -t httpd_var_run_t "/var/pgsql(/.*)?"
    # restorecon -R /var/pgsql
    
  • Uncomment and modify unix_socket_directories parameter in /var/lib/pgsql/9.3/data/postgresql.conf (/tmp is still needed not to break psql and other programs):

    unix_socket_directories = '/tmp,/var/pgsql'
    

And here was another tricky part... In the original post I wrote that I've tried to change the location of Unix domain socket file, and I really did. What I missed was the changed error message. I thought that the error was the same, but it wasn't:

Error (256): Cannot connect to database: SQLSTATE[08006] [7] could not connect to
server: Permission denied
    Is the server running locally and accepting
    connections on Unix domain socket "/var/pgsql/.s.PGSQL.5432"?

Permission denied - this is something one can fight.

Everything works with

# setenforce 0

The problem is:

# audit2allow -a

#============= httpd_t ==============
allow httpd_t initrc_t:unix_stream_socket connectto;

And it can be fixed:

# audit2allow -a -M httpd_postgresql_unix_socket_connect
# semodule -i httpd_postgresql_unix_socket_connect.pp

Content of httpd_postgresql_unix_socket_connect.te:

module httpd_postgresql_unix_socket_connect 1.0;

require {
        type httpd_t;
        type initrc_t;
        class unix_stream_socket connectto;
}

#============= httpd_t ==============
allow httpd_t initrc_t:unix_stream_socket connectto;

Turn SELinux back on:

# setenforce 1

And everything works!

P.S. I'd be glad to know if there is a way to avoid custom SELinux module by changing some file contexts or booleans. It might be the case, because SELinux allows nginx to use php-fpm Unix domain socket (/var/run/php5-fpm.sock), but for some reason blocks usage of PostgreSQL Unix domain socket (/var/pgsql/.s.PGSQL.5432).