Re: POC: Carefully exposing information without authentication

From: Andres Freund <andres(at)anarazel(dot)de>
To: Greg Sabino Mullane <htamfids(at)gmail(dot)com>
Cc: Antonin Houska <ah(at)cybertec(dot)at>, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, "pgsql-hackers(at)lists(dot)postgresql(dot)org" <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: Re: POC: Carefully exposing information without authentication
Date: 2026-02-17 19:58:22
Message-ID: sqammd2ykcy34skbsewpfq4ftm6xl3qt5ebr2plwenoprtvdrm@ayddy4xqbkb5
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

On 2026-02-17 14:42:48 -0500, Greg Sabino Mullane wrote:
> Subject: [PATCH] Allow specific information to be output directly by Postgres.

I strongly encourage you to include a justification for why this is desirable,
so a casual reviewer doesn't have to reread the thread.

> @@ -148,6 +172,14 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac)
> StringInfoData ps_data;
> MemoryContext oldcontext;
>
> + /*
> + * Scan for a simple GET / HEAD request. If this is detected and
> + * handled, we are done and can immediately exit
> + */
> + if ((expose_recovery || expose_sysid || expose_version)
> + && ExposeInformation(client_sock->sock))
> + _exit(0); /* Safe to use exit: no state or resources created yet */
> +
> /* Tell fd.c about the long-lived FD associated with the client_sock */
> ReserveExternalFD();
>

What about direct TLS connections?

How can a cluster coordinator trust unauthenticated plain text communication
that can just be man-in-the-middled?

It's not obvious that it's a good idea to expose this on the same socket as
normal client connections. IMO you'd want to limit this to a smaller set of
interfaces than normal client connections.

> +/*
> + * ExposeInformation
> + *
> + * Handle early socket probe before full backend startup.
> + * Responds to small set of predefined endpoints (e.g. GET /info)
> + *
> + * Requires at least one "expose_" GUC to be true.
> + *
> + * Returns true if any endpoint is recognized.
> + */
> +
> +static bool
> +ExposeInformation(pgsocket fd)
> +{
> + static endpoint_action endpoint_actions[] =
> + {
> + {
> + "HEAD /replica", &expose_recovery, EXPOSE_HEAD_REPLICA
> + },
> + {
> + "GET /replica", &expose_recovery, EXPOSE_GET_REPLICA
> + },
> + {
> + "GET /sysid", &expose_sysid, EXPOSE_GET_SYSID
> + },
> + {
> + "GET /version", &expose_version, EXPOSE_GET_VERSION
> + },
> + {
> + "GET /info", NULL, EXPOSE_GET_ALL
> + }
> + };
> +
> + ssize_t n;
> + char buf[EXPOSE_MAX_QUERY + 1];
> + ExposeReturnType type;
> +
> + Assert(expose_recovery || expose_sysid || expose_version);
> +
> + do
> + {
> + n = recv(fd, buf, EXPOSE_MAX_QUERY, MSG_PEEK);
> + } while (n < 0 && errno == EINTR);
>
> + /*
> + * Leave as soon as possible if no chance we are interested.
> + * (we also leave on partial reads from slow clients)
> + * We also simply return false for n == -1
> + */
> + if (n < EXPOSE_MIN_QUERY)
> + return false;

IIRC the socket is in blocking mode at this point (that's only changed in
pq_init()), therefore this might actually block? While it's unlikely, I don't
see any guarantee that a single receive would actually get the whole message
from the client either, so this seems like it might fail spuriously.

> diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
> index 271c033952e..3e99d9f6b7c 100644
> --- a/src/backend/utils/misc/guc_parameters.dat
> +++ b/src/backend/utils/misc/guc_parameters.dat
> @@ -1010,6 +1010,25 @@
> boot_val => 'false',
> },
>
> +{ name => 'expose_recovery', type => 'bool', context => 'PGC_SIGHUP', group => 'CONN_AUTH_AUTH',
> + short_desc => 'Exposes if the server is in recovery mode without a login.',
> + variable => 'expose_recovery',
> + boot_val => 'false',
> +},
> +
> +{ name => 'expose_sysid', type => 'bool', context => 'PGC_SIGHUP', group => 'CONN_AUTH_AUTH',
> + short_desc => 'Exposes the system identifier without a login.',
> + variable => 'expose_sysid',
> + boot_val => 'false',
> +},
> +
> +{ name => 'expose_version', type => 'bool', context => 'PGC_SIGHUP', group => 'CONN_AUTH_AUTH',
> + short_desc => 'Exposes the server version without a login.',
> + variable => 'expose_version',
> + boot_val => 'false',
> +},
> +

If we were to do this, I'd recommend a single expose GUC that has the
different values as a comma separated list, instead a growing list of GUCs.

Greetings,

Andres Freund

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Andrew Pogrebnoi 2026-02-17 20:06:14 Re: Lowering the default wal_blocksize to 4K
Previous Message Mircea Cadariu 2026-02-17 19:48:18 Re: Propagate XLogFindNextRecord error to callers