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