DBSheriff connects to your account with five dedicated service users,
each with only the privileges its job needs. They use key-pair
authentication (no passwords) and may only connect from the DBSheriff
host. deploy/onprem/snowflake_setup.sql, shipped with the product, does
everything below in one pass if you'd rather run one static SQL script;
this page explains what it creates and why, and also covers the
interactive alternative below.
Run everything as ACCOUNTADMIN.
On an installed host there is no scripts/ folder, so read every scripts/<name>.py
below as sudo /opt/dbsheriff/current/deploy/onprem/stack.sh tool <name> ..., for example
sudo /opt/dbsheriff/current/deploy/onprem/stack.sh tool setup_dbsheriff snowflake myorg-myaccount. The scripts/ form
is for a source checkout. The details are under "How to start the scripts" in
Before you install: creating DBSheriff's database users.
| Script | What it does | Example |
|---|---|---|
setup_dbsheriff.py |
Recommended. One command: creates the accounts, and first checks/enables the network-policy prerequisite when --optional-prereqs=network_policy is added (§3). |
scripts/setup_dbsheriff.py snowflake myorg-myaccount |
install_prereqs.py |
Prerequisite only — run this alone if you want to review it separately before creating any accounts. | scripts/install_prereqs.py snowflake myorg-myaccount |
create_reset_dbuser.py |
Accounts only — creates or resets them once the prerequisite is already in place. | scripts/create_reset_dbuser.py snowflake myorg-myaccount |
All three connect as your ACCOUNTADMIN user — never a cloud credential.
Without a flag they ask how you sign in (password is the default); every
script takes the same options, so an administrator whose login has no
password at all can run them:
| You sign in with | Flag | Notes |
|---|---|---|
| user and password | --admin-auth password (the default) |
Snowflake is phasing out single-factor password logins for people: prefer one of the next three |
| a key pair | --admin-private-key-file ~/.snowflake/admin.p8 |
PEM or DER. An encrypted key's passphrase is read from the environment variable SNOWFLAKE_ADMIN_PRIVATE_KEY_PASSPHRASE, or asked without echo; it is never taken from a flag |
| single sign-on | --admin-auth externalbrowser |
a browser window opens on the machine that runs the script, for your identity provider's sign-in; the user you give must be the one the identity provider signs in |
| user, password and a second factor | --admin-auth mfa |
the authenticator app's code from the environment variable SNOWFLAKE_ADMIN_PASSCODE or typed when asked; blank to approve the push notification on your device |
install_prereqs.py and setup_dbsheriff.py ask for one thing no other
engine's scripts need: the DBSheriff host's own public IP (not the
Snowflake account's address), since §3's network policy pins logins to
wherever DBSheriff actually runs; create_reset_dbuser.py does not ask
for it. Snowflake has no on-prem/cloud-platform choice to make, so there's
no platform prompt at all. install_prereqs.py automates creating the
DBSHERIFF_HOST_POLICY object (§3), and setup_dbsheriff.py does too when
given --optional-prereqs=network_policy; create_reset_dbuser.py binds
each account it creates or resets to that policy automatically whenever it
already exists, and otherwise skips the bind with a note rather than
failing — run the prerequisite first (or re-run account creation
afterward, choosing "reset") to pick it up. Add --dry-run to preview
without changing anything, or --check for a read-only status report.
Which databases. setup_dbsheriff.py and create_reset_dbuser.py ask
which databases DBSheriff may read (or take --databases ANALYTICS,SALES;
all for every customer database, none for none). The monitor and kill
users get USAGE on those databases only, and the classify user USAGE and
SELECT on every table in them, now and in the future (§5). Snowflake's own
databases, DBSHERIFF_DROP, the personal USER$<login> databases and
SNOWFLAKE_LEARNING_DB are not offered, and nothing is granted on a database
you did not choose; naming a database that is not in the account stops the run
before anything is created.
A user that already exists. CREATE USER IF NOT EXISTS does nothing at all
for a user that is already there — it keeps the old public key and says
nothing, so a private key generated afterwards could never log in. When you
choose "create" and a DBSheriff user already exists, the scripts set the new
public key with ALTER USER … SET RSA_PUBLIC_KEY instead (the private key
stored for that user before no longer works) and say so. Every key is then
checked before it is stored: the fingerprint Snowflake holds for the user must be the
generated key's, and one login with the key must not be refused as an invalid
token. A key that fails is reported, not stored; run the script again. A
login refused by a network policy (the machine you run the script on is not
in DBSHERIFF_HOST_POLICY) is reported as not proven from here, because the
fingerprint already matches.
The Without it column says what DBSheriff loses when an item is missing, so you can decide what to enable. Nothing optional or recommended stops the product: detection, response and the evidence pack run on the required items alone.
| Item | Required? | What it unlocks | Without it | How |
|---|---|---|---|---|
SNOWFLAKE.ACCOUNT_USAGE share readable by the posture role (and by the monitor role when no posture login is registered) |
required | query, login, session, ingress, grant and policy history (QUERY_HISTORY, LOGIN_HISTORY, SESSIONS, INGRESS_NETWORK_ACCESS_HISTORY, GRANTS_TO_*, POLICY_REFERENCES) |
DBSheriff's hourly evidence import and its posture checks cannot read the account's usage, grant and policy history; statement detection and response read INFORMATION_SCHEMA and keep working |
GRANT IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE TO ROLE … (§5) |
| Key-pair authentication for the product users | required | no passwords for service users; RSA_PUBLIC_KEY on each user |
password logins for service users; PS02 flags only enabled password users whose name contains SVC or SERVICE (so not the dbsheriff_* users), and the product users are exposed to credential stuffing |
§1, §4 |
| A dedicated warehouse (X-Small, auto-suspend) with a resource monitor | required | the polling and evidence queries run on it; the cost cap bounds them | polling runs on a shared warehouse: no cost cap, contention with your workloads | §2 |
| Network policy for the product users | recommended | the product logins are pinned to the DBSheriff host addresses | the product credentials work from anywhere; PS21 reports it | §3 — automated by install_prereqs.py/setup_dbsheriff.py |
SYSTEM$CLASSIFY and object tags |
optional (Enterprise edition) | native classification evidence and the sensitivity tag write-back | classification relies on column names and sampled values only; no tag write-back, so Snowflake-side masking policies keyed on the tag never attach | §7 |
| Masking / row-access policies readable by the posture role | recommended | PS19 (sensitive column without a masking policy) and the S1 evidence | PS19 (sensitive column without a masking policy) reports NOT_CHECKED; S1 shows access without the policy state | §5 |
| Trust Center | optional | PS16 / PS24 findings from Snowflake's own scanner | PS16 / PS24 stay NOT_CHECKED; DBSheriff's own checks still run | account setting |
ACCOUNTADMIN one-time actions |
required | creating the roles, the warehouse, the resource monitor and the ACCOUNT_USAGE grant | setup cannot be completed | §4–§6 |
for u in monitor kill lock posture classify; do
openssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out dbsheriff_$u.p8 -nocrypt
openssl rsa -in dbsheriff_$u.p8 -pubout -out dbsheriff_$u.pub
done
The public key body (the lines between BEGIN and END, without the
header lines) goes into the RSA_PUBLIC_KEY = '…' placeholders below. The
private .p8 files of the monitor, kill, lock, posture and classify
users are pasted into the matching boxes of the DBSheriff Admin Console
(§8); a blank posture key uses the monitor key.
DBSheriff polls on its own warehouse so its cost is visible and capped.
USE ROLE ACCOUNTADMIN;
CREATE WAREHOUSE IF NOT EXISTS DBSHERIFF_WH
WITH WAREHOUSE_SIZE = 'XSMALL' AUTO_SUSPEND = 60 AUTO_RESUME = TRUE
INITIALLY_SUSPENDED = TRUE MIN_CLUSTER_COUNT = 1 MAX_CLUSTER_COUNT = 1;
CREATE RESOURCE MONITOR IF NOT EXISTS DBSHERIFF_RM
WITH CREDIT_QUOTA = 60 FREQUENCY = MONTHLY START_TIMESTAMP = IMMEDIATELY
TRIGGERS ON 80 PERCENT DO NOTIFY ON 100 PERCENT DO SUSPEND_IMMEDIATE;
ALTER WAREHOUSE DBSHERIFF_WH SET RESOURCE_MONITOR = DBSHERIFF_RM;
60 credits a month is the starting quota this SQL and the scripts create.
DBSheriff's own estimate is about 5 credits a day in balanced poll mode
(about 150 a month) and about 24 a day with a fixed 10-second poll, so size
CREDIT_QUOTA to the cadence you choose. If the quota is spent, Snowflake
suspends the warehouse and DBSheriff reports it instead of spending more.
60 is too small for balanced mode (about 150 credits a month): at about 5 a day it is spent in about 12 days, and polling then stops until the monthly reset. Raise it before you register the server:
USE ROLE ACCOUNTADMIN;
ALTER RESOURCE MONITOR DBSHERIFF_RM SET CREDIT_QUOTA = 200;
scripts/install_prereqs.py snowflake <account> # asks for the DBSheriff host's public IP
# or, together with the five users:
scripts/setup_dbsheriff.py snowflake <account> --optional-prereqs=network_policy
CREATE NETWORK POLICY IF NOT EXISTS DBSHERIFF_HOST_POLICY
ALLOWED_IP_LIST = ('<dbsheriff_host_public_ip>/32');
When the DBSheriff host's address changes, DBSheriff raises a finding.
Re-run the same command with the new IP. The script reads the policy's
current list (DESCRIBE NETWORK POLICY DBSHERIFF_HOST_POLICY); if the new
address is not already covered by it, it adds <new ip>/32 and prints the
statement it ran:
ALTER NETWORK POLICY DBSHERIFF_HOST_POLICY
SET ALLOWED_IP_LIST = (<every entry already in the list>, '<new ip>/32');
It only ever adds an address. The old one stays allowed (so does any range
you added yourself), so the old host keeps working until you retire its
address by hand with the same ALTER and the list you want to keep. A
re-run with an address the policy already allows (on its own or inside a
range) changes nothing. With --check a policy that lacks the address is
reported as "present but missing this host IP", together with the ALTER
that would fix it. If the address is in the policy's BLOCKED_IP_LIST
(Snowflake applies blocked addresses first) the script stops and leaves
that to you. Never bind your own administrator user to this policy.
| User | Role | What it does | Privileges |
|---|---|---|---|
dbsheriff_monitor |
dbsheriff_monitor_role |
reads query and login history | USAGE, MONITOR on DBSHERIFF_WH; MONITOR on every warehouse; MONITOR USAGE ON ACCOUNT; MONITOR ON USER for every user; USAGE on the monitored database (no SELECT) |
dbsheriff_kill |
dbsheriff_kill_role |
cancels statements, suspends warehouses | USAGE, OPERATE on DBSHERIFF_WH; OPERATE on every warehouse |
dbsheriff_lock |
dbsheriff_lock_role |
disables users, aborts sessions, timeouts / revokes, tag write-back | SECURITYADMIN (it disables the users it owns; a user owned by ACCOUNTADMIN or created by an identity provider's role must be handed over, as ACCOUNTADMIN, with GRANT OWNERSHIP ON USER <u> TO ROLE dbsheriff_lock_role COPY CURRENT GRANTS) or ownership of the users DBSheriff may lock; APPLY on the sensitivity tag; APPLY TAG ON ACCOUNT |
dbsheriff_posture |
dbsheriff_posture_role |
reads account usage and Trust Center for posture and evidence | IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE; SNOWFLAKE.TRUST_CENTER_VIEWER |
dbsheriff_classify |
dbsheriff_classify_role |
samples column values for classification (optional) | SELECT on the tables to scan |
CREATE ROLE IF NOT EXISTS dbsheriff_monitor_role;
CREATE ROLE IF NOT EXISTS dbsheriff_kill_role;
CREATE ROLE IF NOT EXISTS dbsheriff_lock_role;
CREATE ROLE IF NOT EXISTS dbsheriff_posture_role;
CREATE ROLE IF NOT EXISTS dbsheriff_classify_role;
GRANT ROLE dbsheriff_monitor_role, dbsheriff_kill_role, dbsheriff_lock_role,
dbsheriff_posture_role, dbsheriff_classify_role TO ROLE SYSADMIN;
-- CREATE USER IF NOT EXISTS leaves a user that already exists exactly as it is, public key included:
-- for such a user run ALTER USER <name> SET RSA_PUBLIC_KEY = '<public_key_body>'; instead
CREATE USER IF NOT EXISTS dbsheriff_monitor TYPE = SERVICE DEFAULT_ROLE = dbsheriff_monitor_role
DEFAULT_WAREHOUSE = DBSHERIFF_WH RSA_PUBLIC_KEY = '<monitor_public_key_body>';
CREATE USER IF NOT EXISTS dbsheriff_kill TYPE = SERVICE DEFAULT_ROLE = dbsheriff_kill_role
DEFAULT_WAREHOUSE = DBSHERIFF_WH RSA_PUBLIC_KEY = '<kill_public_key_body>';
CREATE USER IF NOT EXISTS dbsheriff_lock TYPE = SERVICE DEFAULT_ROLE = dbsheriff_lock_role
DEFAULT_WAREHOUSE = DBSHERIFF_WH RSA_PUBLIC_KEY = '<lock_public_key_body>';
CREATE USER IF NOT EXISTS dbsheriff_posture TYPE = SERVICE DEFAULT_ROLE = dbsheriff_posture_role
DEFAULT_WAREHOUSE = DBSHERIFF_WH RSA_PUBLIC_KEY = '<posture_public_key_body>';
CREATE USER IF NOT EXISTS dbsheriff_classify TYPE = SERVICE DEFAULT_ROLE = dbsheriff_classify_role
DEFAULT_WAREHOUSE = DBSHERIFF_WH RSA_PUBLIC_KEY = '<classify_public_key_body>';
GRANT ROLE dbsheriff_monitor_role TO USER dbsheriff_monitor;
GRANT ROLE dbsheriff_kill_role TO USER dbsheriff_kill;
GRANT ROLE dbsheriff_lock_role TO USER dbsheriff_lock;
GRANT ROLE dbsheriff_posture_role TO USER dbsheriff_posture;
GRANT ROLE dbsheriff_classify_role TO USER dbsheriff_classify;
ALTER USER dbsheriff_monitor SET NETWORK_POLICY = DBSHERIFF_HOST_POLICY;
ALTER USER dbsheriff_kill SET NETWORK_POLICY = DBSHERIFF_HOST_POLICY;
ALTER USER dbsheriff_lock SET NETWORK_POLICY = DBSHERIFF_HOST_POLICY;
ALTER USER dbsheriff_posture SET NETWORK_POLICY = DBSHERIFF_HOST_POLICY;
ALTER USER dbsheriff_classify SET NETWORK_POLICY = DBSHERIFF_HOST_POLICY;
create_reset_dbuser.py runs these five ALTER USER statements for you
automatically whenever DBSHERIFF_HOST_POLICY already exists — one less
thing to copy by hand if you're using the scripts instead of the static
SQL file above.
<monitored_database> is each database you want covered, never every database of
the account: the scripts grant on the databases you choose (§ The three scripts).
-- monitor: history, no data (with HONEYTOKENS=on the role of the read login is also granted SELECT on each decoy table DBSheriff plants, and on nothing else)
GRANT USAGE, MONITOR ON WAREHOUSE DBSHERIFF_WH TO ROLE dbsheriff_monitor_role;
GRANT USAGE ON DATABASE <monitored_database> TO ROLE dbsheriff_monitor_role;
GRANT MONITOR USAGE ON ACCOUNT TO ROLE dbsheriff_monitor_role; -- statements that run on no warehouse
-- GRANT IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE TO ROLE dbsheriff_monitor_role; -- only when no posture login is registered (the provisioning scripts grant it to both roles)
-- kill
GRANT USAGE, OPERATE ON WAREHOUSE DBSHERIFF_WH TO ROLE dbsheriff_kill_role;
GRANT USAGE ON DATABASE <monitored_database> TO ROLE dbsheriff_kill_role;
-- lock (least privilege alternative: GRANT OWNERSHIP ON USER <u> TO ROLE dbsheriff_lock_role COPY CURRENT GRANTS, per user)
GRANT USAGE ON WAREHOUSE DBSHERIFF_WH TO ROLE dbsheriff_lock_role;
GRANT ROLE SECURITYADMIN TO ROLE dbsheriff_lock_role;
-- posture / compliance evidence
GRANT USAGE ON WAREHOUSE DBSHERIFF_WH TO ROLE dbsheriff_posture_role;
GRANT IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE TO ROLE dbsheriff_posture_role;
GRANT APPLICATION ROLE SNOWFLAKE.TRUST_CENTER_VIEWER TO ROLE dbsheriff_posture_role;
-- decoy tables (only with HONEYTOKENS=on): the role that reads them (the posture role, or the monitor role when no posture login is set) also needs USAGE on
-- the decoy's database and schema, which DBSheriff never grants. Above, the monitor role has the database and neither role has the schema:
-- GRANT USAGE ON DATABASE <monitored_database> TO ROLE dbsheriff_posture_role;
-- GRANT USAGE ON SCHEMA <monitored_database>.<decoy_schema> TO ROLE dbsheriff_posture_role;
-- classification (optional value sampling)
GRANT USAGE ON WAREHOUSE DBSHERIFF_WH TO ROLE dbsheriff_classify_role;
GRANT USAGE ON DATABASE <monitored_database> TO ROLE dbsheriff_classify_role;
GRANT USAGE ON ALL SCHEMAS IN DATABASE <monitored_database> TO ROLE dbsheriff_classify_role;
GRANT SELECT ON ALL TABLES IN DATABASE <monitored_database> TO ROLE dbsheriff_classify_role;
GRANT USAGE ON FUTURE SCHEMAS IN DATABASE <monitored_database> TO ROLE dbsheriff_classify_role;
GRANT SELECT ON FUTURE TABLES IN DATABASE <monitored_database> TO ROLE dbsheriff_classify_role;
Snowflake shows another user's statements only on warehouses the monitor
role can MONITOR, cancels only where the kill role can OPERATE, and
shows logins only for users the role can MONITOR:
DECLARE c1 CURSOR FOR SELECT "name" AS name FROM TABLE(RESULT_SCAN(LAST_QUERY_ID()));
BEGIN
SHOW WAREHOUSES;
FOR w IN c1 DO
EXECUTE IMMEDIATE 'GRANT MONITOR ON WAREHOUSE "' || w.name || '" TO ROLE dbsheriff_monitor_role';
EXECUTE IMMEDIATE 'GRANT OPERATE ON WAREHOUSE "' || w.name || '" TO ROLE dbsheriff_kill_role';
END FOR;
RETURN 'done';
END;
DECLARE c2 CURSOR FOR SELECT "name" AS name FROM TABLE(RESULT_SCAN(LAST_QUERY_ID()));
BEGIN
SHOW USERS;
FOR u IN c2 DO
EXECUTE IMMEDIATE 'GRANT MONITOR ON USER "' || u.name || '" TO ROLE dbsheriff_monitor_role';
END FOR;
RETURN 'done';
END;
DBSheriff's posture checks report any warehouse or user still missing these grants.
CREATE DATABASE IF NOT EXISTS DBSHERIFF_DROP;
CREATE SCHEMA IF NOT EXISTS DBSHERIFF_DROP.GOVERNANCE;
CREATE TAG IF NOT EXISTS DBSHERIFF_DROP.GOVERNANCE.SENSITIVITY
ALLOWED_VALUES 'CRITICAL', 'SENSITIVE', 'INTERNAL', 'PUBLIC';
GRANT USAGE ON DATABASE DBSHERIFF_DROP TO ROLE dbsheriff_lock_role;
GRANT USAGE ON SCHEMA DBSHERIFF_DROP.GOVERNANCE TO ROLE dbsheriff_lock_role;
GRANT APPLY ON TAG DBSHERIFF_DROP.GOVERNANCE.SENSITIVITY TO ROLE dbsheriff_lock_role;
GRANT APPLY TAG ON ACCOUNT TO ROLE dbsheriff_lock_role;
Then attach your masking policy to the tag once; switch the write-back on per account in the Admin Console.
Admin Console → Monitored Servers → Add: engine Snowflake, account
identifier, warehouse DBSHERIFF_WH, poll mode balanced, and the
monitor, kill, lock, posture and classify users with their private keys
pasted as text (leave passwords blank). create_reset_dbuser.py can store
the same keys, and the user names, in DBSheriff's registry for a server that
is already registered. A posture user name saved without a key makes the
posture scan sign in as that user with the monitor's key, which Snowflake
refuses; a classify user with neither a key nor a password is skipped for
value sampling.
Saving does not test the logins. DBSheriff connects with the monitor login when it starts monitoring the server (within the registry reload interval, five minutes), and a login the account refuses shows as a connection error in the producer's log; the kill, lock, posture and classify logins are first used when a kill, lock, posture scan or value sampling runs.
DBSheriff leaves its own accounts out of scoring automatically (it reads them from the server's registration), so nothing needs to be added to the excluded-users list for them.
The Scan databases field of the server (Monitored Servers) decides it:
| Field | The scan reads |
|---|---|
| blank | the registered database alone |
a comma-separated list (ANALYTICS,SALES) |
those databases |
the word ALL |
every database the scan login can see (Snowflake's own, the sample share, SNOWFLAKE_LEARNING_DB, DBSHERIFF_DROP, the personal USER$ databases, imported databases and applications are left out), and the registered one |
Columns found by ALL in a database other than the registered one are
proposed and stay pending until a person approves them: the automatic
approval (a column on which two independent evidence sources agree) does not
apply in a database the scan found by browsing. In the registered database and
in the ones you listed it does, unless CLASSIFICATION_AUTO_APPROVE=false.
The scan reads the catalog with the classification login (dbsheriff_classify),
and falls back to the monitor login only when the classification login cannot
connect. Snowflake's INFORMATION_SCHEMA lists a login only the objects it
holds a privilege on, so the classification role needs USAGE on the
database and its schemas and SELECT on the tables (§ 5 grants exactly that). If no
classification login is registered, the monitor role is used and needs USAGE
on the schemas and REFERENCES on the tables of every database to be scanned,
which the monitor role does not get from the grants above. A database in which
the login sees no table at all (empty, or no such grants) is named in the scan's
error text, and a scan that read nothing anywhere ends FAILED with that reason
instead of completing having read nothing.
Your own administrator login is monitored like any other login of the account,
ACCOUNTADMIN included: DBSheriff scores its statements and can cancel one that
looks risky — a long read of SNOWFLAKE.ACCOUNT_USAGE is enough. Exempting a
login (TRUSTED_SECURE in Privileged Users: alerted on, not cancelled) waits for
a second approver, who is not the one who entered it; on an installation with no
second approver the entry starts 24 hours after it is entered
(RISK_LOWERING_DELAY_H). Settle it before you start using the account for
administration: add a second approver in the Admin Console and classify your
login TRUSTED_SECURE, or use a separate account for administrator work.
setup_dbsheriff.py and create_reset_dbuser.py print this reminder, with your
login name, when they finish.
SHOW GRANTS TO ROLE dbsheriff_monitor_role;
SHOW GRANTS TO ROLE dbsheriff_kill_role;
SHOW WAREHOUSES LIKE 'DBSHERIFF_WH';
SHOW RESOURCE MONITORS LIKE 'DBSHERIFF_RM';
DESCRIBE NETWORK POLICY DBSHERIFF_HOST_POLICY;
Paid Trust Center packages (CIS Benchmarks, Threat Intelligence) are enabled in Snowsight under Monitoring → Trust Center → Scanner Packages; their findings appear in DBSheriff within the next hourly import.
If you will let DBSheriff create standing native controls in this Snowflake account (masks, limits, time-boxed grants) through the database's own features, or write the SQL for your DBA to run, read native controls privileges (inside the package) first: it says what each tier can do, what the optional Control account needs here, and how to remove everything DBSheriff made. The account is created only on request, with create_reset_dbuser.py snowflake <host> --with-control; setup_dbsheriff.py never creates it, and native controls are off until an approver turns a server to script or managed.
| User | Can | Cannot |
|---|---|---|
dbsheriff_monitor, dbsheriff_posture |
Read query, login and session history and the account-usage share (IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE), warehouse and user metadata |
Read table data (the one exception: with HONEYTOKENS=on, the role of the posture or monitor login is granted SELECT on each decoy table DBSheriff plants, and on nothing else), change a user |
dbsheriff_kill |
Cancel any statement and suspend warehouses (OPERATE) |
Read data, change a user |
dbsheriff_lock |
The SECURITYADMIN role: create users and roles, alter, disable or drop the users it owns (the ones SECURITYADMIN or USERADMIN created, or that were handed to it with GRANT OWNERSHIP ON USER), and grant itself any privilege that role can grant — data-access-equivalent. It cannot disable a user that ACCOUNTADMIN or an identity provider's role owns until ownership of that user is given to it: Snowflake refuses with error 003001, and DBSheriff records one LOCK_ERROR that names the statement to run. The same limit applies to ending that user's sessions (abort session): the audit row of that rung names the same statement. A login whose automatic lock failed is not locked again for LOCK_ERROR_BACKOFF_S seconds (60 by default), so a burst of statements opens one connection and writes one LOCK_ERROR, not one per statement. The narrower alternative the setup SQL notes is GRANT OWNERSHIP ON USER <u> TO ROLE dbsheriff_lock_role COPY CURRENT GRANTS per user DBSheriff may lock, which limits it to those users |
Act as ACCOUNTADMIN |
dbsheriff_classify |
SELECT on every table of every database it was granted, including future ones |
Write, change a user |
The connections to Snowflake are HTTPS and always verified; the administrator connection of the provisioning scripts has nothing to choose. DBSheriff keeps these keys encrypted in its registry (SERVER_CREDENTIALS_KEY); whoever can read both the registry and that key holds every user above. Create only the users you will use.