diff options
Diffstat (limited to 'hosts/surtr/postgresql/default.nix')
| -rw-r--r-- | hosts/surtr/postgresql/default.nix | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/hosts/surtr/postgresql/default.nix b/hosts/surtr/postgresql/default.nix new file mode 100644 index 00000000..9cf494ae --- /dev/null +++ b/hosts/surtr/postgresql/default.nix | |||
| @@ -0,0 +1,239 @@ | |||
| 1 | { pkgs, sources, config, flake, ... }: | ||
| 2 | let | ||
| 3 | versioning = sources.psql-versioning.src; | ||
| 4 | in { | ||
| 5 | config = { | ||
| 6 | services.postgresql = { | ||
| 7 | enable = true; | ||
| 8 | package = pkgs.postgresql_14; | ||
| 9 | }; | ||
| 10 | |||
| 11 | services.pgbackrest = { | ||
| 12 | enable = true; | ||
| 13 | settings = { | ||
| 14 | "surtr" = { | ||
| 15 | pg1-path = config.services.postgresql.dataDir; | ||
| 16 | |||
| 17 | repo1-path = "/var/lib/pgbackrest"; | ||
| 18 | repo1-retention-full-type = "time"; | ||
| 19 | repo1-retention-full = 7; | ||
| 20 | repo1-retention-archive = 2; | ||
| 21 | |||
| 22 | repo2-host-type = "tls"; | ||
| 23 | repo2-host = "pgbackrest.vidhar.yggdrasil"; | ||
| 24 | repo2-host-ca-file = toString ../../vidhar/pgbackrest/ca/ca.crt; | ||
| 25 | repo2-host-cert-file = toString ./pgbackrest.crt; | ||
| 26 | repo2-host-key-file = config.sops.secrets."pgbackrest.key".path; | ||
| 27 | repo2-retention-full-type = "time"; | ||
| 28 | repo2-retention-full = 14; | ||
| 29 | repo2-retention-archive = 7; | ||
| 30 | }; | ||
| 31 | |||
| 32 | "global" = { | ||
| 33 | compress-type = "zst"; | ||
| 34 | compress-level = 9; | ||
| 35 | |||
| 36 | archive-async = true; | ||
| 37 | spool-path = "/var/spool/pgbackrest"; | ||
| 38 | }; | ||
| 39 | |||
| 40 | "global:server" = { | ||
| 41 | tls-server-address = "2a03:4000:52:ada:1::"; | ||
| 42 | tls-server-ca-file = toString ../../vidhar/pgbackrest/ca/ca.crt; | ||
| 43 | tls-server-cert-file = toString ./pgbackrest.crt; | ||
| 44 | tls-server-key-file = config.sops.secrets."pgbackrest.key".path; | ||
| 45 | tls-server-auth = ["vidhar.yggdrasil=surtr"]; | ||
| 46 | }; | ||
| 47 | |||
| 48 | "global:archive-push" = { | ||
| 49 | process-max = 2; | ||
| 50 | }; | ||
| 51 | "global:archive-get" = { | ||
| 52 | process-max = 2; | ||
| 53 | }; | ||
| 54 | }; | ||
| 55 | |||
| 56 | tlsServer.enable = true; | ||
| 57 | |||
| 58 | backups."surtr-daily" = { | ||
| 59 | stanza = "surtr"; | ||
| 60 | repo = "1"; | ||
| 61 | timerConfig.OnCalendar = "daily"; | ||
| 62 | }; | ||
| 63 | }; | ||
| 64 | |||
| 65 | sops.secrets."pgbackrest.key" = { | ||
| 66 | format = "binary"; | ||
| 67 | sopsFile = ./pgbackrest.key; | ||
| 68 | owner = "postgres"; | ||
| 69 | group = "postgres"; | ||
| 70 | mode = "0400"; | ||
| 71 | }; | ||
| 72 | |||
| 73 | systemd.tmpfiles.rules = [ | ||
| 74 | "d /var/lib/pgbackrest 0750 postgres postgres - -" | ||
| 75 | "d /var/spool/pgbackrest 0750 postgres postgres - -" | ||
| 76 | ]; | ||
| 77 | |||
| 78 | systemd.services.migrate-postgresql = { | ||
| 79 | after = [ "postgresql.service" ]; | ||
| 80 | bindsTo = [ "postgresql.service" ]; | ||
| 81 | wantedBy = [ "postgresql.service" ]; | ||
| 82 | |||
| 83 | serviceConfig = { | ||
| 84 | Type = "oneshot"; | ||
| 85 | inherit (config.systemd.services.postgresql.serviceConfig) User Group; | ||
| 86 | RemainAfterExit = true; | ||
| 87 | }; | ||
| 88 | |||
| 89 | path = [ config.services.postgresql.package ]; | ||
| 90 | script = '' | ||
| 91 | psql postgres postgres -eXf ${pkgs.writeText "schema.sql" '' | ||
| 92 | CREATE DATABASE "matrix-synapse" WITH TEMPLATE "template0" ENCODING "UTF8" LOCALE "C"; | ||
| 93 | CREATE DATABASE "email" WITH TEMPLATE "template0" ENCODING "UTF8" LOCALE "C"; | ||
| 94 | CREATE DATABASE "etebase" WITH TEMPLATE "template0" ENCODING "UTF8" LOCALE "C"; | ||
| 95 | ''} | ||
| 96 | |||
| 97 | psql matrix-synapse postgres -eXf ${pkgs.writeText "matrix-synapse.sql" '' | ||
| 98 | \i ${versioning + "/install.versioning.sql"} | ||
| 99 | |||
| 100 | BEGIN; | ||
| 101 | SELECT _v.register_patch('000-matrix-users', null, null); | ||
| 102 | |||
| 103 | CREATE USER "matrix-synapse"; | ||
| 104 | GRANT ALL PRIVILEGES ON DATABASE "matrix-synapse" TO "matrix-synapse"; | ||
| 105 | GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "matrix-synapse"; | ||
| 106 | COMMIT; | ||
| 107 | ''} | ||
| 108 | |||
| 109 | psql email postgres -eXf ${pkgs.writeText "email.sql" '' | ||
| 110 | \i ${versioning + "/install.versioning.sql"} | ||
| 111 | |||
| 112 | BEGIN; | ||
| 113 | SELECT _v.register_patch('000-users', null, null); | ||
| 114 | |||
| 115 | CREATE USER "postfix"; | ||
| 116 | GRANT CONNECT ON DATABASE "email" TO "postfix"; | ||
| 117 | ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO "postfix"; | ||
| 118 | CREATE USER "dovecot2"; | ||
| 119 | GRANT CONNECT ON DATABASE "email" TO "dovecot2"; | ||
| 120 | ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO "dovecot2"; | ||
| 121 | COMMIT; | ||
| 122 | |||
| 123 | BEGIN; | ||
| 124 | SELECT _v.register_patch('001-spm', null, null); | ||
| 125 | |||
| 126 | CREATE USER "spm"; | ||
| 127 | GRANT CONNECT ON DATABASE "email" TO "spm"; | ||
| 128 | ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES to "spm"; | ||
| 129 | COMMIT; | ||
| 130 | |||
| 131 | BEGIN; | ||
| 132 | SELECT _v.register_patch('000-base', null, null); | ||
| 133 | |||
| 134 | CREATE TABLE mailbox ( | ||
| 135 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
| 136 | mailbox text NOT NULL CONSTRAINT mailbox_non_empty CHECK (mailbox <> '''), | ||
| 137 | quota_bytes bigint CONSTRAINT quota_bytes_positive CHECK (CASE WHEN quota_bytes IS NOT NULL THEN quota_bytes > 0 ELSE true END), | ||
| 138 | CONSTRAINT mailbox_unique UNIQUE (mailbox) | ||
| 139 | ); | ||
| 140 | CREATE TABLE mailbox_mapping ( | ||
| 141 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
| 142 | local text CONSTRAINT local_non_empty CHECK (local IS DISTINCT FROM '''), | ||
| 143 | domain text NOT NULL CONSTRAINT domain_non_empty CHECK (domain <> '''), | ||
| 144 | mailbox uuid REFERENCES mailbox(id), | ||
| 145 | CONSTRAINT local_domain_unique UNIQUE (local, domain) | ||
| 146 | ); | ||
| 147 | CREATE UNIQUE INDEX domain_unique ON mailbox_mapping (domain) WHERE local IS NULL; | ||
| 148 | |||
| 149 | CREATE VIEW virtual_mailbox_domain (domain) AS SELECT DISTINCT domain FROM mailbox_mapping; | ||
| 150 | CREATE VIEW virtual_mailbox_mapping (mailbox, lookup) AS SELECT mailbox.mailbox as mailbox, (CASE WHEN local IS NULL THEN ''' ELSE local END) || '@' || domain AS lookup FROM mailbox_mapping INNER JOIN mailbox on mailbox.id = mailbox_mapping.mailbox; | ||
| 151 | |||
| 152 | CREATE VIEW imap_user ("user", quota_rule) AS SELECT mailbox AS "user", (CASE WHEN quota_bytes IS NULL THEN '*:ignore' ELSE '*:bytes=' || quota_bytes END) AS quota_rule FROM mailbox; | ||
| 153 | COMMIT; | ||
| 154 | |||
| 155 | BEGIN; | ||
| 156 | SELECT _v.register_patch('001-lmtp-mapping', ARRAY['000-base'], null); | ||
| 157 | |||
| 158 | CREATE VIEW lmtp_mapping ("user", quota_rule, local, domain) AS SELECT mailbox.mailbox AS "user", (CASE WHEN quota_bytes IS NULL THEN '*:ignore' ELSE '*:bytes=' || quota_bytes END) AS quota_rule, local, domain FROM mailbox INNER JOIN mailbox_mapping ON mailbox.id = mailbox_mapping.mailbox; | ||
| 159 | COMMIT; | ||
| 160 | |||
| 161 | BEGIN; | ||
| 162 | SELECT _v.register_patch('002-citext', ARRAY['000-base'], null); | ||
| 163 | |||
| 164 | DROP VIEW virtual_mailbox_domain; | ||
| 165 | DROP VIEW virtual_mailbox_mapping; | ||
| 166 | DROP VIEW imap_user; | ||
| 167 | DROP VIEW lmtp_mapping; | ||
| 168 | |||
| 169 | CREATE EXTENSION citext; | ||
| 170 | |||
| 171 | ALTER TABLE mailbox ALTER mailbox TYPE citext; | ||
| 172 | ALTER TABLE mailbox_mapping ALTER local TYPE citext; | ||
| 173 | ALTER TABLE mailbox_mapping ALTER domain TYPE citext; | ||
| 174 | |||
| 175 | CREATE VIEW mailbox_quota_rule (id, mailbox, quota_rule) AS SELECT id, mailbox, (CASE WHEN quota_bytes IS NULL THEN '*:ignore' ELSE '*:bytes=' || quota_bytes END) AS quota_rule FROM mailbox; | ||
| 176 | |||
| 177 | CREATE VIEW virtual_mailbox_domain (domain) AS SELECT DISTINCT domain FROM mailbox_mapping; | ||
| 178 | CREATE VIEW virtual_mailbox_mapping (lookup) AS SELECT (CASE WHEN local IS NULL THEN ''' ELSE local END) || '@' || domain AS lookup FROM mailbox_mapping; | ||
| 179 | CREATE VIEW imap_user ("user", quota_rule) AS SELECT mailbox AS "user", quota_rule FROM mailbox_quota_rule; | ||
| 180 | CREATE VIEW lmtp_mapping ("user", quota_rule, local, domain) AS SELECT mailbox_quota_rule.mailbox AS "user", quota_rule, local, domain FROM mailbox_quota_rule INNER JOIN mailbox_mapping ON mailbox_quota_rule.id = mailbox_mapping.mailbox; | ||
| 181 | COMMIT; | ||
| 182 | |||
| 183 | BEGIN; | ||
| 184 | SELECT _v.register_patch('003-extensions', ARRAY['000-base', '002-citext'], null); | ||
| 185 | |||
| 186 | ALTER TABLE mailbox_mapping ADD COLUMN extension citext CHECK (CASE WHEN extension IS NOT NULL THEN extension NOT LIKE '%+%' ELSE true END); | ||
| 187 | |||
| 188 | DROP VIEW virtual_mailbox_mapping; | ||
| 189 | DROP VIEW lmtp_mapping; | ||
| 190 | |||
| 191 | CREATE VIEW virtual_mailbox_mapping (lookup) AS SELECT (CASE WHEN local IS NULL THEN ''' ELSE local END) || (CASE WHEN extension IS NULL THEN ''' ELSE '+' || extension END) || '@' || domain AS lookup FROM mailbox_mapping WHERE mailbox IS NOT NULL; | ||
| 192 | CREATE VIEW virtual_mailbox_access (lookup, action) AS SELECT (CASE WHEN local IS NULL THEN ''' ELSE local END) || (CASE WHEN extension IS NULL THEN ''' ELSE '+' || extension END) || '@' || domain AS lookup, CASE WHEN mailbox IS NULL THEN 'REJECT' ELSE 'DUNNO' END AS action FROM mailbox_mapping; | ||
| 193 | CREATE VIEW lmtp_mapping ("user", quota_rule, local, extension, domain) AS SELECT mailbox_quota_rule.mailbox AS "user", quota_rule, local, extension, domain FROM mailbox_quota_rule INNER JOIN mailbox_mapping ON mailbox_quota_rule.id = mailbox_mapping.mailbox; | ||
| 194 | COMMIT; | ||
| 195 | |||
| 196 | BEGIN; | ||
| 197 | SELECT _v.register_patch('004-cascade', ARRAY['000-base'], null); | ||
| 198 | |||
| 199 | ALTER TABLE mailbox_mapping DROP CONSTRAINT mailbox_mapping_mailbox_fkey; | ||
| 200 | ALTER TABLE mailbox_mapping ADD CONSTRAINT mailbox_mapping_mailbox_fkey FOREIGN KEY (mailbox) REFERENCES mailbox(id) ON DELETE CASCADE ON UPDATE RESTRICT; | ||
| 201 | COMMIT; | ||
| 202 | |||
| 203 | BEGIN; | ||
| 204 | SELECT _v.register_patch('005-spm', ARRAY['000-base', '002-citext', '003-extensions'], null); | ||
| 205 | |||
| 206 | GRANT INSERT ON "mailbox_mapping" TO "spm"; | ||
| 207 | COMMIT; | ||
| 208 | |||
| 209 | BEGIN; | ||
| 210 | SELECT _v.register_patch('006-spm-mailbox', ARRAY['000-base'], null); | ||
| 211 | |||
| 212 | GRANT SELECT ON ALL TABLES IN SCHEMA public TO "spm"; | ||
| 213 | COMMIT; | ||
| 214 | |||
| 215 | BEGIN; | ||
| 216 | SELECT _v.register_patch('007-ccert-sender-policy', ARRAY['000-base'], null); | ||
| 217 | |||
| 218 | CREATE USER "postfix-ccert-sender-policy"; | ||
| 219 | GRANT CONNECT ON DATABASE "email" TO "postfix-ccert-sender-policy"; | ||
| 220 | ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO "postfix-ccert-sender-policy"; | ||
| 221 | GRANT SELECT ON ALL TABLES IN SCHEMA public TO "postfix-ccert-sender-policy"; | ||
| 222 | COMMIT; | ||
| 223 | ''} | ||
| 224 | |||
| 225 | psql etebase postgres -eXf ${pkgs.writeText "etebase.sql" '' | ||
| 226 | \i ${versioning + "/install.versioning.sql"} | ||
| 227 | |||
| 228 | BEGIN; | ||
| 229 | SELECT _v.register_patch('000-user', null, null); | ||
| 230 | |||
| 231 | CREATE USER "etebase"; | ||
| 232 | GRANT ALL PRIVILEGES ON DATABASE "etebase" TO "etebase"; | ||
| 233 | GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "etebase"; | ||
| 234 | COMMIT; | ||
| 235 | ''} | ||
| 236 | ''; | ||
| 237 | }; | ||
| 238 | }; | ||
| 239 | } | ||
