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