# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one # or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at # https://securityonion.net/license; you may not use this file except in compliance with the # Elastic License 2.0. {% from 'allowed_states.map.jinja' import allowed_states %} {% if sls.split('.')[0] in allowed_states %} {% from 'vars/globals.map.jinja' import GLOBALS %} # Deploys the so_pillar schema (tables, views, audit triggers, secrets, # RLS, pg_cron retention) inside the so-postgres container. Idempotent — # every CREATE / GRANT is wrapped in IF NOT EXISTS / ON CONFLICT or DO # blocks so re-running the state is a no-op when the schema is current. # # Gated on the postgres:so_pillar:enabled feature flag (default false). # Flip to true once the postsalt branch is ready to bring ext_pillar live. include: - postgres.enabled {% set so_pillar_enabled = salt['pillar.get']('postgres:so_pillar:enabled', False) %} {% if so_pillar_enabled %} {% set drift_enabled = salt['pillar.get']('postgres:so_pillar:drift_check_enabled', False) %} {% set schema_dir = '/opt/so/saltstack/default/salt/postgres/files/schema/pillar' %} # Wait for postgres to actually accept TCP connections. Same idiom as # telegraf_users.sls. The docker_container.running state returns earlier than # the database is ready on first init. so_pillar_postgres_wait_ready: cmd.run: - name: | for i in $(seq 1 60); do if docker exec so-postgres pg_isready -h 127.0.0.1 -U postgres -q 2>/dev/null; then exit 0 fi sleep 2 done echo "so-postgres did not accept TCP connections within 120s" >&2 exit 1 - require: - docker_container: so-postgres {% set sql_files = [ '001_schema.sql', '002_views.sql', '003_history_trigger.sql', '004_secrets.sql', '005_seed_roles.sql', '006_rls.sql', ] %} {% if drift_enabled %} {% do sql_files.append('007_drift_pgcron.sql') %} {% endif %} {% for sql_file in sql_files %} so_pillar_apply_{{ sql_file | replace('.', '_') }}: cmd.run: - name: | docker exec -i so-postgres psql -v ON_ERROR_STOP=1 -U postgres -d securityonion \ < {{ schema_dir }}/{{ sql_file }} - require: - cmd: so_pillar_postgres_wait_ready {% if not loop.first %} - cmd: so_pillar_apply_{{ sql_files[loop.index0 - 1] | replace('.', '_') }} {% endif %} {% endfor %} # Set the master encryption key GUC on the secret-owner role. The key itself # is generated by setup/so-functions::secrets_pillar() (extended for postsalt) # and lives in /opt/so/conf/postgres/so_pillar.key (mode 0400) — never read by # Salt itself; the value flows into PG via ALTER ROLE so it sits only in the # server's role catalog. so_pillar_master_key_configure: cmd.run: - name: | if [ -r /opt/so/conf/postgres/so_pillar.key ]; then KEY="$(< /opt/so/conf/postgres/so_pillar.key)" docker exec -i so-postgres psql -v ON_ERROR_STOP=1 -U postgres -d securityonion <&2 exit 1 fi - require: - cmd: so_pillar_apply_006_rls_sql # Run the importer once after the schema is in place. Idempotent — re-runs # with no SLS edits produce zero row changes. so_pillar_initial_import: cmd.run: - name: /usr/sbin/so-pillar-import --yes --reason 'schema_pillar.sls initial import' - require: - cmd: so_pillar_master_key_configure {% else %} so_pillar_disabled_noop: test.nop {% endif %} {% else %} {{sls}}_state_not_allowed: test.fail_without_changes: - name: {{sls}}_state_not_allowed {% endif %}