Skip to content

Commit f0a225f

Browse files
committed
Add pg_hba checksum metric
We want OTel metrics to be as close to parity with the postgres_exporter solution as possible; so this PR adds the initial setup and metric query to get 'ccp_pg_hba_checksum', as well as the util function to mark a pg_hba setting as valid. Issues: [PGO-2395]
1 parent cdc6795 commit f0a225f

File tree

3 files changed

+157
-2
lines changed

3 files changed

+157
-2
lines changed

internal/collector/generated/postgres_5m_metrics.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/collector/postgres_5m_metrics.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,12 @@
140140
attribute_columns: ["dbname"]
141141
static_attributes:
142142
server: "localhost:5432"
143-
143+
144+
- sql: SELECT monitor.pg_hba_checksum() AS status;
145+
metrics:
146+
- metric_name: ccp_pg_hba_checksum
147+
value_column: status
148+
description: |
149+
Value of checksum monitioring status for pg_catalog.pg_settings (postgresql.conf).
150+
0 = valid config. 1 = settings changed.
151+
To reset current config to valid after alert, run monitor.pg_settings_checksum_set_valid().

internal/controller/postgrescluster/metrics_setup.sql

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,150 @@ BEGIN
220220
END;
221221
$$ LANGUAGE plpgsql;
222222

