Thread: Re: pg_dump needs SELECT privileges on irrelevant extension table

Re: pg_dump needs SELECT privileges on irrelevant extension table

From
Jacob Champion
Date:
On 3/20/23 10:43, Tom Lane wrote:
> I'd be more willing to consider the proposed patch if it weren't such
> a hack --- as you say, it doesn't fix the problem when the table has
> policies, so it's hardly a general-purpose solution.  I fear that it's
> also fairly expensive: adding sub-selects to the query we must do
> before we can lock any tables is not appetizing, because making that
> window wider adds to the risk of deadlocks, dump failures, etc.
(moving to -hackers and July CF)

= Recap for Potential Reviewers =

The timescaledb extension has an internal table that's owned by the
superuser. It's not dumpable, and other users can only access its
contents through a filtered view. For our cloud deployments, we
additionally have that common trope where the most privileged users
aren't actually superusers, but we still want them to be able to perform
a subset of maintenance tasks, including pg_dumping their data.

When cloud users try to dump that data, pg_dump sees that this internal
table is an extension member and plans to dump ACLs, security labels,
and RLS policies for it. (This behavior cannot be overridden with
--exclude-table. pg_dump ignores that flag for extension members.)
Dumping policies requires the use of pg_get_expr() on the backend; this,
in turn, requires a lock on the table with ACCESS SHARE.

So pg_dump tries to lock a table, with no policies, that it's not going
to dump the schema or data for anyway, and it fails because our users
don't have (and shouldn't need) SELECT access to it. For an example of
this in action, I've attached a test case in v2-0001.

= Proposal =

Since this is affecting users on released Postgres versions, my end goal
is to find a fix that's backportable.

This situation looks very similar to [1], where non-superusers couldn't
perform a dump because we were eagerly grabbing table locks to read
(non-existent) ACLs. But that was solved with the realization that ACLs
don't need locks anyway, which is unfortunately not applicable to policies.

My initial patch to -bugs was a riff on a related performance fix [2],
which figured out which tables had interesting ACLs and skipped that
part if nothing was found. I added the same kind of subselect for RLS
policies as well, but that had nasty corner cases where it would perform
terribly, as Tom alluded to above. (In a cluster of 200k tables, where
one single table had 10M policies, the query ground to a halt.)

So v2-0002 is instead inspired by Tom's rewrite of that ACL dump logic
[3]. It scans pg_policy separately, stores the tables it finds into the
catalog map on the client side, and then again skips the policy dump
(and therefore the lock) if no policies exist. The performance hit now
scales with the size of pg_policy alone.

This is a bit more invasive than the subselect, but hopefully still
straightforward enough to be applicable to the back branches' old
catalog map strategy. It's still not a general-purpose fix, as Tom
pointed out above, but that was true of the discussion in [1] as well,
so I'm optimistic.

WDYT?

--Jacob

[1]
https://postgr.es/m/CAGPqQf3Uzo-yU1suYyoZR83h6QTxXxkGTtEyeMV7EAVBqn%3DPcQ%40mail.gmail.com
[2] https://git.postgresql.org/cgit/postgresql.git/commit/?id=5d589993
[3] https://git.postgresql.org/cgit/postgresql.git/commit/?id=0c9d8442
Attachment

Re: pg_dump needs SELECT privileges on irrelevant extension table

From
Akshat Jaimini
Date:
The following review has been posted through the commitfest application:
make installcheck-world:  tested, passed
Implements feature:       tested, passed
Spec compliant:           tested, passed
Documentation:            not tested

Passes the default cases; Does not make any trivial changes to the codebase

Re: pg_dump needs SELECT privileges on irrelevant extension table

From
Andrew Dunstan
Date:


On 2023-06-29 Th 12:24, Jacob Champion wrote:
On 3/20/23 10:43, Tom Lane wrote:
I'd be more willing to consider the proposed patch if it weren't such
a hack --- as you say, it doesn't fix the problem when the table has
policies, so it's hardly a general-purpose solution.  I fear that it's
also fairly expensive: adding sub-selects to the query we must do
before we can lock any tables is not appetizing, because making that
window wider adds to the risk of deadlocks, dump failures, etc.
(moving to -hackers and July CF)

