From 88402bb73f41ec5df2ce7e332f4bb9f9c11cf31a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 29 Apr 2020 22:39:27 +0100 Subject: [PATCH 1/3] ansible, docker: Initial attempt at adding coturn --- ansible/files/bin/start-coturn.sh | 14 +++++ ansible/files/prosody.cfg.lua | 4 ++ ansible/files/supervisord.conf | 10 ++++ ansible/files/turnserver.conf | 92 +++++++++++++++++++++++++++++++ ansible/tasks/coturn.yml | 24 ++++++++ docker/entrypoint.sh | 5 ++ 6 files changed, 149 insertions(+) create mode 100644 ansible/files/bin/start-coturn.sh create mode 100644 ansible/files/turnserver.conf create mode 100644 ansible/tasks/coturn.yml diff --git a/ansible/files/bin/start-coturn.sh b/ansible/files/bin/start-coturn.sh new file mode 100644 index 0000000..a043dce --- /dev/null +++ b/ansible/files/bin/start-coturn.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +CERTFILE="/snikket/letsencrypt/live/$SNIKKET_DOMAIN/fullchain.pem"; +KEYFILE="/snikket/letsencrypt/live/$SNIKKET_DOMAIN/privkey.pem"; + +echo "Waiting for certificates to become available..." +while ! test -f "$CERTFILE" -a -f "$KEYFILE"; do + sleep 1; + echo "."; +done + +exec /usr/bin/turnserver -c /etc/turnserver.conf --prod \ + --static-auth-secret="$(cat /snikket/prosody/turn-auth-secret)" \ + --cert="$CERTFILE" --pkey "$KEYFILE" diff --git a/ansible/files/prosody.cfg.lua b/ansible/files/prosody.cfg.lua index 0177f5e..82b61c5 100644 --- a/ansible/files/prosody.cfg.lua +++ b/ansible/files/prosody.cfg.lua @@ -51,6 +51,7 @@ modules_enabled = { "default_bookmarks"; "roster_allinall"; "update_check"; + "turncredentials"; -- TODO... --"groups"; -- Shared roster support @@ -110,6 +111,9 @@ update_check_dns = "_{branch}.update.snikket.net" http_host = DOMAIN http_external_url = "https://"..DOMAIN.."/" +turncredentials_host = DOMAIN +turncredentials_secret = assert(io.open("/snikket/prosody/turn-auth-secret")):read("*a"); + VirtualHost (DOMAIN) authentication = "internal_hashed" diff --git a/ansible/files/supervisord.conf b/ansible/files/supervisord.conf index 4a2357f..4856966 100644 --- a/ansible/files/supervisord.conf +++ b/ansible/files/supervisord.conf @@ -23,3 +23,13 @@ stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 redirect_stderr=true umask=002 + +[program:coturn] +command=start-coturn.sh +startsecs=0 +autorestart=true +stopwaitsecs=30 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +redirect_stderr=true +umask=002 diff --git a/ansible/files/turnserver.conf b/ansible/files/turnserver.conf new file mode 100644 index 0000000..6990ffa --- /dev/null +++ b/ansible/files/turnserver.conf @@ -0,0 +1,92 @@ +# Coturn TURN SERVER configuration file +# +# Boolean values note: where boolean value is supposed to be used, +# you can use '0', 'off', 'no', 'false', 'f' as 'false, +# and you can use '1', 'on', 'yes', 'true', 't' as 'true' +# If the value is missed, then it means 'true'. +# + +# TURN listener port for UDP and TCP (Default: 3478). +# Note: actually, TLS & DTLS sessions can connect to the +# "plain" TCP & UDP port(s), too - if allowed by configuration. +# +listening-port=3478 + +# TURN listener port for TLS (Default: 5349). +# Note: actually, "plain" TCP & UDP sessions can connect to the TLS & DTLS +# port(s), too - if allowed by configuration. The TURN server +# "automatically" recognizes the type of traffic. Actually, two listening +# endpoints (the "plain" one and the "tls" one) are equivalent in terms of +# functionality; but we keep both endpoints to satisfy the RFC 5766 specs. +# For secure TCP connections, we currently support SSL version 3 and +# TLS version 1.0, 1.1 and 1.2. +# For secure UDP connections, we support DTLS version 1. +# +tls-listening-port=5349 + +# Alternative listening port for UDP and TCP listeners; +# default (or zero) value means "listening port plus one". +# This is needed for RFC 5780 support +# (STUN extension specs, NAT behavior discovery). The TURN Server +# supports RFC 5780 only if it is started with more than one +# listening IP address of the same family (IPv4 or IPv6). +# RFC 5780 is supported only by UDP protocol, other protocols +# are listening to that endpoint only for "symmetry". +# +alt-listening-port=0 + +# Alternative listening port for TLS and DTLS protocols. +# Default (or zero) value means "TLS listening port plus one". +# +alt-tls-listening-port=0 + +# Lower and upper bounds of the UDP relay endpoints: +# (default values are 49152 and 65535) +# +min-port=49152 +max-port=65535 + +# TURN REST API flag. +# Flag that sets a special authorization option that is based upon authentication secret. +# This feature can be used with the long-term authentication mechanism, only. +# This feature purpose is to support "TURN Server REST API", see +# "TURN REST API" link in the project's page +# https://github.com/coturn/coturn/ +# +# This option is used with timestamp: +# +# usercombo -> "timestamp:userid" +# turn user -> usercombo +# turn password -> base64(hmac(secret key, usercombo)) +# +# This allows TURN credentials to be accounted for a specific user id. +# If you don't have a suitable id, the timestamp alone can be used. +# This option is just turning on secret-based authentication. +# The actual value of the secret is defined either by option static-auth-secret, +# or can be found in the turn_secret table in the database (see below). +# +use-auth-secret + +# Option to set the log file name. +# By default, the turnserver tries to open a log file in +# /var/log, /var/tmp, /tmp and current directories directories +# (which open operation succeeds first that file will be used). +# With this option you can set the definite log file name. +# The special names are "stdout" and "-" - they will force everything +# to the stdout. Also, the "syslog" name will force everything to +# the system log (syslog). +# In the runtime, the logfile can be reset with the SIGHUP signal +# to the turnserver process. +# +log-file=stdout + +# Flag that can be used to disallow peers on well-known broadcast addresses (224.0.0.0 and above, and FFXX:*). +# This is an extra security measure. +# +no-multicast-peers + +# Turn OFF the CLI support. +# By default it is always ON. +# See also options cli-ip and cli-port. +# +no-cli diff --git a/ansible/tasks/coturn.yml b/ansible/tasks/coturn.yml new file mode 100644 index 0000000..b8b39df --- /dev/null +++ b/ansible/tasks/coturn.yml @@ -0,0 +1,24 @@ +--- + +- name: "Install coturn package" + apt: + name: coturn + state: present + install_recommends: yes +- name: "Disable coturn service" + service: + name: coturn + enabled: no +- name: "Stop coturn if running" + service: + name: coturn + state: stopped +- name: Configure coturn + copy: + src: ../files/turnserver.conf + dest: /etc/turnserver.conf +- name: Deploy coturn start script + copy: + src: ../files/start-coturn.sh + dest: /usr/local/bin/ + mode: 755 diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 7ddfad6..1fc73ec 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -42,4 +42,9 @@ install -o letsencrypt -g letsencrypt -m 755 -d /var/www/.well-known/acme-challe chown -R letsencrypt:letsencrypt /snikket/letsencrypt +## Generate secret for coturn auth if necessary +if ! test -f /snikket/prosody/turn-auth-secret; then + head -c 32 /dev/urandom | sha256sum > /snikket/prosody/turn-auth-secret; +fi + exec supervisord -c /etc/supervisor/supervisord.conf From 166e000b12d2a2773b3d22bbc209884869f3f2fb Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 12 May 2020 15:05:42 +0100 Subject: [PATCH 2/3] docker-compose, prosody: Switch to host networking for the container This simplifies things in preparation for adding coturn and its large number of ports. As Prosody now must listen on the "real" HTTP ports, we had to give permission to the Lua binary to bind low network ports. --- ansible/files/prosody.cfg.lua | 3 +++ ansible/tasks/prosody.yml | 2 ++ docker-compose.yml | 13 +------------ docker/Dockerfile | 3 ++- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/ansible/files/prosody.cfg.lua b/ansible/files/prosody.cfg.lua index 82b61c5..556f9c0 100644 --- a/ansible/files/prosody.cfg.lua +++ b/ansible/files/prosody.cfg.lua @@ -75,6 +75,9 @@ registration_notification = "New user registered: $username" reload_global_modules = { "http" } +http_ports = { ENV_SNIKKET_TWEAK_HTTP_PORT or 80 } +https_ports = { ENV_SNIKKET_TWEAK_HTTPS_PORT or 443 } + legacy_ssl_ports = { 5223 } allow_registration = true diff --git a/ansible/tasks/prosody.yml b/ansible/tasks/prosody.yml index a80bcf2..8009e6e 100644 --- a/ansible/tasks/prosody.yml +++ b/ansible/tasks/prosody.yml @@ -46,6 +46,8 @@ service: name: prosody state: stopped +- name: "Allow Prosody to bind service ports" + command: setcap 'cap_net_bind_service=+ep' /usr/bin/lua5.1 - name: Install Mercurial apt: diff --git a/docker-compose.yml b/docker-compose.yml index d996b94..f30a330 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,18 +3,7 @@ version: "3.3" services: snikket: image: snikket:latest - ports: - # HTTP port - - "80:5280" - # HTTPS port - - "443:5281" - # XMPP client connections (STARTTLS and Direct TLS) - - "5222:5222" - - "5223:5223" - # XMPP server-to-server connections - - "5269:5269" - # Mail viewer (dev only) - - "8025:8025" + network_mode: host volumes: - type: "volume" source: snikket_data diff --git a/docker/Dockerfile b/docker/Dockerfile index a5c5744..35ea2c8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -23,6 +23,7 @@ RUN apt-get update \ software-properties-common \ gpg gpg-agent \ ansible python-passlib python3-passlib \ + libcap2-bin \ && rm -rf /var/lib/apt/lists/* \ && ansible-playbook -c local -i localhost, --extra-vars "ansible_python_interpreter=/usr/bin/python2" /opt/ansible/snikket.yml \ && apt-get remove -y \ @@ -30,7 +31,7 @@ RUN apt-get update \ software-properties-common \ gpg gpg-agent \ python-passlib python3-passlib \ - mercurial \ + mercurial libcap2-bin \ && apt-get autoremove -y \ && rm -rf /var/cache/* From 07931064472300a4527675fa6abdba9625467edb Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 12 May 2020 15:10:59 +0100 Subject: [PATCH 3/3] Flesh out coturn config, autodiscover external IP --- ansible/files/bin/snikket-turn-addresses | 24 ++++++++++++++++++++++++ ansible/files/bin/start-coturn.sh | 6 +++++- ansible/files/turnserver.conf | 7 +++++++ ansible/snikket.yml | 1 + ansible/tasks/coturn.yml | 12 +++++++----- ansible/tasks/prosody.yml | 1 + docker/entrypoint.sh | 4 ++++ 7 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 ansible/files/bin/snikket-turn-addresses diff --git a/ansible/files/bin/snikket-turn-addresses b/ansible/files/bin/snikket-turn-addresses new file mode 100644 index 0000000..95cf543 --- /dev/null +++ b/ansible/files/bin/snikket-turn-addresses @@ -0,0 +1,24 @@ +#!/usr/bin/env lua + +package.path = package.path:gsub("([^;]*)(?[^;]*)","%1prosody/%2;%1%2"); +package.cpath = package.cpath:gsub("([^;]*)(?[^;]*)","%1prosody/%2;%1%2"); + +package.loaded["net.server"] = require "net.server_epoll"; +local net = require "util.net"; +local ip = require "util.ip"; +local dns = require "net.dns"; + +local addresses = net.local_addresses(); + +local ip_addr = ip.new_ip(addresses[1]); + +if not ip_addr.private then + -- Not a private address, no mapping needed + print(ip_addr); +else + local dns_record = dns.lookup(arg[1], ip_addr.proto == "IPv6" and "AAAA" or "A"); + if #dns_record == 0 then + os.exit(1); + end + print(dns_record[1].a.."/"..tostring(ip_addr)); +end diff --git a/ansible/files/bin/start-coturn.sh b/ansible/files/bin/start-coturn.sh index a043dce..7adcb64 100644 --- a/ansible/files/bin/start-coturn.sh +++ b/ansible/files/bin/start-coturn.sh @@ -9,6 +9,10 @@ while ! test -f "$CERTFILE" -a -f "$KEYFILE"; do echo "."; done +TURN_EXTERNAL_IP="$(snikket-turn-addresses "$SNIKKET_DOMAIN")" + + exec /usr/bin/turnserver -c /etc/turnserver.conf --prod \ --static-auth-secret="$(cat /snikket/prosody/turn-auth-secret)" \ - --cert="$CERTFILE" --pkey "$KEYFILE" + --cert="$CERTFILE" --pkey "$KEYFILE" -r "$SNIKKET_DOMAIN" \ + -X "$TURN_EXTERNAL_IP" diff --git a/ansible/files/turnserver.conf b/ansible/files/turnserver.conf index 6990ffa..3ede0f6 100644 --- a/ansible/files/turnserver.conf +++ b/ansible/files/turnserver.conf @@ -90,3 +90,10 @@ no-multicast-peers # See also options cli-ip and cli-port. # no-cli + +# SQLite database file name. +# +# Default file name is /var/db/turndb or /usr/local/var/db/turndb or +# /var/lib/turn/turndb. +# +userdb=/snikket/prosody/turndb diff --git a/ansible/snikket.yml b/ansible/snikket.yml index 4fb1fb1..1e5dac6 100644 --- a/ansible/snikket.yml +++ b/ansible/snikket.yml @@ -10,4 +10,5 @@ - import_tasks: tasks/cron.yml - import_tasks: tasks/certs.yml - import_tasks: tasks/mail.yml + - import_tasks: tasks/coturn.yml - import_tasks: tasks/scripts.yml diff --git a/ansible/tasks/coturn.yml b/ansible/tasks/coturn.yml index b8b39df..b96cab3 100644 --- a/ansible/tasks/coturn.yml +++ b/ansible/tasks/coturn.yml @@ -5,6 +5,13 @@ name: coturn state: present install_recommends: yes + +- name: "Install dnsutils package" + apt: + name: dnsutils + state: present + install_recommends: no + - name: "Disable coturn service" service: name: coturn @@ -17,8 +24,3 @@ copy: src: ../files/turnserver.conf dest: /etc/turnserver.conf -- name: Deploy coturn start script - copy: - src: ../files/start-coturn.sh - dest: /usr/local/bin/ - mode: 755 diff --git a/ansible/tasks/prosody.yml b/ansible/tasks/prosody.yml index 8009e6e..e2da573 100644 --- a/ansible/tasks/prosody.yml +++ b/ansible/tasks/prosody.yml @@ -89,6 +89,7 @@ - mod_default_bookmarks - mod_muc_defaults - mod_firewall + - mod_turncredentials - name: Install Bootstrap and JS libs diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 1fc73ec..0291a7f 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -9,6 +9,10 @@ if [ -z "$SNIKKET_SMTP_URL" ]; then SNIKKET_SMTP_URL="smtp://localhost:1025/;no-tls" fi +if [ -z "$SNIKKET_EXTERNAL_IP" ]; then + SNIKKET_EXTERNAL_IP="$(dig +short $SNIKKET_DOMAIN)" +fi + echo "$SNIKKET_SMTP_URL" | smtp-url-to-msmtp > /etc/msmtprc echo "from snikket@$SNIKKET_DOMAIN" >> /etc/msmtprc