1{
2 config,
3 pkgs,
4 lib,
5 utils,
6 ...
7}:
8let
9 inherit (lib)
10 escapeShellArgs
11 getBin
12 hasPrefix
13 literalExpression
14 mkBefore
15 mkEnableOption
16 mkIf
17 mkOption
18 mkPackageOption
19 optionalString
20 types
21 ;
22 cfg = config.services.victorialogs;
23 startCLIList = [
24 "${cfg.package}/bin/victoria-logs"
25 "-storageDataPath=/var/lib/${cfg.stateDir}"
26 "-httpListenAddr=${cfg.listenAddress}"
27 ]
28 ++ lib.optionals (cfg.basicAuthUsername != null) [
29 "-httpAuth.username=${cfg.basicAuthUsername}"
30 ]
31 ++ lib.optionals (cfg.basicAuthPasswordFile != null) [
32 "-httpAuth.password=file://%d/basic_auth_password"
33 ];
34in
35{
36 options.services.victorialogs = {
37 enable = mkEnableOption "VictoriaLogs is an open source user-friendly database for logs from VictoriaMetrics";
38 package = mkPackageOption pkgs "victorialogs" { };
39 listenAddress = mkOption {
40 default = ":9428";
41 type = types.str;
42 description = ''
43 TCP address to listen for incoming http requests.
44 '';
45 };
46 stateDir = mkOption {
47 type = types.str;
48 default = "victorialogs";
49 description = ''
50 Directory below `/var/lib` to store VictoriaLogs data.
51 This directory will be created automatically using systemd's StateDirectory mechanism.
52 '';
53 };
54 basicAuthUsername = lib.mkOption {
55 default = null;
56 type = lib.types.nullOr lib.types.str;
57 description = ''
58 Basic Auth username used to protect VictoriaLogs instance by authorization
59 '';
60 };
61
62 basicAuthPasswordFile = lib.mkOption {
63 default = null;
64 type = lib.types.nullOr lib.types.str;
65 description = ''
66 File that contains the Basic Auth password used to protect VictoriaLogs instance by authorization
67 '';
68 };
69 extraOptions = mkOption {
70 type = types.listOf types.str;
71 default = [ ];
72 example = literalExpression ''
73 [
74 "-loggerLevel=WARN"
75 ]
76 '';
77 description = ''
78 Extra options to pass to VictoriaLogs. See {command}`victoria-logs -help` for
79 possible options.
80 '';
81 };
82 };
83 config = mkIf cfg.enable {
84
85 assertions = [
86 {
87 assertion =
88 (cfg.basicAuthUsername == null && cfg.basicAuthPasswordFile == null)
89 || (cfg.basicAuthUsername != null && cfg.basicAuthPasswordFile != null);
90 message = "Both basicAuthUsername and basicAuthPasswordFile must be set together to enable basicAuth functionality, or neither should be set.";
91 }
92 ];
93
94 systemd.services.victorialogs = {
95 description = "VictoriaLogs logs database";
96 wantedBy = [ "multi-user.target" ];
97 after = [ "network.target" ];
98 startLimitBurst = 5;
99
100 serviceConfig = {
101 ExecStart = lib.concatStringsSep " " [
102 (escapeShellArgs startCLIList)
103 (utils.escapeSystemdExecArgs cfg.extraOptions)
104 ];
105 DynamicUser = true;
106 LoadCredential = lib.optional (
107 cfg.basicAuthPasswordFile != null
108 ) "basic_auth_password:${cfg.basicAuthPasswordFile}";
109 RestartSec = 1;
110 Restart = "on-failure";
111 RuntimeDirectory = "victorialogs";
112 RuntimeDirectoryMode = "0700";
113 StateDirectory = cfg.stateDir;
114 StateDirectoryMode = "0700";
115
116 # Hardening
117 DeviceAllow = [ "/dev/null rw" ];
118 DevicePolicy = "strict";
119 LockPersonality = true;
120 MemoryDenyWriteExecute = true;
121 NoNewPrivileges = true;
122 PrivateDevices = true;
123 PrivateTmp = true;
124 PrivateUsers = true;
125 ProtectClock = true;
126 ProtectControlGroups = true;
127 ProtectHome = true;
128 ProtectHostname = true;
129 ProtectKernelLogs = true;
130 ProtectKernelModules = true;
131 ProtectKernelTunables = true;
132 ProtectProc = "invisible";
133 ProtectSystem = "full";
134 RemoveIPC = true;
135 RestrictAddressFamilies = [
136 "AF_INET"
137 "AF_INET6"
138 "AF_UNIX"
139 ];
140 RestrictNamespaces = true;
141 RestrictRealtime = true;
142 RestrictSUIDSGID = true;
143 SystemCallArchitectures = "native";
144 SystemCallFilter = [
145 "@system-service"
146 "~@privileged"
147 ];
148 };
149
150 postStart =
151 let
152 bindAddr = (optionalString (hasPrefix ":" cfg.listenAddress) "127.0.0.1") + cfg.listenAddress;
153 in
154 mkBefore ''
155 until ${getBin pkgs.curl}/bin/curl -s -o /dev/null http://${bindAddr}/ping; do
156 sleep 1;
157 done
158 '';
159 };
160 };
161}