= Recap for Potential Reviewers =

The timescaledb extension has an internal table that's owned by the
superuser. It's not dumpable, and other users can only access its
contents through a filtered view. For our cloud deployments, we
additionally have that common trope where the most privileged users
aren't actually superusers, but we still want them to be able to perform
a subset of maintenance tasks, including pg_dumping their data.

When cloud users try to dump that data, pg_dump sees that this internal
table is an extension member and plans to dump ACLs, security labels,
and RLS policies for it. (This behavior cannot be overridden with
--exclude-table. pg_dump ignores that flag for extension members.)
Dumping policies requires the use of pg_get_expr() on the backend; this,
in turn, requires a lock on the table with ACCESS SHARE.

So pg_dump tries to lock a table, with no policies, that it's not going
to dump the schema or data for anyway, and it fails because our users
don't have (and shouldn't need) SELECT access to it. For an example of
this in action, I've attached a test case in v2-0001.

= Proposal =

Since this is affecting users on released Postgres versions, my end goal
is to find a fix that's backportable.

This situation looks very similar to [1], where non-superusers couldn't
perform a dump because we were eagerly grabbing table locks to read
(non-existent) ACLs. But that was solved with the realization that ACLs
don't need locks anyway, which is unfortunately not applicable to policies.

My initial patch to -bugs was a riff on a related performance fix [2],
which figured out which tables had interesting ACLs and skipped that
part if nothing was found. I added the same kind of subselect for RLS
policies as well, but that had nasty corner cases where it would perform
terribly, as Tom alluded to above. (In a cluster of 200k tables, where
one single table had 10M policies, the query ground to a halt.)

So v2-0002 is instead inspired by Tom's rewrite of that ACL dump logic
[3]. It scans pg_policy separately, stores the tables it finds into the
catalog map on the client side, and then again skips the policy dump
(and therefore the lock) if no policies exist. The performance hit now
scales with the size of pg_policy alone.

This is a bit more invasive than the subselect, but hopefully still
straightforward enough to be applicable to the back branches' old
catalog map strategy. It's still not a general-purpose fix, as Tom
pointed out above, but that was true of the discussion in [1] as well,
so I'm optimistic.

WDYT?

--Jacob

[1]
https://postgr.es/m/CAGPqQf3Uzo-yU1suYyoZR83h6QTxXxkGTtEyeMV7EAVBqn%3DPcQ%40mail.gmail.com
[2] https://git.postgresql.org/cgit/postgresql.git/commit/?id=5d589993
[3] https://git.postgresql.org/cgit/postgresql.git/commit/?id=0c9d8442


Seems reasonable at first glance. Isn't it going to save some work anyway later on, so the performance hit could end up negative?


cheers


andrew


--
Andrew Dunstan
EDB: https://www.enterprisedb.com

Re: pg_dump needs SELECT privileges on irrelevant extension table

From
Jacob Champion
Date:
Thanks for the reviews, both of you!

On Fri, Jul 14, 2023 at 5:04 AM Andrew Dunstan <andrew@dunslane.net> wrote:
> Seems reasonable at first glance. Isn't it going to save some work anyway later on, so the performance hit could end
upnegative? 

Theoretically it could, if the OID list sent during getPolicies()
shrinks enough. I tried a quick test against the regression database,
but it's too noisy on my machine to know whether the difference is
really meaningful.

--Jacob



Re: pg_dump needs SELECT privileges on irrelevant extension table

From
Jacob Champion
Date:
Hi all,

v3 fixes a doc comment I forgot to fill in; there are no other code
changes. To try to further reduce the activation energy, I've also
attached an attempt at a backport to 11. The main difference is the
absence of catalogIdHash, which showed up in 15, so we don't get the
benefit of that deduplication.

Thanks,
--Jacob

Attachment

Re: pg_dump needs SELECT privileges on irrelevant extension table

From
Tom Lane
Date:
Jacob Champion <jchampion@timescale.com> writes:
> v3 fixes a doc comment I forgot to fill in; there are no other code
> changes. To try to further reduce the activation energy, I've also
> attached an attempt at a backport to 11. The main difference is the
> absence of catalogIdHash, which showed up in 15, so we don't get the
> benefit of that deduplication.

