1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7with lib;
8let
9 cfg = config.services.promtail;
10
11 format = pkgs.formats.json { };
12 prettyJSON =
13 conf:
14 with lib;
15 pipe conf [
16 (flip removeAttrs [ "_module" ])
17 (format.generate "promtail-config.json")
18 ];
19
20 allowSystemdJournal =
21 cfg.configuration ? scrape_configs && lib.any (v: v ? journal) cfg.configuration.scrape_configs;
22
23 allowPositionsFile = !lib.hasPrefix "/var/cache/promtail" positionsFile;
24 positionsFile = cfg.configuration.positions.filename;
25
26 configFile = if cfg.configFile != null then cfg.configFile else prettyJSON cfg.configuration;
27
28in
29{
30 options.services.promtail = with types; {
31 enable = mkEnableOption "the Promtail ingresser";
32
33 configuration = mkOption {
34 type = format.type;
35 description = ''
36 Specify the configuration for Promtail in Nix.
37 This option will be ignored if `services.promtail.configFile` is defined.
38 '';
39 };
40
41 configFile = mkOption {
42 type = nullOr path;
43 default = null;
44 description = ''
45 Config file path for Promtail.
46 If this option is defined, the value of `services.promtail.configuration` will be ignored.
47 '';
48 };
49
50 extraFlags = mkOption {
51 type = listOf str;
52 default = [ ];
53 example = [ "--server.http-listen-port=3101" ];
54 description = ''
55 Specify a list of additional command line flags,
56 which get escaped and are then passed to Loki.
57 '';
58 };
59 };
60
61 config = mkIf cfg.enable {
62 services.promtail.configuration.positions.filename = mkDefault "/var/cache/promtail/positions.yaml";
63
64 systemd.services.promtail = {
65 description = "Promtail log ingress";
66 wantedBy = [ "multi-user.target" ];
67 stopIfChanged = false;
68
69 preStart = ''
70 ${lib.getExe pkgs.promtail} -config.file=${configFile} -check-syntax
71 '';
72
73 serviceConfig = {
74 Restart = "on-failure";
75 TimeoutStopSec = 10;
76
77 ExecStart = "${pkgs.promtail}/bin/promtail -config.file=${configFile} ${escapeShellArgs cfg.extraFlags}";
78
79 ProtectSystem = "strict";
80 ProtectHome = true;
81 PrivateTmp = true;
82 PrivateDevices = true;
83 ProtectKernelTunables = true;
84 ProtectControlGroups = true;
85 RestrictSUIDSGID = true;
86 PrivateMounts = true;
87 CacheDirectory = "promtail";
88 ReadWritePaths = lib.optional allowPositionsFile (builtins.dirOf positionsFile);
89
90 User = "promtail";
91 Group = "promtail";
92
93 CapabilityBoundingSet = "";
94 NoNewPrivileges = true;
95
96 ProtectKernelModules = true;
97 SystemCallArchitectures = "native";
98 ProtectKernelLogs = true;
99 ProtectClock = true;
100
101 LockPersonality = true;
102 ProtectHostname = true;
103 RestrictRealtime = true;
104 MemoryDenyWriteExecute = true;
105 PrivateUsers = true;
106
107 SupplementaryGroups = lib.optional (allowSystemdJournal) "systemd-journal";
108 }
109 // (optionalAttrs (!pkgs.stdenv.hostPlatform.isAarch64) {
110 # FIXME: figure out why this breaks on aarch64
111 SystemCallFilter = "@system-service";
112 });
113 };
114
115 users.groups.promtail = { };
116 users.users.promtail = {
117 description = "Promtail service user";
118 isSystemUser = true;
119 group = "promtail";
120 };
121 };
122}