Skip to content

Conversation

@bkhomuts
Copy link
Contributor

@bkhomuts bkhomuts commented Sep 1, 2025

This change introduces new variables:
patroni_restapi_protocol
patroni_restapi_certfile
patroni_restapi_keyfile
patroni_restapi_cafile

With existing patroni_restapi_connect_addr and patroni_use_unix_socket_repl, it enables secure communication to the Patroni REST API.

Resolves: #1234

@bkhomuts bkhomuts force-pushed the allow-configuring-https branch 4 times, most recently from ca5fadd to a440050 Compare September 3, 2025 15:39
@bkhomuts bkhomuts marked this pull request as ready for review September 3, 2025 16:09
@bkhomuts bkhomuts marked this pull request as draft September 4, 2025 16:29
@bkhomuts bkhomuts force-pushed the allow-configuring-https branch from a440050 to b0e7ba7 Compare September 4, 2025 17:10
@bkhomuts bkhomuts marked this pull request as ready for review September 4, 2025 17:11
@vitabaks vitabaks added enhancement Improvement of the current functionality automation Automation functionality using Ansible labels Sep 4, 2025
@vitabaks vitabaks added this to the 2.4 milestone Sep 4, 2025
@bkhomuts bkhomuts marked this pull request as draft September 4, 2025 18:56
@bkhomuts bkhomuts force-pushed the allow-configuring-https branch from b0e7ba7 to a76cb0e Compare September 4, 2025 19:15
@bkhomuts bkhomuts marked this pull request as ready for review September 5, 2025 10:42
@bkhomuts
Copy link
Contributor Author

bkhomuts commented Sep 5, 2025

The support for https is very initial, and will require manually enabling https on existing cluster, if switching from http to https, by configuring all parameters manually and reloading patroni. This is because the playbook will try to connect to https, before patroni accepts it.

For my low-scale use it works, but changing the settings effectively on higher scale will require creating additional logic to switch from http to https.

@vitabaks
Copy link
Owner

vitabaks commented Sep 5, 2025

The support for https is very initial, and will require manually enabling https on existing cluster, if switching from http to https, by configuring all parameters manually and reloading patroni. This is because the playbook will try to connect to https, before patroni accepts it.

This change will be part of the upcoming 2.4 release, and we can highlight it in the release notes. For existing clusters, we can recommend explicitly setting patroni_restapi_protocol: http to maintain backward compatibility. For new clusters, however, it makes sense to enable TLS wherever possible to provide stronger security.

@bkhomuts bkhomuts force-pushed the allow-configuring-https branch from 24db962 to 56c8d51 Compare September 11, 2025 13:23
bkhomuts and others added 5 commits September 11, 2025 15:24
This change introduces new variables:
patroni_restapi_protocol
patroni_restapi_certfile
patroni_restapi_keyfile
patroni_restapi_cafile

With existing patroni_restapi_connect_addr and patroni_use_unix_socket_repl,
it enables secure communication to the Patroni REST API.

Resolves: #1234
The role sets default variables.
Other playbooks have this role imported indirectly, but update_pgcluster
doesn't import other roles, before variables are used.

Related to: #1234
@bkhomuts bkhomuts force-pushed the allow-configuring-https branch from 56c8d51 to f8fb7b9 Compare September 11, 2025 13:58
The location is different in Debian-based and RHEL-based.

Related to: #1234
@bkhomuts bkhomuts force-pushed the allow-configuring-https branch from f5d6289 to 521dac2 Compare September 11, 2025 14:34
bkhomuts and others added 4 commits September 11, 2025 16:34
Replace duplicate functionality with import_role.

Related to: #1251
Replaces direct usage of patroni_restapi_cafile with a default to omit when not set, across all relevant Ansible playbooks, roles, and templates. This improves flexibility and prevents errors when the CA file is not provided for Patroni REST API connections.
@vitabaks
Copy link
Owner

vitabaks commented Sep 16, 2025

The test with a self-signed certificate for the Patroni REST API was successful — it works. On the client side, you either need to specify the cacert or disable certificate verification (e.g., the -k option in curl).

TODO: @vitabaks

  • Extend the patroni client package in the Autobase API service to support HTTPS (with certificate verification disabled to allow self-signed certs). If HTTPS fails, fall back to HTTP (for backward compatibility).
  • Test the updated Patroni cluster monitoring functionality in Autobase Console.

Refactored the Patroni client to attempt HTTPS requests (with insecure TLS) before falling back to HTTP for monitoring and cluster info endpoints. Introduced a shared getJSON method to handle both protocols and updated GetMonitoringInfo and GetClusterInfo to use it.
@vitabaks vitabaks added console PostgreSQL Cluster Console API PostgreSQL Cluster Console API labels Sep 17, 2025
@vitabaks vitabaks requested a review from Copilot September 17, 2025 15:20
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR enables HTTPS support for the Patroni REST API by introducing new configuration variables and updating the client code to support both HTTP and HTTPS protocols with automatic fallback.

  • Adds HTTPS support with configurable protocol, certificate files, and CA certificate for secure Patroni REST API communication
  • Implements automatic HTTPS-to-HTTP fallback in the Go client for backward compatibility
  • Updates all Ansible playbooks and tasks to use the new protocol variables consistently across the codebase

