Skip to content

Targets

Edition: Community and Enterprise Offering: Self-managed

A target is a database sealfix may change. Each one belongs to an environment, whose policy decides the approvals, and to a project, which decides who reviews it.

The executor account

The executor connects to each target with its own account. Grant it the least it needs.

CREATE ROLE sealfix_exec LOGIN PASSWORD '...';
GRANT USAGE ON SCHEMA app TO sealfix_exec;
GRANT SELECT, INSERT, UPDATE, DELETE ON app.customers, app.accounts TO sealfix_exec;
CREATE USER sealfix_exec FOR LOGIN sealfix_exec;
GRANT SELECT, INSERT, UPDATE, DELETE ON app.customers TO sealfix_exec;
GRANT VIEW DEFINITION TO sealfix_exec;   -- required: reads the metadata
GRANT SHOWPLAN TO sealfix_exec;          -- query plans in the dry run

Tables with triggers also need VIEW SERVER PERFORMANCE STATE (VIEW SERVER STATE on SQL Server 2019) and SELECT on sys.sql_expression_dependencies.

CREATE USER sealfix_exec IDENTIFIED BY "...";
GRANT CREATE SESSION TO sealfix_exec;
GRANT SELECT, INSERT, UPDATE, DELETE ON app.customers TO sealfix_exec;
GRANT SELECT_CATALOG_ROLE TO sealfix_exec;   -- required: reads the data dictionary

Name tables with their schema in templates (app.customers) when the executor account does not own them.

Add a target

Prerequisites:

  • The executor account exists with the grants above.
  • You know the database's host, port and name, and the account's password. Nothing goes into a file, and the executor is not restarted.

  • Open "Admin" > "Targets" and add the target:

    Field Meaning
    "Name" The label people see
    "Type" PostgreSQL, SQL Server or Oracle
    "Environment" prod, non_prod, or any environment your policies name
    "Max rows" The most rows any fix on this target may touch
    "Host", "Port", "Database" Where the database is. For Oracle the third field is "Service name"
    "User", "Password" The executor account. The password is sealed for the executor when it arrives: the web application cannot open it, and no page shows it again
    "TLS" "Encrypted, certificate checked" unless you have a reason for another; the form warns beside the field when you choose a connection that is not checked or not encrypted, and the list repeats the warning
    "CA certificate (PEM, optional)" PostgreSQL only: the CA that signed the server's certificate, when it is not in the executor's trust store
    "Sensitive columns" Masked in samples, and protected from values written into the SQL
    "Table allowlist" If set, the only tables fixes may change

    The target and its connection are stored together. A form that is refused stores neither, and shows what you typed again, without the password.

    The database and the user are names: letters, digits and . _ $ # - (a user may also hold @). They are kept in clear and written to the audit log, so the form refuses anything that could be a secret in the wrong field. For a name with other characters, enter a connection string under "Advanced".

  • Select "Run connection test". The executor opens the connection as it would for a job. About the connection it says one of four things, or why it could not try (no connection was entered yet, or the stored one could not be opened):

    Finding Meaning
    reachable The executor connected and signed in
    refused No connection could be made at this host and port, or the database turned it away. Check the host, the port and the database name, and that the executor's host may reach the database
    authentication failed The database did not accept the user or the password
    a TLS problem The encrypted connection could not be made, or the server's certificate was not accepted. Check the TLS mode and the CA certificate

    The words are fixed: the test never shows the database's own reply, the host or the password. When the connection is reachable, the test goes on to report the account's privileges, where the database could record a bound value, and the level of side-effect capture it supports. Each finding comes with the fix to apply on the database.

One database, one environment

sealfix recognises a database by what the database says of itself, not by its address. When a job reaches a database that is also registered under an environment whose policy asks for more approvals, the executor refuses the job, whatever name or address led to it. A database is known from its first connection.

Replace a connection

Under "Connections entered here", each target shows where it points, how it is encrypted, and when it was last changed and by whom. Select "Replace", enter the connection again with its password, and select "Save the connection".

  • A new password for the same account changes nothing else.
  • A new place or account (another host, port, database, user, TLS setting or CA certificate) stops the requests that wait for the old one: their values were sealed for it. Requesters submit the values again. Executions made before the change can no longer be rolled back from sealfix. A renewed CA certificate counts: plan it for a time when no request waits.

Other ways to give a connection

Under "Advanced", "Where the connection comes from" offers two more:

  • "Executor environment": the connection string is a variable in executor.env, and the form takes the variable's name. The executor is restarted after the file changes. Each variable belongs to one target.
  • "OpenBao, a database user per job" (Enterprise): the variable holds a connection string without a user, and OpenBao creates a database user for each job.

"Connection string instead of the fields" takes a whole connection string and seals all of it. The page can then show neither its host nor its TLS setting, and replacing it later, even for a new password, stops the requests that wait for it and the rollback of earlier ones. A string that names a file on the executor, another driver or a local socket is refused when a job uses it.

Harden the target

A bound value can still be recorded by the database itself. The connection test points these out; the usual fixes:

Engine Check
PostgreSQL No log_statement = 'all' or 'mod' for the executor; no parameter logging (log_parameter_*, auto_explain); restrict who reads the server logs
SQL Server No Extended Events session or trace capturing the executor's calls; cached plans keep the first values, so limit who can read them
Oracle Bind capture (V$SQL_BIND_CAPTURE, AWR) samples values; no SQL trace with binds on executor sessions; no fine-grained audit capturing them

Troubleshooting

Every request is refused: cannot read the metadata

SQL Server needs VIEW DEFINITION, Oracle needs SELECT_CATALOG_ROLE. Without them sealfix cannot see cascading tables or triggers in other schemas, so it refuses rather than guess.

A table is not on the allowlist (check C6)

Add the table to the target's allowlist, as schema.table.