diff --git a/migrations/db/migrations/20221207154255_create_pgsodium_and_vault.sql b/migrations/db/migrations/20221207154255_create_pgsodium_and_vault.sql new file mode 100644 index 000000000..3d3867ab5 --- /dev/null +++ b/migrations/db/migrations/20221207154255_create_pgsodium_and_vault.sql @@ -0,0 +1,40 @@ +-- migrate:up + +DO $$ +DECLARE + pgsodium_exists boolean; + vault_exists boolean; +BEGIN + pgsodium_exists = ( + select count(*) = 1 + from pg_available_extensions + where name = 'pgsodium' + and default_version in ('3.1.6', '3.1.7', '3.1.8', '3.1.9') + ); + + vault_exists = ( + select count(*) = 1 + from pg_available_extensions + where name = 'supabase_vault' + ); + + IF pgsodium_exists + THEN + create extension if not exists pgsodium; + + grant pgsodium_keyiduser to postgres with admin option; + grant pgsodium_keyholder to postgres with admin option; + grant pgsodium_keymaker to postgres with admin option; + + grant execute on function pgsodium.crypto_aead_det_decrypt(bytea, bytea, uuid, bytea) to service_role; + grant execute on function pgsodium.crypto_aead_det_encrypt(bytea, bytea, uuid, bytea) to service_role; + grant execute on function pgsodium.crypto_aead_det_keygen to service_role; + + IF vault_exists + THEN + create extension if not exists supabase_vault; + END IF; + END IF; +END $$; + +-- migrate:down diff --git a/migrations/db/migrations/20221207154255_create_vault.sql b/migrations/db/migrations/20221207154255_create_vault.sql deleted file mode 100644 index d4f714182..000000000 --- a/migrations/db/migrations/20221207154255_create_vault.sql +++ /dev/null @@ -1,17 +0,0 @@ --- migrate:up - -DO $$ -BEGIN - IF EXISTS (select from pg_available_extensions where name = 'supabase_vault') - THEN - create extension if not exists supabase_vault; - - -- for some reason extension custom scripts aren't run during AMI build, so - -- we manually run it here - grant usage on schema vault to postgres with grant option; - grant select, delete on vault.secrets, vault.decrypted_secrets to postgres with grant option; - grant execute on function vault.create_secret, vault.update_secret, vault._crypto_aead_det_decrypt to postgres with grant option; - END IF; -END $$; - --- migrate:down diff --git a/migrations/db/migrations/20230529180330_alter_api_roles_for_inherit.sql b/migrations/db/migrations/20230529180330_alter_api_roles_for_inherit.sql index 013a074fd..4df82e3f4 100644 --- a/migrations/db/migrations/20230529180330_alter_api_roles_for_inherit.sql +++ b/migrations/db/migrations/20230529180330_alter_api_roles_for_inherit.sql @@ -4,5 +4,7 @@ ALTER ROLE authenticated inherit; ALTER ROLE anon inherit; ALTER ROLE service_role inherit; +GRANT pgsodium_keyholder to service_role; + -- migrate:down diff --git a/migrations/schema-15.sql b/migrations/schema-15.sql index cb031f797..1bff8b9d8 100644 --- a/migrations/schema-15.sql +++ b/migrations/schema-15.sql @@ -44,6 +44,27 @@ CREATE SCHEMA graphql_public; CREATE SCHEMA pgbouncer; +-- +-- Name: pgsodium; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA pgsodium; + + +-- +-- Name: pgsodium; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pgsodium WITH SCHEMA pgsodium; + + +-- +-- Name: EXTENSION pgsodium; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pgsodium IS 'Pgsodium is a modern cryptography library for Postgres.'; + + -- -- Name: realtime; Type: SCHEMA; Schema: -; Owner: - -- @@ -553,6 +574,28 @@ END $$; +-- +-- Name: secrets_encrypt_secret_secret(); Type: FUNCTION; Schema: vault; Owner: - +-- + +CREATE FUNCTION vault.secrets_encrypt_secret_secret() RETURNS trigger + LANGUAGE plpgsql + AS $$ + BEGIN + new.secret = CASE WHEN new.secret IS NULL THEN NULL ELSE + CASE WHEN new.key_id IS NULL THEN NULL ELSE pg_catalog.encode( + pgsodium.crypto_aead_det_encrypt( + pg_catalog.convert_to(new.secret, 'utf8'), + pg_catalog.convert_to((new.id::text || new.description::text || new.created_at::text || new.updated_at::text)::text, 'utf8'), + new.key_id::uuid, + new.nonce + ), + 'base64') END END; + RETURN new; + END; + $$; + + SET default_tablespace = ''; SET default_table_access_method = heap; @@ -739,6 +782,30 @@ CREATE TABLE storage.objects ( ); +-- +-- Name: decrypted_secrets; Type: VIEW; Schema: vault; Owner: - +-- + +CREATE VIEW vault.decrypted_secrets AS + SELECT secrets.id, + secrets.name, + secrets.description, + secrets.secret, + CASE + WHEN (secrets.secret IS NULL) THEN NULL::text + ELSE + CASE + WHEN (secrets.key_id IS NULL) THEN NULL::text + ELSE convert_from(pgsodium.crypto_aead_det_decrypt(decode(secrets.secret, 'base64'::text), convert_to(((((secrets.id)::text || secrets.description) || (secrets.created_at)::text) || (secrets.updated_at)::text), 'utf8'::name), secrets.key_id, secrets.nonce), 'utf8'::name) + END + END AS decrypted_secret, + secrets.key_id, + secrets.nonce, + secrets.created_at, + secrets.updated_at + FROM vault.secrets; + + -- -- Name: refresh_tokens id; Type: DEFAULT; Schema: auth; Owner: - -- diff --git a/migrations/schema-orioledb-17.sql b/migrations/schema-orioledb-17.sql index 7026b99e3..531970c37 100644 --- a/migrations/schema-orioledb-17.sql +++ b/migrations/schema-orioledb-17.sql @@ -45,6 +45,27 @@ CREATE SCHEMA graphql_public; CREATE SCHEMA pgbouncer; +-- +-- Name: pgsodium; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA pgsodium; + + +-- +-- Name: pgsodium; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS pgsodium WITH SCHEMA pgsodium; + + +-- +-- Name: EXTENSION pgsodium; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION pgsodium IS 'Pgsodium is a modern cryptography library for Postgres.'; + + -- -- Name: realtime; Type: SCHEMA; Schema: -; Owner: - -- @@ -568,6 +589,28 @@ END $$; +-- +-- Name: secrets_encrypt_secret_secret(); Type: FUNCTION; Schema: vault; Owner: - +-- + +CREATE FUNCTION vault.secrets_encrypt_secret_secret() RETURNS trigger + LANGUAGE plpgsql + AS $$ + BEGIN + new.secret = CASE WHEN new.secret IS NULL THEN NULL ELSE + CASE WHEN new.key_id IS NULL THEN NULL ELSE pg_catalog.encode( + pgsodium.crypto_aead_det_encrypt( + pg_catalog.convert_to(new.secret, 'utf8'), + pg_catalog.convert_to((new.id::text || new.description::text || new.created_at::text || new.updated_at::text)::text, 'utf8'), + new.key_id::uuid, + new.nonce + ), + 'base64') END END; + RETURN new; + END; + $$; + + SET default_tablespace = ''; SET default_table_access_method = orioledb; @@ -754,6 +797,30 @@ CREATE TABLE storage.objects ( ); +-- +-- Name: decrypted_secrets; Type: VIEW; Schema: vault; Owner: - +-- + +CREATE VIEW vault.decrypted_secrets AS + SELECT id, + name, + description, + secret, + CASE + WHEN (secret IS NULL) THEN NULL::text + ELSE + CASE + WHEN (key_id IS NULL) THEN NULL::text + ELSE convert_from(pgsodium.crypto_aead_det_decrypt(decode(secret, 'base64'::text), convert_to(((((id)::text || description) || (created_at)::text) || (updated_at)::text), 'utf8'::name), key_id, nonce), 'utf8'::name) + END + END AS decrypted_secret, + key_id, + nonce, + created_at, + updated_at + FROM vault.secrets; + + -- -- Name: refresh_tokens id; Type: DEFAULT; Schema: auth; Owner: - -- diff --git a/migrations/tests/database/privs.sql b/migrations/tests/database/privs.sql index ea4f1318a..d164e0cb1 100644 --- a/migrations/tests/database/privs.sql +++ b/migrations/tests/database/privs.sql @@ -2,6 +2,10 @@ SELECT database_privs_are( 'postgres', 'postgres', ARRAY['CONNECT', 'TEMPORARY', 'CREATE'] ); +SELECT function_privs_are('pgsodium', 'crypto_aead_det_decrypt', array['bytea', 'bytea', 'uuid', 'bytea'], 'service_role', array['EXECUTE']); +SELECT function_privs_are('pgsodium', 'crypto_aead_det_encrypt', array['bytea', 'bytea', 'uuid', 'bytea'], 'service_role', array['EXECUTE']); +SELECT function_privs_are('pgsodium', 'crypto_aead_det_keygen', array[]::text[], 'service_role', array['EXECUTE']); + -- Verify public schema privileges SELECT schema_privs_are('public', 'postgres', array['CREATE', 'USAGE']); SELECT schema_privs_are('public', 'anon', array['USAGE']);