Re: POC: Carefully exposing information without authentication - Mailing list pgsql-hackers

From Andres Freund
Subject Re: POC: Carefully exposing information without authentication
Date
Msg-id sqammd2ykcy34skbsewpfq4ftm6xl3qt5ebr2plwenoprtvdrm@ayddy4xqbkb5
Whole thread
In response to Re: POC: Carefully exposing information without authentication  (Greg Sabino Mullane <htamfids@gmail.com>)
Responses Re: POC: Carefully exposing information without authentication
List 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



pgsql-hackers by date:

Previous
From: Mircea Cadariu
Date:
Subject: Re: Propagate XLogFindNextRecord error to callers
Next
From: Andrew Pogrebnoi
Date:
Subject: Re: Lowering the default wal_blocksize to 4K