diff --git a/README.md b/README.md index e69de29..6fd469e 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,27 @@ +deploy playbook after setup +``` +ansible-playbook playbook.yml --ask-vault-pass +``` + +Check containers +``` +ssh matrix.example.com +sudo podman ps +sudo podman logs synapse +``` + +test federation +``` +curl https://{{ matrix_domain }}/_matrix/federation/v1/version +``` + + +| 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 | diff --git a/group_vars/all.yml b/group_vars/all.yml new file mode 100644 index 0000000..a9bcce9 --- /dev/null +++ b/group_vars/all.yml @@ -0,0 +1,15 @@ +# 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! diff --git a/inventory.ini b/inventory.ini new file mode 100644 index 0000000..3d54689 --- /dev/null +++ b/inventory.ini @@ -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) diff --git a/playbook.yml b/playbook.yml new file mode 100644 index 0000000..943c581 --- /dev/null +++ b/playbook.yml @@ -0,0 +1,19 @@ +- name: Deploy Matrix + Nginx + Landing Page + hosts: matrix_servers + become: yes + vars: + synapse_db_pass: "{{ vault_synapse_db_pass }}" + synapse_registration_secret: "{{ vault_synapse_registration_secret }}" + + pre_tasks: + - name: Ensure Podman is installed + import_role: + name: common + + - name: Deploy Nginx reverse proxy & landing page + import_role: + name: nginx-proxy + + - name: Deploy Synapse + import_role: + name: matrix-synapse diff --git a/roles/common/tasks/main.yml b/roles/common/tasks/main.yml new file mode 100644 index 0000000..b5835fe --- /dev/null +++ b/roles/common/tasks/main.yml @@ -0,0 +1,23 @@ +- 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 }}" diff --git a/roles/matrix-synapse/tasks/main.yml b/roles/matrix-synapse/tasks/main.yml new file mode 100644 index 0000000..342510d --- /dev/null +++ b/roles/matrix-synapse/tasks/main.yml @@ -0,0 +1,69 @@ +- 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 diff --git a/roles/matrix-synapse/templates/homeserver.yaml.j2 b/roles/matrix-synapse/templates/homeserver.yaml.j2 new file mode 100644 index 0000000..8a2e0b6 --- /dev/null +++ b/roles/matrix-synapse/templates/homeserver.yaml.j2 @@ -0,0 +1,37 @@ +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 diff --git a/roles/nginx-proxy/tasks/main.yml b/roles/nginx-proxy/tasks/main.yml new file mode 100644 index 0000000..7822a35 --- /dev/null +++ b/roles/nginx-proxy/tasks/main.yml @@ -0,0 +1,41 @@ +- 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: | + + +
Visit Matrix
+ + + 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 diff --git a/roles/nginx-proxy/templates/default.conf.j2 b/roles/nginx-proxy/templates/default.conf.j2 new file mode 100644 index 0000000..40fdbe8 --- /dev/null +++ b/roles/nginx-proxy/templates/default.conf.j2 @@ -0,0 +1,36 @@ +# Landing page (root path) +server { + listen 80; + server_name {{ landing_domain }} {{ matrix_domain }}; + + root {{ nginx_webroot }}; + index index.html; + + location / { + try_files $uri $uri/ =404; + } + + # Matrix Synapse + 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_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Server $host; + proxy_buffering off; + proxy_request_buffering off; + } + + # Federation (port 8448 usually, but often proxied at /_matrix too) + 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: Caddy or Let’s Encrypt ACME +}