1{ config, lib, pkgs, ... }:
2
3let
4 cfg = config.services.journald.remote;
5 format = pkgs.formats.systemd;
6
7 cliArgs = lib.cli.toGNUCommandLineShell { } {
8 inherit (cfg) output;
9 # "-3" specifies the file descriptor from the .socket unit.
10 "listen-${cfg.listen}" = "-3";
11 };
12in
13{
14 meta.maintainers = [ lib.maintainers.raitobezarius ];
15 options.services.journald.remote = {
16 enable = lib.mkEnableOption "receiving systemd journals from the network";
17
18 listen = lib.mkOption {
19 default = "https";
20 type = lib.types.enum [ "https" "http" ];
21 description = ''
22 Which protocol to listen to.
23 '';
24 };
25
26 output = lib.mkOption {
27 default = "/var/log/journal/remote/";
28 type = lib.types.str;
29 description = ''
30 The location of the output journal.
31
32 In case the output file is not specified, journal files will be created
33 underneath the selected directory. Files will be called
34 {file}`remote-hostname.journal`, where the `hostname` part is the
35 escaped hostname of the source endpoint of the connection, or the
36 numerical address if the hostname cannot be determined.
37 '';
38 };
39
40 port = lib.mkOption {
41 default = 19532;
42 type = lib.types.port;
43 description = ''
44 The port to listen to.
45
46 Note that this option is used only if
47 {option}`services.journald.upload.listen` is configured to be either
48 "https" or "http".
49 '';
50 };
51
52 settings = lib.mkOption {
53 default = { };
54
55 description = ''
56 Configuration in the journal-remote configuration file. See
57 {manpage}`journal-remote.conf(5)` for available options.
58 '';
59
60 type = lib.types.submodule {
61 freeformType = format.type;
62
63 options.Remote = {
64 Seal = lib.mkOption {
65 default = false;
66 example = true;
67 type = lib.types.bool;
68 description = ''
69 Periodically sign the data in the journal using Forward Secure
70 Sealing.
71 '';
72 };
73
74 SplitMode = lib.mkOption {
75 default = "host";
76 example = "none";
77 type = lib.types.enum [ "host" "none" ];
78 description = ''
79 With "host", a separate output file is used, based on the
80 hostname of the other endpoint of a connection. With "none", only
81 one output journal file is used.
82 '';
83 };
84
85 ServerKeyFile = lib.mkOption {
86 default = "/etc/ssl/private/journal-remote.pem";
87 type = lib.types.str;
88 description = ''
89 A path to a SSL secret key file in PEM format.
90
91 Note that due to security reasons, `systemd-journal-remote` will
92 refuse files from the world-readable `/nix/store`. This file
93 should be readable by the "" user.
94
95 This option can be used with `listen = "https"`. If the path
96 refers to an `AF_UNIX` stream socket in the file system a
97 connection is made to it and the key read from it.
98 '';
99 };
100
101 ServerCertificateFile = lib.mkOption {
102 default = "/etc/ssl/certs/journal-remote.pem";
103 type = lib.types.str;
104 description = ''
105 A path to a SSL certificate file in PEM format.
106
107 This option can be used with `listen = "https"`. If the path
108 refers to an `AF_UNIX` stream socket in the file system a
109 connection is made to it and the certificate read from it.
110 '';
111 };
112
113 TrustedCertificateFile = lib.mkOption {
114 default = "/etc/ssl/ca/trusted.pem";
115 type = lib.types.str;
116 description = ''
117 A path to a SSL CA certificate file in PEM format, or `all`.
118
119 If `all` is set, then client certificate checking will be
120 disabled.
121
122 This option can be used with `listen = "https"`. If the path
123 refers to an `AF_UNIX` stream socket in the file system a
124 connection is made to it and the certificate read from it.
125 '';
126 };
127 };
128 };
129 };
130 };
131
132 config = lib.mkIf cfg.enable {
133 systemd.additionalUpstreamSystemUnits = [
134 "systemd-journal-remote.service"
135 "systemd-journal-remote.socket"
136 ];
137
138 systemd.services.systemd-journal-remote.serviceConfig.ExecStart = [
139 # Clear the default command line
140 ""
141 "${pkgs.systemd}/lib/systemd/systemd-journal-remote ${cliArgs}"
142 ];
143
144 systemd.sockets.systemd-journal-remote = {
145 wantedBy = [ "sockets.target" ];
146 listenStreams = [
147 # Clear the default port
148 ""
149 (toString cfg.port)
150 ];
151 };
152
153 # User and group used by systemd-journal-remote.service
154 users.groups.systemd-journal-remote = { };
155 users.users.systemd-journal-remote = {
156 isSystemUser = true;
157 group = "systemd-journal-remote";
158 };
159
160 environment.etc."systemd/journal-remote.conf".source =
161 format.generate "journal-remote.conf" cfg.settings;
162 };
163}