223+
/*
224+
* Tables and functions for monitoring changes to pg_hba_file_rules system catalogs.
225+
* Tables allow recording of existing settings so they can be referred back to to see what changed
226+
* If checksum function returns 0, then NO settings have changed
227+
* If checksum function returns 1, then something has changed since last known valid state
228+
* For replicas, logging past settings is not possible to compare what may have changed
229+
* For replicas, by default, it is expected that its settings will match the primary
230+
* For replicas, if the pg_hba.conf are necessarily different from the primary, a known good hash of that replica's
231+
settings can be sent as an argument to the relevant checksum function. Views are provided to easily obtain the hash values used by this monitoring tool.
232+
* If any known hash parameters are passed to the checksum function, note that it will override any past hash values stored in the log table when doing comparisons and completely re-evaluate the entire state. This is true even if done on a primary where the current state will then also be logged for comparison if it differs from the given hash.
233+
Taken from https://github.com/CrunchyData/pgmonitor/blob/development/postgres_exporter/common
234+
*/
235+
236+
-- Table used to store the old pg_hba, hash, and set the valid column
237+
DROP TABLE IF EXISTS monitor.pg_hba_checksum;
238+
CREATE TABLE monitor.pg_hba_checksum (
239+
hba_hash_generated text NOT NULL
240+
, hba_hash_known_provided text
241+
, hba_string text NOT NULL
242+
, created_at timestamptz DEFAULT now() NOT NULL
243+
, valid smallint NOT NULL );
244+
245+
COMMENT ON COLUMN monitor.pg_hba_checksum.valid IS 'Set this column to zero if this group of settings is a valid change';
246+
CREATE INDEX ON monitor.pg_hba_checksum (created_at);
247+
-- End table that stores pg_hba hash
248+
249+
-- Function used to compare old pg_hba hash and current hash
250+
DROP FUNCTION IF EXISTS monitor.pg_hba_checksum(text);
251+
CREATE FUNCTION monitor.pg_hba_checksum(p_known_hba_hash text DEFAULT NULL)
252+
RETURNS smallint
253+
LANGUAGE plpgsql SECURITY DEFINER
254+
SET search_path TO pg_catalog, pg_temp
255+
AS $function$
256+
DECLARE
257+
258+
v_hba_hash text;
259+
v_hba_hash_old text;
260+
v_hba_match smallint := 0;
261+
v_hba_string text;
262+
v_hba_string_old text;
263+
v_is_in_recovery boolean;
264+
v_valid smallint;
265+
266+
BEGIN
267+
268+
SELECT pg_is_in_recovery() INTO v_is_in_recovery;
269+
270+
IF current_setting('server_version_num')::int >= 100000 THEN
271+
272+
SELECT sha256_hash
273+
, hba_string
274+
INTO v_hba_hash
275+
, v_hba_string
276+
FROM monitor.pg_hba_hash;
277+
278+
ELSE
279+
RAISE EXCEPTION 'pg_hba change monitoring unsupported in versions older than PostgreSQL 10';
280+
END IF;
281+
282+
SELECT hba_hash_generated, valid
283+
INTO v_hba_hash_old, v_valid
284+
FROM monitor.pg_hba_checksum
285+
ORDER BY created_at DESC LIMIT 1;
286+
287+
IF p_known_hba_hash IS NOT NULL THEN
288+
v_hba_hash_old := p_known_hba_hash;
289+
-- Do not base validity on the stored value if manual hash is given.
290+
v_valid := 0;
291+
END IF;
292+
293+
IF (v_hba_hash_old IS NOT NULL) THEN
294+
295+
IF (v_hba_hash != v_hba_hash_old) THEN
296+
297+
v_valid := 1;
298+
299+
IF v_is_in_recovery = false THEN
300+
INSERT INTO monitor.pg_hba_checksum (
301+
hba_hash_generated
302+
, hba_hash_known_provided
303+
, hba_string
304+
, valid)
305+
VALUES (
306+
v_hba_hash
307+
, p_known_hba_hash
308+
, v_hba_string
309+
, v_valid);
310+
END IF;
311+
END IF;
312+
313+
ELSE
314+
315+
v_valid := 0;
316+
IF v_is_in_recovery = false THEN
317+
INSERT INTO monitor.pg_hba_checksum (
318+
hba_hash_generated
319+
, hba_hash_known_provided
320+
, hba_string
321+
, valid)
322+
VALUES (v_hba_hash
323+
, p_known_hba_hash
324+
, v_hba_string
325+
, v_valid);
326+
END IF;
327+
328+
END IF;
329+
330+
RETURN v_valid;
331+
332+
END
333+
$function$;
334+
-- End function used to compare hashes
335+
336+
-- View used to create hash of pg_hba
337+
DROP VIEW IF EXISTS monitor.pg_hba_hash;
338+
CREATE VIEW monitor.pg_hba_hash AS
339+
-- Order by line number so it's caught if no content is changed but the order of entries is changed
340+
WITH hba_ordered_list AS (
341+
SELECT COALESCE(type, '<<NULL>>') AS type
342+
, array_to_string(COALESCE(database, ARRAY['<<NULL>>']), ',') AS database
343+
, array_to_string(COALESCE(user_name, ARRAY['<<NULL>>']), ',') AS user_name
344+
, COALESCE(address, '<<NULL>>') AS address
345+
, COALESCE(netmask, '<<NULL>>') AS netmask
346+
, COALESCE(auth_method, '<<NULL>>') AS auth_method
347+
, array_to_string(COALESCE(options, ARRAY['<<NULL>>']), ',') AS options
348+
FROM pg_catalog.pg_hba_file_rules
349+
ORDER BY line_number)
350+
SELECT sha256((string_agg(type||database||user_name||address||netmask||auth_method||options, ','))::bytea) AS sha256_hash
351+
, string_agg(type||database||user_name||address||netmask||auth_method||options, ',') AS hba_string
352+
FROM hba_ordered_list;
353+
-- End view used to create hash of pg_hba
354+
355+
-- Function used to set pg_hba as valid
356+
/*
357+
* This function provides quick, clear interface for resetting the checksum monitor to treat the currently detected configuration as valid after alerting on a change. Note that configuration history will be cleared.
358+
*/
359+
DROP FUNCTION IF EXISTS monitor.pg_hba_checksum_set_valid();
360+
CREATE FUNCTION monitor.pg_hba_checksum_set_valid() RETURNS smallint
361+
LANGUAGE sql
362+
AS $function$
363+
364+
TRUNCATE monitor.pg_hba_checksum;
365+
366+
SELECT monitor.pg_hba_checksum();
367+
368+
$function$;
369+
-- End function used to set pg_hba as valid

0 commit comments

Comments
 (0)