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


Reply via email to