So ... I still do not like anything about this patch.  Putting
has_policies into CatalogIdMapEntry isn't a wart, it's more
nearly a tumor.  Running getTablesWithPolicies before we can
acquire locks is horrid from the standpoint of minimizing the
window between our transaction snapshot and successful acquisition
of all needed locks.  (It might be all right in databases with
few pg_policy entries, but I don't think we can assume that that
holds everywhere.)  And the whole thing is just ugly and solves
the problem only partially.

What I am wondering about is whether we shouldn't just undo what
checkExtensionMembership does, specifically:

    /*
     * In 9.6 and above, mark the member object to have any non-initial ACL,
     * policies, and security labels dumped.
     *
     * Note that any initial ACLs (see pg_init_privs) will be removed when we
     * extract the information about the object.  We don't provide support for
     * initial policies and security labels and it seems unlikely for those to
     * ever exist, but we may have to revisit this later.
     *
     * ...
     */

            dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL |
                                                    DUMP_COMPONENT_SECLABEL |
                                                    DUMP_COMPONENT_POLICY);

Why are we marking extension member objects as being subject to SECLABEL
or POLICY dumping?  As the comment notes, that isn't really sensible
unless what we are dumping is a delta from the extension's initial
assignments.  But we have no infrastructure for that, and none seems
likely to appear in the near future.

Could we not fix this by just reducing the above to

            dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);

When and if someone comes along and implements storage of extensions'
initial policies, they can figure out how to avoid fetching policies for
not-to-be-dumped tables.  (My thoughts would run towards adding a column
to pg_class to help detect whether such work is needed without doing
expensive additional queries.)  But I don't see why we require a solution
to that problem as things stand.

            regards, tom lane



Re: pg_dump needs SELECT privileges on irrelevant extension table

From
Tom Lane
Date:
I wrote:
> Why are we marking extension member objects as being subject to SECLABEL
> or POLICY dumping?  As the comment notes, that isn't really sensible
> unless what we are dumping is a delta from the extension's initial
> assignments.  But we have no infrastructure for that, and none seems
> likely to appear in the near future.

Here's a quick patch that does it that way.  The test changes
are identical to Jacob's v3-0001.

            regards, tom lane

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 83aeef2751..fd6d8bb5dd 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1680,13 +1680,10 @@ checkExtensionMembership(DumpableObject *dobj, Archive *fout)
     addObjectDependency(dobj, ext->dobj.dumpId);

     /*
-     * In 9.6 and above, mark the member object to have any non-initial ACL,
-     * policies, and security labels dumped.
-     *
-     * Note that any initial ACLs (see pg_init_privs) will be removed when we
-     * extract the information about the object.  We don't provide support for
-     * initial policies and security labels and it seems unlikely for those to
-     * ever exist, but we may have to revisit this later.
+     * In 9.6 and above, mark the member object to have any non-initial ACLs
+     * dumped.  (Any initial ACLs will be removed later, using data from
+     * pg_init_privs, so that we'll dump only the delta from the extension's
+     * initial setup.)
      *
      * Prior to 9.6, we do not include any extension member components.
      *
@@ -1694,6 +1691,13 @@ checkExtensionMembership(DumpableObject *dobj, Archive *fout)
      * individually, since the idea is to exactly reproduce the database
      * contents rather than replace the extension contents with something
      * different.
+     *
+     * Note: it might be interesting someday to implement storage and delta
+     * dumping of extension members' RLS policies and/or security labels.
+     * However there is a pitfall for RLS policies: trying to dump them
+     * requires getting a lock on their tables, and the calling user might not
+     * have privileges for that.  We need no lock to examine a table's ACLs,
+     * so the current feature doesn't have a problem of that sort.
      */
     if (fout->dopt->binary_upgrade)
         dobj->dump = ext->dobj.dump;
@@ -1702,9 +1706,7 @@ checkExtensionMembership(DumpableObject *dobj, Archive *fout)
         if (fout->remoteVersion < 90600)
             dobj->dump = DUMP_COMPONENT_NONE;
         else
-            dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL |
-                                                    DUMP_COMPONENT_SECLABEL |
-                                                    DUMP_COMPONENT_POLICY);
+            dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
     }

     return true;
diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl
index d00c3544e9..68a767d2f5 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -175,6 +175,19 @@ my %pgdump_runs = (
             'postgres',
         ],
     },
+
+    # regress_dump_login_role shouldn't need SELECT rights on internal
+    # (undumped) extension tables
+    privileged_internals => {
+        dump_cmd => [
+            'pg_dump', '--no-sync', "--file=$tempdir/privileged_internals.sql",
+            # these two tables are irrelevant to the test case
+            '--exclude-table=regress_pg_dump_schema.external_tab',
+            '--exclude-table=regress_pg_dump_schema.extdependtab',
+            '--username=regress_dump_login_role', 'postgres',
+        ],
+    },
+
     schema_only => {
         dump_cmd => [
             'pg_dump', '--no-sync', "--file=$tempdir/schema_only.sql",
@@ -284,6 +297,7 @@ my %full_runs = (
     exclude_table => 1,
     no_privs => 1,
     no_owner => 1,
+    privileged_internals => 1,
     with_extension => 1,
     without_extension => 1);

@@ -321,6 +335,16 @@ my %tests = (
         like => { pg_dumpall_globals => 1, },
     },

+    'CREATE ROLE regress_dump_login_role' => {
+        create_order => 1,
+        create_sql => 'CREATE ROLE regress_dump_login_role LOGIN;',
+        regexp => qr/^
+            \QCREATE ROLE regress_dump_login_role;\E
+            \n\QALTER ROLE regress_dump_login_role WITH \E.*\Q LOGIN \E.*;
+            \n/xm,
+        like => { pg_dumpall_globals => 1, },
+    },
+
     'GRANT ALTER SYSTEM ON PARAMETER full_page_writes TO regress_dump_test_role'
       => {
         create_order => 2,
@@ -704,6 +728,7 @@ my %tests = (
             data_only => 1,
             extension_schema => 1,
             pg_dumpall_globals => 1,
+            privileged_internals => 1,
             section_data => 1,
             section_pre_data => 1,
             # Excludes this schema as extension is not listed.
@@ -720,6 +745,7 @@ my %tests = (
             data_only => 1,
             extension_schema => 1,
             pg_dumpall_globals => 1,
+            privileged_internals => 1,
             section_data => 1,
             section_pre_data => 1,
             # Excludes this schema as extension is not listed.
@@ -743,6 +769,7 @@ my %tests = (
             # Excludes the extension and keeps the schema's data.
             without_extension_internal_schema => 1,
         },
+        unlike => { privileged_internals => 1 },
     },);

 #########################################
diff --git a/src/test/modules/test_pg_dump/test_pg_dump--1.0.sql b/src/test/modules/test_pg_dump/test_pg_dump--1.0.sql
index 110f7eef66..1c68e146d9 100644
--- a/src/test/modules/test_pg_dump/test_pg_dump--1.0.sql
+++ b/src/test/modules/test_pg_dump/test_pg_dump--1.0.sql
@@ -12,11 +12,13 @@ CREATE SEQUENCE regress_pg_dump_seq;

 CREATE SEQUENCE regress_seq_dumpable;
 SELECT pg_catalog.pg_extension_config_dump('regress_seq_dumpable', '');
+GRANT SELECT ON SEQUENCE regress_seq_dumpable TO public;

 CREATE TABLE regress_table_dumpable (
     col1 int check (col1 > 0)
 );
 SELECT pg_catalog.pg_extension_config_dump('regress_table_dumpable', '');
+GRANT SELECT ON regress_table_dumpable TO public;

 CREATE SCHEMA regress_pg_dump_schema;


Re: pg_dump needs SELECT privileges on irrelevant extension table

From
Stephen Frost
Date:
Greetings,

* Tom Lane (tgl@sss.pgh.pa.us) wrote:
> I wrote:
> > Why are we marking extension member objects as being subject to SECLABEL
> > or POLICY dumping?  As the comment notes, that isn't really sensible
> > unless what we are dumping is a delta from the extension's initial
> > assignments.  But we have no infrastructure for that, and none seems
> > likely to appear in the near future.
>
> Here's a quick patch that does it that way.  The test changes
> are identical to Jacob's v3-0001.

What the comment is talking about is that we don't support initial
policies, not that we don't support policies on extension tables at all.
That said ... even the claim that we don't support such policies isn't
supported by code and there are people out there doing it, which creates
its own set of problems (ones we should really try to find solutions to
though..).

This change would mean that policies added by a user after the extension
is created would just be lost by a pg_dump/reload, doesn't it?

Thanks,

Stephen

Attachment

Re: pg_dump needs SELECT privileges on irrelevant extension table

From
Tom Lane
Date:
Stephen Frost <sfrost@snowman.net> writes:
> Greetings,
> * Tom Lane (tgl@sss.pgh.pa.us) wrote:
>> I wrote:
>>> Why are we marking extension member objects as being subject to SECLABEL
>>> or POLICY dumping?

> This change would mean that policies added by a user after the extension
> is created would just be lost by a pg_dump/reload, doesn't it?

Yes.  But I'd say that's unsupported, just like making other ad-hoc
changes to extension objects is unsupported (and the effects will be
lost on dump/reload).  We specifically have support for user-added
ACLs, and that's good, but don't claim that we have support for
doing the same with policies.

As far as I can see, the current behavior is that we'll dump and
try to reload policies (and seclabels) on extension objects even
if those properties were set by the extension creation script.
That has many more problems than just the one Jacob is moaning
about: you'll see failures at reload if you're not superuser,
and if the destination installation has a newer version of the
extension than what was dumped, the old properties might be
completely inappropriate.  So IMO there's basically nothing
that works properly about this.  To make it work, we'd need
infrastructure comparable to the pg_init_privs infrastructure.

            regards, tom lane



Re: pg_dump needs SELECT privileges on irrelevant extension table

From
Jacob Champion
Date:
On Wed, Oct 18, 2023 at 1:25 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Stephen Frost <sfrost@snowman.net> writes:
> > This change would mean that policies added by a user after the extension
> > is created would just be lost by a pg_dump/reload, doesn't it?
>
> Yes.  But I'd say that's unsupported, just like making other ad-hoc
> changes to extension objects is unsupported (and the effects will be
> lost on dump/reload).  We specifically have support for user-added
> ACLs, and that's good, but don't claim that we have support for
> doing the same with policies.

Is this approach backportable?

(Adding Aleks to CC -- Timescale may want to double-check that the new
proposal still works for them.)

Thanks,
--Jacob



Re: pg_dump needs SELECT privileges on irrelevant extension table

From
Tom Lane
Date:
Jacob Champion <champion.p@gmail.com> writes:
> Is this approach backportable?

The code fix would surely work in the back branches.  Whether the
behavioral change is too big to be acceptable in minor releases
is something I don't have a strong opinion on.

            regards, tom lane



Re: pg_dump needs SELECT privileges on irrelevant extension table

From
Tom Lane
Date:
I wrote:
> Jacob Champion <champion.p@gmail.com> writes:
>> Is this approach backportable?

> The code fix would surely work in the back branches.  Whether the
> behavioral change is too big to be acceptable in minor releases
> is something I don't have a strong opinion on.

I'm hearing nothing but crickets :-(

If nobody objects by say Monday, I'm going to go ahead and
commit (and backpatch) the patch I posted at [1].

            regards, tom lane

[1] https://www.postgresql.org/message-id/2984517.1697577076%40sss.pgh.pa.us



Re: pg_dump needs SELECT privileges on irrelevant extension table

From
Jacob Champion
Date:
On Thu, Nov 9, 2023 at 11:02 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> I'm hearing nothing but crickets :-(

Yeah :/

Based on your arguments above, it sounds like your patch may improve
several other corner cases when backported, so that sounds good
overall to me. My best guess is that Timescale will be happy with this
patch's approach. But I can't speak with any authority.

Aleks -- anything to add?

--Jacob



Re: pg_dump needs SELECT privileges on irrelevant extension table

From
Jacob Champion
Date:
> commit a70f2a57f233244c0a780829baf48c624187d456
> Author: Tom Lane <tgl@sss.pgh.pa.us>
> Date:   Mon Nov 13 17:04:10 2023 -0500
>
>    Don't try to dump RLS policies or security labels for extension objects.

(Thanks Tom!)

--Jacob