grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / web-apps / miniflux.nix
blobb733ceec74dbe3dfdee474ba3db38f90b13f29b0
1 { config, lib, pkgs, ... }:
3 let
4   inherit (lib) mkEnableOption mkPackageOption mkOption types literalExpression mkIf mkDefault;
5   cfg = config.services.miniflux;
7   defaultAddress = "localhost:8080";
9   pgbin = "${config.services.postgresql.package}/bin";
10   preStart = pkgs.writeScript "miniflux-pre-start" ''
11     #!${pkgs.runtimeShell}
12     ${pgbin}/psql "miniflux" -c "CREATE EXTENSION IF NOT EXISTS hstore"
13   '';
17   options = {
18     services.miniflux = {
19       enable = mkEnableOption "miniflux";
21       package = mkPackageOption pkgs "miniflux" { };
23       createDatabaseLocally = mkOption {
24         type = types.bool;
25         default = true;
26         description = ''
27           Whether a PostgreSQL database should be automatically created and
28           configured on the local host. If set to `false`, you need provision a
29           database yourself and make sure to create the hstore extension in it.
30         '';
31       };
33       config = mkOption {
34         type = with types; attrsOf (oneOf [ str int ]);
35         example = literalExpression ''
36           {
37             CLEANUP_FREQUENCY = 48;
38             LISTEN_ADDR = "localhost:8080";
39           }
40         '';
41         description = ''
42           Configuration for Miniflux, refer to
43           <https://miniflux.app/docs/configuration.html>
44           for documentation on the supported values.
46           Correct configuration for the database is already provided.
47           By default, listens on ${defaultAddress}.
48         '';
49       };
51       adminCredentialsFile = mkOption {
52         type = types.nullOr types.path;
53         default = null;
54         description = ''
55           File containing the ADMIN_USERNAME and
56           ADMIN_PASSWORD (length >= 6) in the format of
57           an EnvironmentFile=, as described by systemd.exec(5).
58         '';
59         example = "/etc/nixos/miniflux-admin-credentials";
60       };
61     };
62   };
64   config = mkIf cfg.enable {
65     assertions = [
66       { assertion = cfg.config.CREATE_ADMIN == 0 || cfg.adminCredentialsFile != null;
67         message = "services.miniflux.adminCredentialsFile must be set if services.miniflux.config.CREATE_ADMIN is 1";
68       }
69     ];
70     services.miniflux.config = {
71       LISTEN_ADDR = mkDefault defaultAddress;
72       DATABASE_URL = lib.mkIf cfg.createDatabaseLocally "user=miniflux host=/run/postgresql dbname=miniflux";
73       RUN_MIGRATIONS = 1;
74       CREATE_ADMIN = lib.mkDefault 1;
75       WATCHDOG = 1;
76     };
78     services.postgresql = lib.mkIf cfg.createDatabaseLocally {
79       enable = true;
80       ensureUsers = [ {
81         name = "miniflux";
82         ensureDBOwnership = true;
83       } ];
84       ensureDatabases = [ "miniflux" ];
85     };
87     systemd.services.miniflux-dbsetup = lib.mkIf cfg.createDatabaseLocally {
88       description = "Miniflux database setup";
89       requires = [ "postgresql.service" ];
90       after = [ "network.target" "postgresql.service" ];
91       serviceConfig = {
92         Type = "oneshot";
93         User = config.services.postgresql.superUser;
94         ExecStart = preStart;
95       };
96     };
98     systemd.services.miniflux = {
99       description = "Miniflux service";
100       wantedBy = [ "multi-user.target" ];
101       requires = lib.optional cfg.createDatabaseLocally "miniflux-dbsetup.service";
102       after = [ "network.target" ]
103         ++ lib.optionals cfg.createDatabaseLocally [ "postgresql.service" "miniflux-dbsetup.service" ];
105       serviceConfig = {
106         Type = "notify";
107         ExecStart = lib.getExe cfg.package;
108         User = "miniflux";
109         DynamicUser = true;
110         RuntimeDirectory = "miniflux";
111         RuntimeDirectoryMode = "0750";
112         EnvironmentFile = lib.mkIf (cfg.adminCredentialsFile != null) cfg.adminCredentialsFile;
113         WatchdogSec = 60;
114         WatchdogSignal = "SIGKILL";
115         Restart = "always";
116         RestartSec = 5;
118         # Hardening
119         CapabilityBoundingSet = [ "" ];
120         DeviceAllow = [ "" ];
121         LockPersonality = true;
122         MemoryDenyWriteExecute = true;
123         PrivateDevices = true;
124         PrivateUsers = true;
125         ProcSubset = "pid";
126         ProtectClock = true;
127         ProtectControlGroups = true;
128         ProtectHome = true;
129         ProtectHostname = true;
130         ProtectKernelLogs = true;
131         ProtectKernelModules = true;
132         ProtectKernelTunables = true;
133         ProtectProc = "invisible";
134         RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
135         RestrictNamespaces = true;
136         RestrictRealtime = true;
137         RestrictSUIDSGID = true;
138         SystemCallArchitectures = "native";
139         SystemCallFilter = [ "@system-service" "~@privileged" ];
140         UMask = "0077";
141       };
143       environment = lib.mapAttrs (_: toString) cfg.config;
144     };
145     environment.systemPackages = [ cfg.package ];
147     security.apparmor.policies."bin.miniflux".profile = ''
148       include <tunables/global>
149       ${cfg.package}/bin/miniflux {
150         include <abstractions/base>
151         include <abstractions/nameservice>
152         include <abstractions/ssl_certs>
153         include "${pkgs.apparmorRulesFromClosure { name = "miniflux"; } cfg.package}"
154         r ${cfg.package}/bin/miniflux,
155         r @{sys}/kernel/mm/transparent_hugepage/hpage_pmd_size,
156         rw /run/miniflux/**,
157       }
158     '';
159   };