1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8let
9 cfg = config.services.shiori;
10in
11{
12 options = {
13 services.shiori = {
14 enable = lib.mkEnableOption "Shiori simple bookmarks manager";
15
16 package = lib.mkPackageOption pkgs "shiori" { };
17
18 address = lib.mkOption {
19 type = lib.types.str;
20 default = "";
21 description = ''
22 The IP address on which Shiori will listen.
23 If empty, listens on all interfaces.
24 '';
25 };
26
27 port = lib.mkOption {
28 type = lib.types.port;
29 default = 8080;
30 description = "The port of the Shiori web application";
31 };
32
33 webRoot = lib.mkOption {
34 type = lib.types.str;
35 default = "/";
36 example = "/shiori";
37 description = "The root of the Shiori web application";
38 };
39
40 environmentFile = lib.mkOption {
41 type = lib.types.nullOr lib.types.path;
42 default = null;
43 example = "/path/to/environmentFile";
44 description = ''
45 Path to file containing environment variables.
46 Useful for passing down secrets.
47 <https://github.com/go-shiori/shiori/blob/master/docs/Configuration.md#overall-configuration>
48 '';
49 };
50
51 databaseUrl = lib.mkOption {
52 type = lib.types.nullOr lib.types.str;
53 default = null;
54 example = "postgres:///shiori?host=/run/postgresql";
55 description = "The connection URL to connect to MySQL or PostgreSQL";
56 };
57 };
58 };
59
60 config = lib.mkIf cfg.enable {
61 systemd.services.shiori = {
62 description = "Shiori simple bookmarks manager";
63 wantedBy = [ "multi-user.target" ];
64 after = [
65 "postgresql.service"
66 "mysql.service"
67 ];
68 environment =
69 {
70 SHIORI_DIR = "/var/lib/shiori";
71 }
72 // lib.optionalAttrs (cfg.databaseUrl != null) {
73 SHIORI_DATABASE_URL = cfg.databaseUrl;
74 };
75
76 serviceConfig = {
77 ExecStart = "${cfg.package}/bin/shiori server --address '${cfg.address}' --port '${toString cfg.port}' --webroot '${cfg.webRoot}'";
78
79 DynamicUser = true;
80 StateDirectory = "shiori";
81 # As the RootDirectory
82 RuntimeDirectory = "shiori";
83
84 # Security options
85 EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile;
86 BindReadOnlyPaths =
87 [
88 "/nix/store"
89
90 # For SSL certificates, and the resolv.conf
91 "/etc"
92 ]
93 ++ lib.optional (
94 config.services.postgresql.enable
95 && cfg.databaseUrl != null
96 && lib.strings.hasPrefix "postgres://" cfg.databaseUrl
97 ) "/run/postgresql"
98 ++ lib.optional (
99 config.services.mysql.enable
100 && cfg.databaseUrl != null
101 && lib.strings.hasPrefix "mysql://" cfg.databaseUrl
102 ) "/var/run/mysqld";
103
104 CapabilityBoundingSet = "";
105
106 DeviceAllow = "";
107
108 LockPersonality = true;
109
110 MemoryDenyWriteExecute = true;
111
112 PrivateDevices = true;
113 PrivateUsers = true;
114
115 ProtectClock = true;
116 ProtectControlGroups = true;
117 ProtectHome = true;
118 ProtectHostname = true;
119 ProtectKernelLogs = true;
120 ProtectKernelModules = true;
121 ProtectKernelTunables = true;
122
123 RestrictNamespaces = true;
124 RestrictAddressFamilies = [
125 "AF_INET"
126 "AF_INET6"
127 "AF_UNIX"
128 ];
129 RestrictRealtime = true;
130 RestrictSUIDSGID = true;
131
132 RootDirectory = "/run/shiori";
133
134 SystemCallArchitectures = "native";
135 SystemCallErrorNumber = "EPERM";
136 SystemCallFilter = [
137 "@system-service"
138 "~@cpu-emulation"
139 "~@debug"
140 "~@keyring"
141 "~@memlock"
142 "~@obsolete"
143 "~@privileged"
144 "~@setuid"
145 ];
146 };
147 };
148 };
149
150 meta.maintainers = with lib.maintainers; [
151 minijackson
152 CaptainJawZ
153 ];
154}