1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8with lib;
9
10let
11 cfg = config.services.soju;
12 stateDir = "/var/lib/soju";
13 runtimeDir = "/run/soju";
14 listen = cfg.listen ++ optional cfg.adminSocket.enable "unix+admin://${runtimeDir}/admin";
15 listenCfg = concatMapStringsSep "\n" (l: "listen ${l}") listen;
16 tlsCfg = optionalString (
17 cfg.tlsCertificate != null
18 ) "tls ${cfg.tlsCertificate} ${cfg.tlsCertificateKey}";
19 logCfg = optionalString cfg.enableMessageLogging "message-store fs ${stateDir}/logs";
20
21 configFile = pkgs.writeText "soju.conf" ''
22 ${listenCfg}
23 hostname ${cfg.hostName}
24 ${tlsCfg}
25 db sqlite3 ${stateDir}/soju.db
26 ${logCfg}
27 http-origin ${concatStringsSep " " cfg.httpOrigins}
28 accept-proxy-ip ${concatStringsSep " " cfg.acceptProxyIP}
29
30 ${cfg.extraConfig}
31 '';
32
33 sojuctl = pkgs.writeShellScriptBin "sojuctl" ''
34 exec ${lib.getExe' cfg.package "sojuctl"} --config ${cfg.configFile} "$@"
35 '';
36in
37{
38 ###### interface
39
40 options.services.soju = {
41 enable = mkEnableOption "soju";
42
43 package = mkPackageOption pkgs "soju" { };
44
45 listen = mkOption {
46 type = types.listOf types.str;
47 default = [ ":6697" ];
48 description = ''
49 Where soju should listen for incoming connections. See the
50 `listen` directive in
51 {manpage}`soju(1)`.
52 '';
53 };
54
55 hostName = mkOption {
56 type = types.str;
57 default = config.networking.hostName;
58 defaultText = literalExpression "config.networking.hostName";
59 description = "Server hostname.";
60 };
61
62 tlsCertificate = mkOption {
63 type = types.nullOr types.path;
64 default = null;
65 example = "/var/host.cert";
66 description = "Path to server TLS certificate.";
67 };
68
69 tlsCertificateKey = mkOption {
70 type = types.nullOr types.path;
71 default = null;
72 example = "/var/host.key";
73 description = "Path to server TLS certificate key.";
74 };
75
76 enableMessageLogging = mkOption {
77 type = types.bool;
78 default = true;
79 description = "Whether to enable message logging.";
80 };
81
82 adminSocket.enable = mkOption {
83 type = types.bool;
84 default = true;
85 description = ''
86 Listen for admin connections from sojuctl at /run/soju/admin.
87 '';
88 };
89
90 httpOrigins = mkOption {
91 type = types.listOf types.str;
92 default = [ ];
93 description = ''
94 List of allowed HTTP origins for WebSocket listeners. The parameters are
95 interpreted as shell patterns, see
96 {manpage}`glob(7)`.
97 '';
98 };
99
100 acceptProxyIP = mkOption {
101 type = types.listOf types.str;
102 default = [ ];
103 description = ''
104 Allow the specified IPs to act as a proxy. Proxys have the ability to
105 overwrite the remote and local connection addresses (via the X-Forwarded-\*
106 HTTP header fields). The special name "localhost" accepts the loopback
107 addresses 127.0.0.0/8 and ::1/128. By default, all IPs are rejected.
108 '';
109 };
110
111 extraConfig = mkOption {
112 type = types.lines;
113 default = "";
114 description = "Lines added verbatim to the generated configuration file.";
115 };
116
117 configFile = mkOption {
118 type = types.path;
119 default = configFile;
120 defaultText = "Config file generated from other options.";
121 description = ''
122 Path to config file. If this option is set, it will override any
123 configuration done using other options, including {option}`extraConfig`.
124 '';
125 example = literalExpression "./soju.conf";
126 };
127 };
128
129 ###### implementation
130
131 config = mkIf cfg.enable {
132 assertions = [
133 {
134 assertion = (cfg.tlsCertificate != null) == (cfg.tlsCertificateKey != null);
135 message = ''
136 services.soju.tlsCertificate and services.soju.tlsCertificateKey
137 must both be specified to enable TLS.
138 '';
139 }
140 ];
141
142 environment.systemPackages = [ sojuctl ];
143
144 systemd.services.soju = {
145 description = "soju IRC bouncer";
146 wantedBy = [ "multi-user.target" ];
147 wants = [ "network-online.target" ];
148 after = [ "network-online.target" ];
149 documentation = [ "man:soju(1)" ];
150 serviceConfig = {
151 DynamicUser = true;
152 Restart = "always";
153 ExecStart = "${lib.getExe' cfg.package "soju"} -config ${cfg.configFile}";
154 StateDirectory = "soju";
155 RuntimeDirectory = "soju";
156 };
157 };
158 };
159
160 meta.maintainers = with maintainers; [ malte-v ];
161}