PostgreSQL – Change Permissions of Copy to Exported File

copyexportlinuxpostgresql

I have asked this question on SO but perhaps that was the wrong location. Apologies for double posting but the question may have a better audience here.

The original question is: https://stackoverflow.com/questions/25619290/change-permissions-of-postgres-copy-to-exported-file

And the text is below. Thanks.


When running the postgresql [9.3] "COPY TO" functionality, the postgres user creates a file. I have told it to put the file in the tmp directory (although this can be changed if needed).

This is run as part of a cron job, the user is an "export" user set up especially for this task. The end goal is for the exported file to end up in /home/export/created_file.csv.

1) postgres can't stash the exported file straight into the home directory of another user without setting up the folder to be overly permissive.

2) the export user can copy the file from /tmp to it's home directory, but then can not clean up (delete) the postgres owned file in /tmp.

This is a daily process and so old files will need to be deleted in some way. I can add a cron job to clean up the files afterwards but I was hoping for a cleaner solution – either a way to change the permissions on the file that postgres creates, or some way to give the export user the additional permission of being able to delete that file in advance of it's creation.

Giving the export user the same permissions as postgres in general however isn't an option, as it would then have free reign over the database.

Any advice, be it unix based or postgres based would be very welcome, thanks.

Best Answer

You'll want to create a directory without the t (sticky) permissions bit set, and have PostgreSQL copy the file into there. You should also make the directory setgid (g+s), as that makes the group inherited from the directory, not the creating process; see setgid directories.

You could put this directory in /tmp but personally I'd put it some neutral location like /var/lib, or mounted volume somewhere, as /tmp gets cleaned out automatically so you'll need to have the directory re-created if it's missing.

Assuming you do use /tmp:

mkdir -p /tmp/pgcsv
chgrp users /tmp/pgcsv
chmod u=rwX,g=rwsX,o= /tmp/pgcsv

then create the csv in there. It'll be automatically created with group-owner users, which will make it readable by your users (unless PostgreSQL's umask is too restrictive). Because the directory is group-writable by users (or whatever group you use) and does not have the t (sticky) bit set your users can delete files they do not own from it.

The reason this doesn't work when using /tmp/ directly is that the sticky bit is set in /tmp for security, so that user1 can't delete a file created by user2 and replace it with a symlink that user2 then writes to, allowing user1 to steal their data / trick them into overwriting things.


Other options:

  • Run the cron job as user export, using psql's \copy command, so it writes the data over the client/server socket and the file is created as the export user in the first place. This is by far the simplest option, and only slightly slower than a direct COPY.
  • Have a cron job running as root invoke the PostgreSQL copy then move the data file and change its ownership once the COPY finishes.
  • Give the export user the ability to run a very restricted command as root via sudo to move/delete.
  • Use POSIX ACLs to grant the postgres user special permission to write to /home/export (if POSIX ACLs are enabled on your file system)
  • Set group-owner of /home/export to postgres and make its permissions g=wx. PostgreSQL can add new entries, and overwrite anything within that it has write permission to, but not list entries. I don't like this option much.