Here's a patch that changes walsender to require a special privilege for replication instead of relying on superuser permissions. We discussed this back before 9.0 was finalized, but IIRC we ran out of time. The motivation being that you really want to use superuser as little as possible - and since being a replication slave is a read only role, it shouldn't require the maximum permission available in the system.
Obviously the patch needs docs and some system views updates, which I will add later. But I wanted to post what I have so far for a quick review to confirm whether I'm on the right track or not... How it works should be rather obvious - adds a "WITH REPLICATION/NOREPLICATION" to the create and alter role commands, and then check this when a connection attempts to start the walsender. -- Magnus Hagander Me: http://www.hagander.net/ Work: http://www.redpill-linpro.com/
*** 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); *************** *** 341,346 **** CreateRole(CreateRoleStmt *stmt) --- 353,359 ---- /* 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) --- 452,458 ---- 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) --- 464,470 ---- 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) --- 529,542 ---- 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) --- 579,586 ---- createdb = intVal(dcreatedb->arg); if (dcanlogin) canlogin = intVal(dcanlogin->arg); + if (disreplication) + isreplication = intVal(disreplication->arg); if (dconnlimit) { connlimit = intVal(dconnlimit->arg); *************** *** 600,605 **** AlterRole(AlterRoleStmt *stmt) --- 625,631 ---- createrole < 0 && createdb < 0 && canlogin < 0 && + isreplication < 0 && !dconnlimit && !rolemembers && !validUntil && *************** *** 685,690 **** AlterRole(AlterRoleStmt *stmt) --- 711,722 ---- 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/postinit.c --- b/src/backend/utils/init/postinit.c *************** *** 60,65 **** --- 60,66 ---- static HeapTuple GetDatabaseTuple(const char *dbname); static HeapTuple GetDatabaseTupleByOid(Oid dboid); + static bool is_replication_role(void); static void PerformAuthentication(Port *port); static void CheckMyDatabase(const char *name, bool am_superuser); static void InitCommunication(void); *************** *** 166,171 **** GetDatabaseTupleByOid(Oid dboid) --- 167,189 ---- return tuple; } + /* + * is_replication_role -- check if the role is a replication role + */ + static bool + is_replication_role(void) + { + bool result = false; + HeapTuple utup; + + utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(GetUserId())); + if (HeapTupleIsValid(utup)) + { + result = ((Form_pg_authid) GETSTRUCT(utup))->rolreplication; + ReleaseSysCache(utup); + } + return result; + } /* * PerformAuthentication -- authenticate a remote client *************** *** 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) --- 676,686 ---- { Assert(!bootstrap); ! /* must have authenticated as a replication role */ ! if (!is_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/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/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 (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers