this repo has no description
1{
2 pkgs,
3 lib,
4 config,
5 ...
6}:
7let
8 cfg = config.services.fluxer;
9 systemd_hardenning = {
10 CapabilityBoundingSet = [
11 "" # empty entry forces allow-list instead of deny-list
12 ];
13
14 # mounts entire filesystem read-only (except /dev, /proc, /sys)
15 # different options="full", true, false
16 # ProtectSystem = "strict";
17
18 # makes /home/, /root and /run/user inaccessible
19 # different options="read-only", "tmpfs", false
20 ProtectHome = true;
21
22 # special directories for various purposes do exist, but defaulting them to something "more secure" is not universally possible.
23 # RuntimeDirectory=, StateDirectory=, CacheDirectory=, LogsDirectory=, ConfigurationDirectory=
24 # RuntimeDirectoryMode=, StateDirectoryMode=, CacheDirectoryMode=, LogsDirectoryMode=, ConfigurationDirectoryMode=
25
26 # It might make sense to default /nix/store to ReadOnly
27 # It might also make sense to default everything to NoExec except /run/wrappers/bin and /nix/store. However, doing so is quite radical.
28 # ReadWritePaths=, ReadOnlyPaths=, InaccessiblePaths=, ExecPaths=, NoExecPaths=
29
30 # create a private tmp directory for the process
31 # different options="disconnected" (makes a tmpfs), false
32 # implies a writable tmp path
33 # PrivateTmp = true;
34
35 # PrivateDevices = true; # block direct hardware access. Allows pseudo-devices like /dev/null and /dev/shm
36
37 # sets a new network namespace with only loopback device
38 # limits AF_UNIX and AF_NETLINK access to only services running in a joint namespace
39 # implies PrivateMounts
40 # PrivateNetwork = true;
41
42 # PrivateIPC exists, but has various complicated implications and implicit effects
43 # PrivateIPC=
44
45 # PrivatePIDs exists to define process namespacing. However, it does not work with service type `Forking` and should be used with care.
46 # implies MountAPIVFS=true
47 # mounts /proc in a way such that only processes in the same namespace are visible
48 # PrivatePIDs = true;
49
50 # Use user namespacing
51 # WARNING=this makes the CaCapabilityBoundingSet only affect the user namespace, the service runs completely unprivileged on the host. Use with care!
52 # PrivateUsers = true;
53
54 # protect the hostname from being changed.
55 # WARNING=even though nixos uses /etc/hostname to define and change hostname, this option also prevents services from simply detecting hostname changes.
56 # ProtectHostname = true;
57
58 # prevent modifications and state checks to the system clock.
59 # implies removal of CAP_SYS_TIME and CAP_WAKE_ALARM
60 # implies block clock set system calls
61 # implies DeviceAllow=char-rtc r
62 # ProtectClock = true;
63
64 # read-only=/proc/sys/, /sys/, /proc/sysrq-trigger, /proc/latency_stats, /proc/acpi, /proc/timer_stats, /proc/fs, /proc/irq
65 # inaccessible=/proc/kallsyms, /proc/kcore
66 # implies MountAPIVFS=true
67 # Does not prevent callbacks to other processes/services (e.g. via IPC) from setting kernel tunables
68 # ProtectKernelTunables = true;
69
70 # disable explicit kernel module loading
71 # implies removal of CAP_SYS_MODULE
72 # implies inaccessible /usr/lib/modules
73 # ProtectKernelModules = true;
74
75 # dines access to kernel log ring buffer
76 # implies removal of CAP_SYSLOG
77 # implies inaccessible /dev/kmsg and /proc/kmsg
78 # ProtectKernelLogs = true;
79
80 # true or strict disables write access to control group hierarchies
81 # strict or private makes the unit run in a cgroup namespace with private /sys/fs/cgroup/
82 # different options="private", true, false
83 # ProtectControlGroups = "strict";
84
85 # disable specific access for socket system call
86 # different options=list of AF_UNIX, AF_INET AF_INET6, AF_NETLINK, AF_PACKET
87 # RestrictAddressFamilies = "none";
88
89 # restrict access to namespacing
90 # different options=false, [ "pid" "user" "net" "uts" "mnt" "cgroup" "ipc" ]
91 # see `man namespaces` for a full list
92 # RestrictNamespaces = true;
93
94 # LockPersonality = true; # prevent service from initiating changes to its execution domain
95
96 # prevent creating memory mappings that are writable and executable simultaneously
97 # WARNING=breaks with JIT execution engines
98 # MemoryDenyWriteExecute = true;
99
100 # RestrictRealtime = true; # prevent service from requesting realtime scheduling
101
102 # RestrictSUIDSGID = true; # prevent setting suid/guid bit on files. Does NOT prevent existing suid/sgid binaries from being executed!
103
104 # PrivateMounts can be used to set mount namespaces. However, debugging issues caused by this is quite complex.
105 # PrivateMounts= MountFlags=
106
107 # SystemCallFilter = [ "@system-service" ]; # sensible default, works for most services. Prevents e.g. "@clock", "@mount", "@swap", "@reboot"
108 # SystemCallErrorNumber = "EPERM"; # services violating the SystemSystemCallFilter are killed silently by default. This makes debugging easier.
109
110 # SystemCallArchitectures = "native"; # allow only native system calls for defined SystemCallFilters
111
112 # prevents access to /proc/<pid> of other processes
113 # options="noaccess", "invisible", "ptraceable", "default"
114 # ProtectProc = "invisible";
115
116 # ProcSubset can be set to "pid" to prevent access to non-process files in /proc.
117 # However, this prevents a lot of linux kernel API calls and should not be default
118 # ProcSubset
119
120 # NoNewPrivileges = true; # prevents service process and children from gaining privileges via execve
121
122 # use a dynamic user for this service instead of running as root
123 # WARNING=breaks dbus
124 DynamicUser = true;
125 };
126in
127{
128 options = {
129 services.fluxer = {
130 enable = lib.mkEnableOption "Enable selfhosted Fluxer";
131 data_dir = lib.mkOption {
132 type = lib.types.str;
133 default = "/var/lib/fluxer";
134 };
135 user = {
136 name = lib.mkOption {
137 type = lib.types.str;
138 default = "fluxer";
139 };
140 uid = lib.mkOption {
141 type = lib.types.int;
142 default = 3200;
143 };
144 };
145 group = {
146 name = lib.mkOption {
147 type = lib.types.str;
148 default = "fluxer";
149 };
150 gid = lib.mkOption {
151 type = lib.types.int;
152 default = 320;
153 };
154 };
155 api = {
156 port = lib.mkOption {
157 type = lib.types.int;
158 default = 8080;
159 };
160 media_proxy_host = lib.mkOption {
161 type = lib.types.str;
162 default = "127.0.0.1";
163 };
164 };
165 };
166 };
167
168 config = lib.mkIf cfg.enable {
169 users = {
170 users = {
171 "${cfg.user.name}" = {
172 isSystemUser = true;
173 uid = cfg.user.uid;
174 group = cfg.group.name;
175 home = cfg.data_dir;
176 };
177 };
178
179 groups = {
180 "${cfg.group.name}" = {
181 name = cfg.group.name;
182 gid = cfg.group.gid;
183 members = [ "${cfg.user.name}" ];
184 };
185 };
186 };
187
188 systemd = {
189 tmpfiles.rules = [
190 # Home dir
191 "d ${cfg.data_dir} 0770 ${cfg.user.name} ${cfg.group.name} - -"
192 ];
193
194 services =
195 let
196 # These are passed to all rust services according to the compose prod
197 # rust_env = [
198 # "FLUXER_S3_ACCESS_KEY_ID=52da2eb541a4d09ea427f2638d5d0dcd"
199 # "FLUXER_S3_SECRET_ACCESS_KEY=728502847641b38f27490ffde79d5d0c2c0978c68ef9c7aba7aa927c3a02d78b"
200 # "FLUXER_MEDIA_PROXY_SECRET_KEY=0fbd7b6a65aa972db3e71e3f48eeae37e06611d6e586706aeac96177e446e950"
201 # "FLUXER_ADMIN_SECRET_KEY_BASE=4372971d8a51d4d97141230577b5c33e695b27b9817c6961cb4ca9737677cf67"
202 # "FLUXER_ADMIN_OAUTH_CLIENT_SECRET=3f16b4b188d331be27fd4123bab720e8b63a96af6584b8f7627609f3f3fd3327"
203 # "FLUXER_MARKETING_SECRET_KEY_BASE=c1093dee2774ed33cbd5cdc3071f977bea68f5555d0207d2962e200097fe50c6"
204 # "FLUXER_MEILISEARCH_API_KEY=114153e9b71fe5643e87e6fcb249e73ddf3fc009e64a67449a4c580601d66046"
205 # "FLUXER_AUTH_SUDO_MODE_SECRET=ceddbb2b3ef813e2c3794801b098c359a6e7fbae1a5594b7e340681383007a70"
206 # "FLUXER_AUTH_CONNECTION_INITIATION_SECRET=d5ff35ceb029c549706bcce340159fcecb7eba4e060985184d835bc6479df1a4"
207 # "FLUXER_AUTH_BAN_VIEW_SECRET=b00d8c94857e2a964d391aeda78c2ccffa270c27dc44ef1c32d54966af087237"
208 # "FLUXER_EMAIL_SMTP_PASSWORD=64ea128ee6635e61896cb4ea2fc0451d"
209 # "FLUXER_NATS_AUTH_TOKEN=180168dea004f3cac53eff7a5c913cc22295c0607e1558260280f6beb4b2d536"
210 # "FLUXER_CONFIG_ENCRYPTION_KEY=b1deb0e47f47c775e1adbb5f023bf00b58ea74aab26ad3f1137d68c9d248f88d"
211 # "FLUXER_SETUP_WIZARD_SECRET=2bdb2540a8f82e3847e3fc391ff9016da975bd4582a3756f314a99de51a4e7a5"
212 # "FLUXER_VOICE_API_KEY=4fb89ff7501307991fbbd1fd3fa5350e"
213 # "FLUXER_VOICE_API_SECRET=41ead73a6620928143627d18fba348490faa7a00ad4b4b17180916145669b0c8"
214 # ];
215
216 base_env = [
217 "FLUXER_ENV=production"
218 "NODE_ENV=production"
219 "FLUXER_VERSION=latest"
220 "FLUXER_DATABASE_BACKEND=postgres"
221 "FLUXER_NATS_CORE_URL=nats://127.0.0.1:4222"
222 "FLUXER_NATS_JETSTREAM_URL=nats://127.0.0.1:4222"
223 "FLUXER_POSTGRES_URL=postgresql://fluxer@127.0.0.1:5432/fluxer"
224 "FLUXER_CONFIG_ENCRYPTION_KEY=b1deb0e47f47c775e1adbb5f023bf00b58ea74aab26ad3f1137d68c9d248f88d"
225 "FLUXER_S3_ACCESS_KEY_ID=52da2eb541a4d09ea427f2638d5d0dcd"
226 "FLUXER_S3_SECRET_ACCESS_KEY=728502847641b38f27490ffde79d5d0c2c0978c68ef9c7aba7aa927c3a02d78b"
227 "FLUXER_SETUP_WIZARD_SECRET=2bdb2540a8f82e3847e3fc391ff9016da975bd4582a3756f314a99de51a4e7a5"
228 "FLUXER_VOICE_API_KEY=4fb89ff7501307991fbbd1fd3fa5350e"
229 "FLUXER_VOICE_API_SECRET=41ead73a6620928143627d18fba348490faa7a00ad4b4b17180916145669b0c8"
230 "FLUXER_VOICE_URL=ws://localhost:7880"
231 "FLUXER_INGRESS_DOMAIN=localhost"
232 ];
233 in
234 {
235 fluxer_migrate = {
236 description = "Fluxer's migrations";
237 wantedBy = [ "multi-user.target" ];
238 after = [
239 "network.target"
240 "postgresql.service"
241 "postgresql-setup.service"
242 ];
243 script = ''
244 ${pkgs.fluxer_server}/bin/aggregate-services/fluxer_migrations
245 '';
246 serviceConfig = systemd_hardenning // {
247 Type = "simple";
248 StateDirectory = "${cfg.data_dir}";
249 ReadWritePaths = [ "${cfg.data_dir}" ];
250
251 Environment = base_env ++ [
252 "FLUXER_S3_ENDPOINT=http://127.0.0.1:8333"
253 "FLUXER_MEILISEARCH_URL=http://127.0.0.1:7702"
254 "FLUXER_MEILISEARCH_API_KEY=114153e9b71fe5643e87e6fcb249e73ddf3fc009e64a67449a4c580601d66046"
255 ];
256 SupplementaryGroups = [
257 "fluxer"
258 ];
259 };
260 };
261
262 fluxer_server = {
263 description = "Fluxer's aggregate";
264 wantedBy = [ "multi-user.target" ];
265 after = [
266 "network.target"
267 "fluxer_migrate.service"
268 "nats.service"
269 "postgresql.service"
270 "meilisearch.service"
271 ];
272 script = ''
273 ${pkgs.fluxer_server}/bin/fluxer_aggregate
274 '';
275 serviceConfig = systemd_hardenning // {
276 Type = "simple";
277 StateDirectory = "${cfg.data_dir}";
278 ReadWritePaths = [ "${cfg.data_dir}" ];
279 # BindPaths = [
280 # "${pkgs.fluxer_ingress}/source:/build/source"
281 # ];
282 Environment = base_env ++ [
283 "FLUXER_S3_ENDPOINT=http://127.0.0.1:8333"
284 "FLUXER_MEILISEARCH_URL=http://127.0.0.1:7702"
285 "FLUXER_MEILISEARCH_API_KEY=114153e9b71fe5643e87e6fcb249e73ddf3fc009e64a67449a4c580601d66046"
286 # "FLUXER_AGGREGATE_SERVICE_MODE=AllInOne"
287 "FLUXER_KV_STORE_STREAM_REPLICAS=1"
288 "FLUXER_NATS_CORE_URL=nats://127.0.0.1:4222"
289 "FLUXER_INGRESS_LISTEN=0.0.0.0:7721"
290
291 # THIS DISABLE'S LOCAL TLS FEATURES eventually should be fluxer.awoo.ren
292 # "FLUXER_INGRESS_DOMAIN=localhost"
293 "FLUXER_SPA_ENABLED=true"
294 # "FLUXER_APP_ASSETS_DIR=${pkgs.fluxer_ingress}/assets"
295 # "FLUXER_INGRESS_ROUTES_FILE=${pkgs.fluxer_ingress}/ingress-routes.json"
296 "FLUXER_CSP_SCRIPT_ORIGINS=*"
297 "FLUXER_CSP_STYLE_ORIGINS=*"
298 "FLUXER_CSP_IMG_ORIGINS=*"
299 "FLUXER_CSP_MEDIA_ORIGINS=*"
300 "FLUXER_CSP_FONT_ORIGINS=*"
301 "FLUXER_CSP_CONNECT_ORIGINS=*"
302 "FLUXER_CSP_FRAME_ORIGINS=*"
303 "FLUXER_CSP_WORKER_ORIGINS=*"
304 "FLUXER_CSP_MANIFEST_ORIGINS=*"
305 "RUST_BACKTRACE=1"
306 ];
307 SupplementaryGroups = [
308 "fluxer"
309 ];
310 };
311 };
312
313 fluxer_api = {
314 description = "Fluxer's api";
315 wantedBy = [ "multi-user.target" ];
316 after = [
317 "network.target"
318 "fluxer_server.service"
319 "fluxer_migrate.service"
320 "nats.service"
321 ];
322 script = ''
323 ${pkgs.fluxer_api}/bin/fluxer_api
324 '';
325 path = with pkgs; [
326 bash
327 pnpm
328 ];
329 serviceConfig = systemd_hardenning // {
330 Type = "simple";
331 StateDirectory = "${cfg.data_dir}";
332 ReadWritePaths = [ "${cfg.data_dir}" ];
333
334 Environment = base_env ++ [
335 "PNPM_HOME=${cfg.data_dir}/.local/share/pnpm"
336 ];
337 SupplementaryGroups = [
338 "fluxer"
339 ];
340 };
341 };
342
343 # fluxer_ingress = {
344 # description = "Fluxer's app ingress";
345 # wantedBy = [ "multi-user.target" ];
346 # after = [
347 # "network.target"
348 # "fluxer_api.service"
349 # "fluxer_aggregate.service"
350 # "nats.service"
351 # ];
352 # requires = [
353 # "fluxer_api.service"
354 # "fluxer_aggregate.service"
355 # ];
356 # script = ''
357 # ${pkgs.fluxer_ingress}/bin/fluxer_ingress
358 # '';
359 # serviceConfig = systemd_hardenning // {
360 # Type = "simple";
361 # StateDirectory = "${cfg.data_dir}";
362 # ReadWritePaths = [ "${cfg.data_dir}" ];
363 # Environment = [
364 # "FLUXER_NATS_CORE_URL=nats://127.0.0.1:4222"
365 # "FLUXER_INGRESS_LISTEN=0.0.0.0:7721"
366 # # THIS DISABLE'S LOCAL TLS FEATURES eventually should be fluxer.awoo.ren
367 # # "FLUXER_INGRESS_DOMAIN=localhost"
368 # "FLUXER_SPA_ENABLED=true"
369 # "FLUXER_APP_ASSETS_DIR=${pkgs.fluxer_ingress}/assets"
370 # "FLUXER_INGRESS_ROUTES_FILE=${pkgs.fluxer_ingress}/ingress-routes.json"
371 # "FLUXER_CSP_SCRIPT_ORIGINS=*"
372 # "FLUXER_CSP_STYLE_ORIGINS=*"
373 # "FLUXER_CSP_IMG_ORIGINS=*"
374 # "FLUXER_CSP_MEDIA_ORIGINS=*"
375 # "FLUXER_CSP_FONT_ORIGINS=*"
376 # "FLUXER_CSP_CONNECT_ORIGINS=*"
377 # "FLUXER_CSP_FRAME_ORIGINS=*"
378 # "FLUXER_CSP_WORKER_ORIGINS=*"
379 # "FLUXER_CSP_MANIFEST_ORIGINS=*"
380 # ];
381 # SupplementaryGroups = [
382 # "fluxer"
383 # ];
384 # };
385 # };
386 };
387 };
388 };
389}