|
12 | 12 | #include <getopt.h> |
13 | 13 | #include <inttypes.h> |
14 | 14 | #include <signal.h> |
| 15 | +#include <unistd.h> |
15 | 16 |
|
16 | 17 | #include "postgres_fe.h" |
17 | 18 | #include "pqexpbuffer.h" |
|
30 | 31 | #include "monitor.h" |
31 | 32 | #include "monitor_config.h" |
32 | 33 | #include "pgctl.h" |
| 34 | +#include "pghba.h" |
| 35 | +#include "pgsetup.h" |
| 36 | +#include "pgsql.h" |
33 | 37 | #include "pgtuning.h" |
34 | 38 | #include "primary_standby.h" |
35 | 39 | #include "string_utils.h" |
@@ -393,6 +397,169 @@ keeper_cli_pgsetup_tune(int argc, char **argv) |
393 | 397 | } |
394 | 398 |
|
395 | 399 |
|
| 400 | +/* |
| 401 | + * keeper_cli_pgsetup_hba_lan appends LAN CIDR trust rules to pg_hba.conf |
| 402 | + * without connecting to Postgres (safe when TCP HBA hasn't been set up yet), |
| 403 | + * then reloads the configuration via pg_ctl. |
| 404 | + * |
| 405 | + * Intended for --skip-pg-hba setups where the operator needs to seed the |
| 406 | + * initial trust rules before pg_autoctl or replication can connect via TCP. |
| 407 | + * |
| 408 | + * Sequence: |
| 409 | + * 1. Wait up to 60 s for $PGDATA/pg_hba.conf to appear. |
| 410 | + * 2. Append "host all all <LAN-CIDR> trust" and |
| 411 | + * "host replication all <LAN-CIDR> trust" directly to the file. |
| 412 | + * 3. Wait for Postgres to be running (reads postmaster.pid, no TCP). |
| 413 | + * 4. pg_ctl reload -D $PGDATA. |
| 414 | + */ |
| 415 | +void |
| 416 | +keeper_cli_pgsetup_hba_lan(int argc, char **argv) |
| 417 | +{ |
| 418 | + /* |
| 419 | + * Derive pgdata from command-line options directly, without calling |
| 420 | + * cli_common_pgsetup_init(). cli_common_pgsetup_init() calls |
| 421 | + * ProbeConfigurationFileRole() which fatals when the keeper config file |
| 422 | + * does not exist yet (e.g. node still initializing). We do not need the |
| 423 | + * full pgSetup here — only pgdata, hostname, auth method, and ssl. |
| 424 | + */ |
| 425 | + const char *pgdata = keeperOptions.pgSetup.pgdata; |
| 426 | + |
| 427 | + if (IS_EMPTY_STRING_BUFFER(pgdata)) |
| 428 | + { |
| 429 | + log_fatal("Please provide --pgdata or set PGDATA"); |
| 430 | + exit(EXIT_CODE_BAD_ARGS); |
| 431 | + } |
| 432 | + |
| 433 | + char hbaFile[MAXPGPATH]; |
| 434 | + sformat(hbaFile, MAXPGPATH, "%s/pg_hba.conf", pgdata); |
| 435 | + |
| 436 | + /* Step 1: wait for pg_hba.conf to appear (up to 60 s) */ |
| 437 | + { |
| 438 | + int timeout = 60; |
| 439 | + time_t deadline = time(NULL) + timeout; |
| 440 | + while (!file_exists(hbaFile) && time(NULL) < deadline) |
| 441 | + { |
| 442 | + log_debug("Waiting for \"%s\" to appear", hbaFile); |
| 443 | + pg_usleep(500 * 1000); |
| 444 | + } |
| 445 | + if (!file_exists(hbaFile)) |
| 446 | + { |
| 447 | + log_error("Timed out waiting for \"%s\" to appear", hbaFile); |
| 448 | + exit(EXIT_CODE_PGCTL); |
| 449 | + } |
| 450 | + } |
| 451 | + |
| 452 | + /* |
| 453 | + * Step 2: determine the hostname for LAN CIDR lookups. |
| 454 | + * |
| 455 | + * Preference order: |
| 456 | + * a) keeper config file hostname (written early during pg_autoctl init) |
| 457 | + * b) OS hostname via gethostname() — inside a Docker container this is |
| 458 | + * the service name (e.g. "node2") which resolves correctly on the LAN |
| 459 | + * c) keeperOptions.pgSetup.pghost (last resort; may be a Unix socket |
| 460 | + * path like /var/run/postgresql that does not resolve as a hostname) |
| 461 | + * |
| 462 | + * The keeper config file is created before postgres initialises, so it is |
| 463 | + * normally available by the time pg_hba.conf appears. All three options |
| 464 | + * are tried so the command works even when called very early. |
| 465 | + */ |
| 466 | + char hostname[_POSIX_HOST_NAME_MAX] = ""; |
| 467 | + |
| 468 | + /* option a: keeper config */ |
| 469 | + { |
| 470 | + KeeperConfig hbaConfig = keeperOptions; |
| 471 | + if (keeper_config_set_pathnames_from_pgdata(&hbaConfig.pathnames, |
| 472 | + pgdata) && |
| 473 | + keeper_config_read_file(&hbaConfig, |
| 474 | + false /* missingPgdataIsOk */, |
| 475 | + true /* pgIsNotRunningIsOk */, |
| 476 | + true /* monitorDisabledIsOk */) && |
| 477 | + !IS_EMPTY_STRING_BUFFER(hbaConfig.hostname)) |
| 478 | + { |
| 479 | + strlcpy(hostname, hbaConfig.hostname, sizeof(hostname)); |
| 480 | + } |
| 481 | + } |
| 482 | + |
| 483 | + /* option b: OS hostname */ |
| 484 | + if (IS_EMPTY_STRING_BUFFER(hostname)) |
| 485 | + { |
| 486 | + if (gethostname(hostname, sizeof(hostname)) == 0) |
| 487 | + { |
| 488 | + log_debug("hba-lan: using OS hostname \"%s\"", hostname); |
| 489 | + } |
| 490 | + else |
| 491 | + { |
| 492 | + hostname[0] = '\0'; |
| 493 | + } |
| 494 | + } |
| 495 | + |
| 496 | + /* option c: pghost (may be a socket path — last resort) */ |
| 497 | + if (IS_EMPTY_STRING_BUFFER(hostname)) |
| 498 | + { |
| 499 | + strlcpy(hostname, keeperOptions.pgSetup.pghost, sizeof(hostname)); |
| 500 | + } |
| 501 | + |
| 502 | + /* --auth <method> defaults to "trust"; --ssl enables hostssl rules */ |
| 503 | + const char *authMethod = keeperOptions.pgSetup.authMethod; |
| 504 | + if (IS_EMPTY_STRING_BUFFER(authMethod)) |
| 505 | + { |
| 506 | + authMethod = "trust"; |
| 507 | + } |
| 508 | + bool useSSL = keeperOptions.pgSetup.ssl.active; |
| 509 | + |
| 510 | + /* cert auth for replication needs an ident map */ |
| 511 | + bool isCert = (strcmp(authMethod, "cert") == 0); |
| 512 | + const char *replAuth = isCert ? "cert map=pgautofailover" : authMethod; |
| 513 | + |
| 514 | + if (!pghba_enable_lan_cidr(NULL, useSSL, |
| 515 | + HBA_DATABASE_ALL, NULL, |
| 516 | + hostname, NULL, authMethod, |
| 517 | + HBA_EDIT_MINIMAL, |
| 518 | + pgdata)) |
| 519 | + { |
| 520 | + log_error("Failed to add LAN CIDR HBA rule for \"all\" databases"); |
| 521 | + exit(EXIT_CODE_PGCTL); |
| 522 | + } |
| 523 | + |
| 524 | + if (!pghba_enable_lan_cidr(NULL, useSSL, |
| 525 | + HBA_DATABASE_REPLICATION, NULL, |
| 526 | + hostname, NULL, replAuth, |
| 527 | + HBA_EDIT_MINIMAL, |
| 528 | + pgdata)) |
| 529 | + { |
| 530 | + log_error("Failed to add LAN CIDR HBA rule for replication"); |
| 531 | + exit(EXIT_CODE_PGCTL); |
| 532 | + } |
| 533 | + |
| 534 | + /* cert auth requires an ident map entry in pg_ident.conf */ |
| 535 | + if (isCert) |
| 536 | + { |
| 537 | + if (!pghba_ensure_ident_map_entry(pgdata, |
| 538 | + "pgautofailover", |
| 539 | + PG_AUTOCTL_MONITOR_USERNAME, |
| 540 | + PG_AUTOCTL_REPLICA_USERNAME)) |
| 541 | + { |
| 542 | + log_error("Failed to add cert ident map entry to pg_ident.conf"); |
| 543 | + exit(EXIT_CODE_PGCTL); |
| 544 | + } |
| 545 | + } |
| 546 | + |
| 547 | + /* Step 3: wait for Postgres to be running (PID file, no TCP needed) */ |
| 548 | + if (!pg_setup_wait_until_is_ready(&keeperOptions.pgSetup, 60, LOG_INFO)) |
| 549 | + { |
| 550 | + log_error("Postgres did not become ready within 60 s"); |
| 551 | + exit(EXIT_CODE_PGCTL); |
| 552 | + } |
| 553 | + |
| 554 | + /* Step 4: reload so the new HBA rules take effect */ |
| 555 | + log_info("Reloading Postgres configuration in \"%s\"", pgdata); |
| 556 | + if (!pg_ctl_reload(keeperOptions.pgSetup.pg_ctl, pgdata)) |
| 557 | + { |
| 558 | + exit(EXIT_CODE_PGCTL); |
| 559 | + } |
| 560 | +} |
| 561 | + |
| 562 | + |
396 | 563 | /* |
397 | 564 | * keeper_cli_init_standby initializes a standby |
398 | 565 | */ |
|
0 commit comments