]> git.immae.eu Git - perso/Immae/Config/Nix.git/blob - modules/private/websites/tools/cryptpad/farm.nix
Add cryptpad farm
[perso/Immae/Config/Nix.git] / modules / private / websites / tools / cryptpad / farm.nix
1 { pkgs, config, lib, ... }:
2 let
3 cfg = config.myServices.tools.cryptpad.farm;
4 toService = name:
5 let
6 inherit (cfg.hosts.${name}) package config;
7 in {
8 description = "Cryptpad ${name} Service";
9 wantedBy = [ "multi-user.target" ];
10 after = [ "networking.target" ];
11 serviceConfig = {
12 User = "cryptpad";
13 Group = "cryptpad";
14 Environment = [
15 "CRYPTPAD_CONFIG=${config}"
16 "HOME=%S/cryptpad/${name}"
17 ];
18 ExecStart = "${package}/bin/cryptpad";
19 PrivateTmp = true;
20 Restart = "always";
21 StateDirectory = "cryptpad/${name}";
22 WorkingDirectory = "%S/cryptpad/${name}";
23 };
24 };
25 toVhostRoot = name: "${cfg.hosts.${name}.package}/lib/node_modules/cryptpad";
26 toVhost = name:
27 let
28 inherit (cfg.hosts.${name}) package domain port;
29 api_domain = domain;
30 files_domain = domain;
31 in ''
32 RewriteEngine On
33
34 Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
35 Header set X-XSS-Protection "1; mode=block"
36 Header set X-Content-Type-Options "nosniff"
37 Header set Access-Control-Allow-Origin "*"
38 Header set Permissions-Policy "interest-cohort=()"
39
40 Header set Cross-Origin-Resource-Policy "cross-origin"
41 <If "%{REQUEST_URI} =~ m#^/(sheet|presentation|doc)/.*$#">
42 Header set Cross-Origin-Opener-Policy "same-origin"
43 </If>
44 Header set Cross-Origin-Embedder-Policy "require-corp"
45
46 ErrorDocument 404 /customize.dist/404.html
47
48 <If "%{QUERY_STRING} =~ m#ver=.*?#">
49 Header set Cache-Control "max-age=31536000"
50 </If>
51 <If "%{REQUEST_URI} =~ m#^/.*(\/|\.html)$#">
52 Header set Cache-Control "no-cache"
53 </If>
54
55 SetEnv styleSrc "'unsafe-inline' 'self' ${domain}"
56 SetEnv connectSrc "'self' https://${domain} ${domain} https://${api_domain} blob: wss://${api_domain} ${api_domain} ${files_domain}"
57 SetEnv fontSrc "'self' data: ${domain}"
58 SetEnv imgSrc "'self' data: * blob: ${domain}"
59 SetEnv frameSrc "'self' blob:"
60 SetEnv mediaSrc "'self' data: * blob: ${domain}"
61 SetEnv childSrc "https://${domain}"
62 SetEnv workerSrc "https://${domain}"
63 SetEnv scriptSrc "'self' 'unsafe-eval' 'unsafe-inline' resource: ${domain}"
64
65 Header set Content-Security-Policy "default-src 'none'; child-src %{childSrc}e; worker-src %{workerSrc}e; media-src %{mediaSrc}e; style-src %{styleSrc}e; script-src %{scriptSrc}e; connect-src %{connectSrc}e; font-src %{fontSrc}e; img-src %{imgSrc}e; frame-src %{frameSrc}e;"
66
67 RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
68 RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
69 RewriteRule .* ws://localhost:${toString port}%{REQUEST_URI} [P,NE,QSA,L]
70
71 RewriteRule ^/customize/(.*)$ /customize.dist/$1 [L]
72
73 ProxyPassMatch "^/(api/(config|broadcast).*)$" "http://localhost:${toString port}/$1"
74 ProxyPassReverse /api http://localhost:${toString port}/api
75 ProxyPreserveHost On
76 RequestHeader set X-Real-IP %{REMOTE_ADDR}s
77
78 Alias /blob /var/lib/cryptpad/${name}/blob
79 <Directory /var/lib/cryptpad/${name}/blob>
80 Require all granted
81 AllowOverride None
82 </Directory>
83 Alias /block /var/lib/cryptpad/${name}/block
84 <Directory /var/lib/cryptpad/${name}/block>
85 Require all granted
86 AllowOverride None
87 </Directory>
88 <LocationMatch /blob/>
89 Header set Cache-Control "max-age=31536000"
90 Header set Access-Control-Allow-Origin "*"
91 Header set Access-Control-Allow-Methods "GET, POST, OPTIONS"
92 Header set Access-Control-Allow-Headers "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Content-Length"
93 Header set Access-Control-Expose-Headers "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range,Content-Length"
94
95 RewriteCond %{REQUEST_METHOD} OPTIONS
96 RewriteRule ^(.*)$ $1 [R=204,L]
97 </LocationMatch>
98
99 <LocationMatch /block/>
100 Header set Cache-Control "max-age=0"
101 </locationMatch>
102
103 RewriteRule ^/(register|login|settings|user|pad|drive|poll|slide|code|whiteboard|file|media|profile|contacts|todo|filepicker|debug|kanban|sheet|support|admin|notifications|teams|calendar|presentation|doc)$ $1/ [R=302,L]
104
105 RewriteCond %{DOCUMENT_ROOT}/www/%{REQUEST_URI} -f
106 RewriteRule (.*) /www/$1 [L]
107
108 RewriteCond %{DOCUMENT_ROOT}/www/%{REQUEST_URI}/index.html -f
109 RewriteRule (.*) /www/$1/index.html [L]
110
111 RewriteCond %{DOCUMENT_ROOT}/customize.dist/%{REQUEST_URI} -f
112 RewriteRule (.*) /customize.dist/$1 [L]
113
114 <Directory ${package}/lib/node_modules/cryptpad/www>
115 AllowOverride None
116 Require all granted
117 DirectoryIndex index.html
118 </Directory>
119 <Directory ${package}/lib/node_modules/cryptpad/customize.dist>
120 AllowOverride None
121 Require all granted
122 DirectoryIndex index.html
123 </Directory>
124 '';
125 in
126 {
127 options.myServices.tools.cryptpad.farm = {
128 hosts = lib.mkOption {
129 default = {};
130 description = "Hosts to install";
131 type = lib.types.attrsOf (lib.types.submodule {
132 options = {
133 port = lib.mkOption {
134 type = lib.types.port;
135 };
136 package = lib.mkOption {
137 type = lib.types.package;
138 description = "Cryptpad package to use";
139 default = pkgs.cryptpad;
140 };
141 domain = lib.mkOption {
142 type = lib.types.str;
143 description = "Domain for main host";
144 };
145 config = lib.mkOption {
146 type = lib.types.path;
147 description = "Path to configuration";
148 };
149 };
150 });
151 };
152 vhosts = lib.mkOption {
153 description = "Instance vhosts configs";
154 readOnly = true;
155 type = lib.types.attrsOf lib.types.str;
156 default = lib.genAttrs (builtins.attrNames cfg.hosts) toVhost;
157 };
158 vhostRoots = lib.mkOption {
159 description = "Instance vhosts document roots";
160 readOnly = true;
161 type = lib.types.attrsOf lib.types.path;
162 default = lib.genAttrs (builtins.attrNames cfg.hosts) toVhostRoot;
163 };
164 };
165 config = {
166 users.users = lib.optionalAttrs (cfg.hosts != {}) {
167 cryptpad = {
168 uid = config.ids.uids.cryptpad;
169 group = "cryptpad";
170 description = "Cryptpad user";
171 };
172 };
173 users.groups = lib.optionalAttrs (cfg.hosts != {}) {
174 cryptpad = {
175 gid = config.ids.gids.cryptpad;
176 };
177 };
178 systemd.services = lib.listToAttrs (map (n: lib.nameValuePair "cryptpad-${n}" (toService n)) (builtins.attrNames cfg.hosts));
179 };
180 }