From dbac5ede16a4a88465b08765dccfa85c73cd5c08 Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Thu, 8 Oct 2026 10:23:17 +0200 Subject: [PATCH 1/7] Add a role for Jitsi in Docker Compose We need this role to follow the recommended deployment approach from https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker/ This role enables us to keep our config and customizations separate from releases of the Compose setup the Jitsi project publishs. --- playbooks/deploy.yaml | 7 + roles/jitsi/README.md | 57 ++++++ roles/jitsi/defaults/main.yaml | 8 + roles/jitsi/handlers/main.yaml | 19 ++ roles/jitsi/meta/argument_specs.yaml | 57 ++++++ roles/jitsi/meta/main.yaml | 4 + roles/jitsi/tasks/main.yaml | 186 ++++++++++++++++++ .../jitsi/templates/compose.override.yaml.j2 | 40 ++++ roles/jitsi/templates/jitsi.env.j2 | 46 +++++ roles/jitsi/templates/turnserver.conf.j2 | 34 ++++ roles/jitsi/vars/main.yaml | 4 + 11 files changed, 462 insertions(+) create mode 100644 roles/jitsi/README.md create mode 100644 roles/jitsi/defaults/main.yaml create mode 100644 roles/jitsi/handlers/main.yaml create mode 100644 roles/jitsi/meta/argument_specs.yaml create mode 100644 roles/jitsi/meta/main.yaml create mode 100644 roles/jitsi/tasks/main.yaml create mode 100644 roles/jitsi/templates/compose.override.yaml.j2 create mode 100644 roles/jitsi/templates/jitsi.env.j2 create mode 100644 roles/jitsi/templates/turnserver.conf.j2 create mode 100644 roles/jitsi/vars/main.yaml diff --git a/playbooks/deploy.yaml b/playbooks/deploy.yaml index 2a1498ac..fd86de91 100644 --- a/playbooks/deploy.yaml +++ b/playbooks/deploy.yaml @@ -110,6 +110,13 @@ tags: - docker_compose +- name: Ensure Jitsi deployment on jitsi_hosts + hosts: jitsi_hosts + roles: + - jitsi + tags: + - jitsi + - name: Ensure NGINX deployment on nginx_hosts hosts: nginx_hosts:!public_reverse_proxy_hosts roles: diff --git a/roles/jitsi/README.md b/roles/jitsi/README.md new file mode 100644 index 00000000..bd3db9bb --- /dev/null +++ b/roles/jitsi/README.md @@ -0,0 +1,57 @@ +# Role `jitsi` + +A role for deploying [Jitsi Meet](https://jitsi.org/) using the official [docker-jitsi-meet](https://github.com/jitsi/docker-jitsi-meet) release. + +The given release is downloaded and unpacked unmodified to `/opt/jitsi/releases/` and `/opt/jitsi/release` is pointed to it. +The `.env` file at `/opt/jitsi/.env` is fully managed by this role; `env.example` and `gen-passwords.sh` of the release are not used. +The `.env` also sets `COMPOSE_PROJECT_NAME` and `COMPOSE_FILE`, so `docker compose` can be run by hand in `/opt/jitsi`. +The Jitsi configuration directory (`CONFIG`) is `/opt/jitsi/config`, with persistent state in `/opt/jitsi/config/storage/`. +The image version (`JITSI_IMAGE_VERSION`) always equals the release version, so upgrading means bumping `jitsi__version`. +Releases other than the configured one are removed. + +TLS certificates are obtained by the web container itself using Let's Encrypt, so ports 80/tcp and 443/tcp as well as 10000/udp (media) must be reachable. + +Optionally, a [coturn](https://github.com/coturn/coturn) TURN server and the [Prometheus Jitsi Meet exporter](https://github.com/systemli/prometheus-jitsi-meet-exporter) are deployed as additional services of the same Compose project, defined in `/opt/jitsi/compose.override.yaml`. +coturn uses the certificate obtained by the web container and is restarted weekly to pick up renewals. +It needs 3478/udp+tcp, 5349/tcp and the relay port range 49152-65535/udp to be reachable. +The exporter scrapes the JVB statistics and listens on port 9888. + +## Supported Distributions + +Should work on Debian-based distributions. + +## Required Arguments + +- `jitsi__version`: The docker-jitsi-meet release to deploy, e.g. `stable-11248`. +- `jitsi__domain`: The domain Jitsi is served on. Used for `PUBLIC_URL` and the Let's Encrypt certificate. +- `jitsi__letsencrypt_email`: The e-mail address for the Let's Encrypt account. +- `jitsi__jicofo_auth_password`: XMPP password for Jicofo. Generate using `openssl rand -hex 16`. +- `jitsi__jvb_auth_password`: XMPP password for the JVB. Generate using `openssl rand -hex 16`. + +## Optional Arguments + +- `jitsi__timezone`: The time zone for the containers. + Defaults to `Europe/Berlin`. +- `jitsi__jvb_advertise_ips`: List of IP addresses the JVB should advertise for media (`JVB_ADVERTISE_IPS`). + Defaults to `[ ]`. +- `jitsi__whiteboard`: Whether to deploy the Excalidraw whiteboard backend (`whiteboard.yml`). + Defaults to `false`. +- `jitsi__turn`: Whether to deploy coturn and configure Jitsi to use it. + Defaults to `false`. +- `jitsi__turn_secret`: The shared secret between Prosody and coturn. Required if `jitsi__turn` is `true`. + Generate using `openssl rand -hex 32`. +- `jitsi__prometheus_exporter`: Whether to deploy the Prometheus Jitsi Meet exporter on port 9888. + Defaults to `false`. +- `jitsi__env`: Dictionary of additional variables to put into the `.env` file. + See `env.example` of the release and the handbook for available variables. + Defaults to `{ }`. +- `jitsi__custom_config_js`: Content of `custom-config.js`, which gets appended to the generated `config.js`. + Defaults to `""` (no file). +- `jitsi__custom_interface_config_js`: Content of `custom-interface_config.js`, which gets appended to the generated `interface_config.js`. + Defaults to `""` (no file). + +## Links & Resources + +- +- +- diff --git a/roles/jitsi/defaults/main.yaml b/roles/jitsi/defaults/main.yaml new file mode 100644 index 00000000..9782b636 --- /dev/null +++ b/roles/jitsi/defaults/main.yaml @@ -0,0 +1,8 @@ +jitsi__timezone: Europe/Berlin +jitsi__jvb_advertise_ips: [ ] +jitsi__whiteboard: false +jitsi__turn: false +jitsi__prometheus_exporter: false +jitsi__env: { } +jitsi__custom_config_js: "" +jitsi__custom_interface_config_js: "" diff --git a/roles/jitsi/handlers/main.yaml b/roles/jitsi/handlers/main.yaml new file mode 100644 index 00000000..7bde7f02 --- /dev/null +++ b/roles/jitsi/handlers/main.yaml @@ -0,0 +1,19 @@ +- name: restart jitsi web + community.docker.docker_compose_v2: + project_src: "{{ jitsi__base_dir }}" + project_name: jitsi + files: "{{ jitsi__compose_files }}" + services: + - web + state: restarted + become: true + +- name: restart jitsi coturn + community.docker.docker_compose_v2: + project_src: "{{ jitsi__base_dir }}" + project_name: jitsi + files: "{{ jitsi__compose_files }}" + services: + - coturn + state: restarted + become: true diff --git a/roles/jitsi/meta/argument_specs.yaml b/roles/jitsi/meta/argument_specs.yaml new file mode 100644 index 00000000..2c65ca54 --- /dev/null +++ b/roles/jitsi/meta/argument_specs.yaml @@ -0,0 +1,57 @@ +argument_specs: + main: + options: + jitsi__version: + type: str + required: true + jitsi__domain: + type: str + required: true + jitsi__letsencrypt_email: + type: str + required: true + jitsi__jicofo_auth_password: + type: str + required: true + no_log: true + jitsi__jvb_auth_password: + type: str + required: true + no_log: true + jitsi__timezone: + type: str + required: false + default: Europe/Berlin + jitsi__jvb_advertise_ips: + type: list + elements: str + required: false + default: [ ] + jitsi__whiteboard: + type: bool + required: false + default: false + jitsi__turn: + type: bool + required: false + default: false + jitsi__turn_secret: + type: str + required: false + no_log: true + jitsi__prometheus_exporter: + type: bool + required: false + default: false + jitsi__env: + type: dict + required: false + default: { } + jitsi__custom_config_js: + type: str + required: false + default: "" + jitsi__custom_interface_config_js: + type: str + required: false + default: "" diff --git a/roles/jitsi/meta/main.yaml b/roles/jitsi/meta/main.yaml new file mode 100644 index 00000000..1fbb2364 --- /dev/null +++ b/roles/jitsi/meta/main.yaml @@ -0,0 +1,4 @@ +--- +allow_duplicates: false +dependencies: + - role: docker diff --git a/roles/jitsi/tasks/main.yaml b/roles/jitsi/tasks/main.yaml new file mode 100644 index 00000000..a7beaaa4 --- /dev/null +++ b/roles/jitsi/tasks/main.yaml @@ -0,0 +1,186 @@ +- name: make sure a TURN secret is given, if TURN is enabled + ansible.builtin.assert: + that: + - jitsi__turn_secret is defined + - jitsi__turn_secret | length > 0 + quiet: true + when: jitsi__turn + +- name: make sure the Jitsi base directories exist + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + owner: root + group: root + become: true + loop: + - "{{ jitsi__base_dir }}" + - "{{ jitsi__base_dir }}/releases" + +- name: make sure the docker-jitsi-meet release is present + ansible.builtin.unarchive: + src: "https://github.com/jitsi/docker-jitsi-meet/archive/refs/tags/{{ jitsi__version }}.tar.gz" + remote_src: true + dest: "{{ jitsi__base_dir }}/releases" + creates: "{{ jitsi__release_dir }}/docker-compose.yml" + owner: root + group: root + become: true + +- name: point the `release` symlink to the configured release + ansible.builtin.file: + src: "{{ jitsi__release_dir }}" + dest: "{{ jitsi__base_dir }}/release" + state: link + owner: root + group: root + become: true + +# Configuration read by the containers at startup; managed by Ansible. +- name: make sure the configuration input directories exist + ansible.builtin.file: + path: "{{ jitsi__config_dir }}/{{ item }}" + state: directory + mode: "0755" + owner: root + group: root + become: true + loop: + - "" + - web + - prosody + - prosody/config + - prosody/prosody-plugins-custom + - jicofo + - jvb + - storage + - tmp + +# State written by the containers, which run as uid/gid 1000. +# The certificate directory is created here, as Docker would otherwise create it owned by +# root for the coturn bind mount, before the web container gets to write the certificate. +- name: make sure the storage and tmp directories exist and are writable by the containers + ansible.builtin.file: + path: "{{ jitsi__config_dir }}/{{ item }}" + state: directory + mode: "0750" + owner: "1000" + group: "1000" + become: true + loop: + - storage/prosody + - storage/web + - storage/web/acme-certs + - "storage/web/acme-certs/{{ jitsi__domain }}" + - storage/transcripts + - tmp/web-crontabs + - tmp/web-load-test + +- name: deploy custom web configuration + ansible.builtin.copy: + content: "{{ item.content }}" + dest: "{{ jitsi__config_dir }}/web/{{ item.name }}" + mode: "0644" + owner: root + group: root + become: true + when: item.content != "" + loop: + - name: custom-config.js + content: "{{ jitsi__custom_config_js }}" + - name: custom-interface_config.js + content: "{{ jitsi__custom_interface_config_js }}" + loop_control: + label: "{{ item.name }}" + notify: restart jitsi web + +- name: remove unconfigured custom web configuration + ansible.builtin.file: + path: "{{ jitsi__config_dir }}/web/{{ item.name }}" + state: absent + become: true + when: item.content == "" + loop: + - name: custom-config.js + content: "{{ jitsi__custom_config_js }}" + - name: custom-interface_config.js + content: "{{ jitsi__custom_interface_config_js }}" + loop_control: + label: "{{ item.name }}" + notify: restart jitsi web + +- name: make sure the coturn directory exists + ansible.builtin.file: + path: "{{ jitsi__base_dir }}/coturn" + state: "{{ 'directory' if jitsi__turn else 'absent' }}" + mode: "0755" + owner: root + group: root + become: true + +# Readable by uid 1000, which coturn runs as. +- name: deploy the coturn configuration + ansible.builtin.template: + src: turnserver.conf.j2 + dest: "{{ jitsi__base_dir }}/coturn/turnserver.conf" + mode: "0640" + owner: root + group: "1000" + become: true + when: jitsi__turn + notify: restart jitsi coturn + +# coturn doesn't notice the certificate renewed by the web container on its own. +- name: ensure the weekly coturn restart cron job is present + ansible.builtin.cron: + name: "jitsi coturn restart for certificate renewal" + special_time: weekly + job: "cd {{ jitsi__base_dir }} && docker compose restart coturn >/dev/null" + state: "{{ 'present' if jitsi__turn else 'absent' }}" + become: true + +# Changes to these make `docker compose up` below recreate the affected containers. +- name: deploy the compose override file + ansible.builtin.template: + src: compose.override.yaml.j2 + dest: "{{ jitsi__base_dir }}/compose.override.yaml" + mode: "0644" + owner: root + group: root + become: true + +- name: deploy the .env file + ansible.builtin.template: + src: jitsi.env.j2 + dest: "{{ jitsi__base_dir }}/.env" + mode: "0600" + owner: root + group: root + become: true + +- name: docker compose up + community.docker.docker_compose_v2: + project_src: "{{ jitsi__base_dir }}" + project_name: jitsi + files: "{{ jitsi__compose_files }}" + state: present + pull: missing + remove_orphans: true + become: true + +- name: find old releases + ansible.builtin.find: + paths: "{{ jitsi__base_dir }}/releases" + file_type: directory + excludes: "docker-jitsi-meet-{{ jitsi__version }}" + register: jitsi__old_releases + +- name: remove old releases + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + become: true + loop: "{{ jitsi__old_releases.files }}" + loop_control: + label: "{{ item.path }}" diff --git a/roles/jitsi/templates/compose.override.yaml.j2 b/roles/jitsi/templates/compose.override.yaml.j2 new file mode 100644 index 00000000..05842569 --- /dev/null +++ b/roles/jitsi/templates/compose.override.yaml.j2 @@ -0,0 +1,40 @@ +# {{ ansible_managed }} +# Services in addition to the ones of the docker-jitsi-meet release. + +# Docker Compose explicitly disables IPv6 for networks not enabling it, overriding the +# daemon's default-network-opts. +networks: + meet.jitsi: + enable_ipv6: true + +services: +{% if jitsi__turn %} + coturn: + image: docker.io/coturn/coturn:4.18.0 + restart: unless-stopped + # Recommended by coturn, as Docker performs badly with large port ranges. + network_mode: host + # Needs to be able to read the certificate obtained by the web container. + user: "1000:1000" + command: ["-c", "/etc/coturn/turnserver.conf"] + volumes: + - {{ jitsi__base_dir }}/coturn/turnserver.conf:/etc/coturn/turnserver.conf:ro + - {{ jitsi__config_dir }}/storage/web/acme-certs/{{ jitsi__domain }}:/certs:ro + tmpfs: + - /var/lib/coturn +{% endif %} +{% if jitsi__prometheus_exporter %} + prometheus-jitsi-meet-exporter: + image: docker.io/systemli/prometheus-jitsi-meet-exporter:1.4.2 + restart: unless-stopped + command: ["-videobridge-url", "http://jvb:8080/colibri/stats"] + ports: + - "9888:9888" + depends_on: + - jvb + networks: + meet.jitsi: +{% endif %} +{% if not (jitsi__turn or jitsi__prometheus_exporter) %} + {} +{% endif %} diff --git a/roles/jitsi/templates/jitsi.env.j2 b/roles/jitsi/templates/jitsi.env.j2 new file mode 100644 index 00000000..f09b0414 --- /dev/null +++ b/roles/jitsi/templates/jitsi.env.j2 @@ -0,0 +1,46 @@ +# {{ ansible_managed }} +# Only the settings differing from the defaults are set here. +# Reference: {{ jitsi__base_dir }}/release/env.example and +# https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker + +# Allow running `docker compose` by hand in {{ jitsi__base_dir }}. +COMPOSE_PROJECT_NAME=jitsi +COMPOSE_FILE={{ jitsi__compose_files | join(':') }} + +CONFIG={{ jitsi__config_dir }} +JITSI_IMAGE_VERSION={{ jitsi__version }} +TZ={{ jitsi__timezone }} +PUBLIC_URL=https://{{ jitsi__domain }} + +HTTP_PORT=80 +HTTPS_PORT=443 +ENABLE_HTTP_REDIRECT=1 +ENABLE_LETSENCRYPT=1 +LETSENCRYPT_DOMAIN={{ jitsi__domain }} +LETSENCRYPT_EMAIL={{ jitsi__letsencrypt_email }} +LETSENCRYPT_ACME_SERVER=letsencrypt +{% if jitsi__jvb_advertise_ips | length > 0 %} +JVB_ADVERTISE_IPS={{ jitsi__jvb_advertise_ips | join(',') }} +{% endif %} +{% if jitsi__whiteboard %} + +WHITEBOARD_COLLAB_SERVER_URL_BASE=http://whiteboard.meet.jitsi +{% endif %} +{% if jitsi__turn %} + +TURN_CREDENTIALS={{ jitsi__turn_secret }} +TURN_HOST={{ jitsi__domain }} +TURN_PORT=3478 +TURN_TRANSPORT=udp,tcp +TURNS_HOST={{ jitsi__domain }} +TURNS_PORT=5349 +{% endif %} +{% if jitsi__env | length > 0 %} + +{% for key, value in jitsi__env.items() %} +{{ key }}={{ value }} +{% endfor %} +{% endif %} + +JICOFO_AUTH_PASSWORD={{ jitsi__jicofo_auth_password }} +JVB_AUTH_PASSWORD={{ jitsi__jvb_auth_password }} diff --git a/roles/jitsi/templates/turnserver.conf.j2 b/roles/jitsi/templates/turnserver.conf.j2 new file mode 100644 index 00000000..28770f81 --- /dev/null +++ b/roles/jitsi/templates/turnserver.conf.j2 @@ -0,0 +1,34 @@ +# {{ ansible_managed }} +listening-port=3478 +tls-listening-port=5349 +realm={{ jitsi__domain }} +use-auth-secret +static-auth-secret={{ jitsi__turn_secret }} +cert=/certs/fullchain.pem +pkey=/certs/key.pem +no-tlsv1 +no-tlsv1_1 +no-cli +no-multicast-peers +no-loopback-peers +log-file=stdout +# Don't relay to private and otherwise special addresses. +denied-peer-ip=0.0.0.0-0.255.255.255 +denied-peer-ip=10.0.0.0-10.255.255.255 +denied-peer-ip=100.64.0.0-100.127.255.255 +denied-peer-ip=127.0.0.0-127.255.255.255 +denied-peer-ip=169.254.0.0-169.254.255.255 +denied-peer-ip=172.16.0.0-172.31.255.255 +denied-peer-ip=192.0.0.0-192.0.0.255 +denied-peer-ip=192.0.2.0-192.0.2.255 +denied-peer-ip=192.88.99.0-192.88.99.255 +denied-peer-ip=192.168.0.0-192.168.255.255 +denied-peer-ip=198.18.0.0-198.19.255.255 +denied-peer-ip=198.51.100.0-198.51.100.255 +denied-peer-ip=203.0.113.0-203.0.113.255 +denied-peer-ip=240.0.0.0-255.255.255.255 +denied-peer-ip=::1 +denied-peer-ip=64:ff9b::-64:ff9b::ffff:ffff +denied-peer-ip=::ffff:0.0.0.0-::ffff:255.255.255.255 +denied-peer-ip=fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff +denied-peer-ip=fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff diff --git a/roles/jitsi/vars/main.yaml b/roles/jitsi/vars/main.yaml new file mode 100644 index 00000000..21fe76b6 --- /dev/null +++ b/roles/jitsi/vars/main.yaml @@ -0,0 +1,4 @@ +jitsi__base_dir: /opt/jitsi +jitsi__config_dir: "{{ jitsi__base_dir }}/config" +jitsi__release_dir: "{{ jitsi__base_dir }}/releases/docker-jitsi-meet-{{ jitsi__version }}" +jitsi__compose_files: "{{ ['release/docker-compose.yml'] + (['release/whiteboard.yml'] if jitsi__whiteboard else []) + ['compose.override.yaml'] }}" From 86e793c62420c02538fe9bcb428159b17a521f70 Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Thu, 8 Oct 2026 10:24:38 +0200 Subject: [PATCH 2/7] Use jitsi role to configure jitsi The host has been manually cleaned up (removed old packages and config) and this config has been deployed already. --- .../chaosknoten/host_vars/jitsi.sops.yaml | 7 +- inventories/chaosknoten/host_vars/jitsi.yaml | 13 ++++ inventories/chaosknoten/hosts.yaml | 4 ++ .../chaosknoten/jitsi/nftables/nftables.conf | 65 +++++++++++++++++++ roles/nftables/tasks/main.yaml | 7 ++ 5 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 inventories/chaosknoten/host_vars/jitsi.yaml create mode 100644 resources/chaosknoten/jitsi/nftables/nftables.conf diff --git a/inventories/chaosknoten/host_vars/jitsi.sops.yaml b/inventories/chaosknoten/host_vars/jitsi.sops.yaml index ce335073..adae05aa 100644 --- a/inventories/chaosknoten/host_vars/jitsi.sops.yaml +++ b/inventories/chaosknoten/host_vars/jitsi.sops.yaml @@ -1,4 +1,7 @@ ansible_pull__age_private_key: ENC[AES256_GCM,data:8PCkZx76R5aWPITn5MCZCMckXvaLfVvnDgO70XmF/eeH5xLVOVx+EC3DZjmHxEoW7Kn4LAJTR4mWl/xP+p++elRvTMcHltV+vBQ=,iv:jKq07NLM84MSGU4CCyUS9ODmwiUMhlDONkKJ7P9P2DM=,tag:95yBZlwYdSNeX2O4t/h9kw==,type:str] +jitsi__jicofo_auth_password: ENC[AES256_GCM,data:a7y36RJUR4OBtVNzgmqlGEsg1hNRGgsuwG2YFJrDlMI=,iv:mnGlKOXtNltNe3wY34BnVCJ9CtLQ7aksr4xXbrTOnJg=,tag:j1N7MUTJG8K0JW/a6YESFg==,type:str] +jitsi__jvb_auth_password: ENC[AES256_GCM,data:xUFiIXAzY4pEpwgL/5LPBlhkx73RX6VFT3QJlG5QbFo=,iv:/XqENztrrpj1h3lBhEtJJo78lCrxrMWxRszNjSwx+KQ=,tag:+LRYmv9Tvt0muR47QTd6HA==,type:str] +jitsi__turn_secret: ENC[AES256_GCM,data:UPev9ND9Mf2gGK28huQuuxRyUZvPPCmFL9rU1f0zndSPPBMspSsnlgtMy0+VY/CsGEIEcWsy3eYDPcAW/EKFtA==,iv:ew0MKP5zdQvqfla2y7xkcXLm3qSMlf5sucn8Qu3G3Zs=,tag:dqe239LD+wPVqLQFF+nz2A==,type:str] sops: age: - enc: | @@ -30,8 +33,8 @@ sops: QzBBdJ6TiDwJ1rwwM211smfIbcYhWX6C/EfitI5XoIW8aS4ln1g1Ww== -----END AGE ENCRYPTED FILE----- recipient: age187zdk64205lx7r83a4m26pfa8hq6khc8sf83k266jasngeq08v3sfqswfg - lastmodified: "2026-07-29T20:26:44Z" - mac: ENC[AES256_GCM,data:dHiB768SBppBrjNoSpgDvriCgrSNzIkpSYzm9REx4otbnvNjHYPhP9OdsZ6zTcoFoX+41/zE13r4EoAz4F++jdJidv1+7DMFvVUKMuFNzp2VjDfB1ZNzzyM+px7l83L5eNhgNPp26mweOVMEz92NgB916KMY+5e3uUeSR+iIdho=,iv:7k8rugBXULjPSOZukQiufNn9Vs5uWVhcvFKIhD1o7so=,tag:IE+2uhzGS2xJJgFHuc94CA==,type:str] + lastmodified: "2026-10-08T07:56:43Z" + mac: ENC[AES256_GCM,data:CgD19xHN5nEg9TMQq/c4Xpv1LH0LfcXrkfBMUcbJiIn4FsSzQetLYxueeDXUVOujOb9ZSEin1nR+OZwa/nuwN9mTg37c0XZ6HV0UvNNEIrN/o4doUCQa2jLmbX8etxeNhF7oWJ84OgQIhPLvLwp3Z0A/+kPKbmtz3znQNgKoHQY=,iv:6Dt7z55az2SJovJ7ozG35Q99a08kOrKl/uQCUVcbq8c=,tag:M6p0ZJzoFnWgspnGknq7SQ==,type:str] pgp: - created_at: "2026-09-24T19:54:13Z" enc: |- diff --git a/inventories/chaosknoten/host_vars/jitsi.yaml b/inventories/chaosknoten/host_vars/jitsi.yaml new file mode 100644 index 00000000..3673cbf9 --- /dev/null +++ b/inventories/chaosknoten/host_vars/jitsi.yaml @@ -0,0 +1,13 @@ +nftables__config: "{{ lookup('ansible.builtin.file', 'resources/chaosknoten/jitsi/nftables/nftables.conf') }}" + +# renovate: datasource=github-releases depName=jitsi/docker-jitsi-meet versioning=regex:^stable-(?\d+)$ +jitsi__version: stable-11248 +jitsi__domain: jitsi.hamburg.ccc.de +jitsi__letsencrypt_email: le-admin@hamburg.ccc.de +jitsi__jvb_advertise_ips: + - 212.12.51.139 + - 2a00:14b0:f000:23:51:139:0:1 +jitsi__whiteboard: true +jitsi__turn: true +jitsi__prometheus_exporter: true +# jitsi__jicofo_auth_password, jitsi__jvb_auth_password and jitsi__turn_secret are defined in jitsi.sops.yaml. diff --git a/inventories/chaosknoten/hosts.yaml b/inventories/chaosknoten/hosts.yaml index 7d641ae3..fad62d3c 100644 --- a/inventories/chaosknoten/hosts.yaml +++ b/inventories/chaosknoten/hosts.yaml @@ -141,6 +141,7 @@ systemd_networkd_hosts: router: nftables_hosts: hosts: + jitsi: router: docker_compose_hosts: hosts: @@ -258,6 +259,9 @@ wiki_hosts: netbox_hosts: hosts: netbox: +jitsi_hosts: + hosts: + jitsi: proxmox_vm_template_hosts: hosts: chaosknoten: diff --git a/resources/chaosknoten/jitsi/nftables/nftables.conf b/resources/chaosknoten/jitsi/nftables/nftables.conf new file mode 100644 index 00000000..0c239986 --- /dev/null +++ b/resources/chaosknoten/jitsi/nftables/nftables.conf @@ -0,0 +1,65 @@ +#!/usr/sbin/nft -f + +# Docker manages its own tables (via iptables-nft), so don't use "flush ruleset" here, +# but only replace our own table. This also means that a reload is fine, but stopping +# or restarting nftables.service (which flushes the whole ruleset) requires restarting +# Docker afterwards. +# Traffic to ports published by Docker containers (web, JVB, exporter) gets DNATed in +# prerouting and therefore passes the forward hook instead of the input hook. + +## Variables + +define if_wan = "ens18" + +# Prometheus on grafana (IPv4 via the NAT of router) +define prometheus_v4 = { 212.12.48.123 } +define prometheus_v6 = { 2a00:14b0:42:102::e } + +## Rules + +table inet host_filter +delete table inet host_filter + +table inet host_filter { + # Runs before Docker's DNAT, so this also applies to ports published by containers. + chain prerouting { + type filter hook prerouting priority dstnat - 10; policy accept; + + iifname $if_wan tcp dport 9888 ip saddr $prometheus_v4 accept comment "jitsi exporter (container)" + iifname $if_wan tcp dport 9888 ip6 saddr $prometheus_v6 accept comment "jitsi exporter (container)" + iifname $if_wan tcp dport 9888 drop + } + + chain input { + type filter hook input priority filter; policy drop; + + iifname "lo" accept + ct state invalid drop + ct state established,related accept + + # ICMPv6 is needed for neighbour discovery etc. + meta l4proto ipv6-icmp accept + meta l4proto icmp accept + + # Docker networks + iifname { "docker0", "br-*" } accept + + tcp dport 22 accept comment "ssh" + + # Ports published by containers normally get DNATed and never reach the input + # hook, but they do when handled by docker-proxy (e.g. for IPv6 without IPv6 + # enabled on the Docker network). + tcp dport { 80, 443 } accept comment "jitsi web (container)" + udp dport 10000 accept comment "jitsi jvb (container)" + + # coturn (runs in the host network namespace) + udp dport 3478 accept comment "turn" + tcp dport 3478 accept comment "turn" + tcp dport 5349 accept comment "turns" + udp dport 5349 accept comment "turns (dtls)" + udp dport 49152-65535 accept comment "turn relay ports" + + tcp dport { 9100, 9888 } ip saddr $prometheus_v4 accept comment "node exporter, jitsi exporter (container)" + tcp dport { 9100, 9888 } ip6 saddr $prometheus_v6 accept comment "node exporter, jitsi exporter (container)" + } +} diff --git a/roles/nftables/tasks/main.yaml b/roles/nftables/tasks/main.yaml index 6a72800f..ec5daaf2 100644 --- a/roles/nftables/tasks/main.yaml +++ b/roles/nftables/tasks/main.yaml @@ -13,3 +13,10 @@ group: root become: true notify: Reload nftables service + +- name: ensure nftables service is enabled and started + ansible.builtin.systemd_service: + name: nftables + enabled: true + state: started + become: true From 610a1faca82edc3f3df5b819952e8bcdc28b1c5d Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Thu, 8 Oct 2026 10:50:23 +0200 Subject: [PATCH 3/7] Allow UI customization --- roles/jitsi/README.md | 7 ++++ roles/jitsi/defaults/main.yaml | 1 + roles/jitsi/meta/argument_specs.yaml | 12 ++++++ roles/jitsi/tasks/main.yaml | 40 +++++++++++++++++++ .../jitsi/templates/compose.override.yaml.j2 | 9 ++++- 5 files changed, 68 insertions(+), 1 deletion(-) diff --git a/roles/jitsi/README.md b/roles/jitsi/README.md index bd3db9bb..57f5f8e6 100644 --- a/roles/jitsi/README.md +++ b/roles/jitsi/README.md @@ -16,6 +16,9 @@ coturn uses the certificate obtained by the web container and is restarted weekl It needs 3478/udp+tcp, 5349/tcp and the relay port range 49152-65535/udp to be reachable. The exporter scrapes the JVB statistics and listens on port 9888. +Files can be added to or replaced in the Jitsi Meet web root (`/usr/share/jitsi-meet` in the web container) using `jitsi__web_files`, e.g. for branding. +They are deployed to `/opt/jitsi/web-files/` and bind-mounted into the web container. + ## Supported Distributions Should work on Debian-based distributions. @@ -49,6 +52,10 @@ Should work on Debian-based distributions. Defaults to `""` (no file). - `jitsi__custom_interface_config_js`: Content of `custom-interface_config.js`, which gets appended to the generated `interface_config.js`. Defaults to `""` (no file). +- `jitsi__web_files`: List of files to mount into the Jitsi Meet web root. + Defaults to `[ ]`. +- `jitsi__web_files.*.path`: Path relative to the web root, e.g. `images/watermark.svg` or `static/welcomePageAdditionalContent.html`. +- `jitsi__web_files.*.content`: The content of the file. ## Links & Resources diff --git a/roles/jitsi/defaults/main.yaml b/roles/jitsi/defaults/main.yaml index 9782b636..5bed993e 100644 --- a/roles/jitsi/defaults/main.yaml +++ b/roles/jitsi/defaults/main.yaml @@ -6,3 +6,4 @@ jitsi__prometheus_exporter: false jitsi__env: { } jitsi__custom_config_js: "" jitsi__custom_interface_config_js: "" +jitsi__web_files: [ ] diff --git a/roles/jitsi/meta/argument_specs.yaml b/roles/jitsi/meta/argument_specs.yaml index 2c65ca54..36ee25b6 100644 --- a/roles/jitsi/meta/argument_specs.yaml +++ b/roles/jitsi/meta/argument_specs.yaml @@ -55,3 +55,15 @@ argument_specs: type: str required: false default: "" + jitsi__web_files: + type: list + elements: dict + required: false + default: [ ] + options: + path: + type: str + required: true + content: + type: str + required: true diff --git a/roles/jitsi/tasks/main.yaml b/roles/jitsi/tasks/main.yaml index a7beaaa4..92b9da83 100644 --- a/roles/jitsi/tasks/main.yaml +++ b/roles/jitsi/tasks/main.yaml @@ -140,6 +140,46 @@ state: "{{ 'present' if jitsi__turn else 'absent' }}" become: true +# Files mounted into the web container, replacing or adding to the files of Jitsi Meet. +- name: make sure the directories for the web files exist + ansible.builtin.file: + path: "{{ jitsi__base_dir }}/web-files/{{ item }}" + state: directory + mode: "0755" + owner: root + group: root + become: true + loop: "{{ [''] + (jitsi__web_files | map(attribute='path') | map('dirname') | unique | reject('equalto', '') | list) }}" + +- name: deploy the web files + ansible.builtin.copy: + content: "{{ item.content }}" + dest: "{{ jitsi__base_dir }}/web-files/{{ item.path }}" + mode: "0644" + owner: root + group: root + become: true + loop: "{{ jitsi__web_files }}" + loop_control: + label: "{{ item.path }}" + # Single file bind mounts keep pointing to the replaced file until the container is restarted. + notify: restart jitsi web + +- name: find web files to remove + ansible.builtin.find: + paths: "{{ jitsi__base_dir }}/web-files" + recurse: true + register: jitsi__web_files_found + +- name: remove web files, which should be removed + ansible.builtin.file: + path: "{{ item }}" + state: absent + become: true + loop: "{{ jitsi__web_files_found.files | map(attribute='path') | reject('in', jitsi__web_files_paths) | list }}" + vars: + jitsi__web_files_paths: "{{ jitsi__web_files | map(attribute='path') | map('regex_replace', '^', jitsi__base_dir ~ '/web-files/') | list }}" + # Changes to these make `docker compose up` below recreate the affected containers. - name: deploy the compose override file ansible.builtin.template: diff --git a/roles/jitsi/templates/compose.override.yaml.j2 b/roles/jitsi/templates/compose.override.yaml.j2 index 05842569..628b83bc 100644 --- a/roles/jitsi/templates/compose.override.yaml.j2 +++ b/roles/jitsi/templates/compose.override.yaml.j2 @@ -8,6 +8,13 @@ networks: enable_ipv6: true services: +{% if jitsi__web_files | length > 0 %} + web: + volumes: +{% for file in jitsi__web_files %} + - {{ jitsi__base_dir }}/web-files/{{ file.path }}:/usr/share/jitsi-meet/{{ file.path }}:ro +{% endfor %} +{% endif %} {% if jitsi__turn %} coturn: image: docker.io/coturn/coturn:4.18.0 @@ -35,6 +42,6 @@ services: networks: meet.jitsi: {% endif %} -{% if not (jitsi__turn or jitsi__prometheus_exporter) %} +{% if not (jitsi__web_files or jitsi__turn or jitsi__prometheus_exporter) %} {} {% endif %} From 8bf5690275ad566e9014a9d9bbd4d364ec9c5513 Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Thu, 8 Oct 2026 10:50:48 +0200 Subject: [PATCH 4/7] Customize with our logos --- inventories/chaosknoten/host_vars/jitsi.yaml | 20 +++++++++++++++++++ .../chaosknoten/jitsi/web/background.svg | 18 +++++++++++++++++ resources/chaosknoten/jitsi/web/branding.json | 5 +++++ .../chaosknoten/jitsi/web/ccchh-logo.svg | 5 +++++ .../chaosknoten/jitsi/web/round-logo.svg | 7 +++++++ .../web/welcomePageAdditionalContent.html | 13 ++++++++++++ 6 files changed, 68 insertions(+) create mode 100644 resources/chaosknoten/jitsi/web/background.svg create mode 100644 resources/chaosknoten/jitsi/web/branding.json create mode 100644 resources/chaosknoten/jitsi/web/ccchh-logo.svg create mode 100644 resources/chaosknoten/jitsi/web/round-logo.svg create mode 100644 resources/chaosknoten/jitsi/web/welcomePageAdditionalContent.html diff --git a/inventories/chaosknoten/host_vars/jitsi.yaml b/inventories/chaosknoten/host_vars/jitsi.yaml index 3673cbf9..a22c3827 100644 --- a/inventories/chaosknoten/host_vars/jitsi.yaml +++ b/inventories/chaosknoten/host_vars/jitsi.yaml @@ -11,3 +11,23 @@ jitsi__whiteboard: true jitsi__turn: true jitsi__prometheus_exporter: true # jitsi__jicofo_auth_password, jitsi__jvb_auth_password and jitsi__turn_secret are defined in jitsi.sops.yaml. + +# Branding +jitsi__env: + DYNAMIC_BRANDING_URL: https://jitsi.hamburg.ccc.de/static/ccchh-branding.json +jitsi__custom_interface_config_js: | + // Replace the footer with app store links with our own in welcomePageAdditionalContent.html. + interfaceConfig.DISPLAY_WELCOME_FOOTER = false; + interfaceConfig.DISPLAY_WELCOME_PAGE_CONTENT = true; + interfaceConfig.JITSI_WATERMARK_LINK = 'https://hamburg.ccc.de/'; +jitsi__web_files: + - path: images/watermark.svg + content: "{{ lookup('ansible.builtin.file', 'resources/chaosknoten/jitsi/web/ccchh-logo.svg') }}" + - path: images/favicon.svg + content: "{{ lookup('ansible.builtin.file', 'resources/chaosknoten/jitsi/web/round-logo.svg') }}" + - path: images/ccchh-background.svg + content: "{{ lookup('ansible.builtin.file', 'resources/chaosknoten/jitsi/web/background.svg') }}" + - path: static/ccchh-branding.json + content: "{{ lookup('ansible.builtin.file', 'resources/chaosknoten/jitsi/web/branding.json') }}" + - path: static/welcomePageAdditionalContent.html + content: "{{ lookup('ansible.builtin.file', 'resources/chaosknoten/jitsi/web/welcomePageAdditionalContent.html') }}" diff --git a/resources/chaosknoten/jitsi/web/background.svg b/resources/chaosknoten/jitsi/web/background.svg new file mode 100644 index 00000000..15f8531a --- /dev/null +++ b/resources/chaosknoten/jitsi/web/background.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/resources/chaosknoten/jitsi/web/branding.json b/resources/chaosknoten/jitsi/web/branding.json new file mode 100644 index 00000000..acb28508 --- /dev/null +++ b/resources/chaosknoten/jitsi/web/branding.json @@ -0,0 +1,5 @@ +{ + "logoImageUrl": "https://jitsi.hamburg.ccc.de/images/watermark.svg", + "logoClickUrl": "https://hamburg.ccc.de/", + "backgroundImageUrl": "https://jitsi.hamburg.ccc.de/images/ccchh-background.svg" +} diff --git a/resources/chaosknoten/jitsi/web/ccchh-logo.svg b/resources/chaosknoten/jitsi/web/ccchh-logo.svg new file mode 100644 index 00000000..84abd70e --- /dev/null +++ b/resources/chaosknoten/jitsi/web/ccchh-logo.svg @@ -0,0 +1,5 @@ + + +Chaos Computer Club Hamburg Logo + + diff --git a/resources/chaosknoten/jitsi/web/round-logo.svg b/resources/chaosknoten/jitsi/web/round-logo.svg new file mode 100644 index 00000000..9d139ddc --- /dev/null +++ b/resources/chaosknoten/jitsi/web/round-logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/resources/chaosknoten/jitsi/web/welcomePageAdditionalContent.html b/resources/chaosknoten/jitsi/web/welcomePageAdditionalContent.html new file mode 100644 index 00000000..982f09ef --- /dev/null +++ b/resources/chaosknoten/jitsi/web/welcomePageAdditionalContent.html @@ -0,0 +1,13 @@ + From 442231323c18bb9ddbe99c59fb5143717b4d14c6 Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Tue, 20 Oct 2026 15:05:55 +0200 Subject: [PATCH 5/7] disable ccchoir.de for now --- .../ccchoir/docker_compose/compose.yaml.j2 | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/resources/chaosknoten/ccchoir/docker_compose/compose.yaml.j2 b/resources/chaosknoten/ccchoir/docker_compose/compose.yaml.j2 index 72d898fa..00cac00e 100644 --- a/resources/chaosknoten/ccchoir/docker_compose/compose.yaml.j2 +++ b/resources/chaosknoten/ccchoir/docker_compose/compose.yaml.j2 @@ -16,24 +16,24 @@ services: backend: restart: unless-stopped - app: - image: docker.io/library/wordpress:7-php8.2 - environment: - - "WORDPRESS_DB_HOST=database" - - "WORDPRESS_DB_NAME=wordpress" - - "WORDPRESS_DB_USER=wordpress" - - "WORDPRESS_TABLE_PREFIX=wp_" - - "WORDPRESS_DB_PASSWORD={{ secret__wordpress_db_password }}" - volumes: - - wordpress:/var/www/html/wp-content - ports: - - "127.0.0.1:3000:80" - networks: - backend: - frontend: - restart: unless-stopped - depends_on: - - database +# app: +# image: docker.io/library/wordpress:7-php8.2 +# environment: +# - "WORDPRESS_DB_HOST=database" +# - "WORDPRESS_DB_NAME=wordpress" +# - "WORDPRESS_DB_USER=wordpress" +# - "WORDPRESS_TABLE_PREFIX=wp_" +# - "WORDPRESS_DB_PASSWORD={{ secret__wordpress_db_password }}" +# volumes: +# - wordpress:/var/www/html/wp-content +# ports: +# - "127.0.0.1:3000:80" +# networks: +# backend: +# frontend: +# restart: unless-stopped +# depends_on: +# - database volumes: database: {} From ce96105463987a00251665223b4dc595dcbc1e6e Mon Sep 17 00:00:00 2001 From: Stefan Bethke Date: Tue, 20 Oct 2026 15:34:05 +0200 Subject: [PATCH 6/7] Make Jitsi JVB work correctly with IPv6 Since the Jitsi Docker Compose stack does not properly take IPv6 into consideration, patch the JVB config to add mappings from the private IPv6 address of the Docker network to the public IPv6 address. This should be the source of the reported problems with conferences with 3+ people. --- roles/jitsi/README.md | 8 +++++++- roles/jitsi/defaults/main.yaml | 2 ++ roles/jitsi/handlers/main.yaml | 10 ++++++++++ roles/jitsi/meta/argument_specs.yaml | 8 ++++++++ roles/jitsi/tasks/main.yaml | 19 +++++++++++++++++++ .../jitsi/templates/compose.override.yaml.j2 | 14 +++++++++++++- roles/jitsi/templates/custom-jvb.conf.j2 | 11 +++++++++++ roles/jitsi/templates/jitsi.env.j2 | 4 ++-- roles/jitsi/vars/main.yaml | 4 ++++ 9 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 roles/jitsi/templates/custom-jvb.conf.j2 diff --git a/roles/jitsi/README.md b/roles/jitsi/README.md index 57f5f8e6..b3392c6a 100644 --- a/roles/jitsi/README.md +++ b/roles/jitsi/README.md @@ -35,8 +35,14 @@ Should work on Debian-based distributions. - `jitsi__timezone`: The time zone for the containers. Defaults to `Europe/Berlin`. -- `jitsi__jvb_advertise_ips`: List of IP addresses the JVB should advertise for media (`JVB_ADVERTISE_IPS`). +- `jitsi__jvb_advertise_ips`: List of IP addresses the JVB should advertise for media. + IPv4 addresses are put into `JVB_ADVERTISE_IPS`, which docker-jitsi-meet maps to the IPv4 address of the JVB container. + IPv6 addresses are mapped to the IPv6 address of the JVB container in `custom-jvb.conf` instead, for which the `meet.jitsi` network gets a fixed IPv6 subnet and the JVB a fixed IPv6 address. Defaults to `[ ]`. +- `jitsi__ipv6_subnet`: The IPv6 subnet of the `meet.jitsi` network, if IPv6 addresses are advertised. + Defaults to `fd4a:7c1e:93b5:1::/64`. +- `jitsi__jvb_ipv6_address`: The IPv6 address of the JVB container within `jitsi__ipv6_subnet`. + Defaults to `fd4a:7c1e:93b5:1::10`. - `jitsi__whiteboard`: Whether to deploy the Excalidraw whiteboard backend (`whiteboard.yml`). Defaults to `false`. - `jitsi__turn`: Whether to deploy coturn and configure Jitsi to use it. diff --git a/roles/jitsi/defaults/main.yaml b/roles/jitsi/defaults/main.yaml index 5bed993e..8532b275 100644 --- a/roles/jitsi/defaults/main.yaml +++ b/roles/jitsi/defaults/main.yaml @@ -1,5 +1,7 @@ jitsi__timezone: Europe/Berlin jitsi__jvb_advertise_ips: [ ] +jitsi__ipv6_subnet: fd4a:7c1e:93b5:1::/64 +jitsi__jvb_ipv6_address: fd4a:7c1e:93b5:1::10 jitsi__whiteboard: false jitsi__turn: false jitsi__prometheus_exporter: false diff --git a/roles/jitsi/handlers/main.yaml b/roles/jitsi/handlers/main.yaml index 7bde7f02..6f0eb2e3 100644 --- a/roles/jitsi/handlers/main.yaml +++ b/roles/jitsi/handlers/main.yaml @@ -8,6 +8,16 @@ state: restarted become: true +- name: restart jitsi jvb + community.docker.docker_compose_v2: + project_src: "{{ jitsi__base_dir }}" + project_name: jitsi + files: "{{ jitsi__compose_files }}" + services: + - jvb + state: restarted + become: true + - name: restart jitsi coturn community.docker.docker_compose_v2: project_src: "{{ jitsi__base_dir }}" diff --git a/roles/jitsi/meta/argument_specs.yaml b/roles/jitsi/meta/argument_specs.yaml index 36ee25b6..e2df54fe 100644 --- a/roles/jitsi/meta/argument_specs.yaml +++ b/roles/jitsi/meta/argument_specs.yaml @@ -27,6 +27,14 @@ argument_specs: elements: str required: false default: [ ] + jitsi__ipv6_subnet: + type: str + required: false + default: fd4a:7c1e:93b5:1::/64 + jitsi__jvb_ipv6_address: + type: str + required: false + default: fd4a:7c1e:93b5:1::10 jitsi__whiteboard: type: bool required: false diff --git a/roles/jitsi/tasks/main.yaml b/roles/jitsi/tasks/main.yaml index 92b9da83..4846ad5a 100644 --- a/roles/jitsi/tasks/main.yaml +++ b/roles/jitsi/tasks/main.yaml @@ -110,6 +110,25 @@ label: "{{ item.name }}" notify: restart jitsi web +- name: deploy custom JVB configuration + ansible.builtin.template: + src: custom-jvb.conf.j2 + dest: "{{ jitsi__config_dir }}/jvb/custom-jvb.conf" + mode: "0644" + owner: root + group: root + become: true + when: jitsi__jvb_advertise_ipv6s | length > 0 + notify: restart jitsi jvb + +- name: remove unconfigured custom JVB configuration + ansible.builtin.file: + path: "{{ jitsi__config_dir }}/jvb/custom-jvb.conf" + state: absent + become: true + when: jitsi__jvb_advertise_ipv6s | length == 0 + notify: restart jitsi jvb + - name: make sure the coturn directory exists ansible.builtin.file: path: "{{ jitsi__base_dir }}/coturn" diff --git a/roles/jitsi/templates/compose.override.yaml.j2 b/roles/jitsi/templates/compose.override.yaml.j2 index 628b83bc..2cefd134 100644 --- a/roles/jitsi/templates/compose.override.yaml.j2 +++ b/roles/jitsi/templates/compose.override.yaml.j2 @@ -6,8 +6,20 @@ networks: meet.jitsi: enable_ipv6: true +{% if jitsi__jvb_advertise_ipv6s | length > 0 %} + # A fixed subnet, so the JVB can have a fixed IPv6 address to map the public ones to. + ipam: + config: + - subnet: {{ jitsi__ipv6_subnet }} +{% endif %} services: +{% if jitsi__jvb_advertise_ipv6s | length > 0 %} + jvb: + networks: + meet.jitsi: + ipv6_address: {{ jitsi__jvb_ipv6_address }} +{% endif %} {% if jitsi__web_files | length > 0 %} web: volumes: @@ -42,6 +54,6 @@ services: networks: meet.jitsi: {% endif %} -{% if not (jitsi__web_files or jitsi__turn or jitsi__prometheus_exporter) %} +{% if not (jitsi__jvb_advertise_ipv6s or jitsi__web_files or jitsi__turn or jitsi__prometheus_exporter) %} {} {% endif %} diff --git a/roles/jitsi/templates/custom-jvb.conf.j2 b/roles/jitsi/templates/custom-jvb.conf.j2 new file mode 100644 index 00000000..050511a9 --- /dev/null +++ b/roles/jitsi/templates/custom-jvb.conf.j2 @@ -0,0 +1,11 @@ +# {{ ansible_managed }} +# Included at the end of the generated jvb.conf. +# Map the advertised IPv6 addresses to the pinned IPv6 address of the JVB, in addition to +# the mappings of the IPv4 addresses generated from JVB_ADVERTISE_IPS. +{% for ip in jitsi__jvb_advertise_ipv6s %} +ice4j.harvest.mapping.static-mappings += { + local-address = "{{ jitsi__jvb_ipv6_address }}" + public-address = "{{ ip }}" + name = "ipv6-{{ loop.index0 }}" +} +{% endfor %} diff --git a/roles/jitsi/templates/jitsi.env.j2 b/roles/jitsi/templates/jitsi.env.j2 index f09b0414..f5d8a447 100644 --- a/roles/jitsi/templates/jitsi.env.j2 +++ b/roles/jitsi/templates/jitsi.env.j2 @@ -19,8 +19,8 @@ ENABLE_LETSENCRYPT=1 LETSENCRYPT_DOMAIN={{ jitsi__domain }} LETSENCRYPT_EMAIL={{ jitsi__letsencrypt_email }} LETSENCRYPT_ACME_SERVER=letsencrypt -{% if jitsi__jvb_advertise_ips | length > 0 %} -JVB_ADVERTISE_IPS={{ jitsi__jvb_advertise_ips | join(',') }} +{% if jitsi__jvb_advertise_ipv4s | length > 0 %} +JVB_ADVERTISE_IPS={{ jitsi__jvb_advertise_ipv4s | join(',') }} {% endif %} {% if jitsi__whiteboard %} diff --git a/roles/jitsi/vars/main.yaml b/roles/jitsi/vars/main.yaml index 21fe76b6..993aa412 100644 --- a/roles/jitsi/vars/main.yaml +++ b/roles/jitsi/vars/main.yaml @@ -1,4 +1,8 @@ jitsi__base_dir: /opt/jitsi jitsi__config_dir: "{{ jitsi__base_dir }}/config" jitsi__release_dir: "{{ jitsi__base_dir }}/releases/docker-jitsi-meet-{{ jitsi__version }}" +# docker-jitsi-meet maps all JVB_ADVERTISE_IPS to the container's IPv4 address, so IPv6 +# addresses are mapped to the pinned IPv6 address of the JVB in custom-jvb.conf instead. +jitsi__jvb_advertise_ipv4s: "{{ jitsi__jvb_advertise_ips | reject('search', ':') | list }}" +jitsi__jvb_advertise_ipv6s: "{{ jitsi__jvb_advertise_ips | select('search', ':') | list }}" jitsi__compose_files: "{{ ['release/docker-compose.yml'] + (['release/whiteboard.yml'] if jitsi__whiteboard else []) + ['compose.override.yaml'] }}" From 0ec52411e429f79714054c8b703e68efcd69c250 Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 20 Oct 2026 14:16:58 +0000 Subject: [PATCH 7/7] Update all stable non-major dependencies --- .forgejo/workflows/deploy-docs.yaml | 2 +- .forgejo/workflows/lint.yaml | 2 +- inventories/chaosknoten/host_vars/cloud.yaml | 2 +- inventories/chaosknoten/host_vars/netbox.yaml | 2 +- inventories/chaosknoten/host_vars/pad.yaml | 2 +- .../chaosknoten/draupnir/docker_compose/compose.yaml | 2 +- .../chaosknoten/grafana/docker_compose/compose.yaml.j2 | 8 ++++---- resources/chaosknoten/ntfy/docker_compose/compose.yaml.j2 | 2 +- .../chaosknoten/pretalx/docker_compose/compose.yaml.j2 | 2 +- .../chaosknoten/tickets/docker_compose/compose.yaml.j2 | 2 +- resources/external/status/docker_compose/compose.yaml.j2 | 4 ++-- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.forgejo/workflows/deploy-docs.yaml b/.forgejo/workflows/deploy-docs.yaml index 6e0acd6d..ed31e899 100644 --- a/.forgejo/workflows/deploy-docs.yaml +++ b/.forgejo/workflows/deploy-docs.yaml @@ -11,7 +11,7 @@ jobs: build: runs-on: docker container: - image: docker.io/library/python:3.14-trixie + image: docker.io/library/python:3.15-trixie steps: - name: Pipeline info run: | diff --git a/.forgejo/workflows/lint.yaml b/.forgejo/workflows/lint.yaml index 5c1b82ac..6a31829c 100644 --- a/.forgejo/workflows/lint.yaml +++ b/.forgejo/workflows/lint.yaml @@ -24,7 +24,7 @@ jobs: # work in our environmnet. # Rather manually setup python (pip) before instead. - name: Run ansible-lint - uses: https://github.com/ansible/ansible-lint@v26.6.0 + uses: https://github.com/ansible/ansible-lint@v26.10.1 with: setup_python: "false" requirements_file: "requirements.yml" diff --git a/inventories/chaosknoten/host_vars/cloud.yaml b/inventories/chaosknoten/host_vars/cloud.yaml index 8f299b2d..a441ac84 100644 --- a/inventories/chaosknoten/host_vars/cloud.yaml +++ b/inventories/chaosknoten/host_vars/cloud.yaml @@ -1,7 +1,7 @@ # renovate: datasource=docker depName=git.hamburg.ccc.de/ccchh/oci-images/nextcloud nextcloud__version: 34 # renovate: datasource=docker depName=docker.io/library/postgres -nextcloud__postgres_version: 15.18 +nextcloud__postgres_version: 15.19 nextcloud__fqdn: cloud.hamburg.ccc.de nextcloud__data_dir: /data/nextcloud nextcloud__extra_configuration: "{{ lookup('ansible.builtin.template', 'resources/chaosknoten/cloud/nextcloud/extra_configuration.config.php.j2') }}" diff --git a/inventories/chaosknoten/host_vars/netbox.yaml b/inventories/chaosknoten/host_vars/netbox.yaml index 312b1d70..50bf76b2 100644 --- a/inventories/chaosknoten/host_vars/netbox.yaml +++ b/inventories/chaosknoten/host_vars/netbox.yaml @@ -1,5 +1,5 @@ # renovate: datasource=github-releases depName=netbox packageName=netbox-community/netbox -netbox__version: "v4.6.4" +netbox__version: "v4.7.3" netbox__config: "{{ lookup('ansible.builtin.template', 'resources/chaosknoten/netbox/netbox/configuration.py.j2') }}" netbox__custom_pipeline_oidc_group_and_role_mapping: true diff --git a/inventories/chaosknoten/host_vars/pad.yaml b/inventories/chaosknoten/host_vars/pad.yaml index 0c9b29ea..c2fa834f 100644 --- a/inventories/chaosknoten/host_vars/pad.yaml +++ b/inventories/chaosknoten/host_vars/pad.yaml @@ -13,4 +13,4 @@ nginx__configurations: content: "{{ lookup('ansible.builtin.file', 'resources/chaosknoten/pad/nginx/pad.hamburg.ccc.de.conf') }}" # renovate: datasource=docker depName=quay.io/hedgedoc/hedgedoc -host_pad__docker_compose__hedgedoc_version: 1.11.1 +host_pad__docker_compose__hedgedoc_version: 1.12.0 diff --git a/resources/chaosknoten/draupnir/docker_compose/compose.yaml b/resources/chaosknoten/draupnir/docker_compose/compose.yaml index 42bf8fda..e346c6bb 100644 --- a/resources/chaosknoten/draupnir/docker_compose/compose.yaml +++ b/resources/chaosknoten/draupnir/docker_compose/compose.yaml @@ -3,7 +3,7 @@ services: draupnir: - image: ghcr.io/the-draupnir-project/draupnir:v3.1.0 + image: ghcr.io/the-draupnir-project/draupnir:v3.2.0 command: [ "bot", "--draupnir-config", "/etc/draupnir/production.yaml" ] restart: unless-stopped volumes: diff --git a/resources/chaosknoten/grafana/docker_compose/compose.yaml.j2 b/resources/chaosknoten/grafana/docker_compose/compose.yaml.j2 index 05d1c7e5..9931b7c6 100644 --- a/resources/chaosknoten/grafana/docker_compose/compose.yaml.j2 +++ b/resources/chaosknoten/grafana/docker_compose/compose.yaml.j2 @@ -2,7 +2,7 @@ services: prometheus: - image: docker.io/prom/prometheus:v3.13.1 + image: docker.io/prom/prometheus:v3.15.0 container_name: prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' @@ -19,7 +19,7 @@ services: - prom_data:/prometheus alertmanager: - image: docker.io/prom/alertmanager:v0.33.1 + image: docker.io/prom/alertmanager:v0.34.1 container_name: alertmanager command: - '--config.file=/etc/alertmanager/alertmanager.yaml' @@ -32,7 +32,7 @@ services: - alertmanager_data:/alertmanager grafana: - image: docker.io/grafana/grafana:13.1.0 + image: docker.io/grafana/grafana:13.2.4 container_name: grafana ports: - 3000:3000 @@ -46,7 +46,7 @@ services: - graf_data:/var/lib/grafana pve-exporter: - image: docker.io/prompve/prometheus-pve-exporter:3.9.0 + image: docker.io/prompve/prometheus-pve-exporter:3.10.1 container_name: pve-exporter ports: - 9221:9221 diff --git a/resources/chaosknoten/ntfy/docker_compose/compose.yaml.j2 b/resources/chaosknoten/ntfy/docker_compose/compose.yaml.j2 index a982bf3a..fbd42fa6 100644 --- a/resources/chaosknoten/ntfy/docker_compose/compose.yaml.j2 +++ b/resources/chaosknoten/ntfy/docker_compose/compose.yaml.j2 @@ -1,7 +1,7 @@ --- services: ntfy: - image: docker.io/binwiederhier/ntfy:v2.26.0 + image: docker.io/binwiederhier/ntfy:v2.32.0 container_name: ntfy command: - serve diff --git a/resources/chaosknoten/pretalx/docker_compose/compose.yaml.j2 b/resources/chaosknoten/pretalx/docker_compose/compose.yaml.j2 index 6b1683cc..5e3ff3e6 100644 --- a/resources/chaosknoten/pretalx/docker_compose/compose.yaml.j2 +++ b/resources/chaosknoten/pretalx/docker_compose/compose.yaml.j2 @@ -15,7 +15,7 @@ services: - pretalx_net redis: - image: docker.io/library/redis:8.8.0 + image: docker.io/library/redis:8.10.2 restart: unless-stopped volumes: - redis:/data diff --git a/resources/chaosknoten/tickets/docker_compose/compose.yaml.j2 b/resources/chaosknoten/tickets/docker_compose/compose.yaml.j2 index 28cef213..eb92c851 100644 --- a/resources/chaosknoten/tickets/docker_compose/compose.yaml.j2 +++ b/resources/chaosknoten/tickets/docker_compose/compose.yaml.j2 @@ -13,7 +13,7 @@ services: restart: unless-stopped redis: - image: docker.io/library/redis:8.8.0 + image: docker.io/library/redis:8.10.2 ports: - "6379:6379" volumes: diff --git a/resources/external/status/docker_compose/compose.yaml.j2 b/resources/external/status/docker_compose/compose.yaml.j2 index 638ebbe2..5329b644 100644 --- a/resources/external/status/docker_compose/compose.yaml.j2 +++ b/resources/external/status/docker_compose/compose.yaml.j2 @@ -4,7 +4,7 @@ services: database: - image: docker.io/library/postgres:18.4 + image: docker.io/library/postgres:18.6 restart: always volumes: - ./database:/var/lib/postgresql @@ -16,7 +16,7 @@ services: - gatus gatus: - image: ghcr.io/twin/gatus:v5.36.0 + image: ghcr.io/twin/gatus:v5.37.0 restart: always ports: - "8080:8080"