Feature: Restrict watchdog and heartbeat receiver to listen only on configured addresses.
authorBo Peng <pengbo@sraoss.co.jp>
Fri, 22 Aug 2025 04:37:58 +0000 (13:37 +0900)
committerBo Peng <pengbo@sraoss.co.jp>
Fri, 22 Aug 2025 04:37:58 +0000 (13:37 +0900)
Previously, both the watchdog and heartbeat receiver processes listen on all interfaces.
For security reasons, they now listen only on the addresses
specified by "hostname" and "heartbeat_hostname".

src/config/pool_config_variables.c
src/include/pool_config.h
src/watchdog/watchdog.c
src/watchdog/wd_heartbeat.c
src/watchdog/wd_lifecheck.c

index d7d5fe49fc2b326e3d5bbfe3dae5b5a1caa77d51..5bbe46d3aa49c1a0b531f8b092317a6ed79edfa7 100644 (file)
@@ -5335,14 +5335,17 @@ SetPgpoolNodeId(int elevel)
 static bool
 SetHBDestIfFunc(int elevel)
 {
-       int                     idx = 0;
+       int                     dest_if_idx = 0;
+       int                     local_if_idx = 0;
        char      **addrs;
        char      **if_names;
        int                     i,
                                j,
+                               k,
                                n_addr,
                                n_if_name;
 
+       g_pool_config.num_hb_local_if = 0;
        g_pool_config.num_hb_dest_if = 0;
 
        if (g_pool_config.wd_lifecheck_method != LIFECHECK_BY_HB)
@@ -5352,22 +5355,14 @@ SetHBDestIfFunc(int elevel)
 
        /*
         * g_pool_config.hb_ifs is the information for sending/receiving heartbeat
-        * for all nodes specified in pgpool.conf. If it is local pgpool node
-        * information, set dest_port to g_pool_config.wd_heartbeat_port and
-        * ignore addr and if_name. g_pool_config.hb_dest_if is the heartbeat
+        * for all nodes specified in pgpool.conf. g_pool_config.hb_local_if is
+        * the local node information. g_pool_config.hb_dest_if is the heartbeat
         * destination information.
         */
        for (i = 0; i < WD_MAX_IF_NUM; i++)
        {
                if (g_pool_config.hb_ifs[i].dest_port > 0)
                {
-                       /* Ignore local pgpool node */
-                       if (i == g_pool_config.pgpool_node_id)
-                       {
-                               g_pool_config.wd_heartbeat_port = g_pool_config.hb_ifs[i].dest_port;
-                               continue;
-                       }
-
                        WdHbIf     *hbNodeInfo = &g_pool_config.hb_ifs[i];
 
                        addrs = get_list_from_string(hbNodeInfo->addr, ";", &n_addr);
@@ -5375,7 +5370,10 @@ SetHBDestIfFunc(int elevel)
 
                        if (!addrs || n_addr < 0)
                        {
-                               g_pool_config.hb_dest_if[idx].addr[0] = '\0';
+                               if (i == g_pool_config.pgpool_node_id)
+                                       g_pool_config.hb_local_if[local_if_idx].addr[0] = '\0';
+                               else
+                                       g_pool_config.hb_dest_if[dest_if_idx].addr[0] = '\0';
 
                                if (addrs)
                                        pfree(addrs);
@@ -5391,19 +5389,46 @@ SetHBDestIfFunc(int elevel)
 
                        for (j = 0; j < n_addr; j++)
                        {
-                               strlcpy(g_pool_config.hb_dest_if[idx].addr, addrs[j], WD_MAX_HOST_NAMELEN - 1);
-                               g_pool_config.hb_dest_if[idx].dest_port = hbNodeInfo->dest_port;
-                               if (n_if_name > j)
+                               /* local pgpool node */
+                               if (i == g_pool_config.pgpool_node_id)
                                {
-                                       strlcpy(g_pool_config.hb_dest_if[idx].if_name, if_names[j], WD_MAX_IF_NAME_LEN - 1);
-                                       pfree(if_names[j]);
+                                       for (k = 0; k < g_pool_config.wd_nodes.num_wd - 1; k++)
+                                       {
+                                               strlcpy(g_pool_config.hb_local_if[local_if_idx].addr, addrs[j], WD_MAX_HOST_NAMELEN - 1);
+                                               g_pool_config.hb_local_if[local_if_idx].dest_port = hbNodeInfo->dest_port;
+
+                                               if (n_if_name > j )
+                                                       strlcpy(g_pool_config.hb_local_if[local_if_idx].if_name, if_names[j], WD_MAX_IF_NAME_LEN - 1);
+                                               else
+                                                       g_pool_config.hb_local_if[local_if_idx].if_name[0] = '\0';
+
+                                               g_pool_config.num_hb_local_if = local_if_idx + 1;
+                                               local_if_idx++;
+                                       }
+
+                                       if (n_if_name > j )
+                                               pfree(if_names[j]);
+
+                                       pfree(addrs[j]);
+
                                }
+                               /* destination pgpool node */
                                else
-                                       g_pool_config.hb_dest_if[idx].if_name[0] = '\0';
+                               {
+                                       strlcpy(g_pool_config.hb_dest_if[dest_if_idx].addr, addrs[j], WD_MAX_HOST_NAMELEN - 1);
+                                       g_pool_config.hb_dest_if[dest_if_idx].dest_port = hbNodeInfo->dest_port;
+                                       if (n_if_name > j)
+                                       {
+                                               strlcpy(g_pool_config.hb_dest_if[dest_if_idx].if_name, if_names[j], WD_MAX_IF_NAME_LEN - 1);
+                                               pfree(if_names[j]);
+                                       }
+                                       else
+                                               g_pool_config.hb_dest_if[dest_if_idx].if_name[0] = '\0';
 
-                               g_pool_config.num_hb_dest_if = idx + 1;
-                               idx++;
-                               pfree(addrs[j]);
+                                       g_pool_config.num_hb_dest_if = dest_if_idx + 1;
+                                       dest_if_idx++;
+                                       pfree(addrs[j]);
+                               }
                        }
 
                        if (addrs)
index 3d12c0a9f66ace11fad11c7002c48e5292f76480..be82750e54ffab264b92e92d76b0d8e01ce87e39 100644 (file)
@@ -665,16 +665,17 @@ typedef struct
                                                                                 * lifecheck */
        char       *wd_lifecheck_user;  /* PostgreSQL user name for watchdog */
        char       *wd_lifecheck_password;      /* password for watchdog user */
-       int                     wd_heartbeat_port;      /* Port number for heartbeat lifecheck */
        int                     wd_heartbeat_keepalive; /* Interval time of sending heartbeat
                                                                                 * signal (sec) */
        int                     wd_heartbeat_deadtime;  /* Deadtime interval for heartbeat
                                                                                 * signal (sec) */
        WdHbIf          hb_ifs[WD_MAX_IF_NUM];  /* heartbeat interfaces of all
                                                                                 * watchdog nodes */
+       WdHbIf          hb_local_if[WD_MAX_IF_NUM];     /* local heartbeat interfaces */
+       int                     num_hb_local_if;                /* number of local interface */
        WdHbIf          hb_dest_if[WD_MAX_IF_NUM];      /* heartbeat destination
                                                                                         * interfaces */
-       int                     num_hb_dest_if; /* number of interface devices */
+       int                     num_hb_dest_if; /* number of destination interface */
        char      **wd_monitoring_interfaces_list;      /* network interface name list
                                                                                                 * to be monitored by watchdog */
        bool            health_check_test;      /* if on, enable health check testing */
index 3913d692dfddd19cd7a27ec33d5c2a7933b06d90..ffd1cef506787c1a75dafa03fb6b1bfb57cad24e 100644 (file)
@@ -590,7 +590,7 @@ static bool get_authhash_for_node(WatchdogNode *wdNode, char *authhash);
 static bool verify_authhash_for_node(WatchdogNode *wdNode, char *authhash);
 
 static void print_watchdog_node_info(WatchdogNode *wdNode);
-static List *wd_create_recv_socket(int port);
+static List *wd_create_recv_socket(char *hostname, int port);
 static void wd_check_config(void);
 static pid_t watchdog_main(void);
 static pid_t fork_watchdog_child(void);
@@ -865,7 +865,7 @@ clear_command_node_result(WDCommandNodeResult *nodeResult)
 }
 
 static List *
-wd_create_recv_socket(int port)
+wd_create_recv_socket(char *hostname, int port)
 {
        int                     one = 1;
        int                     sock = -1;
@@ -886,7 +886,7 @@ wd_create_recv_socket(int port)
        hints.ai_protocol = 0;
        hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE;
 
-       if ((gai_ret = getaddrinfo(NULL, portstr, &hints, &res)) != 0)
+       if ((gai_ret = getaddrinfo(!hostname ? NULL : hostname, portstr, &hints, &res)) != 0)
        {
                ereport(ERROR,
                                (errmsg("getaddrinfo failed with error \"%s\"", gai_strerror(gai_ret))));
@@ -1299,7 +1299,7 @@ watchdog_main(void)
        /* initialize all the local structures for watchdog */
        wd_cluster_initialize();
        /* create a server socket for incoming watchdog connections */
-       g_wd_recv_socks = wd_create_recv_socket(g_cluster.localNode->wd_port);
+       g_wd_recv_socks = wd_create_recv_socket(g_cluster.localNode->hostname, g_cluster.localNode->wd_port);
 
        /* open the command server */
        g_cluster.command_server_sock = wd_create_command_server_socket();
index a9e99dec98928a8cbb294a2f8d317a3604fa7783..acececc1ad9034842dedb9e02a0188df911b5a82 100644 (file)
@@ -262,7 +262,7 @@ wd_create_hb_recv_socket(WdHbIf *hb_if)
                           *walk,
                           *res = NULL;
 
-       portstr = psprintf("%d", pool_config->wd_heartbeat_port);
+       portstr = psprintf("%d", hb_if->dest_port);
 
        memset(&hints, 0x00, sizeof(hints));
        hints.ai_family = AF_UNSPEC;
@@ -270,7 +270,7 @@ wd_create_hb_recv_socket(WdHbIf *hb_if)
        hints.ai_protocol = 0;
        hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE;
 
-       if ((gai_ret = getaddrinfo(NULL, portstr, &hints, &res)) != 0)
+       if ((gai_ret = getaddrinfo(!hb_if->addr ? NULL : hb_if->addr, portstr, &hints, &res)) != 0)
        {
                ereport(WARNING,
                                (errmsg("getaddrinfo() failed with error \"%s\"", gai_strerror(gai_ret))));
@@ -285,7 +285,7 @@ wd_create_hb_recv_socket(WdHbIf *hb_if)
        if (n == 0)
        {
                ereport(ERROR, (errmsg("failed to create watchdog heartbeat receive socket"),
-                                               errdetail("getaddrinfo() result is empty: no sockets can be created because no available local address with port:%d", pool_config->wd_heartbeat_port)));
+                                               errdetail("getaddrinfo() result is empty: no sockets can be created because no available local address with port:%d", hb_if->dest_port)));
                return NULL;
        }
        else
@@ -380,7 +380,7 @@ wd_create_hb_recv_socket(WdHbIf *hb_if)
                }
                ereport(LOG,
                                (errmsg("creating watchdog heartbeat receive socket."),
-                                errdetail("creating socket on %s:%d", buf, pool_config->wd_heartbeat_port)));
+                                errdetail("creating socket on %s:%d", buf, hb_if->dest_port)));
 
                bind_is_done = 0;
                for (bind_tries = 0; !bind_is_done && bind_tries < MAX_BIND_TRIES; bind_tries++)
index 0694a222802ef23476e7715db346366251c71d3a..86caf9b4bd1c28cabd2120fbb9a788e50f11470d 100644 (file)
@@ -133,11 +133,14 @@ lifecheck_child_name(pid_t pid)
 {
        int                     i;
 
-       for (i = 0; i < pool_config->num_hb_dest_if; i++)
+       for (i = 0; i < pool_config->num_hb_local_if; i++)
        {
                if (g_hb_receiver_pid && pid == g_hb_receiver_pid[i])
                        return "heartBeat receiver";
-               else if (g_hb_sender_pid && pid == g_hb_sender_pid[i])
+       }
+       for (i = 0; i < pool_config->num_hb_dest_if; i++)
+       {
+               if (g_hb_sender_pid && pid == g_hb_sender_pid[i])
                        return "heartBeat sender";
        }
        /* Check if it was a ping to trusted server process */
@@ -211,13 +214,13 @@ wd_reaper_lifecheck(pid_t pid, int status)
        if (g_hb_receiver_pid == NULL && g_hb_sender_pid == NULL)
                return -1;
 
-       for (i = 0; i < pool_config->num_hb_dest_if; i++)
+       for (i = 0; i < pool_config->num_hb_local_if; i++)
        {
                if (g_hb_receiver_pid && pid == g_hb_receiver_pid[i])
                {
                        if (restart_child)
                        {
-                               g_hb_receiver_pid[i] = wd_hb_receiver(1, &(pool_config->hb_dest_if[i]));
+                               g_hb_receiver_pid[i] = wd_hb_receiver(1, &(pool_config->hb_local_if[i]));
                                ereport(LOG,
                                                (errmsg("fork a new %s process with pid: %d", proc_name, g_hb_receiver_pid[i])));
                        }
@@ -226,8 +229,11 @@ wd_reaper_lifecheck(pid_t pid, int status)
 
                        return g_hb_receiver_pid[i];
                }
+       }
 
-               else if (g_hb_sender_pid && pid == g_hb_sender_pid[i])
+       for (i = 0; i < pool_config->num_hb_dest_if; i++)
+       {
+               if (g_hb_sender_pid && pid == g_hb_sender_pid[i])
                {
                        if (restart_child)
                        {
@@ -253,17 +259,21 @@ lifecheck_kill_all_children(int sig)
                && g_hb_receiver_pid && g_hb_sender_pid)
        {
                int                     i;
+               pid_t           pid_child;
 
-               for (i = 0; i < pool_config->num_hb_dest_if; i++)
+               for (i = 0; i < pool_config->num_hb_local_if; i++)
                {
-                       pid_t           pid_child = g_hb_receiver_pid[i];
+                       pid_child = g_hb_receiver_pid[i];
 
                        if (pid_child > 0)
                        {
                                kill(pid_child, sig);
                                ret = true;
                        }
+               }
 
+               for (i = 0; i < pool_config->num_hb_dest_if; i++)
+               {
                        pid_child = g_hb_sender_pid[i];
 
                        if (pid_child > 0)
@@ -477,14 +487,17 @@ spawn_lifecheck_children(void)
        {
                int                     i;
 
-               g_hb_receiver_pid = palloc0(sizeof(pid_t) * pool_config->num_hb_dest_if);
+               g_hb_receiver_pid = palloc0(sizeof(pid_t) * pool_config->num_hb_local_if);
                g_hb_sender_pid = palloc0(sizeof(pid_t) * pool_config->num_hb_dest_if);
 
-               for (i = 0; i < pool_config->num_hb_dest_if; i++)
+               for (i = 0; i < pool_config->num_hb_local_if; i++)
                {
                        /* heartbeat receiver process */
-                       g_hb_receiver_pid[i] = wd_hb_receiver(1, &(pool_config->hb_dest_if[i]));
+                       g_hb_receiver_pid[i] = wd_hb_receiver(1, &(pool_config->hb_local_if[i]));
+               }
 
+               for (i = 0; i < pool_config->num_hb_dest_if; i++)
+               {
                        /* heartbeat sender process */
                        g_hb_sender_pid[i] = wd_hb_sender(1, &(pool_config->hb_dest_if[i]));
                }