Compare commits

..

1 Commits

Author SHA1 Message Date
95778fc407 updated to allow voice chat 2026-03-19 22:05:03 -06:00
24 changed files with 571 additions and 530 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,2 @@
vault_pw
*.key
notes

View File

@@ -1,44 +1,64 @@
## Test deployment ##
deploy playbook after setup
```
ansible-playbook secure.yml -i inventory/hosts.yml --check --diff
```
```
ansible-playbook deploy_matrix.yml -i inventory/hosts.yml --check --diff
ansible-playbook playbook.yml --ask-vault-pass
```
## Deploy ##
Check containers
```
ansible-playbook secure.yml -i inventory/hosts.yml
```
```
ansible-playbook deploy_matrix.yml -i inventory/hosts.yml
```
## Matrix Administration ##
[synapse-admin on github](https://github.com/Awesome-Technologies/synapse-admin)
- Create User
```
sudo podman exec -it synapse register_new_matrix_user -c /data/homeserver.yaml http://localhost:8008
ssh matrix.example.com
sudo podman ps
sudo podman logs synapse
```
# List users
sudo podman exec -it synapse curl -H "Authorization: Bearer YOUR_ADMIN_TOKEN" "http://localhost:8008/_synapse/admin/v2/users"
# Deactivate user
sudo podman exec -i synapse curl -X POST -H "Authorization: Bearer YOUR_ADMIN_TOKEN" -H "Content-Type: application/json" -d '{"erase": true}' "http://localhost:8008/_synapse/admin/v1/deactivate/@user:domain.com"
# List rooms
sudo podman exec -it synapse curl -H "Authorization: Bearer YOUR_ADMIN_TOKEN" "http://localhost:8008/_synapse/admin/v1/rooms"
# Delete room
sudo podman exec -i synapse curl -X DELETE -H "Authorization: Bearer YOUR_ADMIN_TOKEN" -H "Content-Type: application/json" -d '{}' "http://localhost:8008/_synapse/admin/v2/rooms/!roomid:domain.com"
## Clean up everything ##
you will lose all persistence as well
test federation
```
ansible-playbook prune.yml -i inventory/hosts.yml
curl https://{{ matrix_domain }}/_matrix/federation/v1/version
```
## to generate config files default before deploymenta and templating ##
```
podman run -it --rm \
-v "$(pwd)":/data \
-e SYNAPSE_SERVER_NAME=my.matrix.host \
-e SYNAPSE_REPORT_STATS=yes \
docker.io/matrixdotorg/synapse:latest generate
```
| Area | Recommendation |
|------|----------------|
| **TLS** | Use Certbot + Nginx plugin (`community.general.acme` Ansible module) |
| **Secrets** | Store DB passwords, secrets in **Ansible Vault** |
| **Updates** | Use `container_image: tag` with version pins (e.g., `v1.100.0`) |
| **Monitoring** | Add Prometheus metrics (`enable_metrics: true`) |
| **Coturn** | Deploy `coturn` (STUN/TURN) as separate container if needed |
| **Backups** | Backup `/var/lib/matrix-synapse` regularly |
| **Rootless Podman** | Use `podman user` namespaces + `podman system migrate` for portability |
## Using Hugo to create site ##
```
# Create & build site locally (run ON CONTROL NODE)
hugo new site mysite
cd mysite
echo 'theme = "ananke"' >> config.toml
hugo new posts/my-first-post.md
hugo # builds to /public
cp -r public ../fpov/roles/nginx-proxy/files/mysite/
```
1. Host boots: Podman creates pod with ports 80, 443, 8008, 8448 open.
2. Nginx starts, runs certbot-entrypoint.sh:
- Checks for /etc/letsencrypt/live/matrix.example.com
- If missing → runs certbot certonly --standalone on port 8080
- Writes certs to /etc/letsencrypt
3. Nginx loads TLS certs, serves static site + proxies /_matrix/
4. User visits https://matrix.example.com → gets your Hugo site.
5. User visits https://matrix.example.com/_matrix/client → gets Synapse.
6. ACME renewal: Certbot’s renew is dry-run on each start (you can also add cron via systemd unit if needed).
Certificate renewal
```
# /etc/cron.d/certbot-renew
0 3 * * * root podman exec nginx-matrix certbot renew --quiet
```
### Consider this ###
LDAP ldif file

View File

@@ -1,212 +0,0 @@
---
- name: Deploy Matrix + TURN + NGINX-PM + Teamspeak
hosts: all
become: true
collections:
- containers.podman
vars_files:
- ../vars/vars.yml # ← our variable file
tasks:
# -----------------------------------------------------------------
# Install podman, create network, create volumes …
# -----------------------------------------------------------------
- name: Install podman package
ansible.builtin.package:
name: podman
state: present
- name: Create podman network "{{ podman_network }}"
containers.podman.podman_network:
name: "{{ podman_network }}"
driver: bridge
subnet: "10.36.0.0/16"
state: present
- name: Ensure required podman volumes exist
containers.podman.podman_volume:
name: "{{ item }}"
driver: local
state: present
loop:
- "{{ vol_synapse }}"
- "{{ vol_media }}"
- "{{ vol_turn }}"
- "{{ vol_nginx }}"
- "{{ vol_teamspeak }}"
- "{{ vol_db }}"
- "{{ npm_data }}"
- "{{ npm_letsencrypt }}"
# -----------------------------------------------------------------
# Pull required images (including Teamspeak)
# -----------------------------------------------------------------
- name: Pull coturn image
containers.podman.podman_image:
name: "{{ turn_image }}"
state: present
- name: Pull Synapse image
containers.podman.podman_image:
name: "{{ synapse_image }}"
state: present
- name: Pull NGINX image
containers.podman.podman_image:
name: "{{ nginx_image }}"
state: present
- name: Pull Teamspeak image
containers.podman.podman_image:
name: "{{ teamspeak_image }}"
state: present
- name: Pull Nginx Proxy Manager image
containers.podman.podman_image:
name: "{{ npm_image }}"
state: present
- name: Pull Postgres DB image
containers.podman.podman_image:
name: "{{ db_image }}"
state: present
# -----------------------------------------------------------------
# Render all configuration files from the templates
# -----------------------------------------------------------------
- name: Render coturn config
ansible.builtin.template:
src: turnserver.conf.j2
dest: "{{ turn_conf_path }}"
mode: "0644"
- name: Render Synapse homeserver.yaml
ansible.builtin.template:
src: homeserver.yaml.j2
dest: "{{ synapse_cfg_path }}"
mode: "0644"
- name: Render landing page index.html
ansible.builtin.template:
src: landing.html.j2
dest: "{{ nginx_html_path }}"
mode: "0644"
- name: Render Synapse Secret key
ansible.builtin.template:
src: matrix.fpov.space.signing.key
dest: "{{ synapse_secret_key_path }}"
mode: "0644"
- name: Render Synapse log config file
ansible.builtin.template:
src: matrix.fpov.space.log.config.j2
dest: "{{ synapse_log_config }}"
mode: "0644"
# -----------------------------------------------------------------
# Start the NGINX Proxy Manager Container
# -----------------------------------------------------------------
- name: Start Nginx Proxy manager
containers.podman.podman_container:
name: npm
image: "{{ npm_image }}"
state: started
ports:
- "80:80"
- "443:443"
- "81:81"
volumes:
- "{{ npm_data }}:/data"
- "{{ npm_letsencrypt }}:/etc/letsencrypt"
network: "{{ podman_network }}"
# -----------------------------------------------------------------
# Start the Coturn container
# -----------------------------------------------------------------
- name: Start coturn container
containers.podman.podman_container:
name: turn
image: "{{ turn_image }}"
state: started
ports:
- "{{ turn_listen_port }}:{{ turn_listen_port }}/udp"
- "{{ turn_listen_port }}:{{ turn_listen_port }}/tcp"
- "{{ turn_tls_port }}:{{ turn_tls_port }}/udp"
- "{{ turn_tls_port }}:{{ turn_tls_port }}/tcp"
volumes:
- "{{ vol_turn }}:/var/lib/turn"
- "{{ turn_conf_path }}:/etc/turnserver.conf:ro"
command: "-c /etc/turnserver.conf"
network: "{{ podman_network }}"
restart_policy: always
# -----------------------------------------------------------------
# Start the Matrix-Synaps and supporting DB containers
# -----------------------------------------------------------------
- name: start Database container
containers.podman.podman_container:
name: synapse-db
image: "{{ db_image }}"
state: started
env:
POSTGRES_USER: "{{ postgres_user }}"
POSTGRES_PASSWORD: "{{ postgres_pw }}"
POSTGRES_DB: "{{ postgres_db }}"
volumes:
- "{{ vol_db }}:/var/lib/postgresql/data"
network: "{{ podman_network }}"
restart_policy: always
- name: Start Synapse container
containers.podman.podman_container:
name: "{{ matrix_container_name }}"
image: "{{ synapse_image }}"
state: started
ports:
- "{{ synapse_http_port }}:{{ synapse_http_port }}"
volumes:
- "{{ vol_synapse }}:/data"
- "{{ vol_media }}:/data/media_store"
- "{{ synapse_cfg_path }}:/data/homeserver.yaml:Z"
- "{{ synapse_secret_key_path }}:/data/matrix.fpov.space.signing.key:Z"
- "{{ synapse_log_config }}:/data/matrix.fpov.space.log.config:Z"
network: "{{ podman_network }}"
restart_policy: always
# -----------------------------------------------------------------
# Start the Teamspeak Container
# -----------------------------------------------------------------
- name: Start Teamspeak container
containers.podman.podman_container:
name: "{{ teamspeak_container_name }}"
image: "{{ teamspeak_image }}"
env:
TS3SERVER_LICENSE: accept
state: started
ports:
# UDP voice port – exposed directly on the host (no Traefik)
- "{{ teamspeak_udp_port }}:{{ teamspeak_udp_port }}/udp"
# TCP query port – Traefik will forward it (see static config)
- "{{ teamspeak_tcp_port }}:{{ teamspeak_tcp_port }}/tcp"
# Optional file‑transfer port (exposed directly)
- "{{ teamspeak_file_port }}:{{ teamspeak_file_port }}/tcp"
volumes:
- "{{ vol_teamspeak }}:/var/ts3server"
network: "{{ podman_network }}"
restart_policy: always

31
group_vars/all.yml Normal file
View File

@@ -0,0 +1,31 @@
# Variables
matrix_domain: "matrix.example.com"
landing_domain: "example.com" # or same as matrix_domain + path-based routing
podman_user: "matrix" # set to 'root' for rootful
podman_pod_name: "matrix-pod"
synapse_data_dir: "/var/lib/matrix-synapse"
nginx_config_dir: "/etc/nginx/conf.d"
nginx_webroot: "/var/www/landing"
# Database (PostgreSQL)
db_name: "synapse"
db_user: "synapse"
db_pass: "{{ synapse_db_pass | mandatory }}"
synapse_db_pass: "supersecretpassword" # use vault for real deployments!
matrix_domain: "matrix.example.com"
landing_domain: "example.com" # optional, or use same domain + path-based routing
# For certbot
certbot_email: "admin@example.com"
# Static site path
nginx_webroot: "/var/lib/matrix-nginx"
coturn_shared_secret: "{{ 'your-secret-key' | password_hash('sha512', 'salt') }}"
# Or generate randomly:
# coturn_shared_secret: "{{ 24 | random_string | password_hash('sha512') }}"
coturn_username: "matrix-turn"
coturn_password: "{{ vault_coturn_password }}"

7
inventory.ini Normal file
View File

@@ -0,0 +1,7 @@
[matrix_servers]
matrix.example.com ansible_host=203.0.113.10
[all:vars]
ansible_user=deploy
ansible_ssh_private_key_file=~/.ssh/id_rsa
podman_user=matrix # rootless user (or 'root' for rootful)

View File

@@ -1,6 +0,0 @@
all:
hosts:
podman-host1: # <-- change to the host that will run Podman
ansible_host: 15.204.117.53 # its IP or DNS name
ansible_user: fedora # user that can sudo
ansible_become: true

19
playbook.yml Normal file
View File

@@ -0,0 +1,19 @@
- name: Deploy Matrix + Nginx + Static Site with TLS
hosts: matrix_servers
become: yes
vars:
synapse_db_pass: "{{ vault_synapse_db_pass }}"
certbot_email: "{{ vault_certbot_email | default('admin@example.com') }}"
tasks:
- name: Setup common dependencies
import_role:
name: common
- name: Deploy Nginx + Static Site + TLS (with Certbot)
import_role:
name: nginx-proxy
- name: Deploy Synapse (PostgreSQL + containers)
import_role:
name: matrix-synapse

View File

@@ -1,14 +0,0 @@
---
- name: prune all Podman resources
hosts: all
become: true
collections:
- containers.podman
tasks:
- name: Prune all Podman resources
containers.podman.podman_prune:
system: true
system_all: true
system_volumes: true

View File

@@ -0,0 +1,40 @@
# Configuration file for Synapse.
#
# This is a YAML file: see [1] for a quick introduction. Note in particular
# that *indentation is important*: all the elements of a list or dictionary
# should have the same indentation.
#
# [1] https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html
#
# For more information on how to configure Synapse, including a complete accounting of
# each option, go to docs/usage/configuration/config_documentation.md or
# https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html
server_name: "my.matrix.host"
pid_file: /data/homeserver.pid
listeners:
- port: 8008
resources:
- compress: false
names:
- client
- federation
tls: false
type: http
x_forwarded: true
database:
name: sqlite3
args:
database: /data/homeserver.db
log_config: "/data/my.matrix.host.log.config"
media_store_path: /data/media_store
registration_shared_secret: "uC7Pv@:r=@Slvhx7Ex*An8QHXWnPRyI9+COZr_&;Kh*,~lGpql"
report_stats: true
macaroon_secret_key: "+QUdDhvNc9*@MORp9K-TjuGp1j5Ff-mUBS+pjkmAFWR0Y3#da8"
form_secret: "meZSlI7K.EI-6hmXcjzCNf2IwrThcnoVr2X0iohOV7K5a_w^Ud"
signing_key_path: "/data/my.matrix.host.signing.key"
trusted_key_servers:
- server_name: "matrix.org"
# vim:ft=yaml

View File

@@ -21,7 +21,7 @@ loggers:
synapse.storage.psycopg2:
synapse.storage.SQL:
# beware: increasing this to DEBUG will make synapse log sensitive
# information such as access tokens.
level: INFO
@@ -34,3 +34,6 @@ root:
handlers: [console]
disable_existing_loggers: false

View File

@@ -0,0 +1,30 @@
- name: Ensure Podman is installed
package:
name: podman
state: present
- name: Create matrix user (for rootless)
user:
name: "{{ podman_user }}"
shell: /bin/bash
createhome: yes
groups: wheel
when: podman_user != "root"
- name: Create data directories
file:
path: "{{ item }}"
state: directory
owner: "{{ podman_user }}"
group: "{{ podman_user }}"
mode: '0755'
loop:
- "{{ synapse_data_dir }}"
- "{{ nginx_webroot }}"
# roles/matrix-synapse/tasks/main.yml
- name: Restart Synapse to reload TURN config
container:
name: synapse
state: started
restart: yes

View File

@@ -0,0 +1,94 @@
- name: Generate signing key (if missing)
command: >
podman run --rm -v "{{ synapse_data_dir }}:/data"
ghcr.io/element-hq/synapse:latest
generate-config --server-name "{{ matrix_domain }}"
--report-stats no
-o /data/homeserver.yaml
args:
creates: "{{ synapse_data_dir }}/homeserver.yaml"
- name: Copy homeserver.yaml (configured)
template:
src: synapse/homeserver.yaml.j2
dest: "{{ synapse_data_dir }}/homeserver.yaml"
owner: "{{ podman_user }}"
group: "{{ podman_user }}"
mode: '0644'
- name: Ensure PostgreSQL database exists
become_user: postgres
community.postgresql.postgresql_db:
name: "{{ db_name }}"
state: present
delegate_to: "{{ inventory_hostname }}" # assuming DB on same host
- name: Ensure PostgreSQL user exists
become_user: postgres
community.postgresql.postgresql_user:
name: "{{ db_user }}"
password: "{{ db_pass }}"
priv: "{{ db_name }}.*:ALL"
state: present
delegate_to: "{{ inventory_hostname }}"
- name: Create Podman pod
command: >
podman pod create
--name {{ podman_pod_name }}
--publish 8008:8008 # synapse
--publish 8448:8448 # federation
when: podman_user != "root"
changed_when: false
- name: Run PostgreSQL container
container:
name: synapse-db
image: docker.io/library/postgres:15-alpine
state: started
restart_policy: unless-stopped
pod: "{{ podman_pod_name }}"
env:
POSTGRES_DB: "{{ db_name }}"
POSTGRES_USER: "{{ db_user }}"
POSTGRES_PASSWORD: "{{ db_pass }}"
volumes:
- "{{ synapse_data_dir }}/db:/var/lib/postgresql/data"
register: pg_cont
- name: Run Synapse container
container:
name: synapse
image: ghcr.io/element-hq/synapse:latest
state: started
restart_policy: unless-stopped
pod: "{{ podman_pod_name }}"
volumes:
- "{{ synapse_data_dir }}:/data"
command: >
-c /data/homeserver.yaml
#----- Coturn + Synapse
- name: Run Coturn container (STUN/TURN server)
container:
name: coturn
image: coturn/coturn:latest
state: started
restart_policy: unless-stopped
pod: "{{ podman_pod_name }}"
env:
LISTENING_PORT: "3478"
ALTERNATE_LISTENING_PORT: "5349"
RELAY_ADDRESS: "{{ ansible_default_ipv4.address }}" # or your server IP
EXTERNAL_IP: "{{ ansible_default_ipv4.address }}"
# Shared secret for dynamic credentials
TURNSERVER_STATIC_AUTH_SECRET: "{{ coturn_shared_secret }}"
# Enable TLS
TURNSERVER_TLS_LISTENING_PORT: "5349"
TURNSERVER_CERT: "/etc/turnserver/tls.crt"
TURNSERVER_PKEY: "/etc/turnserver/tls.key"
NO_AUTH_TIMESTAMPS: "true"
REALM: "{{ matrix_domain }}"
# Mount TLS certs (same as Nginx) if you want encrypted TURN (recommended)
volumes:
- "/var/lib/matrix-nginx/certbot/live/{{ matrix_domain }}:/etc/turnserver:ro"
register: coturn_cont

View File

@@ -0,0 +1,58 @@
server_name: "{{ matrix_domain }}"
enable_metrics: false
report_stats: false
listeners:
- port: 8008
type: http
tls: false
resources:
- names: [client, federation]
compress: true
database:
name: psycopg2
args:
user: "{{ db_user }}"
password: "{{ db_pass }}"
database: "{{ db_name }}"
host: "localhost"
port: 5432
registration_shared_secret: "{{ synapse_registration_secret }}"
tls_certificate_path: "/data/tls.crt"
private_key_path: "/data/tls.key"
federation_domain_whitelist:
- matrix.org
- gitter.im
# Optional: Rate limits
rc_flood_control:
interval: 1000
burst_count: 3
# Enable password login
enable_registration: true
enable_registration_without_verification: true
## -- turn settings
# Voice & Video Chat Settings
media_store_path: /data/media_store
max_image_pixels: 32000000 # 32MP max
# TURN/STUN server config
turn_uris:
- "turn:{{ matrix_domain }}:3478?transport=udp"
- "turn:{{ matrix_domain }}:3478?transport=tcp"
- "turns:{{ matrix_domain }}:5349?transport=tcp" # encrypted TURN
turn_shared_secret: "{{ coturn_shared_secret }}"
turn_username: "{{ coturn_username }}"
turn_password: "{{ coturn_password }}"
turn_time_limit: 86400 # 24h (optional)
turn_allow_guests: true # allow non-Matrix users (e.g., web guests)
# Optional: Enable call features
enable_call_signalling: true

View File

@@ -0,0 +1,104 @@
- name: Ensure web root exists (for ACME)
file:
path: "/var/www/{{ matrix_domain }}"
state: directory
mode: '0755'
- name: Copy compiled site to host (for volume mount)
copy:
src: "files/mysite/"
dest: "{{ nginx_webroot }}/mysite/"
mode: '0644'
vars:
nginx_webroot: "/var/lib/matrix-nginx"
- name: Ensure nginx data dir exists
file:
path: "{{ item }}"
state: directory
mode: '0755'
loop:
- "/var/lib/matrix-nginx"
- "/var/lib/matrix-nginx/certbot"
- name: Create custom Nginx image (if missing)
command: >
podman build
-f "{{ role_path }}/templates/Dockerfile-nginx-certbot.j2"
-t nginx-matrix:latest
"{{ role_path }}/templates/"
args:
creates: /var/lib/containers/storage/overlay-images/... # (use container_exists check instead)
register: build_result
changed_when: false
- name: Create Podman pod
command: >
podman pod create
--name {{ podman_pod_name }}
--publish 80:80
--publish 443:443
--publish 8008:8008
--publish 8448:8448
changed_when: false
- name: Run Nginx container (with TLS & site)
container:
name: nginx-matrix
image: nginx-matrix:latest
state: started
restart_policy: unless-stopped
pod: "{{ podman_pod_name }}"
volumes:
- "/var/lib/matrix-nginx/mysite:/usr/share/nginx/html/mysite:ro"
- "/var/lib/matrix-nginx/certbot:/etc/letsencrypt"
env:
NGINX_HOST: "{{ matrix_domain }}"
security_opt:
- label: disable # if rootless: disable SELinux
register: nginx_cont
- name: Run Synapse (PostgreSQL & all)
# ... (existing synapse task from before)
- name: Install Nginx
package:
name: nginx
state: present
- name: Create web root for landing page
file:
path: "{{ nginx_webroot }}"
state: directory
mode: '0755'
- name: Deploy landing page HTML
copy:
content: |
<!DOCTYPE html>
<html>
<head><title>Welcome</title></head>
<body>
<h1>Welcome to Matrix!</h1>
<p>Visit <a href="/_matrix">Matrix</a></p>
</body>
</html>
dest: "{{ nginx_webroot }}/index.html"
mode: '0644'
- name: Deploy Nginx config
template:
src: nginx/default.conf.j2
dest: "/etc/nginx/conf.d/{{ matrix_domain }}.conf"
mode: '0644'
- name: Remove default config (optional)
file:
path: "/etc/nginx/conf.d/default.conf"
state: absent
- name: Restart & enable Nginx
service:
name: nginx
state: restarted
enabled: yes

View File

@@ -0,0 +1,11 @@
FROM nginx:1.25-alpine
# Install Certbot (Python-based)
RUN apk add --no-cache certbot python3 py3-pip
# Add entrypoint script to auto-renew certs (optional)
COPY certbot-entrypoint.sh /usr/local/bin/certbot-entrypoint.sh
RUN chmod +x /usr/local/bin/certbot-entrypoint.sh
ENTRYPOINT ["/usr/local/bin/certbot-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

View File

@@ -0,0 +1,23 @@
#!/bin/sh
set -e
# Generate cert if not exists (first-run)
if [ ! -d "/etc/letsencrypt/live/{{ matrix_domain }}" ]; then
echo "Requesting initial Let's Encrypt certificate..."
certbot certonly --standalone \
--non-interactive --agree-tos --email admin@{{ matrix_domain }} \
-d {{ matrix_domain }} \
--domain {{ matrix_domain }} \
--preferred-challenges http \
--http-01-port 8080 \
--server https://acme-v02.api.letsencrypt.org/directory || true
# For staging (dev): use --server https://acme-staging-v02.api.letsencrypt.org/directory
fi
# Try to renew (non-blocking)
echo "Attempting certificate renewal..."
certbot renew --dry-run --quiet || true
# Start Nginx (certs will be loaded if they exist)
exec "$@"

View File

@@ -0,0 +1,93 @@
# Redirect HTTP → HTTPS (except ACME challenges)
server {
listen 80;
server_name {{ matrix_domain }} {{ landing_domain }};
# ACME-challenge (for Let's Encrypt)
location ~ /.well-known/acme-challenge/ {
root /var/www;
try_files $uri =404;
}
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS server
server {
listen 443 ssl http2;
server_name {{ matrix_domain }};
# TLS certs (mounted from Let's Encrypt dir)
ssl_certificate /etc/letsencrypt/live/{{ matrix_domain }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ matrix_domain }}/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/{{ matrix_domain }}/chain.pem;
ssl_session_cache shared:SSL:10m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Serve static site (root)
location / {
root /usr/share/nginx/html/mysite;
try_files $uri $uri/ /index.html;
}
# Matrix Synapse (client)
location /_matrix/ {
proxy_pass http://127.0.0.1:8008/_matrix/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
}
# Federation
location /_matrix/federation/v1/ {
proxy_pass http://127.0.0.1:8448/_matrix/federation/v1/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Optional: Add a Matrix admin link
location /admin {
return 301 https://matrix-admin.example.com; # or proxy to a UI like SynapseAdmin
}
}
#--- Coturn changes --- #
# TURN over TLS (port 443) — proxied to Coturn's 5349
# Only needed if you want TURN on 443 (avoids firewall issues)
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream coturn_tls {
server 127.0.0.1:5349;
}
server {
listen 443 ssl http2;
server_name {{ matrix_domain }};
# ... [existing TLS config] ...
# TURN over TLS (for UDP/TCP fallback on port 443)
location /turn {
proxy_pass https://coturn_tls;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
# Or simpler: proxy UDP via a dedicated TCP tunnel (advanced)
# For most cases, just expose 3478/5349 directly → no Nginx needed!
}

View File

@@ -1,51 +0,0 @@
---
- name: Secure and Update Fedora 43 Server
hosts: all
become: true
gather_facts: true
vars:
ansible_python_interpreter: /usr/bin/python3.14
tasks:
# Update system
- name: Update all packages (safe upgrade)
ansible.builtin.dnf:
name: "*"
state: latest
update_cache: true
- name: Install security tools
ansible.builtin.dnf:
name:
- audit
- fail2ban
- dnf-automatic
state: present
- name: Configure dnf-automatic for security updates only
ansible.builtin.template:
src: automatic.conf.j2
dest: /etc/dnf/automatic.conf
- name: Ensure dnf-automatic timer is enabled
ansible.builtin.systemd:
name: dnf-automatic.timer
state: started
enabled: true
# Ensure SELinux is enforcing
- name: Ensure SELinux is in enforcing mode
ansible.posix.selinux:
policy: targeted
state: enforcing
# Enable and start fail2ban
- name: Enable and start fail2ban
ansible.builtin.service:
name: fail2ban
state: started
enabled: true

View File

@@ -1,4 +0,0 @@
[commands]
upgrade_type = security
apply_updates = yes
download_updates = yes

View File

@@ -1,60 +0,0 @@
server_name: "{{ matrix_domain1 }}"
pid_file: /data/homeserver.pid
listeners:
- port: {{ synapse_http_port }}
resources:
- compress: true
names:
- client
- federation
tls: false
type: http
x_forwarded: true
room_list_publication_rules:
- user_id: "*"
alias: "*"
room_id: "*"
action: allow
turn_uris:
- "turn:{{ turn_domain }}:{{ turn_listen_port }}?transport=udp"
- "turn:{{ turn_domain }}:{{ turn_tls_port }}?transport=tcp"
- "turns:{{ turn_domain }}:{{ turn_tls_port }}?transport=tcp"
- "turns:{{ turn_domain }}:{{ turn_listen_port }}?transport=udp"
turn_shared_secret: "{{ turn_secret }}"
turn_user_lifetime: "1h"
turn_allow_guests: "true"
enable_registration: true
registration_requires_token: true
federation_domain_whitelist: []
auto_join_rooms:
- "#star-citizen:matrix.fpov.space"
- "#welcome:matrix.fpov.space"
media_store_path: /data/media_store
log_config: "/data/matrix.fpov.space.log.config"
registration_shared_secret: "{{ registration_shared_secret }}"
report_stats: false
macaroon_secret_key: "{{ macaroon_secret_key }}"
form_secret: "{{ form_secret }}"
signing_key_path: "/data/matrix.fpov.space.signing.key"
trusted_key_servers:
- server_name: "{{ matrix_domain1 }}"
database:
name: psycopg2
allow_unsafe_locale: true
args:
user: "{{ postgres_user }}"
password: "{{ postgres_pw }}"
database: "{{ postgres_db }}"
host: synapse-db
port: 5432
cp_min: 5
cp_max: 10

View File

@@ -1,26 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ landing_title }}</title>
<style>
body { font-family: Arial, Helvetica, sans-serif; text-align: center; margin-top: 5rem; }
h1 { font-size: 2.2rem; }
p { font-size: 1.1rem; line-height: 1.5; max-width: 600px; margin: 0 auto; }
a { color: #0066cc; }
</style>
</head>
<body>
<h1>{{ landing_title }}</h1>
<p>{{ landing_tagline }}</p>
<p>
{{ landing_message | replace('\n','<br>') }}
</p>
<p>
<a href="https://{{ matrix_domain }}">Go to Matrix login page →</a>
</p>
</body>
</html>

View File

@@ -1,14 +0,0 @@
syslog
lt-cred-mech
use-auth-secret
static-auth-secret="{{ turn_static_secret }}"
realm="{{ matrix_domain1 }}"
# cert=/etc/letsencrypt/live/turn.example.org/fullchain.pem
# pkey=/etc/letsencrypt/live/turn.example.org/privkey.pem
no-udp
external-ip="{{ public_ipv4 }}"
min-port=64000
max-port=65535

View File

@@ -1,104 +0,0 @@
# --------------------------------------------------------------
# General
# --------------------------------------------------------------
public_ipv4: "15.204.117.53"
matrix_domain1: "matrix.fpov.space"
site_domain: "org.fpov.space"
teamspeak_domain: "ts.fpov.space"
turn_domain: "turn.fpov.space"
matrix_email: "boxswappers@gmail.com"
# --------------------------------------------------------------
# Ports
# --------------------------------------------------------------
traefik_http_port: 80
traefik_https_port: 443
# --- TURN -------------------------------------------------------
turn_listen_port: 3478
turn_tls_port: 5349
turn_secret: "7A9F2C4E1B8D6035A2F7E9C1B4D68053"
turn_static_secret: "fordeathyoudietoday"
# --- Synapse ----------------------------------------------------
synapse_http_port: 8008
synapse_api_port: 8448
# --- Matrix ------------------------------------------------------
registration_shared_secret: "1KFs@=d&l6BGPD0GAwa55ePu+bF7Vgtk@~x#l3tQyKl#_q0etp"
macaroon_secret_key: "5GongrU~_kQ9L1-f.nU21sdiz~e63+Wwq;iS7XdPA2giDqZ#xH"
form_secret: "Ubwo=AThaygGi0iySK*H4i9xUOORCQM#eDMd@E,DqW3DcfqYJG"
matrix_container_name: "synapse"
# --- Teamspeak ---------------------------------------------------
teamspeak_image: "docker.io/teamspeak:3.13.7"
teamspeak_container_name: teamspeak
teamspeak_udp_port: 9987 # voice (UDP) – exposed directly on the host
teamspeak_tcp_port: 10011 # query (TCP) – will be routed via Traefik
teamspeak_file_port: 30033 # file‑transfer (TCP) – optional, expose directly
#----Postgres-----------------------------------------------------
postgres_db: synapse
postgres_user: mMwHLfdO6tz5N
postgres_pw: 9mKeSFE7tH2eY
# --------------------------------------------------------------
# Container images
# --------------------------------------------------------------
db_image: "docker.io/postgres:16-alpine"
traefik_image: "docker.io/traefik:v3.6.11"
turn_image: "docker.io/coturn/coturn:4.9.0"
synapse_image: "docker.io/matrixdotorg/synapse:latest"
nginx_image: "docker.io/nginx:alpine"
npm_image: "docker.io/jc21/nginx-proxy-manager:latest"
# --------------------------------------------------------------
# Podman names & volumes
# --------------------------------------------------------------
podman_network: matrix_net
vol_traefik: traefik_data
vol_synapse: synapse_config
vol_media: synapse_media
vol_turn: turn_data
vol_nginx: nginx_html
vol_teamspeak: teamspeak_data
npm_data: npm_data
npm_letsencrypt: letsencrypt
vol_db: postgres_db
# --------------------------------------------------------------
# Paths on the target host (temporary files)
# --------------------------------------------------------------
static_traefik_path: "/tmp/traefik_static.toml"
dynamic_traefik_path: "/tmp/traefik_dynamic.yml"
turn_conf_path: "/tmp/turnserver.conf"
synapse_cfg_path: "/tmp/homeserver.yaml"
nginx_html_path: "/tmp/index.html"
synapse_secret_key_path: "/tmp/matrix.fpov.space.signing.key"
synapse_log_config: "/tmp/matrix.fpov.space.log.config"
# -----------------------------------------------------------------
# NEW – Google OAuth2 (OpenID Connect) credentials
# -----------------------------------------------------------------
google_oidc_client_id: "YOUR_GOOGLE_CLIENT_ID"
google_oidc_client_secret: "YOUR_GOOGLE_CLIENT_SECRET"
# Optional – the scopes you want to request (the default works for most cases)
google_oidc_scopes:
- openid
- email
- profile
# --------------------------------------------------------------
# Landing page HTML
# --------------------------------------------------------------
matrix_domain: "matrix.example.com"
landing_title: "Welcome to {{ matrix_domain }}" # you can embed other vars here
landing_tagline: "Matrix + Traefik + Teamspeak – all in one stack"
landing_message: |
This host runs a Matrix homeserver (Synapse), a reverse-proxy (Traefik) with
automatic Let’s Encrypt certificates, a TURN server for voice/video, and a
simple landing page.