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 {
75 Restart = "on-failure";
76 TimeoutStopSec = 10;
77
78 ExecStart = "${pkgs.promtail}/bin/promtail -config.file=${configFile} ${escapeShellArgs cfg.extraFlags}";
79
80 ProtectSystem = "strict";
81 ProtectHome = true;
82 PrivateTmp = true;
83 PrivateDevices = true;
84 ProtectKernelTunables = true;
85 ProtectControlGroups = true;
86 RestrictSUIDSGID = true;
87 PrivateMounts = true;
88 CacheDirectory = "promtail";
89 ReadWritePaths = lib.optional allowPositionsFile (builtins.dirOf positionsFile);
90
91 User = "promtail";
92 Group = "promtail";
93
94 CapabilityBoundingSet = "";
95 NoNewPrivileges = true;
96
97 ProtectKernelModules = true;
98 SystemCallArchitectures = "native";
99 ProtectKernelLogs = true;
100 ProtectClock = true;
101
102 LockPersonality = true;
103 ProtectHostname = true;
104 RestrictRealtime = true;
105 MemoryDenyWriteExecute = true;
106 PrivateUsers = true;
107
108 SupplementaryGroups = lib.optional (allowSystemdJournal) "systemd-journal";
109 }
110 // (optionalAttrs (!pkgs.stdenv.hostPlatform.isAarch64) {
111 # FIXME: figure out why this breaks on aarch64
112 SystemCallFilter = "@system-service";
113 });
114 };
115
116 users.groups.promtail = { };
117 users.users.promtail = {
118 description = "Promtail service user";
119 isSystemUser = true;
120 group = "promtail";
121 };
122 };
123}