1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.jigasi;
7 homeDirName = "jigasi-home";
8 stateDir = "/tmp";
9 sipCommunicatorPropertiesFile = "${stateDir}/${homeDirName}/sip-communicator.properties";
10 sipCommunicatorPropertiesFileUnsubstituted = "${pkgs.jigasi}/etc/jitsi/jigasi/sip-communicator.properties";
11in
12{
13 options.services.jigasi = with types; {
14 enable = mkEnableOption "Jitsi Gateway to SIP - component of Jitsi Meet";
15
16 xmppHost = mkOption {
17 type = str;
18 example = "localhost";
19 description = ''
20 Hostname of the XMPP server to connect to.
21 '';
22 };
23
24 xmppDomain = mkOption {
25 type = nullOr str;
26 example = "meet.example.org";
27 description = ''
28 Domain name of the XMMP server to which to connect as a component.
29
30 If null, <option>xmppHost</option> is used.
31 '';
32 };
33
34 componentPasswordFile = mkOption {
35 type = str;
36 example = "/run/keys/jigasi-component";
37 description = ''
38 Path to file containing component secret.
39 '';
40 };
41
42 userName = mkOption {
43 type = str;
44 default = "callcontrol";
45 description = ''
46 User part of the JID for XMPP user connection.
47 '';
48 };
49
50 userDomain = mkOption {
51 type = str;
52 example = "internal.meet.example.org";
53 description = ''
54 Domain part of the JID for XMPP user connection.
55 '';
56 };
57
58 userPasswordFile = mkOption {
59 type = str;
60 example = "/run/keys/jigasi-user";
61 description = ''
62 Path to file containing password for XMPP user connection.
63 '';
64 };
65
66 bridgeMuc = mkOption {
67 type = str;
68 example = "jigasibrewery@internal.meet.example.org";
69 description = ''
70 JID of the internal MUC used to communicate with Videobridges.
71 '';
72 };
73
74 defaultJvbRoomName = mkOption {
75 type = str;
76 default = "";
77 example = "siptest";
78 description = ''
79 Name of the default JVB room that will be joined if no special header is included in SIP invite.
80 '';
81 };
82
83 environmentFile = mkOption {
84 type = types.nullOr types.path;
85 default = null;
86 description = ''
87 File containing environment variables to be passed to the jigasi service,
88 in which secret tokens can be specified securely by defining values for
89 <literal>JIGASI_SIPUSER</literal>,
90 <literal>JIGASI_SIPPWD</literal>,
91 <literal>JIGASI_SIPSERVER</literal> and
92 <literal>JIGASI_SIPPORT</literal>.
93 '';
94 };
95
96 config = mkOption {
97 type = attrsOf str;
98 default = { };
99 example = literalExpression ''
100 {
101 "org.jitsi.jigasi.auth.URL" = "XMPP:jitsi-meet.example.com";
102 }
103 '';
104 description = ''
105 Contents of the <filename>sip-communicator.properties</filename> configuration file for jigasi.
106 '';
107 };
108 };
109
110 config = mkIf cfg.enable {
111 services.jicofo.config = {
112 "org.jitsi.jicofo.jigasi.BREWERY" = "${cfg.bridgeMuc}";
113 };
114
115 services.jigasi.config = mapAttrs (_: v: mkDefault v) {
116 "org.jitsi.jigasi.BRIDGE_MUC" = cfg.bridgeMuc;
117 };
118
119 users.groups.jitsi-meet = {};
120
121 systemd.services.jigasi = let
122 jigasiProps = {
123 "-Dnet.java.sip.communicator.SC_HOME_DIR_LOCATION" = "${stateDir}";
124 "-Dnet.java.sip.communicator.SC_HOME_DIR_NAME" = "${homeDirName}";
125 "-Djava.util.logging.config.file" = "${pkgs.jigasi}/etc/jitsi/jigasi/logging.properties";
126 };
127 in
128 {
129 description = "Jitsi Gateway to SIP";
130 wantedBy = [ "multi-user.target" ];
131 after = [ "network.target" ];
132
133 preStart = ''
134 [ -f "${sipCommunicatorPropertiesFile}" ] && rm -f "${sipCommunicatorPropertiesFile}"
135 mkdir -p "$(dirname ${sipCommunicatorPropertiesFile})"
136 temp="${sipCommunicatorPropertiesFile}.unsubstituted"
137
138 export DOMAIN_BASE="${cfg.xmppDomain}"
139 export JIGASI_XMPP_PASSWORD=$(cat "${cfg.userPasswordFile}")
140 export JIGASI_DEFAULT_JVB_ROOM_NAME="${cfg.defaultJvbRoomName}"
141
142 # encode the credentials to base64
143 export JIGASI_SIPPWD=$(echo -n "$JIGASI_SIPPWD" | base64 -w 0)
144 export JIGASI_XMPP_PASSWORD_BASE64=$(cat "${cfg.userPasswordFile}" | base64 -w 0)
145
146 cp "${sipCommunicatorPropertiesFileUnsubstituted}" "$temp"
147 chmod 644 "$temp"
148 cat <<EOF >>"$temp"
149 net.java.sip.communicator.impl.protocol.sip.acc1403273890647.SERVER_PORT=$JIGASI_SIPPORT
150 net.java.sip.communicator.impl.protocol.sip.acc1403273890647.PREFERRED_TRANSPORT=udp
151 EOF
152 chmod 444 "$temp"
153
154 # Replace <<$VAR_NAME>> from example config to $VAR_NAME for environment substitution
155 sed -i -E \
156 's/<<([^>]+)>>/\$\1/g' \
157 "$temp"
158
159 sed -i \
160 's|\(net\.java\.sip\.communicator\.impl\.protocol\.jabber\.acc-xmpp-1\.PASSWORD=\).*|\1\$JIGASI_XMPP_PASSWORD_BASE64|g' \
161 "$temp"
162
163 sed -i \
164 's|\(#\)\(org.jitsi.jigasi.DEFAULT_JVB_ROOM_NAME=\).*|\2\$JIGASI_DEFAULT_JVB_ROOM_NAME|g' \
165 "$temp"
166
167 ${pkgs.envsubst}/bin/envsubst \
168 -o "${sipCommunicatorPropertiesFile}" \
169 -i "$temp"
170
171 # Set the brewery room name
172 sed -i \
173 's|\(net\.java\.sip\.communicator\.impl\.protocol\.jabber\.acc-xmpp-1\.BREWERY=\).*|\1${cfg.bridgeMuc}|g' \
174 "${sipCommunicatorPropertiesFile}"
175 sed -i \
176 's|\(org\.jitsi\.jigasi\.ALLOWED_JID=\).*|\1${cfg.bridgeMuc}|g' \
177 "${sipCommunicatorPropertiesFile}"
178
179
180 # Disable certificate verification for self-signed certificates
181 sed -i \
182 's|\(# \)\(net.java.sip.communicator.service.gui.ALWAYS_TRUST_MODE_ENABLED=true\)|\2|g' \
183 "${sipCommunicatorPropertiesFile}"
184 '';
185
186 restartTriggers = [
187 config.environment.etc."jitsi/jigasi/sip-communicator.properties".source
188 ];
189 environment.JAVA_SYS_PROPS = concatStringsSep " " (mapAttrsToList (k: v: "${k}=${toString v}") jigasiProps);
190
191 script = ''
192 ${pkgs.jigasi}/bin/jigasi \
193 --host="${cfg.xmppHost}" \
194 --domain="${if cfg.xmppDomain == null then cfg.xmppHost else cfg.xmppDomain}" \
195 --secret="$(cat ${cfg.componentPasswordFile})" \
196 --user_name="${cfg.userName}" \
197 --user_domain="${cfg.userDomain}" \
198 --user_password="$(cat ${cfg.userPasswordFile})" \
199 --configdir="${stateDir}" \
200 --configdirname="${homeDirName}"
201 '';
202
203 serviceConfig = {
204 Type = "exec";
205
206 DynamicUser = true;
207 User = "jigasi";
208 Group = "jitsi-meet";
209
210 CapabilityBoundingSet = "";
211 NoNewPrivileges = true;
212 ProtectSystem = "strict";
213 ProtectHome = true;
214 PrivateTmp = true;
215 PrivateDevices = true;
216 ProtectHostname = true;
217 ProtectKernelTunables = true;
218 ProtectKernelModules = true;
219 ProtectControlGroups = true;
220 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
221 RestrictNamespaces = true;
222 LockPersonality = true;
223 RestrictRealtime = true;
224 RestrictSUIDSGID = true;
225 StateDirectory = baseNameOf stateDir;
226 EnvironmentFile = cfg.environmentFile;
227 };
228 };
229
230 environment.etc."jitsi/jigasi/sip-communicator.properties".source =
231 mkDefault "${sipCommunicatorPropertiesFile}";
232 environment.etc."jitsi/jigasi/logging.properties".source =
233 mkDefault "${stateDir}/logging.properties-journal";
234 };
235
236 meta.maintainers = lib.teams.jitsi.members;
237}