On Mon, Dec 27, 2010 at 16:40, Magnus Hagander <[email protected]> wrote:
> On Mon, Dec 27, 2010 at 16:33, Tom Lane <[email protected]> wrote:
>> Magnus Hagander <[email protected]> writes:
>>> On Mon, Dec 27, 2010 at 10:53, Magnus Hagander <[email protected]> wrote:
>>>> We could quite easily make a replication role *never* be able to
>>>> connect to a non-walsender backend. That would mean that if you set
>>>> your role to WITH REPLICATION, it can *only* be used for replication
>>>> and nothing else (well, you could still SET ROLE to it, but given that
>>>> it's not a superuser (anymore), that doesn't have any security
>>>> implications.
>>
>>> Actually, having implemented that and tested it, I realize that's a
>>> pretty bad idea.
>>
>> OK, so if we're not going to recommend that REPLICATION roles be
>> NOLOGIN, we're back to the original question: should the REPLICATION
>> bit give any other special privileges? I can see the point of allowing
>> such a user to issue pg_start_backup and pg_stop_backup.
>
> Yes, those would definitely be useful.
Updated patch, still pending docs, but otherwise updated: allow
start/stop backup, make sure only superuser can turn on/off the flag,
include in system views, show properly in psql.
--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 8301,8310 **** pg_start_backup(PG_FUNCTION_ARGS)
struct stat stat_buf;
FILE *fp;
! if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! errmsg("must be superuser to run a backup")));
if (RecoveryInProgress())
ereport(ERROR,
--- 8301,8310 ----
struct stat stat_buf;
FILE *fp;
! if (!superuser() && !is_authenticated_user_replication_role())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! errmsg("must be superuser or replication role to run a backup")));
if (RecoveryInProgress())
ereport(ERROR,
***************
*** 8493,8502 **** pg_stop_backup(PG_FUNCTION_ARGS)
int waits = 0;
bool reported_waiting = false;
! if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! (errmsg("must be superuser to run a backup"))));
if (RecoveryInProgress())
ereport(ERROR,
--- 8493,8502 ----
int waits = 0;
bool reported_waiting = false;
! if (!superuser() && !is_authenticated_user_replication_role())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! (errmsg("must be superuser or replication role to run a backup"))));
if (RecoveryInProgress())
ereport(ERROR,
*** a/src/backend/catalog/system_views.sql
--- b/src/backend/catalog/system_views.sql
***************
*** 15,20 **** CREATE VIEW pg_roles AS
--- 15,21 ----
rolcreatedb,
rolcatupdate,
rolcanlogin,
+ rolreplication,
rolconnlimit,
'********'::text as rolpassword,
rolvaliduntil,
***************
*** 30,35 **** CREATE VIEW pg_shadow AS
--- 31,37 ----
rolcreatedb AS usecreatedb,
rolsuper AS usesuper,
rolcatupdate AS usecatupd,
+ rolreplication AS userepl,
rolpassword AS passwd,
rolvaliduntil::abstime AS valuntil,
setconfig AS useconfig
***************
*** 54,59 **** CREATE VIEW pg_user AS
--- 56,62 ----
usecreatedb,
usesuper,
usecatupd,
+ userepl,
'********'::text as passwd,
valuntil,
useconfig
*** a/src/backend/commands/user.c
--- b/src/backend/commands/user.c
***************
*** 94,99 **** CreateRole(CreateRoleStmt *stmt)
--- 94,100 ----
bool createrole = false; /* Can this user create roles? */
bool createdb = false; /* Can the user create databases? */
bool canlogin = false; /* Can this user login? */
+ bool isreplication = false; /* Is this a replication role? */
int connlimit = -1; /* maximum connections allowed */
List *addroleto = NIL; /* roles to make this a member of */
List *rolemembers = NIL; /* roles to be members of this role */
***************
*** 107,112 **** CreateRole(CreateRoleStmt *stmt)
--- 108,114 ----
DefElem *dcreaterole = NULL;
DefElem *dcreatedb = NULL;
DefElem *dcanlogin = NULL;
+ DefElem *disreplication = NULL;
DefElem *dconnlimit = NULL;
DefElem *daddroleto = NULL;
DefElem *drolemembers = NULL;
***************
*** 190,195 **** CreateRole(CreateRoleStmt *stmt)
--- 192,205 ----
errmsg("conflicting or redundant options")));
dcanlogin = defel;
}
+ else if (strcmp(defel->defname, "isreplication") == 0)
+ {
+ if (disreplication)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ disreplication = defel;
+ }
else if (strcmp(defel->defname, "connectionlimit") == 0)
{
if (dconnlimit)
***************
*** 247,252 **** CreateRole(CreateRoleStmt *stmt)
--- 257,264 ----
createdb = intVal(dcreatedb->arg) != 0;
if (dcanlogin)
canlogin = intVal(dcanlogin->arg) != 0;
+ if (disreplication)
+ isreplication = intVal(disreplication->arg) != 0;
if (dconnlimit)
{
connlimit = intVal(dconnlimit->arg);
***************
*** 272,277 **** CreateRole(CreateRoleStmt *stmt)
--- 284,296 ----
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to create superusers")));
}
+ else if (isreplication)
+ {
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to create replication users")));
+ }
else
{
if (!have_createrole_privilege())
***************
*** 341,346 **** CreateRole(CreateRoleStmt *stmt)
--- 360,366 ----
/* superuser gets catupdate right by default */
new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper);
new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
+ new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
if (password)
***************
*** 439,444 **** AlterRole(AlterRoleStmt *stmt)
--- 459,465 ----
int createrole = -1; /* Can this user create roles? */
int createdb = -1; /* Can the user create databases? */
int canlogin = -1; /* Can this user login? */
+ int isreplication = -1; /* Is this a replication role? */
int connlimit = -1; /* maximum connections allowed */
List *rolemembers = NIL; /* roles to be added/removed */
char *validUntil = NULL; /* time the login is valid until */
***************
*** 450,455 **** AlterRole(AlterRoleStmt *stmt)
--- 471,477 ----
DefElem *dcreaterole = NULL;
DefElem *dcreatedb = NULL;
DefElem *dcanlogin = NULL;
+ DefElem *disreplication = NULL;
DefElem *dconnlimit = NULL;
DefElem *drolemembers = NULL;
DefElem *dvalidUntil = NULL;
***************
*** 514,519 **** AlterRole(AlterRoleStmt *stmt)
--- 536,549 ----
errmsg("conflicting or redundant options")));
dcanlogin = defel;
}
+ else if (strcmp(defel->defname, "isreplication") == 0)
+ {
+ if (disreplication)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ disreplication = defel;
+ }
else if (strcmp(defel->defname, "connectionlimit") == 0)
{
if (dconnlimit)
***************
*** 556,561 **** AlterRole(AlterRoleStmt *stmt)
--- 586,593 ----
createdb = intVal(dcreatedb->arg);
if (dcanlogin)
canlogin = intVal(dcanlogin->arg);
+ if (disreplication)
+ isreplication = intVal(disreplication->arg);
if (dconnlimit)
{
connlimit = intVal(dconnlimit->arg);
***************
*** 594,605 **** AlterRole(AlterRoleStmt *stmt)
--- 626,645 ----
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to alter superusers")));
}
+ else if (((Form_pg_authid) GETSTRUCT(tuple))->rolreplication || isreplication >= 0)
+ {
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to alter replication users")));
+ }
else if (!have_createrole_privilege())
{
if (!(inherit < 0 &&
createrole < 0 &&
createdb < 0 &&
canlogin < 0 &&
+ isreplication < 0 &&
!dconnlimit &&
!rolemembers &&
!validUntil &&
***************
*** 685,690 **** AlterRole(AlterRoleStmt *stmt)
--- 725,736 ----
new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true;
}
+ if (isreplication >= 0)
+ {
+ new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication > 0);
+ new_record_repl[Anum_pg_authid_rolreplication - 1] = true;
+ }
+
if (dconnlimit)
{
new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 510,517 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
MAPPING MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NOCREATEDB
! NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOSUPERUSER
! NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NULLS_P NUMERIC
OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR
ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
--- 510,518 ----
MAPPING MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NOCREATEDB
! NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOREPLICATION_P
! NOSUPERUSER NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
! NULLS_P NUMERIC
OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR
ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
***************
*** 523,530 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
QUOTE
RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REINDEX
! RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART
! RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE
SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SHARE
--- 524,532 ----
QUOTE
RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REINDEX
! RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA REPLICATION_P
! RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK
! ROW ROWS RULE
SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SHARE
***************
*** 864,869 **** AlterOptRoleElem:
--- 866,879 ----
{
$$ = makeDefElem("canlogin", (Node *)makeInteger(FALSE));
}
+ | REPLICATION_P
+ {
+ $$ = makeDefElem("isreplication", (Node *)makeInteger(TRUE));
+ }
+ | NOREPLICATION_P
+ {
+ $$ = makeDefElem("isreplication", (Node *)makeInteger(FALSE));
+ }
| CONNECTION LIMIT SignedIconst
{
$$ = makeDefElem("connectionlimit", (Node *)makeInteger($3));
***************
*** 11288,11293 **** unreserved_keyword:
--- 11298,11304 ----
| NOCREATEUSER
| NOINHERIT
| NOLOGIN_P
+ | NOREPLICATION_P
| NOSUPERUSER
| NOTHING
| NOTIFY
***************
*** 11330,11335 **** unreserved_keyword:
--- 11341,11347 ----
| REPEATABLE
| REPLACE
| REPLICA
+ | REPLICATION_P
| RESET
| RESTART
| RESTRICT
*** a/src/backend/utils/init/miscinit.c
--- b/src/backend/utils/init/miscinit.c
***************
*** 231,236 **** static int SecurityRestrictionContext = 0;
--- 231,240 ----
static bool SetRoleIsActive = false;
+ /* Remember if the authenticated user is a replication role */
+ static bool AuthenticatedUserIsReplicationRole = false;
+
+
/*
* GetUserId - get the current effective user ID.
*
***************
*** 389,394 **** SetUserIdAndContext(Oid userid, bool sec_def_context)
--- 393,407 ----
/*
+ * Check if the authenticated user is a replication role
+ */
+ bool
+ is_authenticated_user_replication_role(void)
+ {
+ return AuthenticatedUserIsReplicationRole;
+ }
+
+ /*
* Initialize user identity during normal backend startup
*/
void
***************
*** 418,423 **** InitializeSessionUserId(const char *rolename)
--- 431,437 ----
AuthenticatedUserId = roleid;
AuthenticatedUserIsSuperuser = rform->rolsuper;
+ AuthenticatedUserIsReplicationRole = rform->rolreplication;
/* This sets OuterUserId/CurrentUserId too */
SetSessionUserId(roleid, AuthenticatedUserIsSuperuser);
*** a/src/backend/utils/init/postinit.c
--- b/src/backend/utils/init/postinit.c
***************
*** 658,668 **** InitPostgres(const char *in_dbname, Oid dboid, const char *username,
{
Assert(!bootstrap);
! /* must have authenticated as a superuser */
! if (!am_superuser)
ereport(FATAL,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! errmsg("must be superuser to start walsender")));
/* process any options passed in the startup packet */
if (MyProcPort != NULL)
--- 658,668 ----
{
Assert(!bootstrap);
! /* must have authenticated as a replication role */
! if (!is_authenticated_user_replication_role())
ereport(FATAL,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! errmsg("must be replication role to start walsender")));
/* process any options passed in the startup packet */
if (MyProcPort != NULL)
*** a/src/bin/psql/describe.c
--- b/src/bin/psql/describe.c
***************
*** 2195,2200 **** describeRoles(const char *pattern, bool verbose)
--- 2195,2204 ----
appendPQExpBufferStr(&buf, "\n, pg_catalog.shobj_description(r.oid, 'pg_authid') AS description");
ncols++;
}
+ if (pset.sversion >= 90100)
+ {
+ appendPQExpBufferStr(&buf,"\n, r.rolreplication");
+ }
appendPQExpBufferStr(&buf, "\nFROM pg_catalog.pg_roles r\n");
***************
*** 2254,2259 **** describeRoles(const char *pattern, bool verbose)
--- 2258,2267 ----
if (strcmp(PQgetvalue(res, i, 5), "t") != 0)
add_role_attribute(&buf, _("Cannot login"));
+ if (pset.sversion >= 90100)
+ if (strcmp(PQgetvalue(res, i, 8), "t") == 0)
+ add_role_attribute(&buf, _("Replication"));
+
conns = atoi(PQgetvalue(res, i, 6));
if (conns >= 0)
{
*** a/src/include/catalog/pg_authid.h
--- b/src/include/catalog/pg_authid.h
***************
*** 51,56 **** CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MAC
--- 51,57 ----
bool rolcreatedb; /* allowed to create databases? */
bool rolcatupdate; /* allowed to alter catalogs manually? */
bool rolcanlogin; /* allowed to log in as session user? */
+ bool rolreplication; /* role used for streaming replication */
int4 rolconnlimit; /* max connections allowed (-1=no limit) */
/* remaining fields may be null; use heap_getattr to read them! */
***************
*** 72,78 **** typedef FormData_pg_authid *Form_pg_authid;
* compiler constants for pg_authid
* ----------------
*/
! #define Natts_pg_authid 10
#define Anum_pg_authid_rolname 1
#define Anum_pg_authid_rolsuper 2
#define Anum_pg_authid_rolinherit 3
--- 73,79 ----
* compiler constants for pg_authid
* ----------------
*/
! #define Natts_pg_authid 11
#define Anum_pg_authid_rolname 1
#define Anum_pg_authid_rolsuper 2
#define Anum_pg_authid_rolinherit 3
***************
*** 80,88 **** typedef FormData_pg_authid *Form_pg_authid;
#define Anum_pg_authid_rolcreatedb 5
#define Anum_pg_authid_rolcatupdate 6
#define Anum_pg_authid_rolcanlogin 7
! #define Anum_pg_authid_rolconnlimit 8
! #define Anum_pg_authid_rolpassword 9
! #define Anum_pg_authid_rolvaliduntil 10
/* ----------------
* initial contents of pg_authid
--- 81,90 ----
#define Anum_pg_authid_rolcreatedb 5
#define Anum_pg_authid_rolcatupdate 6
#define Anum_pg_authid_rolcanlogin 7
! #define Anum_pg_authid_rolreplication 8
! #define Anum_pg_authid_rolconnlimit 9
! #define Anum_pg_authid_rolpassword 10
! #define Anum_pg_authid_rolvaliduntil 11
/* ----------------
* initial contents of pg_authid
***************
*** 91,97 **** typedef FormData_pg_authid *Form_pg_authid;
* user choices.
* ----------------
*/
! DATA(insert OID = 10 ( "POSTGRES" t t t t t t -1 _null_ _null_ ));
#define BOOTSTRAP_SUPERUSERID 10
--- 93,99 ----
* user choices.
* ----------------
*/
! DATA(insert OID = 10 ( "POSTGRES" t t t t t t f -1 _null_ _null_ ));
#define BOOTSTRAP_SUPERUSERID 10
*** a/src/include/miscadmin.h
--- b/src/include/miscadmin.h
***************
*** 357,362 **** extern void ValidatePgVersion(const char *path);
--- 357,363 ----
extern void process_shared_preload_libraries(void);
extern void process_local_preload_libraries(void);
extern void pg_bindtextdomain(const char *domain);
+ extern bool is_authenticated_user_replication_role(void);
/* in access/transam/xlog.c */
extern bool BackupInProgress(void);
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
***************
*** 250,255 **** PG_KEYWORD("nocreateuser", NOCREATEUSER, UNRESERVED_KEYWORD)
--- 250,256 ----
PG_KEYWORD("noinherit", NOINHERIT, UNRESERVED_KEYWORD)
PG_KEYWORD("nologin", NOLOGIN_P, UNRESERVED_KEYWORD)
PG_KEYWORD("none", NONE, COL_NAME_KEYWORD)
+ PG_KEYWORD("noreplication", NOREPLICATION_P, UNRESERVED_KEYWORD)
PG_KEYWORD("nosuperuser", NOSUPERUSER, UNRESERVED_KEYWORD)
PG_KEYWORD("not", NOT, RESERVED_KEYWORD)
PG_KEYWORD("nothing", NOTHING, UNRESERVED_KEYWORD)
***************
*** 313,318 **** PG_KEYWORD("rename", RENAME, UNRESERVED_KEYWORD)
--- 314,320 ----
PG_KEYWORD("repeatable", REPEATABLE, UNRESERVED_KEYWORD)
PG_KEYWORD("replace", REPLACE, UNRESERVED_KEYWORD)
PG_KEYWORD("replica", REPLICA, UNRESERVED_KEYWORD)
+ PG_KEYWORD("replication", REPLICATION_P, UNRESERVED_KEYWORD)
PG_KEYWORD("reset", RESET, UNRESERVED_KEYWORD)
PG_KEYWORD("restart", RESTART, UNRESERVED_KEYWORD)
PG_KEYWORD("restrict", RESTRICT, UNRESERVED_KEYWORD)
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers