Re[2]: On login trigger: take three
Hi, >Tue, March 29, 2022, 0:31 +03:00 from Andres Freund : > >Hi, > >On 2022-03-28 23:27:56 +0200, Daniel Gustafsson wrote: >> > On 28 Mar 2022, at 19:10, Andres Freund < and...@anarazel.de > wrote: >> > On 2022-03-28 15:57:37 +0300, a.soko...@postgrespro.ru wrote: >> >> >> + data initialization. It is vital that any event trigger using the >> >> + login event checks whether or not the database is in >> >> + recovery. >> » >> >> Does any trigger really have to contain a pg_is_in_recovery() call? >> > >> > Not *any* trigger, just any trigger that writes. >> >> Thats correct, the docs should be updated with something like the below I >> reckon. >> >> It is vital that event trigger using the login event >> which has side-effects checks whether or not the database is in recovery to >> ensure they are not performing modifications to hot standby nodes. > >Maybe side-effects is a bit too general? Emitting a log message, rejecting a >login, setting some GUCs, etc are all side-effects too. Something like this: The login triggers fire also on standby servers. To keep them from becoming inaccessible, such triggers should avoid writing anything to the database when running on a standby. This can be achieved by checking pg_is_in_recovery(), see an example below. > Also, please fix a typo in doc/src/sgml/ref/create_event_trigger.sgml : - single-user mode and you'll be able to do that. Even triggers can also be + single-user mode and you'll be able to do that. Event triggers can also be Regarding the trigger function example: It does not do anything if run on a standby. To show that it can do something on a standby to, I propose to move throwing the night exception to the beginning. So it will be: CREATE OR REPLACE FUNCTION init_session() RETURNS event_trigger SECURITY DEFINER LANGUAGE plpgsql AS $$ DECLARE hour integer = EXTRACT('hour' FROM current_time); rec boolean; BEGIN -- 1) Forbid logging in late: IF hour BETWEEN 2 AND 4 THEN RAISE EXCEPTION 'Login forbidden'; -- do not allow to login these hours END IF; -- The remaining stuff cannot be done on standbys, -- so ensure the database is not in recovery SELECT pg_is_in_recovery() INTO rec; IF rec THEN RETURN; END IF -- 2) Assign some roles IF hour BETWEEN 8 AND 20 THEN -- at daytime grant the day_worker role EXECUTE 'REVOKE night_worker FROM ' || quote_ident(session_user); EXECUTE 'GRANTday_worker TO ' || quote_ident(session_user); ELSE -- at other time grant the night_worker role EXECUTE 'REVOKE day_worker FROM ' || quote_ident(session_user); EXECUTE 'GRANT night_worker TO ' || quote_ident(session_user); END IF; -- 3) Initialize some user session data CREATE TEMP TABLE session_storage (x float, y integer); -- 4) Log the connection time INSERT INTO user_login_log VALUES (session_user, current_timestamp); END; $$; Finally, let me propose to append to the regression test the following: \c SELECT dathasloginevt FROM pg_database WHERE datname= :'DBNAME'; which should output: dathasloginevt f (1 row) So we can check that removal of the event trigger resets this flag in pg_database. Note that reconnect (\c) is necessary here. Regards, Ivan > >> >> In this message >> >> ( >> >> https://www.postgresql.org/message-id/20220312024652.lvgehszwke4hhove%40alap3.anarazel.de >> >> ) >> >> it was only about triggers on hot standby, which run not read-only queries >> > >> > The problem precisely is that the login triggers run on hot standby nodes, >> > and >> > that if they do writes, you can't login anymore. >> >> Do you think this potential foot-gun is scary enough to reject this patch? >> There are lots of creative ways to cause Nagios alerts from ones database, >> but >> this has the potential to do so with a small bug in userland code. Still, I >> kind of like the feature so I'm indecisive. > >It does seem like a huge footgun. But also kinda useful. So I'm really +-0. > >Greetings, > >Andres Freund
Re[2]: On login trigger: take three
Dear colleagues, Please see my suggestions below and the updated patch attached. >Среда, 29 сентября 2021, 15:14 +03:00 от Teodor Sigaev < teo...@sigaev.ru >: > >Hi! > >Nice feature, but, sorry, I see some design problem in suggested feature. >AFAIK, >there is two use cases for this feature: >1 A permission / prohibition to login some users >2 Just a logging of facts of user's login > >Suggested patch proposes prohibition of login only by failing of login trigger >and it has at least two issues: >1 In case of prohibition to login, there is no clean way to store information >about unsuccessful login. Ok, it could be solved by dblink module but it seems >to scary. This is a common problem of logging errors and unsuccessful transactions of any kind. It can be solved by: - logging to external log storage (stupid logging to files or syslog or whatever you can imagine with PL/Perl (sorry)) - logging inside the database by db_link or through background worker (like described in https://dpavlin.wordpress.com/2017/05/09/david-rader-autonomous-transactions-using-pg_background/ ) - or by implementing autonomous transactions in PostgreSQL, which is already under development by some of my and Teodor’s colleagues. So I propose not to invent another solution to this common problem here. >2 For logging purpose failing of trigger will cause impossibility to login, it >could be workarounded by catching error in trigger function, but it's not so >obvious for DBA. If the trigger contains an error, nobody can login. The database is bricked. Previous variant of the patch proposed to fix this with going to single-user mode. For faster recovery I propose to have also a GUC variable to turn on/off the login triggers. It should be 'on' by default. > >some other issues: >3 It's easy to create security hole, see attachment where non-privileged user >can close access to database even for superuser. Such cases can be avoided by careful design of the login triggers and related permissions. Added such note to the documentation. >4 Feature is not applicable for logging unsuccessful login with wrong >username/password or not matched by pg_hba.conf. Oracle could operate with such >cases. But I don't think that this issue is a stopper for the patch. Yes. Btw, note that pg_hba functionality can be implemented completely inside the login trigger :) ! > >May be, to solve that issues we could introduce return value of trigger and/or >add something like IGNORE ERROR to CREATE EVENT TRIGGER command. Any solutions which make syntax more complicated can lead Postgres to become Oracle (in the worst sense). I do not like this. A new version of the patch is attached. It applies to the current master and contains the above mentioned GUC code, relevant tests and docs. Regards, Ivan Panchenko > >On 15.09.2021 16:32, Greg Nancarrow wrote: >> On Wed, Sep 8, 2021 at 10:56 PM Daniel Gustafsson < dan...@yesql.se > wrote: >>> >>> I took a look at this, and while I like the proposed feature I think the >>> patch >>> has a bit more work required. >>> >> >> Thanks for reviewing the patch. >> I am not the original patch author (who no longer seems active) but >> I've been contributing a bit and keeping the patch alive because I >> think it's a worthwhile feature. >> >>> >>> + >>> +-- 2) Initialize some user session data >>> + CREATE TEMP TABLE session_storage (x float, y integer); >>> The example in the documentation use a mix of indentations, neither of >>> which is >>> the 2-space indentation used elsewhere in the docs. >>> >> >> Fixed, using 2-space indentation. >> (to be honest, the indentation seems inconsistent elsewhere in the >> docs e.g. I'm seeing a nearby case of 2-space on 1st indent, 6-space >> on 2nd indent) >> >>> >>> + /* Get the command tag. */ >>> + tag = parsetree ? CreateCommandTag(parsetree) : CMDTAG_CONNECT; >>> This is hardcoding knowledge that currently only this trigger wont have a >>> parsetree, and setting the tag accordingly. This should instead check the >>> event and set CMDTAG_UNKNOWN if it isn't the expected one. >>> >> >> I updated that, but maybe not exactly how you expected? >> >>> >>> + /* database has on-login triggers */ >>> + bool dathaslogontriggers; >>> This patch uses three different names for the same thing: client connection, >>> logontrigger and login trigger. Since this trigger only fires after >>> successful >>> authentication it’s not accurate to name it client connection, as that >>> implies >>> it running on connections rather than logins. The nomenclature used in the >>> tree is "login" so the patch should be adjusted everywhere to use that. >>> >> >> Fixed. >> >>> >>> +/* Hook for plugins to get control at start of session */ >>> +client_connection_hook_type client_connection_hook = EventTriggerOnConnect; >>> I don't see the reason for adding core functionality by hooks. Having a hook >>> might be a useful thing on its own (to be discussed in a new thread, not >>> hidden >>>
Re[2]: On login trigger: take three
Hi, Thank you, Konstantin, for this very good feature with numerous use cases. Please find the modified patch attached. I’ve added the ‘enable_client_connection_trigger’ GUC to the sample config file and also an additional example page to the docs. Check world has passed and it is ready for committer. >Четверг, 28 января 2021, 12:04 +03:00 от Konstantin Knizhnik >: > > > >On 28.01.2021 5:47, Amit Kapila wrote: >> On Mon, Dec 28, 2020 at 5:46 PM Masahiko Sawada < sawada.m...@gmail.com > >> wrote: >>> On Sat, Dec 26, 2020 at 4:04 PM Pavel Stehule < pavel.steh...@gmail.com > >>> wrote: so 26. 12. 2020 v 8:00 odesílatel Pavel Stehule < pavel.steh...@gmail.com > napsal: > Hi > > >> Thank you. >> I have applied all your fixes in on_connect_event_trigger-12.patch. >> >> Concerning enable_client_connection_trigger GUC, I think that it is >> really useful: it is the fastest and simplest way to disable login >> triggers in case >> of some problems with them (not only for superuser itself, but for all >> users). Yes, it can be also done using "ALTER EVENT TRIGGER DISABLE". >> But assume that you have a lot of databases with different login >> policies enforced by on-login event triggers. And you want temporary >> disable them all, for example for testing purposes. >> In this case GUC is most convenient way to do it. >> > There was typo in patch > > diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml > index f810789..8861f1b 100644 > --- a/doc/src/sgml/config.sgml > +++ b/doc/src/sgml/config.sgml > @@ -1,4 +1,4 @@ > - > +\ > > I have not any objections against functionality or design. I tested the > performance, and there are no negative impacts when this feature is not > used. There is significant overhead related to plpgsql runtime > initialization, but when this trigger will be used, then probably some > other PLpgSQL procedures and functions will be used too, and then this > overhead can be ignored. > > * make without warnings > * make check-world passed > * doc build passed > > Possible ToDo: > > The documentation can contain a note so usage connect triggers in > environments with short life sessions and very short fast queries without > usage PLpgSQL functions or procedures can have negative impact on > performance due overhead of initialization of PLpgSQL engine. > > I'll mark this patch as ready for committers looks so this patch has not entry in commitfestapp 2021-01 >>> Yeah, please register this patch before the next CommitFest[1] starts, >>> 2021-01-01 AoE[2]. >>> >> Konstantin, did you register this patch in any CF? Even though the >> reviewer seems to be happy with the patch, I am afraid that we might >> lose track of this unless we register it. >> Yes, certainly: >https://commitfest.postgresql.org/31/2900/ > >-- >Konstantin Knizhnik >Postgres Professional: http://www.postgrespro.com >The Russian Postgres Company > > on_connect_event_trigger-13a.patch Description: Binary data