diff --git a/doc/src/sgml/backup.sgml b/doc/src/sgml/backup.sgml
index 9d8e69056f..a9912eb418 100644
--- a/doc/src/sgml/backup.sgml
+++ b/doc/src/sgml/backup.sgml
@@ -1095,7 +1095,11 @@ SELECT pg_stop_backup();
and postmaster.opts, which record information
about the running postmaster, not about the
postmaster which will eventually use this backup.
- (These files can confuse pg_ctl.)
+ (These files can confuse pg_ctl.) If group read
+ access is enabled on the data directory and an unprivileged user in the
+ PostgreSQL group is performing the backup, then
+ postmaster.pid will not be readable and must be
+ excluded.
diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml
index 585665f161..5f57b933b9 100644
--- a/doc/src/sgml/ref/initdb.sgml
+++ b/doc/src/sgml/ref/initdb.sgml
@@ -77,6 +77,14 @@ PostgreSQL documentation
+ For security reasons the new cluster created by initdb
+ will only be accessible by the cluster owner by default. The
+ option allows any user in the same
+ group as the cluster owner to read files in the cluster. This is useful
+ for performing backups as a non-privileged user.
+
+
+ initdb initializes the database cluster's default
locale and character set encoding. The character set encoding,
collation order (LC_COLLATE) and character set classes
@@ -302,6 +310,17 @@ PostgreSQL documentation
+
+
+
+
+ Allows users in the same group as the cluster owner to read all cluster
+ files created by initdb.
+
+
+
+
+
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index a2ebd3e21c..8f602dc705 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -137,7 +137,10 @@ postgres$ initdb -D /usr/local/pgsql/data
database, it is essential that it be secured from unauthorized
access. initdb therefore revokes access
permissions from everyone but the
- PostgreSQL user.
+ PostgreSQL user, and optionally, group.
+ Group access, when enabled, is read-only. This allows an unprivileged
+ user in the PostgreSQL group to take a backup of
+ the cluster data or perform other operations that only require read access.
@@ -2237,6 +2240,15 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
+ If the data directory allows group read access then certificate files may
+ need to be located outside of the data directory in order to conform to the
+ security requirements outlined above. Generally, group access is enabled
+ to allow an unprivileged user to backup the database, and in that case the
+ backup software will not be able to read the certificate files and will
+ likely error.
+
+
+
If the private key is protected with a passphrase, the
server will prompt for the passphrase and will not start until it has
been entered.
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index aeda54f00b..b780b70e53 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -587,7 +587,8 @@ PostmasterMain(int argc, char *argv[])
IsPostmasterEnvironment = true;
/*
- * for security, no dir or file created can be group or other accessible
+ * By default, no dir or file created can be group or other accessible. This
+ * may be modified later depending in the permissions of the data directory.
*/
umask(S_IRWXG | S_IRWXO);
@@ -1524,25 +1525,30 @@ checkDataDir(void)
#endif
/*
- * Check if the directory has group or world access. If so, reject.
- *
- * It would be possible to allow weaker constraints (for example, allow
- * group access) but we cannot make a general assumption that that is
- * okay; for example there are platforms where nearly all users
- * customarily belong to the same group. Perhaps this test should be
- * configurable.
+ * Check if the directory has correct permissions. If not, reject.
*
* XXX temporarily suppress check when on Windows, because there may not
* be proper support for Unix-y file permissions. Need to think of a
* reasonable check to apply on Windows.
*/
#if !defined(WIN32) && !defined(__CYGWIN__)
- if (stat_buf.st_mode & (S_IRWXG | S_IRWXO))
+ if (stat_buf.st_mode & PG_MODE_MASK_ALLOW_GROUP)
ereport(FATAL,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
- errmsg("data directory \"%s\" has group or world access",
- DataDir),
- errdetail("Permissions should be u=rwx (0700).")));
+ errmsg("data directory \"%s\" has invalid permissions",
+ DataDir),
+ errdetail("Permissions should be u=rwx (0700) or u=rwx,g=rx (0750).")));
+#endif
+
+ /*
+ * Reset the file mode creation mask based on the mode of the data
+ * directory.
+ *
+ * Suppress when on Windows, because there may not be proper support
+ * for Unix-y file permissions.
+ */
+#if !defined(WIN32) && !defined(__CYGWIN__)
+ umask(~(stat_buf.st_mode & PG_DIR_MODE_DEFAULT) & 0777);
#endif
/* Look for PG_VERSION before looking for pg_control */
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index 342401b8dd..52a4a31ce1 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -125,12 +125,6 @@
#define FD_MINFREE 10
/*
- * Default mode for created files, unless something else is specified using
- * the *Perm() function variants.
- */
-#define PG_FILE_MODE_DEFAULT (S_IRUSR | S_IWUSR)
-
-/*
* A number of platforms allow individual processes to open many more files
* than they can really support when *many* processes do the same thing.
* This GUC parameter lets the DBA limit max_safe_fds to something less than
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index ddc850db1c..afc94c7f94 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -113,6 +113,12 @@ static const char *const auth_methods_local[] = {
NULL
};
+/* Default file mode creation mask */
+#define PG_MODE_MASK_DEFAULT (S_IRWXG | S_IRWXO)
+
+/* File mode mask that allows group access */
+#define PG_MODE_MASK_ALLOW_GROUP (S_IWGRP | S_IRWXO)
+
/*
* these values are passed in by makefile defines
*/
@@ -144,7 +150,13 @@ static bool data_checksums = false;
static char *xlog_dir = NULL;
static char *str_wal_segment_size_mb = NULL;
static int wal_segment_size_mb;
+static mode_t file_mode_mask = PG_MODE_MASK_DEFAULT;
+
+/* File mode to use with chmod on files */
+#define PG_FILE_MODE ((S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) & ~file_mode_mask)
+/* File mode to use with chmod on directories */
+#define PG_DIR_MODE ((S_IRWXU | S_IRWXG | S_IRWXO) & ~file_mode_mask)
/* internal vars */
static const char *progname;
@@ -1170,7 +1182,7 @@ setup_config(void)
snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data);
writefile(path, conflines);
- if (chmod(path, S_IRUSR | S_IWUSR) != 0)
+ if (chmod(path, PG_FILE_MODE) != 0)
{
fprintf(stderr, _("%s: could not change permissions of \"%s\": %s\n"),
progname, path, strerror(errno));
@@ -1190,7 +1202,7 @@ setup_config(void)
sprintf(path, "%s/postgresql.auto.conf", pg_data);
writefile(path, autoconflines);
- if (chmod(path, S_IRUSR | S_IWUSR) != 0)
+ if (chmod(path, PG_FILE_MODE) != 0)
{
fprintf(stderr, _("%s: could not change permissions of \"%s\": %s\n"),
progname, path, strerror(errno));
@@ -1277,7 +1289,7 @@ setup_config(void)
snprintf(path, sizeof(path), "%s/pg_hba.conf", pg_data);
writefile(path, conflines);
- if (chmod(path, S_IRUSR | S_IWUSR) != 0)
+ if (chmod(path, PG_FILE_MODE) != 0)
{
fprintf(stderr, _("%s: could not change permissions of \"%s\": %s\n"),
progname, path, strerror(errno));
@@ -1293,7 +1305,7 @@ setup_config(void)
snprintf(path, sizeof(path), "%s/pg_ident.conf", pg_data);
writefile(path, conflines);
- if (chmod(path, S_IRUSR | S_IWUSR) != 0)
+ if (chmod(path, PG_FILE_MODE) != 0)
{
fprintf(stderr, _("%s: could not change permissions of \"%s\": %s\n"),
progname, path, strerror(errno));
@@ -2311,6 +2323,7 @@ usage(const char *progname)
printf(_(" --auth-local=METHOD default authentication method for local-socket connections\n"));
printf(_(" [-D, --pgdata=]DATADIR location for this database cluster\n"));
printf(_(" -E, --encoding=ENCODING set default encoding for new databases\n"));
+ printf(_(" -g, --allow-group-access set file mode creation mask to 0027\n"));
printf(_(" --locale=LOCALE set default locale for new databases\n"));
printf(_(" --lc-collate=, --lc-ctype=, --lc-messages=LOCALE\n"
" --lc-monetary=, --lc-numeric=, --lc-time=LOCALE\n"
@@ -2692,7 +2705,7 @@ create_data_directory(void)
pg_data);
fflush(stdout);
- if (pg_mkdir_p(pg_data, S_IRWXU) != 0)
+ if (pg_mkdir_p(pg_data, PG_DIR_MODE) != 0)
{
fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
progname, pg_data, strerror(errno));
@@ -2710,7 +2723,7 @@ create_data_directory(void)
pg_data);
fflush(stdout);
- if (chmod(pg_data, S_IRWXU) != 0)
+ if (chmod(pg_data, PG_DIR_MODE) != 0)
{
fprintf(stderr, _("%s: could not change permissions of directory \"%s\": %s\n"),
progname, pg_data, strerror(errno));
@@ -2778,7 +2791,8 @@ create_xlog_or_symlink(void)
xlog_dir);
fflush(stdout);
- if (pg_mkdir_p(xlog_dir, S_IRWXU) != 0)
+ printf("MASK: %04o\n", file_mode_mask);
+ if (pg_mkdir_p(xlog_dir, PG_DIR_MODE) != 0)
{
fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
progname, xlog_dir, strerror(errno));
@@ -2796,7 +2810,8 @@ create_xlog_or_symlink(void)
xlog_dir);
fflush(stdout);
- if (chmod(xlog_dir, S_IRWXU) != 0)
+ printf("MASK: %04o\n", file_mode_mask);
+ if (chmod(xlog_dir, PG_DIR_MODE) != 0)
{
fprintf(stderr, _("%s: could not change permissions of directory \"%s\": %s\n"),
progname, xlog_dir, strerror(errno));
@@ -2846,7 +2861,7 @@ create_xlog_or_symlink(void)
else
{
/* Without -X option, just make the subdirectory normally */
- if (mkdir(subdirloc, S_IRWXU) < 0)
+ if (mkdir(subdirloc, PG_DIR_MODE) < 0)
{
fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
progname, subdirloc, strerror(errno));
@@ -2882,7 +2897,9 @@ initialize_data_directory(void)
setup_signals();
- umask(S_IRWXG | S_IRWXO);
+ /* Set the file mode creation mask. */
+ umask(file_mode_mask);
+ printf(_("Initializing with file mode creation mask: %04o\n"), file_mode_mask);
create_data_directory();
@@ -2902,7 +2919,7 @@ initialize_data_directory(void)
* The parent directory already exists, so we only need mkdir() not
* pg_mkdir_p() here, which avoids some failure modes; cf bug #13853.
*/
- if (mkdir(path, S_IRWXU) < 0)
+ if (mkdir(path, PG_DIR_MODE) < 0)
{
fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
progname, path, strerror(errno));
@@ -3016,6 +3033,7 @@ main(int argc, char *argv[])
{"waldir", required_argument, NULL, 'X'},
{"wal-segsize", required_argument, NULL, 12},
{"data-checksums", no_argument, NULL, 'k'},
+ {"allow-group-access", no_argument, NULL, 'g'},
{NULL, 0, NULL, 0}
};
@@ -3057,7 +3075,7 @@ main(int argc, char *argv[])
/* process command-line options */
- while ((c = getopt_long(argc, argv, "dD:E:kL:nNU:WA:sST:X:", long_options, &option_index)) != -1)
+ while ((c = getopt_long(argc, argv, "dD:E:kL:nNU:WA:sST:X:g", long_options, &option_index)) != -1)
{
switch (c)
{
@@ -3150,6 +3168,8 @@ main(int argc, char *argv[])
break;
case 12:
str_wal_segment_size_mb = pg_strdup(optarg);
+ case 'g':
+ file_mode_mask = PG_MODE_MASK_ALLOW_GROUP;
break;
default:
/* getopt_long already emitted a complaint */
diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h
index bc2d7bc063..5258584cd3 100644
--- a/src/include/storage/fd.h
+++ b/src/include/storage/fd.h
@@ -58,10 +58,28 @@ extern PGDLLIMPORT int max_files_per_process;
extern int max_safe_fds;
/*
+ * Default mode mask for data directory permissions that does not allow
+ * group execute/read.
+ */
+#define PG_MODE_MASK_DEFAULT (S_IRWXG | S_IRWXO)
+
+/*
+ * Optional mode mask for data directory permissions that allows group
+ * read/execute.
+ */
+#define PG_MODE_MASK_ALLOW_GROUP (S_IWGRP | S_IRWXO)
+
+/*
+ * Default mode for created files, unless something else is specified using
+ * the *Perm() function variants.
+ */
+#define PG_FILE_MODE_DEFAULT (S_IRUSR | S_IWUSR | S_IRGRP)
+
+/*
* Default mode for created directories, unless something else is specified
* using the MakeDirectoryPerm() function variant.
*/
-#define PG_DIR_MODE_DEFAULT (S_IRWXU)
+#define PG_DIR_MODE_DEFAULT (S_IRWXU | S_IRGRP | S_IXGRP)
/*
* prototypes for functions in fd.c