From 61ca60a94cd5050067f8fd82548f9376c1efa7dd Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Wed, 13 May 2026 17:28:07 -0400 Subject: [PATCH 01/11] prep for soc db config --- salt/common/tools/sbin/so-log-check | 1 + salt/kratos/soc_kratos.yaml | 2 +- salt/postgres/config.sls | 6 +++--- salt/postgres/enabled.sls | 4 ++-- salt/postgres/files/{init-users.sh => init-db.sh} | 5 +++++ salt/postgres/telegraf_users.sls | 2 +- salt/soc/soc_soc.yaml | 1 + 7 files changed, 14 insertions(+), 7 deletions(-) rename salt/postgres/files/{init-users.sh => init-db.sh} (89%) diff --git a/salt/common/tools/sbin/so-log-check b/salt/common/tools/sbin/so-log-check index a3d9c51d0..94fdd7229 100755 --- a/salt/common/tools/sbin/so-log-check +++ b/salt/common/tools/sbin/so-log-check @@ -165,6 +165,7 @@ if [[ $EXCLUDE_FALSE_POSITIVE_ERRORS == 'Y' ]]; then EXCLUDED_ERRORS="$EXCLUDED_ERRORS|upgrading component template" # false positive (elasticsearch index or template names contain 'error') EXCLUDED_ERRORS="$EXCLUDED_ERRORS|upgrading composable template" # false positive (elasticsearch composable template names contain 'error') EXCLUDED_ERRORS="$EXCLUDED_ERRORS|Error while parsing document for index \[.ds-logs-kratos-so-.*object mapping for \[file\]" # false positive (mapping error occuring BEFORE kratos index has rolled over in 2.4.210) + EXCLUDED_ERRORS="$EXCLUDED_ERRORS|No such container" # false positive (telegraf trying to run stats on an old container) fi if [[ $EXCLUDE_KNOWN_ERRORS == 'Y' ]]; then diff --git a/salt/kratos/soc_kratos.yaml b/salt/kratos/soc_kratos.yaml index 4cfe2c1c3..267c4bc50 100644 --- a/salt/kratos/soc_kratos.yaml +++ b/salt/kratos/soc_kratos.yaml @@ -103,7 +103,7 @@ kratos: config: session: lifespan: - description: Defines the length of a login session. + description: Defines the length of a login session before it will timeout, and require a new login. global: True helpLink: kratos whoami: diff --git a/salt/postgres/config.sls b/salt/postgres/config.sls index 11ca52649..e458e8455 100644 --- a/salt/postgres/config.sls +++ b/salt/postgres/config.sls @@ -46,10 +46,10 @@ postgresinitdir: - require: - file: postgresconfdir -postgresinitusers: +postgresinitdb: file.managed: - - name: /opt/so/conf/postgres/init/init-users.sh - - source: salt://postgres/files/init-users.sh + - name: /opt/so/conf/postgres/init/init-db.sh + - source: salt://postgres/files/init-db.sh - user: 939 - group: 939 - mode: 755 diff --git a/salt/postgres/enabled.sls b/salt/postgres/enabled.sls index b3abb621e..79ef6f997 100644 --- a/salt/postgres/enabled.sls +++ b/salt/postgres/enabled.sls @@ -31,7 +31,7 @@ so-postgres: - POSTGRES_DB=securityonion # Passwords are delivered via mounted 0600 secret files, not plaintext env vars. # The upstream postgres image resolves POSTGRES_PASSWORD_FILE; entrypoint.sh and - # init-users.sh resolve SO_POSTGRES_PASS_FILE the same way. + # init-db.sh resolve SO_POSTGRES_PASS_FILE the same way. - POSTGRES_PASSWORD_FILE=/run/secrets/postgres_password - SO_POSTGRES_USER={{ SO_POSTGRES_USER }} - SO_POSTGRES_PASS_FILE=/run/secrets/so_postgres_pass @@ -46,7 +46,7 @@ so-postgres: - /opt/so/conf/postgres/postgresql.conf:/conf/postgresql.conf:ro - /opt/so/conf/postgres/pg_hba.conf:/conf/pg_hba.conf:ro - /opt/so/conf/postgres/secrets:/run/secrets:ro - - /opt/so/conf/postgres/init/init-users.sh:/docker-entrypoint-initdb.d/init-users.sh:ro + - /opt/so/conf/postgres/init/init-db.sh:/docker-entrypoint-initdb.d/init-db.sh:ro - /etc/pki/postgres.crt:/conf/postgres.crt:ro - /etc/pki/postgres.key:/conf/postgres.key:ro - /etc/pki/tls/certs/intca.crt:/conf/ca.crt:ro diff --git a/salt/postgres/files/init-users.sh b/salt/postgres/files/init-db.sh similarity index 89% rename from salt/postgres/files/init-users.sh rename to salt/postgres/files/init-db.sh index e28b11f0f..03e6d08dd 100644 --- a/salt/postgres/files/init-users.sh +++ b/salt/postgres/files/init-db.sh @@ -32,3 +32,8 @@ EOSQL if ! psql -U "$POSTGRES_USER" -tAc "SELECT 1 FROM pg_database WHERE datname='so_telegraf'" | grep -q 1; then psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -c "CREATE DATABASE so_telegraf" fi + +# Bootstrap the SOC database. +if ! psql -U "$POSTGRES_USER" -tAc "SELECT 1 FROM pg_database WHERE datname='so_soc'" | grep -q 1; then + psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -c "CREATE DATABASE so_soc" +fi diff --git a/salt/postgres/telegraf_users.sls b/salt/postgres/telegraf_users.sls index 62490ea52..1ac7c80ed 100644 --- a/salt/postgres/telegraf_users.sls +++ b/salt/postgres/telegraf_users.sls @@ -39,7 +39,7 @@ postgres_wait_ready: - require: - docker_container: so-postgres -# Ensure the shared Telegraf database exists. init-users.sh only runs on a +# Ensure the shared Telegraf database exists. init-db.sh only runs on a # fresh data dir, so hosts upgraded onto an existing /nsm/postgres volume # would otherwise never get so_telegraf. postgres_create_telegraf_db: diff --git a/salt/soc/soc_soc.yaml b/salt/soc/soc_soc.yaml index 6a2f79629..647bdd778 100644 --- a/salt/soc/soc_soc.yaml +++ b/salt/soc/soc_soc.yaml @@ -818,6 +818,7 @@ soc: description: List of available external tools visible in the SOC UI. Each tool is defined in JSON object notation, and must include the "name" key and "link" key, where the link is the tool's URL. global: True advanced: True + multiline: True forcedType: "[]{}" exportNodeId: description: The node ID on which export jobs will be executed. From 907f699721c1376e85ec2788fb2c994d61056117 Mon Sep 17 00:00:00 2001 From: Jason Ertel Date: Thu, 14 May 2026 11:03:08 -0400 Subject: [PATCH 02/11] state rename --- salt/postgres/enabled.sls | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/postgres/enabled.sls b/salt/postgres/enabled.sls index 79ef6f997..20d256ae8 100644 --- a/salt/postgres/enabled.sls +++ b/salt/postgres/enabled.sls @@ -70,7 +70,7 @@ so-postgres: - watch: - file: postgresconf - file: postgreshba - - file: postgresinitusers + - file: postgresinitdb - file: postgres_super_secret - file: postgres_app_secret - x509: postgres_crt @@ -78,7 +78,7 @@ so-postgres: - require: - file: postgresconf - file: postgreshba - - file: postgresinitusers + - file: postgresinitdb - file: postgres_super_secret - file: postgres_app_secret - x509: postgres_crt From 6f273d7d9795a47b8cd31c507eac2bbec49c3752 Mon Sep 17 00:00:00 2001 From: Mike Reeves Date: Thu, 14 May 2026 15:53:00 -0400 Subject: [PATCH 03/11] Rename init-users.sh to init-db.sh and update all references --- salt/postgres/config.sls | 6 +++--- salt/postgres/enabled.sls | 8 ++++---- salt/postgres/files/{init-users.sh => init-db.sh} | 0 salt/postgres/telegraf_users.sls | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) rename salt/postgres/files/{init-users.sh => init-db.sh} (100%) diff --git a/salt/postgres/config.sls b/salt/postgres/config.sls index 11ca52649..e458e8455 100644 --- a/salt/postgres/config.sls +++ b/salt/postgres/config.sls @@ -46,10 +46,10 @@ postgresinitdir: - require: - file: postgresconfdir -postgresinitusers: +postgresinitdb: file.managed: - - name: /opt/so/conf/postgres/init/init-users.sh - - source: salt://postgres/files/init-users.sh + - name: /opt/so/conf/postgres/init/init-db.sh + - source: salt://postgres/files/init-db.sh - user: 939 - group: 939 - mode: 755 diff --git a/salt/postgres/enabled.sls b/salt/postgres/enabled.sls index b3abb621e..20d256ae8 100644 --- a/salt/postgres/enabled.sls +++ b/salt/postgres/enabled.sls @@ -31,7 +31,7 @@ so-postgres: - POSTGRES_DB=securityonion # Passwords are delivered via mounted 0600 secret files, not plaintext env vars. # The upstream postgres image resolves POSTGRES_PASSWORD_FILE; entrypoint.sh and - # init-users.sh resolve SO_POSTGRES_PASS_FILE the same way. + # init-db.sh resolve SO_POSTGRES_PASS_FILE the same way. - POSTGRES_PASSWORD_FILE=/run/secrets/postgres_password - SO_POSTGRES_USER={{ SO_POSTGRES_USER }} - SO_POSTGRES_PASS_FILE=/run/secrets/so_postgres_pass @@ -46,7 +46,7 @@ so-postgres: - /opt/so/conf/postgres/postgresql.conf:/conf/postgresql.conf:ro - /opt/so/conf/postgres/pg_hba.conf:/conf/pg_hba.conf:ro - /opt/so/conf/postgres/secrets:/run/secrets:ro - - /opt/so/conf/postgres/init/init-users.sh:/docker-entrypoint-initdb.d/init-users.sh:ro + - /opt/so/conf/postgres/init/init-db.sh:/docker-entrypoint-initdb.d/init-db.sh:ro - /etc/pki/postgres.crt:/conf/postgres.crt:ro - /etc/pki/postgres.key:/conf/postgres.key:ro - /etc/pki/tls/certs/intca.crt:/conf/ca.crt:ro @@ -70,7 +70,7 @@ so-postgres: - watch: - file: postgresconf - file: postgreshba - - file: postgresinitusers + - file: postgresinitdb - file: postgres_super_secret - file: postgres_app_secret - x509: postgres_crt @@ -78,7 +78,7 @@ so-postgres: - require: - file: postgresconf - file: postgreshba - - file: postgresinitusers + - file: postgresinitdb - file: postgres_super_secret - file: postgres_app_secret - x509: postgres_crt diff --git a/salt/postgres/files/init-users.sh b/salt/postgres/files/init-db.sh similarity index 100% rename from salt/postgres/files/init-users.sh rename to salt/postgres/files/init-db.sh diff --git a/salt/postgres/telegraf_users.sls b/salt/postgres/telegraf_users.sls index 62490ea52..1ac7c80ed 100644 --- a/salt/postgres/telegraf_users.sls +++ b/salt/postgres/telegraf_users.sls @@ -39,7 +39,7 @@ postgres_wait_ready: - require: - docker_container: so-postgres -# Ensure the shared Telegraf database exists. init-users.sh only runs on a +# Ensure the shared Telegraf database exists. init-db.sh only runs on a # fresh data dir, so hosts upgraded onto an existing /nsm/postgres volume # would otherwise never get so_telegraf. postgres_create_telegraf_db: From b7a13899f776c48cff48d7c9af9add92ee3dd298 Mon Sep 17 00:00:00 2001 From: Mike Reeves Date: Thu, 14 May 2026 15:56:04 -0400 Subject: [PATCH 04/11] Suppress output logging for postgres telegraf role provisioning --- salt/postgres/telegraf_users.sls | 1 + 1 file changed, 1 insertion(+) diff --git a/salt/postgres/telegraf_users.sls b/salt/postgres/telegraf_users.sls index 1ac7c80ed..6d6a30d84 100644 --- a/salt/postgres/telegraf_users.sls +++ b/salt/postgres/telegraf_users.sls @@ -118,6 +118,7 @@ postgres_telegraf_role_{{ u }}: GRANT CONNECT ON DATABASE so_telegraf TO "{{ u }}"; GRANT so_telegraf TO "{{ u }}"; EOSQL + - hide_output: True - require: - cmd: postgres_telegraf_group_role From 450eacca417fcc7503b06e491f5b8cbbb1bdcd92 Mon Sep 17 00:00:00 2001 From: Mike Reeves Date: Thu, 14 May 2026 16:15:54 -0400 Subject: [PATCH 05/11] Move telegraf role provisioning to external script with env vars --- salt/postgres/config.sls | 8 ++++++++ salt/postgres/files/telegraf_role.sh | 23 +++++++++++++++++++++++ salt/postgres/telegraf_users.sls | 21 ++++++--------------- 3 files changed, 37 insertions(+), 15 deletions(-) create mode 100644 salt/postgres/files/telegraf_role.sh diff --git a/salt/postgres/config.sls b/salt/postgres/config.sls index e458e8455..f5bf856eb 100644 --- a/salt/postgres/config.sls +++ b/salt/postgres/config.sls @@ -94,6 +94,14 @@ postgres_app_secret: - require: - file: postgressecretsdir +postgrestelegrafrole: + file.managed: + - name: /usr/local/bin/telegraf_role.sh + - source: salt://postgres/files/telegraf_role.sh + - user: root + - group: root + - mode: 755 + postgres_sbin: file.recurse: - name: /usr/sbin diff --git a/salt/postgres/files/telegraf_role.sh b/salt/postgres/files/telegraf_role.sh new file mode 100644 index 000000000..352efa018 --- /dev/null +++ b/salt/postgres/files/telegraf_role.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -e + +# Provision or update a Telegraf postgres role. +# Expects ROLE_USER and ROLE_PASS environment variables. + +docker exec -i so-postgres psql \ + -v ON_ERROR_STOP=1 \ + -v role_user="$ROLE_USER" \ + -v role_pass="$ROLE_PASS" \ + -U postgres -d so_telegraf <<'EOSQL' +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = :role_user) THEN + EXECUTE format('CREATE ROLE %I WITH LOGIN PASSWORD %L', :role_user, :role_pass); + ELSE + EXECUTE format('ALTER ROLE %I WITH LOGIN PASSWORD %L', :role_user, :role_pass); + END IF; +END +$$; +GRANT CONNECT ON DATABASE so_telegraf TO :"role_user"; +GRANT so_telegraf TO :"role_user"; +EOSQL diff --git a/salt/postgres/telegraf_users.sls b/salt/postgres/telegraf_users.sls index 6d6a30d84..bafa781cc 100644 --- a/salt/postgres/telegraf_users.sls +++ b/salt/postgres/telegraf_users.sls @@ -100,26 +100,17 @@ postgres_telegraf_group_role: {% for mid, entry in creds.items() %} {% if entry.get('user') and entry.get('pass') %} {% set u = entry.user %} -{% set p = entry.pass | replace("'", "''") %} +{% set p = entry.pass %} postgres_telegraf_role_{{ u }}: cmd.run: - - name: | - docker exec -i so-postgres psql -v ON_ERROR_STOP=1 -U postgres -d so_telegraf <<'EOSQL' - DO $$ - BEGIN - IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = '{{ u }}') THEN - EXECUTE format('CREATE ROLE %I WITH LOGIN PASSWORD %L', '{{ u }}', '{{ p }}'); - ELSE - EXECUTE format('ALTER ROLE %I WITH PASSWORD %L', '{{ u }}', '{{ p }}'); - END IF; - END - $$; - GRANT CONNECT ON DATABASE so_telegraf TO "{{ u }}"; - GRANT so_telegraf TO "{{ u }}"; - EOSQL + - name: /usr/local/bin/telegraf_role.sh + - env: + - ROLE_USER: {{ u }} + - ROLE_PASS: {{ p }} - hide_output: True - require: + - file: postgrestelegrafrole - cmd: postgres_telegraf_group_role {% endif %} From 03fa01a705db80ff9ef146400d5c62115ce0e08b Mon Sep 17 00:00:00 2001 From: Mike Reeves Date: Thu, 14 May 2026 16:18:01 -0400 Subject: [PATCH 06/11] Move telegraf_role.sh to postgres tools/sbin --- salt/postgres/config.sls | 8 -------- salt/postgres/telegraf_users.sls | 4 ++-- salt/postgres/{files => tools/sbin}/telegraf_role.sh | 0 3 files changed, 2 insertions(+), 10 deletions(-) rename salt/postgres/{files => tools/sbin}/telegraf_role.sh (100%) diff --git a/salt/postgres/config.sls b/salt/postgres/config.sls index f5bf856eb..e458e8455 100644 --- a/salt/postgres/config.sls +++ b/salt/postgres/config.sls @@ -94,14 +94,6 @@ postgres_app_secret: - require: - file: postgressecretsdir -postgrestelegrafrole: - file.managed: - - name: /usr/local/bin/telegraf_role.sh - - source: salt://postgres/files/telegraf_role.sh - - user: root - - group: root - - mode: 755 - postgres_sbin: file.recurse: - name: /usr/sbin diff --git a/salt/postgres/telegraf_users.sls b/salt/postgres/telegraf_users.sls index bafa781cc..369e30dbc 100644 --- a/salt/postgres/telegraf_users.sls +++ b/salt/postgres/telegraf_users.sls @@ -104,13 +104,13 @@ postgres_telegraf_group_role: postgres_telegraf_role_{{ u }}: cmd.run: - - name: /usr/local/bin/telegraf_role.sh + - name: /usr/sbin/telegraf_role.sh - env: - ROLE_USER: {{ u }} - ROLE_PASS: {{ p }} - hide_output: True - require: - - file: postgrestelegrafrole + - file: postgres_sbin - cmd: postgres_telegraf_group_role {% endif %} diff --git a/salt/postgres/files/telegraf_role.sh b/salt/postgres/tools/sbin/telegraf_role.sh similarity index 100% rename from salt/postgres/files/telegraf_role.sh rename to salt/postgres/tools/sbin/telegraf_role.sh From b9f2d56932d870a9b8bd9b0daacd4f96a9191587 Mon Sep 17 00:00:00 2001 From: Mike Reeves Date: Thu, 14 May 2026 16:37:08 -0400 Subject: [PATCH 07/11] Consolidate telegraf postgres SQL into multi-mode script Replace inline psql heredocs in telegraf_users.sls with subcommand dispatcher telegraf_postgres.sh: create_db, group_role, user, retention. --- salt/postgres/telegraf_users.sls | 63 ++-------- salt/postgres/tools/sbin/telegraf_postgres.sh | 108 ++++++++++++++++++ salt/postgres/tools/sbin/telegraf_role.sh | 23 ---- 3 files changed, 117 insertions(+), 77 deletions(-) create mode 100644 salt/postgres/tools/sbin/telegraf_postgres.sh delete mode 100644 salt/postgres/tools/sbin/telegraf_role.sh diff --git a/salt/postgres/telegraf_users.sls b/salt/postgres/telegraf_users.sls index 369e30dbc..4b95ac45b 100644 --- a/salt/postgres/telegraf_users.sls +++ b/salt/postgres/telegraf_users.sls @@ -44,12 +44,10 @@ postgres_wait_ready: # would otherwise never get so_telegraf. postgres_create_telegraf_db: cmd.run: - - name: | - if ! docker exec so-postgres psql -U postgres -tAc "SELECT 1 FROM pg_database WHERE datname='so_telegraf'" | grep -q 1; then - docker exec so-postgres psql -v ON_ERROR_STOP=1 -U postgres -c "CREATE DATABASE so_telegraf" - fi + - name: /usr/sbin/telegraf_postgres.sh create_db - require: - cmd: postgres_wait_ready + - file: postgres_sbin # Provision the shared group role and schema once. Every per-minion role is a # member of so_telegraf, and each Telegraf connection does SET ROLE so_telegraf @@ -57,44 +55,10 @@ postgres_create_telegraf_db: # on first write are owned by the group role and every member can INSERT/SELECT. postgres_telegraf_group_role: cmd.run: - - name: | - docker exec -i so-postgres psql -v ON_ERROR_STOP=1 -U postgres -d so_telegraf <<'EOSQL' - DO $$ - BEGIN - IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'so_telegraf') THEN - CREATE ROLE so_telegraf NOLOGIN; - END IF; - END - $$; - GRANT CONNECT ON DATABASE so_telegraf TO so_telegraf; - CREATE SCHEMA IF NOT EXISTS telegraf AUTHORIZATION so_telegraf; - GRANT USAGE, CREATE ON SCHEMA telegraf TO so_telegraf; - CREATE SCHEMA IF NOT EXISTS partman; - CREATE EXTENSION IF NOT EXISTS pg_partman SCHEMA partman; - CREATE EXTENSION IF NOT EXISTS pg_cron; - -- Telegraf (running as so_telegraf) calls partman.create_parent() - -- on first write of each metric, which needs USAGE on the partman - -- schema, EXECUTE on its functions/procedures, and write access to - -- partman.part_config so it can register new partitioned parents. - GRANT USAGE, CREATE ON SCHEMA partman TO so_telegraf; - GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA partman TO so_telegraf; - GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA partman TO so_telegraf; - GRANT EXECUTE ON ALL PROCEDURES IN SCHEMA partman TO so_telegraf; - -- partman creates per-parent template tables (partman.template_*) at - -- runtime; default privileges extend DML/sequence access to them. - ALTER DEFAULT PRIVILEGES IN SCHEMA partman - GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO so_telegraf; - ALTER DEFAULT PRIVILEGES IN SCHEMA partman - GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO so_telegraf; - -- Hourly partman maintenance. cron.schedule is idempotent by jobname. - SELECT cron.schedule( - 'telegraf-partman-maintenance', - '17 * * * *', - 'CALL partman.run_maintenance_proc()' - ); - EOSQL + - name: /usr/sbin/telegraf_postgres.sh group_role - require: - cmd: postgres_create_telegraf_db + - file: postgres_sbin {% set creds = salt['pillar.get']('telegraf:postgres_creds', {}) %} {% for mid, entry in creds.items() %} @@ -104,7 +68,7 @@ postgres_telegraf_group_role: postgres_telegraf_role_{{ u }}: cmd.run: - - name: /usr/sbin/telegraf_role.sh + - name: /usr/sbin/telegraf_postgres.sh user - env: - ROLE_USER: {{ u }} - ROLE_PASS: {{ p }} @@ -122,21 +86,12 @@ postgres_telegraf_role_{{ u }}: {% set retention = salt['pillar.get']('postgres:telegraf:retention_days', 14) | int %} postgres_telegraf_retention_reconcile: cmd.run: - - name: | - docker exec -i so-postgres psql -v ON_ERROR_STOP=1 -U postgres -d so_telegraf <<'EOSQL' - DO $$ - BEGIN - IF EXISTS (SELECT 1 FROM pg_catalog.pg_extension WHERE extname = 'pg_partman') THEN - UPDATE partman.part_config - SET retention = '{{ retention }} days', - retention_keep_table = false - WHERE parent_table LIKE 'telegraf.%'; - END IF; - END - $$; - EOSQL + - name: /usr/sbin/telegraf_postgres.sh retention + - env: + - RETENTION_DAYS: {{ retention }} - require: - cmd: postgres_telegraf_group_role + - file: postgres_sbin {% endif %} diff --git a/salt/postgres/tools/sbin/telegraf_postgres.sh b/salt/postgres/tools/sbin/telegraf_postgres.sh new file mode 100644 index 000000000..7f2061543 --- /dev/null +++ b/salt/postgres/tools/sbin/telegraf_postgres.sh @@ -0,0 +1,108 @@ +#!/bin/bash +set -e + +# Provision Telegraf state inside the so-postgres container. +# Usage: telegraf_postgres.sh +# create_db Ensure the so_telegraf database exists. +# group_role Provision the so_telegraf group role, telegraf/partman schemas, +# pg_partman, pg_cron, and the hourly partman maintenance job. +# user Create or update a per-minion login role granted to so_telegraf. +# Env: ROLE_USER, ROLE_PASS. +# retention Reconcile partman retention on telegraf parents. +# Env: RETENTION_DAYS. + +cmd="${1:?subcommand required}" + +case "$cmd" in + create_db) + if ! docker exec so-postgres psql -U postgres -tAc \ + "SELECT 1 FROM pg_database WHERE datname='so_telegraf'" | grep -q 1; then + docker exec so-postgres psql -v ON_ERROR_STOP=1 -U postgres \ + -c "CREATE DATABASE so_telegraf" + fi + ;; + + group_role) + docker exec -i so-postgres psql -v ON_ERROR_STOP=1 -U postgres -d so_telegraf <<'EOSQL' +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'so_telegraf') THEN + CREATE ROLE so_telegraf NOLOGIN; + END IF; +END +$$; +GRANT CONNECT ON DATABASE so_telegraf TO so_telegraf; +CREATE SCHEMA IF NOT EXISTS telegraf AUTHORIZATION so_telegraf; +GRANT USAGE, CREATE ON SCHEMA telegraf TO so_telegraf; +CREATE SCHEMA IF NOT EXISTS partman; +CREATE EXTENSION IF NOT EXISTS pg_partman SCHEMA partman; +CREATE EXTENSION IF NOT EXISTS pg_cron; +-- Telegraf (running as so_telegraf) calls partman.create_parent() +-- on first write of each metric, which needs USAGE on the partman +-- schema, EXECUTE on its functions/procedures, and write access to +-- partman.part_config so it can register new partitioned parents. +GRANT USAGE, CREATE ON SCHEMA partman TO so_telegraf; +GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA partman TO so_telegraf; +GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA partman TO so_telegraf; +GRANT EXECUTE ON ALL PROCEDURES IN SCHEMA partman TO so_telegraf; +-- partman creates per-parent template tables (partman.template_*) at +-- runtime; default privileges extend DML/sequence access to them. +ALTER DEFAULT PRIVILEGES IN SCHEMA partman + GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO so_telegraf; +ALTER DEFAULT PRIVILEGES IN SCHEMA partman + GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO so_telegraf; +-- Hourly partman maintenance. cron.schedule is idempotent by jobname. +SELECT cron.schedule( + 'telegraf-partman-maintenance', + '17 * * * *', + 'CALL partman.run_maintenance_proc()' +); +EOSQL + ;; + + user) + : "${ROLE_USER:?ROLE_USER is required}" + : "${ROLE_PASS:?ROLE_PASS is required}" + docker exec -i so-postgres psql \ + -v ON_ERROR_STOP=1 \ + -v role_user="$ROLE_USER" \ + -v role_pass="$ROLE_PASS" \ + -U postgres -d so_telegraf <<'EOSQL' +DO $$ +BEGIN + IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = :role_user) THEN + EXECUTE format('CREATE ROLE %I WITH LOGIN PASSWORD %L', :role_user, :role_pass); + ELSE + EXECUTE format('ALTER ROLE %I WITH LOGIN PASSWORD %L', :role_user, :role_pass); + END IF; +END +$$; +GRANT CONNECT ON DATABASE so_telegraf TO :"role_user"; +GRANT so_telegraf TO :"role_user"; +EOSQL + ;; + + retention) + : "${RETENTION_DAYS:?RETENTION_DAYS is required}" + docker exec -i so-postgres psql \ + -v ON_ERROR_STOP=1 \ + -v retention_days="$RETENTION_DAYS" \ + -U postgres -d so_telegraf <<'EOSQL' +DO $$ +BEGIN + IF EXISTS (SELECT 1 FROM pg_catalog.pg_extension WHERE extname = 'pg_partman') THEN + UPDATE partman.part_config + SET retention = :'retention_days' || ' days', + retention_keep_table = false + WHERE parent_table LIKE 'telegraf.%'; + END IF; +END +$$; +EOSQL + ;; + + *) + echo "Unknown subcommand: $cmd" >&2 + exit 1 + ;; +esac diff --git a/salt/postgres/tools/sbin/telegraf_role.sh b/salt/postgres/tools/sbin/telegraf_role.sh deleted file mode 100644 index 352efa018..000000000 --- a/salt/postgres/tools/sbin/telegraf_role.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -set -e - -# Provision or update a Telegraf postgres role. -# Expects ROLE_USER and ROLE_PASS environment variables. - -docker exec -i so-postgres psql \ - -v ON_ERROR_STOP=1 \ - -v role_user="$ROLE_USER" \ - -v role_pass="$ROLE_PASS" \ - -U postgres -d so_telegraf <<'EOSQL' -DO $$ -BEGIN - IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = :role_user) THEN - EXECUTE format('CREATE ROLE %I WITH LOGIN PASSWORD %L', :role_user, :role_pass); - ELSE - EXECUTE format('ALTER ROLE %I WITH LOGIN PASSWORD %L', :role_user, :role_pass); - END IF; -END -$$; -GRANT CONNECT ON DATABASE so_telegraf TO :"role_user"; -GRANT so_telegraf TO :"role_user"; -EOSQL From 8e38bff0c31ddbf7603dead157a741fafc708664 Mon Sep 17 00:00:00 2001 From: Mike Reeves Date: Thu, 14 May 2026 16:55:53 -0400 Subject: [PATCH 08/11] Rename telegraf_postgres.sh to so-telegraf-postgres --- salt/postgres/telegraf_users.sls | 8 ++++---- .../sbin/{telegraf_postgres.sh => so-telegraf-postgres} | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) rename salt/postgres/tools/sbin/{telegraf_postgres.sh => so-telegraf-postgres} (98%) diff --git a/salt/postgres/telegraf_users.sls b/salt/postgres/telegraf_users.sls index 4b95ac45b..b4226f1fd 100644 --- a/salt/postgres/telegraf_users.sls +++ b/salt/postgres/telegraf_users.sls @@ -44,7 +44,7 @@ postgres_wait_ready: # would otherwise never get so_telegraf. postgres_create_telegraf_db: cmd.run: - - name: /usr/sbin/telegraf_postgres.sh create_db + - name: /usr/sbin/so-telegraf-postgres create_db - require: - cmd: postgres_wait_ready - file: postgres_sbin @@ -55,7 +55,7 @@ postgres_create_telegraf_db: # on first write are owned by the group role and every member can INSERT/SELECT. postgres_telegraf_group_role: cmd.run: - - name: /usr/sbin/telegraf_postgres.sh group_role + - name: /usr/sbin/so-telegraf-postgres group_role - require: - cmd: postgres_create_telegraf_db - file: postgres_sbin @@ -68,7 +68,7 @@ postgres_telegraf_group_role: postgres_telegraf_role_{{ u }}: cmd.run: - - name: /usr/sbin/telegraf_postgres.sh user + - name: /usr/sbin/so-telegraf-postgres user - env: - ROLE_USER: {{ u }} - ROLE_PASS: {{ p }} @@ -86,7 +86,7 @@ postgres_telegraf_role_{{ u }}: {% set retention = salt['pillar.get']('postgres:telegraf:retention_days', 14) | int %} postgres_telegraf_retention_reconcile: cmd.run: - - name: /usr/sbin/telegraf_postgres.sh retention + - name: /usr/sbin/so-telegraf-postgres retention - env: - RETENTION_DAYS: {{ retention }} - require: diff --git a/salt/postgres/tools/sbin/telegraf_postgres.sh b/salt/postgres/tools/sbin/so-telegraf-postgres similarity index 98% rename from salt/postgres/tools/sbin/telegraf_postgres.sh rename to salt/postgres/tools/sbin/so-telegraf-postgres index 7f2061543..e15d4c450 100644 --- a/salt/postgres/tools/sbin/telegraf_postgres.sh +++ b/salt/postgres/tools/sbin/so-telegraf-postgres @@ -2,7 +2,7 @@ set -e # Provision Telegraf state inside the so-postgres container. -# Usage: telegraf_postgres.sh +# Usage: so-telegraf-postgres # create_db Ensure the so_telegraf database exists. # group_role Provision the so_telegraf group role, telegraf/partman schemas, # pg_partman, pg_cron, and the hourly partman maintenance job. From 249b126312641375450f0614e9b928948df2aa79 Mon Sep 17 00:00:00 2001 From: Mike Reeves Date: Thu, 14 May 2026 17:08:51 -0400 Subject: [PATCH 09/11] Quote telegraf role env vars to survive YAML-special chars in passwords --- salt/postgres/telegraf_users.sls | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/postgres/telegraf_users.sls b/salt/postgres/telegraf_users.sls index b4226f1fd..28d9d6247 100644 --- a/salt/postgres/telegraf_users.sls +++ b/salt/postgres/telegraf_users.sls @@ -70,8 +70,8 @@ postgres_telegraf_role_{{ u }}: cmd.run: - name: /usr/sbin/so-telegraf-postgres user - env: - - ROLE_USER: {{ u }} - - ROLE_PASS: {{ p }} + - ROLE_USER: {{ u | tojson }} + - ROLE_PASS: {{ p | tojson }} - hide_output: True - require: - file: postgres_sbin From 024fece607315553a71ab94c463d46809bda4e5b Mon Sep 17 00:00:00 2001 From: Josh Brower Date: Thu, 14 May 2026 17:08:57 -0400 Subject: [PATCH 10/11] Tweak for nginx upgrade --- salt/nginx/etc/nginx.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/salt/nginx/etc/nginx.conf b/salt/nginx/etc/nginx.conf index 0c98b7b28..8150265f5 100644 --- a/salt/nginx/etc/nginx.conf +++ b/salt/nginx/etc/nginx.conf @@ -225,6 +225,7 @@ http { limit_req zone=auth_throttle burst={{ NGINXMERGED.config.throttle_login_burst }} nodelay; limit_req_status 429; proxy_pass http://{{ GLOBALS.manager }}:4433; + proxy_set_header Connection "Close"; proxy_read_timeout 90; proxy_connect_timeout 90; proxy_set_header Host $host; @@ -237,6 +238,7 @@ http { location ~ ^/auth/.*?(whoami|logout|settings|errors|webauthn.js) { rewrite /auth/(.*) /$1 break; proxy_pass http://{{ GLOBALS.manager }}:4433; + proxy_set_header Connection "Close"; proxy_read_timeout 90; proxy_connect_timeout 90; proxy_set_header Host $host; From 64731c73ba954df47c4fad887d265f12ee1ca20b Mon Sep 17 00:00:00 2001 From: Mike Reeves Date: Thu, 14 May 2026 17:17:49 -0400 Subject: [PATCH 11/11] Fix psql :var substitution in telegraf role and retention SQL psql does not substitute :var references inside dollar-quoted strings, so the DO blocks in the user and retention subcommands were receiving literal colons and failing (silently for user, via hide_output: True). Rewrite the conditional CREATE/ALTER ROLE with SELECT format(...) \\gexec and guard the retention UPDATE with \\gset + \\if. --- salt/postgres/tools/sbin/so-telegraf-postgres | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/salt/postgres/tools/sbin/so-telegraf-postgres b/salt/postgres/tools/sbin/so-telegraf-postgres index e15d4c450..ef7c3f9e6 100644 --- a/salt/postgres/tools/sbin/so-telegraf-postgres +++ b/salt/postgres/tools/sbin/so-telegraf-postgres @@ -63,20 +63,22 @@ EOSQL user) : "${ROLE_USER:?ROLE_USER is required}" : "${ROLE_PASS:?ROLE_PASS is required}" + # psql does not substitute :vars inside dollar-quoted strings, so the + # conditional CREATE/ALTER is built outside any DO block and dispatched + # with \gexec. format() handles identifier/literal quoting. docker exec -i so-postgres psql \ -v ON_ERROR_STOP=1 \ -v role_user="$ROLE_USER" \ -v role_pass="$ROLE_PASS" \ -U postgres -d so_telegraf <<'EOSQL' -DO $$ -BEGIN - IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = :role_user) THEN - EXECUTE format('CREATE ROLE %I WITH LOGIN PASSWORD %L', :role_user, :role_pass); - ELSE - EXECUTE format('ALTER ROLE %I WITH LOGIN PASSWORD %L', :role_user, :role_pass); - END IF; -END -$$; +SELECT format( + CASE WHEN EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = :'role_user') + THEN 'ALTER ROLE %I WITH LOGIN PASSWORD %L' + ELSE 'CREATE ROLE %I WITH LOGIN PASSWORD %L' + END, + :'role_user', + :'role_pass' +) \gexec GRANT CONNECT ON DATABASE so_telegraf TO :"role_user"; GRANT so_telegraf TO :"role_user"; EOSQL @@ -84,20 +86,20 @@ EOSQL retention) : "${RETENTION_DAYS:?RETENTION_DAYS is required}" + # \gset + \if guards against a missing pg_partman without using a DO + # block (psql :var substitution doesn't reach into dollar-quoted code). docker exec -i so-postgres psql \ -v ON_ERROR_STOP=1 \ -v retention_days="$RETENTION_DAYS" \ -U postgres -d so_telegraf <<'EOSQL' -DO $$ -BEGIN - IF EXISTS (SELECT 1 FROM pg_catalog.pg_extension WHERE extname = 'pg_partman') THEN - UPDATE partman.part_config - SET retention = :'retention_days' || ' days', - retention_keep_table = false - WHERE parent_table LIKE 'telegraf.%'; - END IF; -END -$$; +SELECT CASE WHEN EXISTS (SELECT 1 FROM pg_catalog.pg_extension WHERE extname = 'pg_partman') + THEN 'true' ELSE 'false' END AS has_partman \gset +\if :has_partman +UPDATE partman.part_config +SET retention = :'retention_days' || ' days', + retention_keep_table = false +WHERE parent_table LIKE 'telegraf.%'; +\endif EOSQL ;;