diff --git a/Makefile b/Makefile index 71969db..a3b4d8f 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ build-all: BUILD_ARGS=$(BUILD_ARGS) $(MAKE) -C prosody build BUILD_ARGS=$(BUILD_ARGS) $(MAKE) -C jicofo build BUILD_ARGS=$(BUILD_ARGS) $(MAKE) -C jvb build + BUILD_ARGS=$(BUILD_ARGS) $(MAKE) -C jigasi build push-all: cd base && docker push jitsi/base && cd .. @@ -20,5 +21,6 @@ push-all: cd prosody && docker push jitsi/prosody && cd .. cd jicofo && docker push jitsi/jicofo && cd .. cd jvb && docker push jitsi/jvb && cd .. + cd jigasi && docker push jitsi/jigasi && cd .. .PHONY: build-all push-all diff --git a/README.md b/README.md index 576bf79..599748d 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,9 @@ follow these steps: * Access the web UI at ``https://localhost:8443`` (or ``http://localhost:8000 for HTTP, or a different port, in case you edited the compose file). +If you want to use jigasi too, first configure your env file with SIP credentials +and then run Docker Compose as follows: ``docker-compose -f docker-compose.yml -f jigasi.yml up -d`` + ## Architecture A Jitsi Meet installation can be broken down into the following components: @@ -42,6 +45,7 @@ A Jitsi Meet installation can be broken down into the following components: * An XMPP server * A conference focus component * A video router (could be more than one) +* A SIP gateway for audio calls ![](resources/docker-jitsi-meet.png) @@ -61,10 +65,7 @@ several container images are provided. * **prosody**: [Prosody], the XMPP server. * **jicofo**: [Jicofo], the XMPP focus component. * **jvb**: [Jitsi Videobridge], the video router. - -Note: see the README on each image for a description of all possible configuration options. -Not all of them need to be set for a compose setup, please check ``docker-compose.yml`` and -``env.example`` for the required ones. +* **jigasi**: [Jigasi], the SIP (audio only) gateway. ### Design considerations @@ -92,6 +93,14 @@ Variable | Description | Example `JICOFO_AUTH_PASSWORD` | XMPP password for Jicofo client connections | passw0rd `DOCKER_HOST_ADDRESS` | IP addrss of the Docker host, needed for LAN environments | 192.168.1.1 +If you want to enable the SIP gateway, these options are required: + +Variable | Description | Example +--- | --- | --- +`JIGASI_SIP_URI` | SIP URI for incoming / outgoing calls | test@sip2sip.info +`JIGASI_SIP_PASSWORD` | Password for the specified SIP account | passw0rd +`JIGASI_SIP_SERVER` | SIP server (use the SIP account domain if in doubt) | sip2sip.info + ### Advanced configuration These configuration options are already set and generally don't need to be changed. @@ -107,6 +116,11 @@ Variable | Description | Default value `JVB_AUTH_USER` | XMPP user for JVB MUC client connections | jvb `JVB_PORT` | Port for media used by Jitsi Videobridge | 10000 `JVB_BREWERY_MUC` | MUC name for the JVB pool | jvbbrewery +`JIGASI_XMPP_USER` | XMPP user for Jigasi MUC client connections | jigasi +`JIGASI_XMPP_PASSWORD` | XMPP password for Jigasi MUC client connections | passw0rd +`JIGASI_BREWERY_MUC` | MUC name for the Jigasi pool | jigasibrewery +`JIGASI_PORT_MIN` | Minimum port for media used by Jigasi | 20000 +`JIGASI_PORT_MAX` | Maximum port for media used by Jigasi | 20050 ### Running on a LAN environment @@ -124,9 +138,9 @@ option. * Support container replicas (where applicable). * Docker Swarm mode. * Native Let's Encrypt support. -* Jigasi and Jibri containers. -* TURN server container. - +* More services: + * Jibri. + * TURN server. [Jitsi]: https://jitsi.org/ [Jitsi Meet]: https://jitsi.org/jitsi-meet/ @@ -138,5 +152,6 @@ option. [Prosody]: https://prosody.im/ [Jicofo]: https://github.com/jitsi/jicofo [Jitsi Videobridge]: https://github.com/jitsi/jitsi-videobridge +[Jigasi]: https://github.com/jitsi/jigasi [ICE]: https://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment [STUN]: https://en.wikipedia.org/wiki/STUN diff --git a/docker-compose.yml b/docker-compose.yml index ab42439..a28445c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,6 +38,8 @@ services: - JICOFO_AUTH_PASSWORD - JVB_AUTH_USER - JVB_AUTH_PASSWORD + - JIGASI_XMPP_USER + - JIGASI_XMPP_PASSWORD - TZ networks: meet.jitsi: @@ -58,6 +60,7 @@ services: - JICOFO_AUTH_USER - JICOFO_AUTH_PASSWORD - JVB_BREWERY_MUC + - JIGASI_BREWERY_MUC - TZ networks: meet.jitsi: @@ -87,4 +90,3 @@ services: # Custom network so all services can communicate using a FQDN networks: meet.jitsi: - diff --git a/env.example b/env.example index a335c57..e3d30e1 100644 --- a/env.example +++ b/env.example @@ -53,6 +53,30 @@ JICOFO_AUTH_USER=focus # XMPP password for Jicofo client connections. JICOFO_AUTH_PASSWORD=passw0rd +# SIP URI for incoming / outgoing calls +#JIGASI_SIP_URI=test@sip2sip.info + +# Password for the specified SIP account +#JIGASI_SIP_PASSWORD=passw0rd + +# SIP server (use the SIP account domain if in doubt) +#JIGASI_SIP_SERVER=sip2sip.info + +# XMPP user for Jigasi MUC client connections +JIGASI_XMPP_USER=jigasi + +# XMPP password for Jigasi MUC client connections +JIGASI_XMPP_PASSWORD=passw0rd + +# MUC name for the Jigasi pool +JIGASI_BREWERY_MUC=jigasibrewery + +# Minimum port for media used by Jigasi +JIGASI_PORT_MIN=20000 + +# Maximum port for media used by Jigasi +JIGASI_PORT_MAX=20050 + # IP address of the Docker host. See the "Running on a LAN environment" section # in the README. #DOCKER_HOST_ADDRESS=192.168.1.1 diff --git a/jicofo/rootfs/defaults/sip-communicator.properties b/jicofo/rootfs/defaults/sip-communicator.properties index e1129bd..045f462 100644 --- a/jicofo/rootfs/defaults/sip-communicator.properties +++ b/jicofo/rootfs/defaults/sip-communicator.properties @@ -1,2 +1,4 @@ org.jitsi.jicofo.ALWAYS_TRUST_MODE_ENABLED=true org.jitsi.jicofo.BRIDGE_MUC={{ .Env.JVB_BREWERY_MUC }}@{{ .Env.XMPP_INTERNAL_MUC_DOMAIN }} +org.jitsi.jicofo.jigasi.BREWERY={{ .Env.JIGASI_BREWERY_MUC}}@{{ .Env.XMPP_INTERNAL_MUC_DOMAIN }} + diff --git a/jigasi.yml b/jigasi.yml new file mode 100644 index 0000000..2fb9aa1 --- /dev/null +++ b/jigasi.yml @@ -0,0 +1,26 @@ +version: '3' + +services: + # SIP gateway (audio) + jigasi: + image: jitsi/jigasi + ports: + - '${JIGASI_PORT_MIN}-${JIGASI_PORT_MAX}:${JIGASI_PORT_MIN}-${JIGASI_PORT_MAX}/udp' + volumes: + - ${CONFIG}/jigasi:/config + environment: + - XMPP_AUTH_DOMAIN + - XMPP_INTERNAL_MUC_DOMAIN + - XMPP_SERVER=xmpp.meet.jitsi + - XMPP_DOMAIN + - JIGASI_SIP_URI + - JIGASI_SIP_PASSWORD + - JIGASI_SIP_SERVER + - JIGASI_XMPP_USER + - JIGASI_XMPP_PASSWORD + - JIGASI_BREWERY_MUC + - JIGASI_PORT_MIN + - JIGASI_PORT_MAX + - TZ + networks: + meet.jitsi: diff --git a/jigasi/Dockerfile b/jigasi/Dockerfile new file mode 100644 index 0000000..0112891 --- /dev/null +++ b/jigasi/Dockerfile @@ -0,0 +1,10 @@ +FROM jitsi/base-java + +RUN \ + apt-dpkg-wrap apt-get update && \ + apt-dpkg-wrap apt-get install -y jigasi && \ + apt-cleanup + +COPY rootfs/ / + +VOLUME /config diff --git a/jigasi/Makefile b/jigasi/Makefile new file mode 100644 index 0000000..a3735ac --- /dev/null +++ b/jigasi/Makefile @@ -0,0 +1,4 @@ +build: + docker build $(BUILD_ARGS) -t jitsi/jigasi . + +.PHONY: build diff --git a/jigasi/rootfs/defaults/logging.properties b/jigasi/rootfs/defaults/logging.properties new file mode 100644 index 0000000..188619a --- /dev/null +++ b/jigasi/rootfs/defaults/logging.properties @@ -0,0 +1,18 @@ +handlers= java.util.logging.ConsoleHandler + +java.util.logging.ConsoleHandler.level = ALL +java.util.logging.ConsoleHandler.formatter = net.java.sip.communicator.util.ScLogFormatter + +net.java.sip.communicator.util.ScLogFormatter.programname=Jigasi + +.level=INFO +net.sf.level=SEVERE +net.java.sip.communicator.plugin.reconnectplugin.level=FINE +org.ice4j.level=SEVERE +org.jitsi.impl.neomedia.level=SEVERE + +# Do not worry about missing strings +net.java.sip.communicator.service.resources.AbstractResourcesService.level=SEVERE + +#net.java.sip.communicator.service.protocol.level=ALL + diff --git a/jigasi/rootfs/defaults/sip-communicator.properties b/jigasi/rootfs/defaults/sip-communicator.properties new file mode 100644 index 0000000..d557e4f --- /dev/null +++ b/jigasi/rootfs/defaults/sip-communicator.properties @@ -0,0 +1,93 @@ +net.java.sip.communicator.impl.protocol.SingleCallInProgressPolicy.enabled=false + +# Adjust opus encoder complexity +net.java.sip.communicator.impl.neomedia.codec.audio.opus.encoder.COMPLEXITY=10 + +# Disables packet logging +net.java.sip.communicator.packetlogging.PACKET_LOGGING_ENABLED=false + +# SIP account +net.java.sip.communicator.impl.protocol.sip.acc1=acc1 +net.java.sip.communicator.impl.protocol.sip.acc1.ACCOUNT_UID=SIP\:{{ .Env.JIGASI_SIP_URI }} +net.java.sip.communicator.impl.protocol.sip.acc1.PASSWORD={{ .Env.JIGASI_SIP_PASSWORD | b64enc }} +net.java.sip.communicator.impl.protocol.sip.acc1.PROTOCOL_NAME=SIP +net.java.sip.communicator.impl.protocol.sip.acc1.SERVER_ADDRESS={{ .Env.JIGASI_SIP_SERVER }} +net.java.sip.communicator.impl.protocol.sip.acc1.USER_ID={{ .Env.JIGASI_SIP_URI }} +net.java.sip.communicator.impl.protocol.sip.acc1.KEEP_ALIVE_INTERVAL=25 +net.java.sip.communicator.impl.protocol.sip.acc1.KEEP_ALIVE_METHOD=OPTIONS +net.java.sip.communicator.impl.protocol.sip.acc1.VOICEMAIL_ENABLED=false +net.java.sip.communicator.impl.protocol.sip.acc1.JITSI_MEET_ROOM_HEADER_NAME=X-Room-Name +net.java.sip.communicator.impl.protocol.sip.acc1.JITSI_MEET_DOMAIN_BASE_HEADER_NAME=X-Domain-Base +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.AMR-WB/16000=750 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.G722/8000=700 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.GSM/8000=0 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.H263-1998/90000=0 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.H264/90000=0 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.PCMA/8000=600 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.PCMU/8000=650 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.SILK/12000=0 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.SILK/16000=0 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.SILK/24000=0 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.SILK/8000=0 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.VP8/90000=0 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.iLBC/8000=10 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.opus/48000=1000 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.red/90000=0 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.speex/16000=0 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.speex/32000=0 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.speex/8000=0 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.telephone-event/8000=1 +net.java.sip.communicator.impl.protocol.sip.acc1.Encodings.ulpfec/90000=0 +net.java.sip.communicator.impl.protocol.sip.acc1.OVERRIDE_ENCODINGS=true + +# XMPP account used for control +net.java.sip.communicator.impl.protocol.jabber.acc1=acc1 +net.java.sip.communicator.impl.protocol.jabber.acc1.ACCOUNT_UID=Jabber:{{ .Env.JIGASI_XMPP_USER }}@{{ .Env.XMPP_AUTH_DOMAIN }} +net.java.sip.communicator.impl.protocol.jabber.acc1.USER_ID={{ .Env.JIGASI_XMPP_USER }}@{{ .Env.XMPP_AUTH_DOMAIN }} +net.java.sip.communicator.impl.protocol.jabber.acc1.IS_SERVER_OVERRIDDEN=true +net.java.sip.communicator.impl.protocol.jabber.acc1.SERVER_ADDRESS={{ .Env.XMPP_SERVER }} +net.java.sip.communicator.impl.protocol.jabber.acc1.PASSWORD={{ .Env.JIGASI_XMPP_PASSWORD | b64enc }} +net.java.sip.communicator.impl.protocol.jabber.acc1.AUTO_GENERATE_RESOURCE=true +net.java.sip.communicator.impl.protocol.jabber.acc1.RESOURCE_PRIORITY=30 +net.java.sip.communicator.impl.protocol.jabber.acc1.IS_CARBON_DISABLED=true +net.java.sip.communicator.impl.protocol.jabber.acc1.DEFAULT_ENCRYPTION=true +net.java.sip.communicator.impl.protocol.jabber.acc1.IS_USE_ICE=true +net.java.sip.communicator.impl.protocol.jabber.acc1.IS_ACCOUNT_DISABLED=false +net.java.sip.communicator.impl.protocol.jabber.acc1.IS_PREFERRED_PROTOCOL=false +net.java.sip.communicator.impl.protocol.jabber.acc1.AUTO_DISCOVER_JINGLE_NODES=false +net.java.sip.communicator.impl.protocol.jabber.acc1.PROTOCOL=Jabber +net.java.sip.communicator.impl.protocol.jabber.acc1.IS_USE_UPNP=false +net.java.sip.communicator.impl.protocol.jabber.acc1.USE_DEFAULT_STUN_SERVER=true +net.java.sip.communicator.impl.protocol.jabber.acc1.ENCRYPTION_PROTOCOL.DTLS-SRTP=0 +net.java.sip.communicator.impl.protocol.jabber.acc1.ENCRYPTION_PROTOCOL_STATUS.DTLS-SRTP=true +net.java.sip.communicator.impl.protocol.jabber.acc1.VIDEO_CALLING_DISABLED=true +net.java.sip.communicator.impl.protocol.jabber.acc1.OVERRIDE_ENCODINGS=true +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.G722/8000=705 +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.GSM/8000=0 +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.H263-1998/90000=0 +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.H264/90000=0 +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.PCMA/8000=0 +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.PCMU/8000=0 +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.SILK/12000=0 +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.SILK/16000=0 +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.SILK/24000=0 +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.SILK/8000=0 +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.VP8/90000=0 +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.iLBC/8000=0 +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.opus/48000=750 +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.speex/16000=0 +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.speex/32000=0 +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.speex/8000=0 +net.java.sip.communicator.impl.protocol.jabber.acc1.Encodings.telephone-event/8000=0 +net.java.sip.communicator.impl.protocol.jabber.acc1.BREWERY={{ .Env.JIGASI_BREWERY_MUC }}@{{ .Env.XMPP_INTERNAL_MUC_DOMAIN }} +net.java.sip.communicator.impl.protocol.jabber.acc1.DOMAIN_BASE={{ .Env.XMPP_DOMAIN }} + +org.jitsi.jigasi.BREWERY_ENABLED=true + +org.jitsi.jigasi.xmpp.acc.IS_SERVER_OVERRIDDEN=true +org.jitsi.jigasi.xmpp.acc.SERVER_ADDRESS={{ .Env.XMPP_SERVER }} + +# Activate this property if you are using self-signed certificates or other +# type of non-trusted certicates. In this mode your service trust in the +# remote certificates always. +net.java.sip.communicator.service.gui.ALWAYS_TRUST_MODE_ENABLED=true diff --git a/jigasi/rootfs/etc/cont-init.d/10-config b/jigasi/rootfs/etc/cont-init.d/10-config new file mode 100644 index 0000000..23bf808 --- /dev/null +++ b/jigasi/rootfs/etc/cont-init.d/10-config @@ -0,0 +1,9 @@ +#!/usr/bin/with-contenv bash + +if [[ ! -f /config/sip-communicator.properties ]]; then + tpl /defaults/sip-communicator.properties > /config/sip-communicator.properties +fi + +if [[ ! -f /config/logging.properties ]]; then + cp /defaults/logging.properties /config +fi diff --git a/jigasi/rootfs/etc/services.d/jigasi/run b/jigasi/rootfs/etc/services.d/jigasi/run new file mode 100644 index 0000000..660753b --- /dev/null +++ b/jigasi/rootfs/etc/services.d/jigasi/run @@ -0,0 +1,9 @@ +#!/usr/bin/with-contenv bash + +JAVA_SYS_PROPS="-Djava.util.logging.config.file=/config/logging.properties" + +DAEMON=/usr/share/jigasi/jigasi.sh +DAEMON_OPTS="--nocomponent=true --configdir=/ --configdirname=config --min-port=$JIGASI_PORT_MIN --max-port=$JIGASI_PORT_MAX" + +exec s6-setuidgid jigasi /bin/bash -c "JAVA_SYS_PROPS=\"$JAVA_SYS_PROPS\" exec $DAEMON $DAEMON_OPTS" + diff --git a/prosody/rootfs/etc/cont-init.d/10-config b/prosody/rootfs/etc/cont-init.d/10-config index 8345eb4..c677e19 100644 --- a/prosody/rootfs/etc/cont-init.d/10-config +++ b/prosody/rootfs/etc/cont-init.d/10-config @@ -13,6 +13,7 @@ if [[ ! -f $PROSODY_CFG ]]; then prosodyctl --config $PROSODY_CFG register $JICOFO_AUTH_USER $XMPP_AUTH_DOMAIN $JICOFO_AUTH_PASSWORD prosodyctl --config $PROSODY_CFG register $JVB_AUTH_USER $XMPP_AUTH_DOMAIN $JVB_AUTH_PASSWORD + prosodyctl --config $PROSODY_CFG register $JIGASI_XMPP_USER $XMPP_AUTH_DOMAIN $JIGASI_XMPP_PASSWORD fi mkdir -p /config/certs