Обсуждение: sudo-like behavior

Поиск
Список
Период
Сортировка

sudo-like behavior

От
"A.M."
Дата:
Hello,

I have written a crontab-like daemon which accepts jobs from users through
a table and executes SQL statements after certain events or intervals.
This daemon maintains a persistent connection to the database as a
superuser.

The problem is that I wish to run arbitrary SQL as an unprivileged user
but SET SESSION AUTHORIZATION is easily reversed via RESET SESSION
AUTHORIZATION. Since I don't have the role's password, I cannot connect as
him through a secondary connection.

It seems I am stuck so please allow me to propose an extension:
SET SESSION AUTHORIZATION user [WITH PASSWORD 'password];

If a password is specified, then any call to RESET SESSION AUTHORIZATION
would also need to include the WITH PASSWORD clause (and the correct
password) to be successful. This would allow for blocks of foreign code to
be executed as an arbitrary user. I am not sure this would work for SET
ROLE because of role inheritance.

Does anyone have a better idea?

Thanks,

-M


Re: sudo-like behavior

От
Tom Lane
Дата:
"A.M." <agentm@themactionfaction.com> writes:
> It seems I am stuck so please allow me to propose an extension:
> SET SESSION AUTHORIZATION user [WITH PASSWORD 'password];

This idea is extremely unlikely to be accepted, as the password would be
at risk of exposure in places like the pg_stat_activity view.

I think the correct way to do what you want is via a SECURITY DEFINER
function.

            regards, tom lane

Re: sudo-like behavior

От
Karsten Hilbert
Дата:
On Thu, Apr 20, 2006 at 04:06:19PM -0400, A.M. wrote:

> The problem is that I wish to run arbitrary SQL as an unprivileged user

Would wrapping the SQL in a stored procedure with "security
definer" help any ?

Karsten
--
GPG key ID E4071346 @ wwwkeys.pgp.net
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346

Re: sudo-like behavior

От
"A.M."
Дата:
On Thu, April 20, 2006 4:21 pm, Tom Lane wrote:
> "A.M." <agentm@themactionfaction.com> writes:
>
>> It seems I am stuck so please allow me to propose an extension:
>> SET SESSION AUTHORIZATION user [WITH PASSWORD 'password];
>>
>
> This idea is extremely unlikely to be accepted, as the password would be
> at risk of exposure in places like the pg_stat_activity view.
>
> I think the correct way to do what you want is via a SECURITY DEFINER
> function.

Perhaps I can't wrap my head around it- I have the SQL as a string in a
table. I interpret that you propose that I accept only function names and
allow users to create security definer functions which I then call as the
superuser (carefully checking for the security definer flag). What about
commands that can't be run from within transactions?

I guess there is no way to stream arbitrary SQL in a permissions sandbox
if the original login user isn't the one I want. The security definer
method is a good enough workaround. Thanks.

-M


Re: sudo-like behavior

От
Tom Lane
Дата:
"A.M." <agentm@themactionfaction.com> writes:
> On Thu, April 20, 2006 4:21 pm, Tom Lane wrote:
>> I think the correct way to do what you want is via a SECURITY DEFINER
>> function.

> Perhaps I can't wrap my head around it- I have the SQL as a string in a
> table.

Well, the simplest thing would be

    create function exec(text) returns void as $$
    begin
        execute $1;
    end$$ language plpgsql strict security definer;

    revoke execute on exec(text) from public;
    grant execute on exec(text) to whoever-you-trust;

although personally I'd try to restrict what the function can be used
for a bit more than that.  If the allowed commands are in a table, you
could perhaps pass the table's key to exec() and let it pull the string
from the table for itself.

> What about commands that can't be run from within transactions?

There aren't that many of those.  Do you really need this for them?

For that matter, do you really need this at all?  Have you considered
granting role membership as an alternative solution path?  The SQL
permissions mechanism is quite powerful as of 8.1, and if it won't
do what you want, maybe you have not thought hard enough.

            regards, tom lane

Re: sudo-like behavior

От
Agent M
Дата:
I really haven't provided enough details- my fault. What I want to
accomplish is a general-purpose timer facility for postgresql. Ideally,
arbitrary roles provide statements to run at certain intervals. The
benefit here is that the user connections can go away and only a single
timer connection is maintained (waiting on notifications to update).

Examples of where this could be useful:
1) simulated materialized views
2) daily tasks such as cache cleanup/refresh/updates
3) expensive tasks which run regularly

