mirror of
https://github.com/postgres/postgres.git
synced 2025-07-02 09:02:37 +03:00
Add option to bgworkers to allow the bypass of role login check
This adds a new option called BGWORKER_BYPASS_ROLELOGINCHECK to the
flags available to BackgroundWorkerInitializeConnection() and
BackgroundWorkerInitializeConnectionByOid().
This gives the possibility to bgworkers to bypass the role login check,
making possible the use of a role that has no login rights while not
being a superuser. PostgresInit() gains a new flag called
INIT_PG_OVERRIDE_ROLE_LOGIN, taking advantage of the refactoring done in
4800a5dfb4
.
Regression tests are added to worker_spi to check the behavior of this
new option with bgworkers.
Author: Bertrand Drouvot
Reviewed-by: Nathan Bossart, Michael Paquier, Bharath Rupireddy
Discussion: https://postgr.es/m/bcc36259-7850-4882-97ef-d6b905d2fc51@gmail.com
This commit is contained in:
@ -201,6 +201,9 @@ typedef struct BackgroundWorker
|
|||||||
during <command>initdb</command>. If <literal>BGWORKER_BYPASS_ALLOWCONN</literal>
|
during <command>initdb</command>. If <literal>BGWORKER_BYPASS_ALLOWCONN</literal>
|
||||||
is specified as <varname>flags</varname> it is possible to bypass the restriction
|
is specified as <varname>flags</varname> it is possible to bypass the restriction
|
||||||
to connect to databases not allowing user connections.
|
to connect to databases not allowing user connections.
|
||||||
|
If <literal>BGWORKER_BYPASS_ROLELOGINCHECK</literal> is specified as
|
||||||
|
<varname>flags</varname> it is possible to bypass the login check for the
|
||||||
|
role used to connect to databases.
|
||||||
A background worker can only call one of these two functions, and only
|
A background worker can only call one of these two functions, and only
|
||||||
once. It is not possible to switch databases.
|
once. It is not possible to switch databases.
|
||||||
</para>
|
</para>
|
||||||
|
@ -5567,6 +5567,9 @@ BackgroundWorkerInitializeConnection(const char *dbname, const char *username, u
|
|||||||
/* ignore datallowconn? */
|
/* ignore datallowconn? */
|
||||||
if (flags & BGWORKER_BYPASS_ALLOWCONN)
|
if (flags & BGWORKER_BYPASS_ALLOWCONN)
|
||||||
init_flags |= INIT_PG_OVERRIDE_ALLOW_CONNS;
|
init_flags |= INIT_PG_OVERRIDE_ALLOW_CONNS;
|
||||||
|
/* ignore rolcanlogin? */
|
||||||
|
if (flags & BGWORKER_BYPASS_ROLELOGINCHECK)
|
||||||
|
init_flags |= INIT_PG_OVERRIDE_ROLE_LOGIN;
|
||||||
|
|
||||||
/* XXX is this the right errcode? */
|
/* XXX is this the right errcode? */
|
||||||
if (!(worker->bgw_flags & BGWORKER_BACKEND_DATABASE_CONNECTION))
|
if (!(worker->bgw_flags & BGWORKER_BACKEND_DATABASE_CONNECTION))
|
||||||
@ -5598,6 +5601,9 @@ BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, uint32 flags)
|
|||||||
/* ignore datallowconn? */
|
/* ignore datallowconn? */
|
||||||
if (flags & BGWORKER_BYPASS_ALLOWCONN)
|
if (flags & BGWORKER_BYPASS_ALLOWCONN)
|
||||||
init_flags |= INIT_PG_OVERRIDE_ALLOW_CONNS;
|
init_flags |= INIT_PG_OVERRIDE_ALLOW_CONNS;
|
||||||
|
/* ignore rolcanlogin? */
|
||||||
|
if (flags & BGWORKER_BYPASS_ROLELOGINCHECK)
|
||||||
|
init_flags |= INIT_PG_OVERRIDE_ROLE_LOGIN;
|
||||||
|
|
||||||
/* XXX is this the right errcode? */
|
/* XXX is this the right errcode? */
|
||||||
if (!(worker->bgw_flags & BGWORKER_BACKEND_DATABASE_CONNECTION))
|
if (!(worker->bgw_flags & BGWORKER_BACKEND_DATABASE_CONNECTION))
|
||||||
|
@ -725,7 +725,7 @@ has_rolreplication(Oid roleid)
|
|||||||
* Initialize user identity during normal backend startup
|
* Initialize user identity during normal backend startup
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
InitializeSessionUserId(const char *rolename, Oid roleid)
|
InitializeSessionUserId(const char *rolename, Oid roleid, bool bypass_login_check)
|
||||||
{
|
{
|
||||||
HeapTuple roleTup;
|
HeapTuple roleTup;
|
||||||
Form_pg_authid rform;
|
Form_pg_authid rform;
|
||||||
@ -789,7 +789,7 @@ InitializeSessionUserId(const char *rolename, Oid roleid)
|
|||||||
/*
|
/*
|
||||||
* Is role allowed to login at all?
|
* Is role allowed to login at all?
|
||||||
*/
|
*/
|
||||||
if (!rform->rolcanlogin)
|
if (!bypass_login_check && !rform->rolcanlogin)
|
||||||
ereport(FATAL,
|
ereport(FATAL,
|
||||||
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
|
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
|
||||||
errmsg("role \"%s\" is not permitted to log in",
|
errmsg("role \"%s\" is not permitted to log in",
|
||||||
|
@ -684,6 +684,7 @@ BaseInit(void)
|
|||||||
* flags:
|
* flags:
|
||||||
* - INIT_PG_LOAD_SESSION_LIBS to honor [session|local]_preload_libraries.
|
* - INIT_PG_LOAD_SESSION_LIBS to honor [session|local]_preload_libraries.
|
||||||
* - INIT_PG_OVERRIDE_ALLOW_CONNS to connect despite !datallowconn.
|
* - INIT_PG_OVERRIDE_ALLOW_CONNS to connect despite !datallowconn.
|
||||||
|
* - INIT_PG_OVERRIDE_ROLE_LOGIN to connect despite !rolcanlogin.
|
||||||
* out_dbname: optional output parameter, see below; pass NULL if not used
|
* out_dbname: optional output parameter, see below; pass NULL if not used
|
||||||
*
|
*
|
||||||
* The database can be specified by name, using the in_dbname parameter, or by
|
* The database can be specified by name, using the in_dbname parameter, or by
|
||||||
@ -901,7 +902,8 @@ InitPostgres(const char *in_dbname, Oid dboid,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
InitializeSessionUserId(username, useroid);
|
InitializeSessionUserId(username, useroid,
|
||||||
|
(flags & INIT_PG_OVERRIDE_ROLE_LOGIN) != 0);
|
||||||
am_superuser = superuser();
|
am_superuser = superuser();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -910,7 +912,7 @@ InitPostgres(const char *in_dbname, Oid dboid,
|
|||||||
/* normal multiuser case */
|
/* normal multiuser case */
|
||||||
Assert(MyProcPort != NULL);
|
Assert(MyProcPort != NULL);
|
||||||
PerformAuthentication(MyProcPort);
|
PerformAuthentication(MyProcPort);
|
||||||
InitializeSessionUserId(username, useroid);
|
InitializeSessionUserId(username, useroid, false);
|
||||||
/* ensure that auth_method is actually valid, aka authn_id is not NULL */
|
/* ensure that auth_method is actually valid, aka authn_id is not NULL */
|
||||||
if (MyClientConnectionInfo.authn_id)
|
if (MyClientConnectionInfo.authn_id)
|
||||||
InitializeSystemUser(MyClientConnectionInfo.authn_id,
|
InitializeSystemUser(MyClientConnectionInfo.authn_id,
|
||||||
|
@ -364,7 +364,8 @@ extern bool InSecurityRestrictedOperation(void);
|
|||||||
extern bool InNoForceRLSOperation(void);
|
extern bool InNoForceRLSOperation(void);
|
||||||
extern void GetUserIdAndContext(Oid *userid, bool *sec_def_context);
|
extern void GetUserIdAndContext(Oid *userid, bool *sec_def_context);
|
||||||
extern void SetUserIdAndContext(Oid userid, bool sec_def_context);
|
extern void SetUserIdAndContext(Oid userid, bool sec_def_context);
|
||||||
extern void InitializeSessionUserId(const char *rolename, Oid roleid);
|
extern void InitializeSessionUserId(const char *rolename, Oid roleid,
|
||||||
|
bool bypass_login_check);
|
||||||
extern void InitializeSessionUserIdStandalone(void);
|
extern void InitializeSessionUserIdStandalone(void);
|
||||||
extern void SetSessionAuthorization(Oid userid, bool is_superuser);
|
extern void SetSessionAuthorization(Oid userid, bool is_superuser);
|
||||||
extern Oid GetCurrentRoleId(void);
|
extern Oid GetCurrentRoleId(void);
|
||||||
@ -466,6 +467,7 @@ extern PGDLLIMPORT AuxProcType MyAuxProcType;
|
|||||||
/* flags for InitPostgres() */
|
/* flags for InitPostgres() */
|
||||||
#define INIT_PG_LOAD_SESSION_LIBS 0x0001
|
#define INIT_PG_LOAD_SESSION_LIBS 0x0001
|
||||||
#define INIT_PG_OVERRIDE_ALLOW_CONNS 0x0002
|
#define INIT_PG_OVERRIDE_ALLOW_CONNS 0x0002
|
||||||
|
#define INIT_PG_OVERRIDE_ROLE_LOGIN 0x0004
|
||||||
extern void pg_split_opts(char **argv, int *argcp, const char *optstr);
|
extern void pg_split_opts(char **argv, int *argcp, const char *optstr);
|
||||||
extern void InitializeMaxBackends(void);
|
extern void InitializeMaxBackends(void);
|
||||||
extern void InitPostgres(const char *in_dbname, Oid dboid,
|
extern void InitPostgres(const char *in_dbname, Oid dboid,
|
||||||
|
@ -150,9 +150,11 @@ extern void BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, ui
|
|||||||
* Flags to BackgroundWorkerInitializeConnection et al
|
* Flags to BackgroundWorkerInitializeConnection et al
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Allow bypassing datallowconn restrictions when connecting to database
|
* Allow bypassing datallowconn restrictions and login check when connecting
|
||||||
|
* to database
|
||||||
*/
|
*/
|
||||||
#define BGWORKER_BYPASS_ALLOWCONN 1
|
#define BGWORKER_BYPASS_ALLOWCONN 0x0001
|
||||||
|
#define BGWORKER_BYPASS_ROLELOGINCHECK 0x0002
|
||||||
|
|
||||||
|
|
||||||
/* Block/unblock signals in a background worker process */
|
/* Block/unblock signals in a background worker process */
|
||||||
|
@ -131,4 +131,34 @@ ok( $node->poll_query_until(
|
|||||||
qq[noconndb|myrole|WorkerSpiMain]),
|
qq[noconndb|myrole|WorkerSpiMain]),
|
||||||
'dynamic bgworker with BYPASS_ALLOWCONN started');
|
'dynamic bgworker with BYPASS_ALLOWCONN started');
|
||||||
|
|
||||||
|
# Check BGWORKER_BYPASS_ROLELOGINCHECK.
|
||||||
|
# First create a role without login access.
|
||||||
|
$node->safe_psql(
|
||||||
|
'postgres', qq[
|
||||||
|
CREATE ROLE nologrole WITH NOLOGIN;
|
||||||
|
GRANT CREATE ON DATABASE mydb TO nologrole;
|
||||||
|
]);
|
||||||
|
my $nologrole_id = $node->safe_psql('mydb',
|
||||||
|
"SELECT oid FROM pg_roles where rolname = 'nologrole';");
|
||||||
|
$log_offset = -s $node->logfile;
|
||||||
|
|
||||||
|
# bgworker cannot be launched with login restriction.
|
||||||
|
$node->psql('postgres',
|
||||||
|
qq[SELECT worker_spi_launch(13, $mydb_id, $nologrole_id);]);
|
||||||
|
$node->wait_for_log(qr/role "nologrole" is not permitted to log in/,
|
||||||
|
$log_offset);
|
||||||
|
|
||||||
|
# bgworker bypasses the login restriction, and can be launched.
|
||||||
|
$log_offset = -s $node->logfile;
|
||||||
|
my $worker5_pid = $node->safe_psql('mydb',
|
||||||
|
qq[SELECT worker_spi_launch(13, $mydb_id, $nologrole_id, '{"ROLELOGINCHECK"}');]
|
||||||
|
);
|
||||||
|
ok( $node->poll_query_until(
|
||||||
|
'mydb',
|
||||||
|
qq[SELECT datname, usename, wait_event FROM pg_stat_activity
|
||||||
|
WHERE backend_type = 'worker_spi dynamic' AND
|
||||||
|
pid = $worker5_pid;],
|
||||||
|
'mydb|nologrole|WorkerSpiMain'),
|
||||||
|
'dynamic bgworker with BYPASS_ROLELOGINCHECK launched');
|
||||||
|
|
||||||
done_testing();
|
done_testing();
|
||||||
|
@ -452,6 +452,8 @@ worker_spi_launch(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
if (strcmp(optname, "ALLOWCONN") == 0)
|
if (strcmp(optname, "ALLOWCONN") == 0)
|
||||||
flags |= BGWORKER_BYPASS_ALLOWCONN;
|
flags |= BGWORKER_BYPASS_ALLOWCONN;
|
||||||
|
else if (strcmp(optname, "ROLELOGINCHECK") == 0)
|
||||||
|
flags |= BGWORKER_BYPASS_ROLELOGINCHECK;
|
||||||
else
|
else
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
Reference in New Issue
Block a user