summaryrefslogtreecommitdiff
path: root/hosts/surtr/postgresql/default.nix
diff options
context:
space:
mode:
Diffstat (limited to 'hosts/surtr/postgresql/default.nix')
-rw-r--r--hosts/surtr/postgresql/default.nix239
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, ... }:
2let
3 versioning = sources.psql-versioning.src;
4in {
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}