Reviewed Changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
console/service/pkg/patroni/client.go Refactored client to support HTTPS with fallback to HTTP, added TLS configuration
automation/roles/patroni/templates/patroni.yml.j2 Added conditional TLS certificate configuration to Patroni config template
automation/roles/common/defaults/main.yml Defined new protocol and certificate variables with defaults
automation/roles/patroni/tasks/*.yml Updated health check URLs to use configurable protocol and CA certificates
automation/roles/update/tasks/*.yml Updated REST API calls to support HTTPS protocol and CA validation
automation/roles/upgrade/tasks/*.yml Updated Patroni API calls to use new protocol variables
automation/playbooks/*.yml Updated playbook API calls to use HTTPS-capable configuration
automation/molecule/tests/patroni/patroni.yml Updated test to use configurable protocol

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

disable certificate verification by setting the tls_skip_verify field to true
@vitabaks
Copy link
Owner

Test: Consul service heath-checks & Partoni API with https

HTTP checks expect a valid TLS certificate by default. You can disable certificate verification by setting the tls_skip_verify field to true.

Doc: https://developer.hashicorp.com/consul/docs/register/health-check/vm#http-checks

commit aba7655

result:

cat /etc/consul/conf.d/service_test-pgcluster-master.json

{
  "service": {
    "name": "test-pgcluster",
    "id": "test-pgcluster-master",
    "port": 6432,
    "checks": [{"http": "https://10.172.0.20:8008/primary", "interval": "2s", "tls_skip_verify": true}, {"args": ["systemctl", "status", "pgbouncer"], "interval": "5s"}],
    "tags": ["master", "primary"]
  }

cat /etc/consul/conf.d/service_test-pgcluster-replica.json

{
  "service": {
    "name": "test-pgcluster",
    "id": "test-pgcluster-replica",
    "port": 6432,
    "checks": [{"http": "https://10.172.0.20:8008/replica?lag=100MB", "interval": "2s", "tls_skip_verify": true}, {"args": ["systemctl", "status", "pgbouncer"], "interval": "5s"}],
    "tags": ["replica"]
  }
}

check:

root@pgnode01:/# dig @127.0.0.1 -p 8600 +short master.test-pgcluster.service.consul SRV
1 1 6432 pgnode03.node.dc1.consul.
root@pgnode01:/# 
root@pgnode01:/# dig @127.0.0.1 -p 8600 +short replica.test-pgcluster.service.consul SRV
1 1 6432 pgnode01.node.dc1.consul.
1 1 6432 pgnode02.node.dc1.consul.

connect:

root@pgnode01:/# psql -h master.test-pgcluster.service.consul -p 6432 -U postgres -d postgres
Password for user postgres: 
psql (17.6 (Ubuntu 17.6-1.pgdg22.04+1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off, ALPN: none)
Type "help" for help.

postgres=# select pg_is_in_recovery();
 pg_is_in_recovery 
-------------------
 f
(1 row)

postgres=# \q
root@pgnode01:/# psql -h replica.test-pgcluster.service.consul -p 6432 -U postgres -d postgres
Password for user postgres: 
psql (17.6 (Ubuntu 17.6-1.pgdg22.04+1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off, ALPN: none)
Type "help" for help.

postgres=# select pg_is_in_recovery();
 pg_is_in_recovery 
-------------------
 t
(1 row)

postgres=# \q

passed

@vitabaks
Copy link
Owner

Test: Autobase console & Patroni API with https

Test the updated Patroni cluster monitoring functionality in Autobase Console.

Result:

if node is unavailable

{"level":"debug","app":"pg_console","version":"2.3.2","module":"cluster_watcher","cluster":"vitabaks-pgcluster-01","error":"http fallback failed after https: Get \"https://172.31.42.40:8008/cluster\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)","time":"2025-09-17T17:28:40Z","message":"failed to get patroni info"}
{"level":"debug","app":"pg_console","version":"2.3.2","cid":"03a1ca28-815f-4bf0-b180-8c95a147391b","error":"Get \"https://172.31.44.144:8008/cluster\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)","time":"2025-09-17T17:28:42Z","message":"https request failed, falling back to http"}
{"level":"debug","app":"pg_console","version":"2.3.2","module":"cluster_watcher","cluster":"vitabaks-pgcluster-01","error":"http fallback failed after https: Get \"https://172.31.44.144:8008/cluster\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)","time":"2025-09-17T17:28:44Z","message":"failed to get patroni info"}
image

if node is available

{"level":"debug","app":"pg_console","version":"2.3.2","query_id":"1ec5035f-6643-4fb2-96c6-803e91904870","cid":"65021616-f8b1-4294-851d-b49556232d15","sql":"insert into servers(cluster_id, ip_address, server_name, server_role, server_status, timeline, lag, tags, pending_restart)values($1, $2, $3, $4, $5, $6, $7, $8, $9) on conflict(cluster_id, ip_address) do update    set server_name = case when EXCLUDED.server_name = '' then servers.server_name else EXCLUDED.server_name end,        server_role = coalesce(EXCLUDED.server_role, servers.server_role),        server_status = coalesce(EXCLUDED.server_status, servers.server_status),        timeline = coalesce(EXCLUDED.timeline, servers.timeline),        lag = EXCLUDED.lag,        tags = coalesce(EXCLUDED.tags, servers.tags),        pending_restart = coalesce(EXCLUDED.pending_restart, servers.pending_restart) returning *","args":"(,85,\"172.31.36.231\",\"ip-172-31-36-231\",(*string)(\"replica\"),(*string)(\"streaming\"),(*int64)(1),(*int64)(0),(*interface{})(nil)(*bool)(false))","time":"2025-09-17T17:43:49Z","message":"TraceQueryStart"}
{"level":"debug","app":"pg_console","version":"2.3.2","query_id":"1ec5035f-6643-4fb2-96c6-803e91904870","cid":"65021616-f8b1-4294-851d-b49556232d15","time":"2025-09-17T17:43:49Z","message":"TraceQueryEnd duration: 2.857574ms"}
{"level":"debug","app":"pg_console","version":"2.3.2","query_id":"cbc3f30e-4734-440c-8a57-c7a419822e33","cid":"65021616-f8b1-4294-851d-b49556232d15","sql":"insert into servers(cluster_id, ip_address, server_name, server_role, server_status, timeline, lag, tags, pending_restart)values($1, $2, $3, $4, $5, $6, $7, $8, $9) on conflict(cluster_id, ip_address) do update    set server_name = case when EXCLUDED.server_name = '' then servers.server_name else EXCLUDED.server_name end,        server_role = coalesce(EXCLUDED.server_role, servers.server_role),        server_status = coalesce(EXCLUDED.server_status, servers.server_status),        timeline = coalesce(EXCLUDED.timeline, servers.timeline),        lag = EXCLUDED.lag,        tags = coalesce(EXCLUDED.tags, servers.tags),        pending_restart = coalesce(EXCLUDED.pending_restart, servers.pending_restart) returning *","args":"(,85,\"172.31.42.40\",\"ip-172-31-42-40\",(*string)(\"leader\"),(*string)(\"running\"),(*int64)(1),(*int64)(nil),(*interface{})(nil)(*bool)(false))","time":"2025-09-17T17:43:49Z","message":"TraceQueryStart"}
{"level":"debug","app":"pg_console","version":"2.3.2","query_id":"cbc3f30e-4734-440c-8a57-c7a419822e33","cid":"65021616-f8b1-4294-851d-b49556232d15","time":"2025-09-17T17:43:49Z","message":"TraceQueryEnd duration: 708.206µs"}
{"level":"debug","app":"pg_console","version":"2.3.2","query_id":"5f665979-d6b6-46a5-8901-8fe3dd5474ae","cid":"65021616-f8b1-4294-851d-b49556232d15","sql":"insert into servers(cluster_id, ip_address, server_name, server_role, server_status, timeline, lag, tags, pending_restart)values($1, $2, $3, $4, $5, $6, $7, $8, $9) on conflict(cluster_id, ip_address) do update    set server_name = case when EXCLUDED.server_name = '' then servers.server_name else EXCLUDED.server_name end,        server_role = coalesce(EXCLUDED.server_role, servers.server_role),        server_status = coalesce(EXCLUDED.server_status, servers.server_status),        timeline = coalesce(EXCLUDED.timeline, servers.timeline),        lag = EXCLUDED.lag,        tags = coalesce(EXCLUDED.tags, servers.tags),        pending_restart = coalesce(EXCLUDED.pending_restart, servers.pending_restart) returning *","args":"(,85,\"172.31.44.144\",\"ip-172-31-44-144\",(*string)(\"replica\"),(*string)(\"streaming\"),(*int64)(1),(*int64)(0),(*interface{})(nil)(*bool)(false))","time":"2025-09-17T17:43:49Z","message":"TraceQueryStart"}
{"level":"debug","app":"pg_console","version":"2.3.2","query_id":"5f665979-d6b6-46a5-8901-8fe3dd5474ae","cid":"65021616-f8b1-4294-851d-b49556232d15","time":"2025-09-17T17:43:49Z","message":"TraceQueryEnd duration: 618.966µs"}
image image

passed

@vitabaks vitabaks merged commit 92c08a5 into vitabaks:master Sep 17, 2025
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

API PostgreSQL Cluster Console API automation Automation functionality using Ansible console PostgreSQL Cluster Console enhancement Improvement of the current functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] Support https for restapi

2 participants