Arbitrary statements could be executed on a timed basis without needing
local access for crontab or persistent remote access.

Anyway, here is the table:
CREATE TABLE pgtimer._timer
(
    id SERIAL PRIMARY KEY,
    repeats INTEGER NOT NULL, --repeats X times as countdown
    lastfired TIMESTAMP,

    waitinterval INTERVAL, --OR
    specialeventid INTEGER REFERENCES pgtimer.specialevent, --various
special events such as startup, autovacuum, or notifications
    detail TEXT, --stores notification event name if applicable
    statement TEXT NOT NULL,
    asrole TEXT NOT NULL
);

A separate view with rules handles insert/update capabilities and
throws a notification so that the daemon is notified to refresh its
countdown to the next event. The actual statement execution is all I
have left to do. I could force users to define security definer
functions but then vacuuming capability is lost (autovacuum can't
handle everything).

If there is an architecture change I could make to rectify this, I am
all ears. Thanks!

-M

On Apr 20, 2006, at 5:03 PM, Tom Lane wrote:

> "A.M." <agentm@themactionfaction.com> writes:
>> On Thu, April 20, 2006 4:21 pm, Tom Lane wrote:
>>> I think the correct way to do what you want is via a SECURITY DEFINER
>>> function.
>
>> Perhaps I can't wrap my head around it- I have the SQL as a string in
>> a
>> table.
>
> Well, the simplest thing would be
>
>     create function exec(text) returns void as $$
>     begin
>         execute $1;
>     end$$ language plpgsql strict security definer;
>
>     revoke execute on exec(text) from public;
>     grant execute on exec(text) to whoever-you-trust;
>
> although personally I'd try to restrict what the function can be used
> for a bit more than that.  If the allowed commands are in a table, you
> could perhaps pass the table's key to exec() and let it pull the string
> from the table for itself.
>
>> What about commands that can't be run from within transactions?
>
> There aren't that many of those.  Do you really need this for them?
>
> For that matter, do you really need this at all?  Have you considered
> granting role membership as an alternative solution path?  The SQL
> permissions mechanism is quite powerful as of 8.1, and if it won't
> do what you want, maybe you have not thought hard enough.
>
>             regards, tom lane
>
¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬
AgentM
agentm@themactionfaction.com
¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬


Re: sudo-like behavior

От
Tom Lane
Дата:
Agent M <agentm@themactionfaction.com> writes:
> I really haven't provided enough details- my fault. What I want to
> accomplish is a general-purpose timer facility for postgresql.

I'm not really sure why you think it'd be a good idea for such a thing
to operate as an unprivileged user that gets around its lack of
privilege by storing copies of everyone else's passwords.  I can think
of several reasonable ways to design the privilege handling for a
cron-like facility, but giving it cleartext copies of everyone's
passwords is not one of them.

            regards, tom lane

Re: sudo-like behavior

От
Agent M
Дата:
Sorry, but you misunderstand- nowhere am I interested in the role's
password. My previous suggestion was to add a password to set session
authorization itself so that if the authorization were to be reset, it
would need to be done with that password; the password itself could be
machine-generated. It it would merely allow a secure sandbox to be
established between:

SET SESSION AUTHORIZATION somerole WITH PASSWORD 'abc';
--arbitrary SQL run as somerole
RESET SESSION AUTHORIZATION; --fails- requires password
RESET SESSION AUTHORIZATION WITH PASSWORD 'pass'; --fails
RESET SESSION AUTHORIZATION WITH PASSWORD 'abc'; --succeeds- we are
done with this role

The password ensures that the session authorization initiator is the
only one that can terminate it as well.

-M

On Apr 20, 2006, at 10:44 PM, Tom Lane wrote:

> Agent M <agentm@themactionfaction.com> writes:
>> I really haven't provided enough details- my fault. What I want to
>> accomplish is a general-purpose timer facility for postgresql.
>
> I'm not really sure why you think it'd be a good idea for such a thing
> to operate as an unprivileged user that gets around its lack of
> privilege by storing copies of everyone else's passwords.  I can think
> of several reasonable ways to design the privilege handling for a
> cron-like facility, but giving it cleartext copies of everyone's
> passwords is not one of them.
>
>             regards, tom lane
>
¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬
AgentM
agentm@themactionfaction.com
¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬


Re: sudo-like behavior

