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.
-
Please delete the old question.Craig Ringer– Craig Ringer2014年09月03日 09:24:00 +00:00Commented Sep 3, 2014 at 9:24
-
Original SO question deleted.Mitch Kent– Mitch Kent2014年09月03日 11:55:24 +00:00Commented Sep 3, 2014 at 11:55
-
...and link to old question on SO too?Neil Gaetano Lindberg– Neil Gaetano Lindberg2021年01月27日 00:00:12 +00:00Commented Jan 27, 2021 at 0:00
2 Answers 2
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
, usingpsql
's\copy
command, so it writes the data over the client/server socket and the file is created as theexport
user in the first place. This is by far the simplest option, and only slightly slower than a directCOPY
. - Have a cron job running as
root
invoke the PostgreSQLcopy
then move the data file and change its ownership once theCOPY
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
topostgres
and make its permissionsg=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.
-
A very well documented answer, thank you. There are at least some options that I would never have known about, and at least a couple I'm kicking myself for not having thought of. Many thanks.Mitch Kent– Mitch Kent2014年09月03日 11:54:54 +00:00Commented Sep 3, 2014 at 11:54
-
Sticky bit was itAndrew Lazarus– Andrew Lazarus2021年12月11日 23:36:36 +00:00Commented Dec 11, 2021 at 23:36
I'm probably super late for this, but I just want to add this in case someone else gets stuck:
Since the program you're running in COPY TO PROGRAM
is just a regular bash command that receives the result of the select as its input, you could just append a chmod and change file permissions before being done with the file.
For example, you could change:
COPY (SELECT * from ...) TO PROGRAM 'gzip > my_new_file'
to
COPY (SELECT * from ...) TO PROGRAM 'gzip > my_new_file_path && chmod 755 my_new_file_path'
and that would set permissions to the file before finishing the copy.
-
That would have been mighty useful answer, thank you! But you're right in that you're a little late for me :-)Mitch Kent– Mitch Kent2016年03月17日 21:13:39 +00:00Commented Mar 17, 2016 at 21:13
-
Great answer. I would also add that, if you don't want your output file to be zipped, you can just replace
gzip
withcat
.Russ– Russ2017年06月21日 14:01:02 +00:00Commented Jun 21, 2017 at 14:01 -
Even later :-). I'd use GNU coreutils install instead for the
TO PROGRAM
e.g.install -m=755 /dev/stdin my_new_file_path
. Games with file/group ownership still need to be played ifpostgres
doesn't belong to a desired group.ldav1s– ldav1s2023年05月30日 21:03:42 +00:00Commented May 30, 2023 at 21:03