diff --git a/ansible/meetserver/.env.local b/ansible/meetserver/.env.local new file mode 100644 --- /dev/null +++ b/ansible/meetserver/.env.local @@ -0,0 +1,36 @@ +MFA_DSN=mysql://root:Welcome2KolabSystems@127.0.0.1/roundcube +APP_DOMAIN={{ host }} +APP_WEBSITE_DOMAIN={{ host }} +APP_KEY=base64:FG6ECzyAMSmyX+eYwO/FW3bwnarbKkBhqtO65vlMb1E= +APP_PUBLIC_URL=https://{{ host }} +COTURN_STATIC_SECRET=uzYguvIl9tpZFMuQOE78DpOi6Jc7VFSD0UAnvgMsg5n4e74MgIf6vQvbc6LWzZjz +COTURN_PUBLIC_IP='{{ public_ip }}' +MEET_TURN_SERVER='turn:{{ public_ip }}:3478' +MEET_WEBRTC_LISTEN_IP='{{ public_ip }}' +MEET_PUBLIC_DOMAIN={{ host }} +MEET_SERVER_URLS=https://{{ host }}/meetmedia/api/ +APP_URL=https://{{ host }} +ASSET_URL=https://{{ host }} + +MOLLIE_KEY= +STRIPE_KEY= +STRIPE_PUBLIC_KEY= +STRIPE_WEBHOOK_SECRET= + +SWOOLE_HOT_RELOAD_ENABLE=true +SWOOLE_HTTP_HOST={{ host }} +SWOOLE_HTTP_PORT=8000 +#SWOOLE_HTTP_ACCESS_LOG=true +SWOOLE_HTTP_REACTOR_NUM=8 +SWOOLE_HTTP_WORKER_NUM=32 +OPENEXCHANGERATES_API_KEY={{ openexchangerates_api_key }} +FIREBASE_API_KEY={{ firebase_api_key }} + +#Generated by php artisan passport:client --password, but can be left hardcoded (the seeder will pick it up) +PASSPORT_PROXY_OAUTH_CLIENT_ID=942edef5-3dbd-4a14-8e3e-d5d59b727bee +PASSPORT_PROXY_OAUTH_CLIENT_SECRET=L6L0n56ecvjjK0cJMjeeV1pPAeffUBO0YSSH63wf + +APP_TENANT_ID=42 +APP_PASSPHRASE=simple123 + +MAIL_DRIVER=log diff --git a/ansible/meetserver/Makefile b/ansible/meetserver/Makefile new file mode 100644 --- /dev/null +++ b/ansible/meetserver/Makefile @@ -0,0 +1,10 @@ +HOSTNAME=10.10.4.5 +PUBLIC_IP=212.103.80.171 +PUBLIC_DOMAIN=stun-dev.kolab.io +AUTH_TOKEN="AUTHTOKEN" +TURN_STATIC_SECRET="TURNSTATICSECRET" + +setup: + touch ./hosts + echo "${HOSTNAME}" > ./hosts + ansible-playbook -v --inventory=./hosts --extra-vars="hostname=${HOSTNAME} public_domain=${PUBLIC_DOMAIN} public_ip=${PUBLIC_IP} auth_token=${AUTH_TOKEN} turn_static_secret=${TURN_STATIC_SECRET}" setup.yml diff --git a/ansible/meetserver/README.md b/ansible/meetserver/README.md new file mode 100644 --- /dev/null +++ b/ansible/meetserver/README.md @@ -0,0 +1,3 @@ +# Setup a new meet media node. + +Includes coturn and the media server. diff --git a/ansible/meetserver/hosts b/ansible/meetserver/hosts new file mode 100644 --- /dev/null +++ b/ansible/meetserver/hosts @@ -0,0 +1 @@ +10.10.4.5 diff --git a/ansible/meetserver/kolabmeet.service b/ansible/meetserver/kolabmeet.service new file mode 100644 --- /dev/null +++ b/ansible/meetserver/kolabmeet.service @@ -0,0 +1,13 @@ +[Unit] +Description=Kolab Meet +After=network.target + +[Service] +Environment=DEBUG="kolabmeet-server*" +Type=simple +User=kolab +ExecStart=/usr/bin/node /home/kolab/kolab/meet/server/server.js +Restart=on-failure + +[Install] +WantedBy=multi-user.target diff --git a/ansible/meetserver/meetconfig.js b/ansible/meetserver/meetconfig.js new file mode 100644 --- /dev/null +++ b/ansible/meetserver/meetconfig.js @@ -0,0 +1,122 @@ +const os = require('os'); + +module.exports = +{ + // Authentication token for API (not websocket) requests + authToken: '{{ auth_token }}', + // Turn server configuration + turn: { + urls: [ + 'turn:{{ public_domain }}:3478', + // 'turns:{{ public_ip }}:443', + ], + staticSecret: '{{ turn_static_secret }}', + }, + // Webhook URL + webhookURL: 'xtian.dev.kolab.io/api/webhooks/meet', + // Webhook authentication token + webhookToken: 'Welcome2KolabSystems', + // if you use encrypted private key the set the passphrase + tls: { + cert: '/etc/letsencrypt/live/stun-dev.kolab.io/fullchain.pem', + key: '/etc/letsencrypt/live/stun-dev.kolab.io/privkey.pem', + }, + // listening Host or IP + // Use "0.0.0.0" or "::") to listen on every IP. + listeningHost: "0.0.0.0", + // Listening port for https server. + listeningPort: 12443, + // Used to establish the websocket connection from the client. + publicDomain: '{{ public_domain }}:12443', + // API path prefix + pathPrefix: '/meetmedia', + // Room size before spreading to new router + routerScaleSize: 16, + // Socket timeout value + requestTimeout: 20000, + // Socket retries when timeout + requestRetries: 3, + // Mediasoup settings + mediasoup: { + numWorkers: Object.keys(os.cpus()).length, + // mediasoup Worker settings. + worker: { + logLevel: 'warn', + logTags: [ + 'info', + 'ice', + 'dtls', + 'rtp', + 'srtp', + 'rtcp' + ], + rtcMinPort: 40000, + rtcMaxPort: 49999 + }, + // mediasoup Router settings. + router: { + // Router media codecs. + mediaCodecs: [ + { + kind : 'audio', + mimeType : 'audio/opus', + clockRate : 48000, + channels : 2 + }, + { + kind : 'video', + mimeType : 'video/VP8', + clockRate : 90000, + parameters : + { + 'x-google-start-bitrate' : 1000 + } + }, + { + kind : 'video', + mimeType : 'video/VP9', + clockRate : 90000, + parameters : + { + 'profile-id' : 2, + 'x-google-start-bitrate' : 1000 + } + }, + { + kind : 'video', + mimeType : 'video/h264', + clockRate : 90000, + parameters : + { + 'packetization-mode' : 1, + 'profile-level-id' : '4d0032', + 'level-asymmetry-allowed' : 1, + 'x-google-start-bitrate' : 1000 + } + }, + { + kind : 'video', + mimeType : 'video/h264', + clockRate : 90000, + parameters : + { + 'packetization-mode' : 1, + 'profile-level-id' : '42e01f', + 'level-asymmetry-allowed' : 1, + 'x-google-start-bitrate' : 1000 + } + } + ] + }, + // mediasoup WebRtcTransport settings. + webRtcTransport: { + listenIps: [ + { ip: '{{ public_ip }}', announcedIp: null } + ], + initialAvailableOutgoingBitrate: 1000000, + minimumAvailableOutgoingBitrate: 600000, + // Additional options that are not part of WebRtcTransportOptions. + maxIncomingBitrate: 1500000 + } + } +}; diff --git a/ansible/meetserver/packages.yml b/ansible/meetserver/packages.yml new file mode 100644 --- /dev/null +++ b/ansible/meetserver/packages.yml @@ -0,0 +1,14 @@ +--- +- name: Install list of required packages + package: name={{ item }} state=installed + with_items: + - git + - npm + - certbot + - coturn + - make + - clang + - meson + - ninja-build + - python3 + - python3-pip diff --git a/ansible/meetserver/setup.yml b/ansible/meetserver/setup.yml new file mode 100755 --- /dev/null +++ b/ansible/meetserver/setup.yml @@ -0,0 +1,127 @@ +#!/usr/bin/ansible-playbook +- name: Setup kolab deployment on fedora server + hosts: "{{ hostname }}" + remote_user: root + tasks: + - import_tasks: packages.yml + + - name: Setup user kolab + ansible.builtin.user: + name: kolab + shell: /bin/bash + groups: wheel, audio + append: yes + + - name: sudo without password + ansible.builtin.lineinfile: + path: /etc/sudoers + state: present + regexp: '^%wheel\s' + line: '%wheel ALL=(ALL) NOPASSWD: ALL' + + - name: get kolab git repo + become: true + become_user: kolab + git: + repo: https://git.kolab.org/source/kolab.git + dest: /home/kolab/kolab + version: dev/mollekopf + force: true + + - name: Permit https traffic + firewalld: + port: 12443/tcp + permanent: yes + state: enabled + zone: FedoraServer + + - name: Permit TCP trafic for coturn + firewalld: + port: 3478/tcp + permanent: yes + state: enabled + zone: FedoraServer + + - name: Permit TCP trafic for coturn + firewalld: + port: 5349/tcp + permanent: yes + state: enabled + zone: FedoraServer + + - name: Permit UDP trafic for coturn + firewalld: + port: 3478/udp + permanent: yes + state: enabled + zone: FedoraServer + + - name: Permit UDP trafic for coturn + firewalld: + port: 5349/udp + permanent: yes + state: enabled + zone: FedoraServer + + - name: "coturn config" + vars: + public_ip: "{{ public_ip }}" + turn_static_secret: "{{ turn_static_secret }}" + ansible.builtin.template: + src: turnserver.conf + dest: /etc/coturn/turnserver.conf + owner: root + group: coturn + mode: '0766' + + - name: Start coturn service + ansible.builtin.service: + name: coturn + state: restarted + + - name: "meet config" + vars: + public_ip: "{{ public_ip }}" + public_domain: "{{ public_domain }}" + turn_static_secret: "{{ turn_static_secret }}" + auth_token: "{{ auth_token }}" + ansible.builtin.template: + src: meetconfig.js + dest: /home/kolab/kolab/meet/server/config/config.js + owner: kolab + group: kolab + mode: '0766' + + - name: "meet service file" + ansible.builtin.template: + src: kolabmeet.service + dest: /usr/lib/systemd/system/kolabmeet.service + + - name: Start meet + ansible.builtin.service: + name: meet + daemon_reload: yes + state: restarted + + # Certbot + - name: stop firewall + ansible.builtin.service: + name: firewalld + state: stopped + + - name: Create letsencrypt certificate + shell: certonly --standalone -d {{ public_domain }} --staple-ocsp -m test@{{ public_domain }} --agree-tos + args: + creates: /etc/letsencrypt/live/{{ public_domain }} + + - name: chmod letsencrypt certificate + shell: chmod 755 /etc/letsencrypt/live + shell: chmod 755 /etc/letsencrypt/archive + + - name: start firewall + ansible.builtin.service: + name: firewalld + state: started + + # # TODO build and start meet + # # TODO coturn on port 443? diff --git a/ansible/meetserver/turnserver.conf b/ansible/meetserver/turnserver.conf new file mode 100644 --- /dev/null +++ b/ansible/meetserver/turnserver.conf @@ -0,0 +1,22 @@ +external-ip={{ public_ip }} +listening-ip={{ public_ip }} +listening-port=3478 +fingerprint + +# For testing +#allow-loopback-peers +#cli-password=qwerty + +# Disabled by default to avoid DoS attacks. Logs all bind attempts in verbose log mode (useful for debugging) +#log-binding + +max-port=65535 +min-port=40000 +realm=kolabmeet +syslog + +# Dynamically generate username/password for turn +use-auth-secret +static-auth-secret={{ turn_static_secret }} + +# verbose