От
"Florian G. Pflug"
Дата:
Agent M wrote:
> Sorry, but you misunderstand- nowhere am I interested in the role's
> password. My previous suggestion was to add a password to set session
> authorization itself so that if the authorization were to be reset, it
> would need to be done with that password; the password itself could be
> machine-generated. It it would merely allow a secure sandbox to be
> established between:
>
> SET SESSION AUTHORIZATION somerole WITH PASSWORD 'abc';
> --arbitrary SQL run as somerole
> RESET SESSION AUTHORIZATION; --fails- requires password
> RESET SESSION AUTHORIZATION WITH PASSWORD 'pass'; --fails
> RESET SESSION AUTHORIZATION WITH PASSWORD 'abc'; --succeeds- we are done
> with this role

Why don't you just use "SET SESSION AUTHORIZATION somerole", and then scan
the to-be-executel sql scripts for any occurence of "reset session authorization",
and ignore the script it matches.

Of course you'd need to be a bit carefull to catch all syntactially valid
variations (like ReSeT SeSsIoN AuToRiZaTiOn), but that should be doable.

If you design your "matched" carefully, the only way to defeat that protection
would be to wrap the "reset session authorization" command in a function, which
I believe is not possible.

greetings, Florian Pflug

Re: sudo-like behavior

От
Tom Lane
Дата:
"Florian G. Pflug" <fgp@phlo.org> writes:
> Why don't you just use "SET SESSION AUTHORIZATION somerole", and then scan
> the to-be-executel sql scripts for any occurence of "reset session authorization",
> and ignore the script it matches.

What would probably be better is a way to do SET SESSION AUTHORIZATION
and then abandon the underlying superuser privilege, thereby absolutely
guaranteeing that the session can't do anything the selected userid
shouldn't be able to do.  You'd have to start a new session for each
cronjob, but that would be a Really Good Idea anyway, given the lack of
any way to fully restore a session to default state.

            regards, tom lane

Re: sudo-like behavior

От
"Florian G. Pflug"
Дата:
Tom Lane wrote:
> "Florian G. Pflug" <fgp@phlo.org> writes:
>
>>Why don't you just use "SET SESSION AUTHORIZATION somerole", and then scan
>>the to-be-executel sql scripts for any occurence of "reset session authorization",
>>and ignore the script it matches.
>
> What would probably be better is a way to do SET SESSION AUTHORIZATION
> and then abandon the underlying superuser privilege, thereby absolutely
> guaranteeing that the session can't do anything the selected userid
> shouldn't be able to do.  You'd have to start a new session for each
> cronjob, but that would be a Really Good Idea anyway, given the lack of
> any way to fully restore a session to default state.

My "solution" (or hack ;-) ) was meant to work with current versions of postgres..
Of course, a command like "set session authorization <user> final" or such would be
a better way - maybe something for 8.2? ;-))

mfg, Florian Pflug


Re: sudo-like behavior

От
Agent M
Дата:
On Apr 22, 2006, at 1:13 PM, Florian G. Pflug wrote:
>
> Why don't you just use "SET SESSION AUTHORIZATION somerole", and then
> scan
> the to-be-executel sql scripts for any occurence of "reset session
> authorization",
> and ignore the script it matches.
>
> Of course you'd need to be a bit carefull to catch all syntactially
> valid
> variations (like ReSeT SeSsIoN AuToRiZaTiOn), but that should be
> doable.
>
> If you design your "matched" carefully, the only way to defeat that
> protection
> would be to wrap the "reset session authorization" command in a
> function, which
> I believe is not possible.

Unfortunately, it is possible:
agentm=# CREATE OR REPLACE FUNCTION testacl() RETURNS void AS $$ RESET
SESSION AUTHORIZATION; $$ LANGUAGE SQL;
CREATE FUNCTION
agentm=# select current_user;
  current_user
--------------
  agentm
(1 row)
agentm=# set session authorization test;
SET
agentm=> select current_user;
  current_user
--------------
  test
(1 row)
agentm=> select testacl();
  testacl
---------

(1 row)
agentm=# select current_user;
  current_user
--------------
  agentm
(1 row)

So, currently, there is a security limitation in postgresql which
effectively prohibits switching roles midstream unless you can control
the statements of that role, i.e. there is no sandbox feature
available. (Such a feature would also be great for pooled connections,
but that has already been discussed as well.)

-M

¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬
AgentM
agentm@themactionfaction.com
¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