diff --git a/roles/podman_forgejo/templates/forgejo.container b/roles/podman_forgejo/templates/forgejo.container index 9d8e00c..a213a64 100644 --- a/roles/podman_forgejo/templates/forgejo.container +++ b/roles/podman_forgejo/templates/forgejo.container @@ -11,8 +11,23 @@ Environment=FORGEJO__database__HOST=mariadb:3306 Environment=FORGEJO__database__NAME={{ podman_forgejo_mariadb_database }} Environment=FORGEJO__database__USER={{ podman_forgejo_mariadb_user }} Environment=FORGEJO__database__PASSWD={{ podman_forgejo_mariadb_password }} +Environment=FORGEJO__email_0X2E_incoming__ENABLED=true +Environment=FORGEJO__email_0X2E_incoming__REPLY_TO_ADDRESS={{ podman_forgejo_reply_to }} +Environment=FORGEJO__email_0X2E_incoming__HOST={{ podman_forgejo_imap_hostname }} +Environment=FORGEJO__email_0X2E_incoming__PORT=993 +Environment=FORGEJO__email_0X2E_incoming__USERNAME={{ podman_forgejo_imap_username }} +Environment=FORGEJO__email_0X2E_incoming__PASSWORD={{ podman_forgejo_imap_password }} +Environment=FORGEJO__email_0X2E_incoming__USE_TLS=true Environment=FORGEJO__oauth2_client__ENABLE_AUTO_REGISTRATION=true Environment=FORGEJO__server__LANDING_PAGE=/explore/repos +Environment=FORGEJO__service__ENABLE_NOTIFY_MAIL=true +Environment=FORGEJO__mailer__ENABLED=true +Environment=FORGEJO__mailer__PROTOCOL=smtps +Environment=FORGEJO__mailer__SMTP_ADDR={{ podman_forgejo_smtp_hostname }} +Environment=FORGEJO__mailer__SMTP_PORT=465 +Environment=FORGEJO__mailer__USER={{ podman_forgejo_smtp_username }} +Environment=FORGEJO__mailer__PASSWD={{ podman_forgejo_smtp_password }} +Environment=FORGEJO__mailer__FROM={{ podman_forgejo_smtp_from }} Image=codeberg.org/forgejo/forgejo:11 Network=frontend.network Network=forgejo.network diff --git a/roles/podman_host/defaults/main.yml b/roles/podman_host/defaults/main.yml deleted file mode 100644 index 558c897..0000000 --- a/roles/podman_host/defaults/main.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -podman_host_minimum_unpriv_port: "22" -podman_host_rootless_users: ["podman"] diff --git a/roles/podman_host/tasks/main.yml b/roles/podman_host/tasks/main.yml deleted file mode 100644 index 36e5ca9..0000000 --- a/roles/podman_host/tasks/main.yml +++ /dev/null @@ -1,69 +0,0 @@ ---- -- name: set unprivileged port minimum - ansible.posix.sysctl: - name: net.ipv4.ip_unprivileged_port_start - value: "{{ podman_host_minimum_unpriv_port }}" - sysctl_set: true - sysctl_file: /etc/sysctl.d/zzz-podman-unpriv-port.conf - reload: true - become: true - -- name: create users for rootless podman - ansible.builtin.user: - name: "{{ item }}" - become: true - with_items: "{{ podman_host_rootless_users }}" - -- name: set XDG_RUNTIME_DIR in .profile for rootless users - ansible.builtin.lineinfile: - path: "/home/{{ item }}/.bashrc" - line: "export XDG_RUNTIME_DIR=/run/user/$(id -u)" - create: false - become: true - become_user: "{{ item }}" - with_items: "{{ podman_host_rootless_users }}" - -- name: enable linger for rootless users - ansible.builtin.command: - argv: - - /usr/bin/loginctl - - enable-linger - - "{{ item }}" - creates: "/var/lib/systemd/linger/{{ item }}" - become: true - with_items: "{{ podman_host_rootless_users }}" - -- name: create /etc/subuid and /etc/subgid - ansible.builtin.template: - dest: "/etc/{{ item }}" - src: subXid.j2 - with_items: - - subuid - - subgid - become: true - -- name: install podman - ansible.builtin.apt: - pkg: podman - state: latest - become: true - -- name: create quadlets directory - ansible.builtin.file: - path: "/home/{{ item }}/.config/containers/systemd" - state: directory - owner: "{{ item }}" - group: "{{ item }}" - mode: "0700" - with_items: "{{ podman_host_rootless_users }}" - become: true - -- name: enable podman auto update timer for rootless users - ansible.builtin.systemd_service: - name: podman-auto-update.timer - scope: user - state: started - enabled: true - become: true - become_user: "{{ item }}" - with_items: "{{ podman_host_rootless_users }}" diff --git a/roles/podman_host/templates/subXid.j2 b/roles/podman_host/templates/subXid.j2 deleted file mode 100644 index a8022c6..0000000 --- a/roles/podman_host/templates/subXid.j2 +++ /dev/null @@ -1,4 +0,0 @@ -# {{ ansible_managed }} -{% for username in podman_host_rootless_users %} -{{ username }}:{{ 100000 + ((loop.index - 1) * 65536) }}:65536 -{% endfor %} diff --git a/roles/podman_link/defaults/main.yml b/roles/podman_link/defaults/main.yml new file mode 100644 index 0000000..debbddf --- /dev/null +++ b/roles/podman_link/defaults/main.yml @@ -0,0 +1,25 @@ +podman_link_podman_rootless_user: link +podman_link_web_hostname: "{{ inventory_hostname }}" +podman_link_postgres_zammad_user: postgres +podman_link_postgres_zammad_database: zammad_production +podman_link_postgres_link_user: link +podman_link_postgres_link_database: link +# podman_link_postgres_zammad_password: +# podman_link_postgres_link_password: +# podman_link_postgres_root_password: +# podman_link_zammad_redis_password: +# podman_link_opensearch_password: +podman_link_opensearch_memory_limit: 2048 +podman_link_setup_mode: false +podman_link_leafcutter_enabled: false +podman_link_dashboard_url: "" +podman_link_zammad_api_token: "" +# podman_link_nextauth_secret: +# podman_link_google_client_id: +# podman_link_google_client_secret: +# +# the following may seem useless, but unless they are included as variables in the environment file template, +# they automatically get wrapped in quotes by systemd causing zammad to not connect +podman_link_postgres_zammad_postgresql_host: zammad-postgresql +podman_link_postgres_zammad_es_host: opensearch +podman_link_postgres_zammad_memcached_server: zammad-memcached:11211 diff --git a/roles/podman_link/handlers/main.yml b/roles/podman_link/handlers/main.yml new file mode 100644 index 0000000..19d7e1d --- /dev/null +++ b/roles/podman_link/handlers/main.yml @@ -0,0 +1,26 @@ +- name: restart zammad-init + ansible.builtin.systemd_service: + name: zammad-init + state: restarted + scope: user + daemon_reload: true + become: true + become_user: "{{ podman_link_podman_rootless_user }}" + +- name: restart zammad-postgresql + ansible.builtin.systemd_service: + name: zammad-postgresql + state: restarted + scope: user + daemon_reload: true + become: true + become_user: "{{ podman_link_podman_rootless_user }}" + +- name: restart link.slice + ansible.builtin.systemd_service: + name: link.slice + state: restarted + scope: user + daemon_reload: true + become: true + become_user: "{{ podman_link_podman_rootless_user }}" diff --git a/roles/podman_link/tasks/main.yml b/roles/podman_link/tasks/main.yml new file mode 100644 index 0000000..642d883 --- /dev/null +++ b/roles/podman_link/tasks/main.yml @@ -0,0 +1,229 @@ +--- +- name: create service configuration directories + ansible.builtin.file: + path: "/home/{{ podman_link_podman_rootless_user }}/{{ item }}" + state: directory + owner: "{{ podman_link_podman_rootless_user }}" + group: "{{ podman_link_podman_rootless_user }}" + mode: "0755" + become: true + with_items: + - zammad-storage + - zammad-var + - zammad-backup + - zammad-data + - signal-cli-rest-api-data + - bridge-postgresql-data + - bridge-whatsapp-data + - redis-data + - postgresql-data + +- name: create configuration directories where containers need to execute scripts + ansible.builtin.file: + path: "/home/{{ podman_link_podman_rootless_user }}/{{ item }}" + state: directory + owner: "{{ podman_link_podman_rootless_user }}" + group: "{{ podman_link_podman_rootless_user }}" + mode: "0777" + become: true + with_items: + - zammad-config-nginx + - opensearch-data + +- name: install zammad railsserver database configuration file + ansible.builtin.template: + src: "{{ item }}" + dest: "/home/{{ podman_link_podman_rootless_user }}/{{ item }}" + owner: "{{ podman_link_podman_rootless_user }}" + group: "{{ podman_link_podman_rootless_user }}" + mode: "0444" + become: true + with_items: + - zammad-database.yml + +- name: install env configuration files + ansible.builtin.template: + src: "{{ item }}" + dest: "/home/{{ podman_link_podman_rootless_user }}/.config/containers/systemd/{{ item }}" + owner: "{{ podman_link_podman_rootless_user }}" + mode: "0600" + become: true + with_items: + - common-zammad.env + - common-bridge.env + +- name: Set sysctl settings for elasticsearch + sysctl: + name: vm.max_map_count + value: '262144' + state: present + become: true + +- name: Set vm.overcommit_memory for Memcached + sysctl: + name: vm.overcommit_memory + value: '1' + state: present + become: true + +- name: install opensearch config + ansible.builtin.copy: + src: templates/opensearch-config.yml + dest: "/home/{{ podman_link_podman_rootless_user }}/opensearch-config.yml" + mode: "0444" + owner: "{{ podman_link_podman_rootless_user }}" + group: "{{ podman_link_podman_rootless_user }}" + become: true + +- name: install podman quadlet for rootless podman user + ansible.builtin.template: + src: "{{ item }}" + dest: "/home/{{ podman_link_podman_rootless_user }}/.config/containers/systemd/{{ item }}" + owner: "{{ podman_link_podman_rootless_user }}" + mode: "0400" + with_items: + - link.container + - zammad-opensearch.container + - opensearch-dashboards.container + - bridge-worker.container + - bridge-postgresql.container + - bridge-whatsapp.container + - signal-cli-rest-api.container + - zammad-init.container + - zammad-nginx.container + - zammad-railsserver.container + - zammad-scheduler.container + - zammad-postgresql.container + - zammad-websocket.container + - zammad-redis.container + - zammad-memcached.container + become: true + + +- name: install network quadlets for rootless podman user + ansible.builtin.template: + src: "{{ item }}" + dest: "/home/{{ podman_link_podman_rootless_user }}/.config/containers/systemd/{{ item }}" + owner: "{{ podman_link_podman_rootless_user }}" + mode: "0400" + with_items: + - frontend.network + - link.network + become: true + +- name: verify quadlets are correctly defined + ansible.builtin.command: /usr/libexec/podman/quadlet -dryrun -user + register: podman_link_quadlet_result + ignore_errors: true + changed_when: false + become: true + become_user: "{{ podman_link_podman_rootless_user }}" + +- name: assert that the quadlet verification succeeded + ansible.builtin.assert: + that: + - podman_link_quadlet_result.rc == 0 + fail_msg: "'/usr/libexec/podman/quadlet -dryrun -user' failed! Output withheld to prevent leaking secrets." + +#- name: set up nginx +# ansible.builtin.include_role: +# name: irl.wip.podman_nginx +# vars: +# podman_nginx_frontend_network: frontend +# podman_nginx_podman_rootless_user: "{{ podman_link_podman_rootless_user }}" +# podman_nginx_primary_hostname: "{{ podman_link_web_hostname }}" +# podman_nginx_systemd_service_slice: "link.slice" +# podman_nginx_systemd_service_requires: ["zammad-nginx"] +# +# +#- name: create nginx configuration file +# ansible.builtin.template: +# src: nginx.conf +# dest: "/home/{{ podman_link_podman_rootless_user }}/nginx/nginx.conf" +# owner: "{{ podman_link_podman_rootless_user }}" +# group: "{{ podman_link_podman_rootless_user }}" +# mode: "0644" +# become: true + +- name: install services slice for rootless podman user + ansible.builtin.template: + src: "link.slice" + dest: "/home/{{ podman_link_podman_rootless_user }}/.config/systemd/user/link.slice" + owner: "{{ podman_link_podman_rootless_user }}" + group: "{{ podman_link_podman_rootless_user }}" + mode: "0655" + become: true + +- name: make sure services are started on boot + ansible.builtin.systemd_service: + name: "link.slice" + enabled: true + state: started + daemon_reload: true + scope: user + become: true + become_user: "{{ podman_link_podman_rootless_user }}" + notify: + - "restart link.slice" + + +- name: set es verify false + ansible.builtin.shell: > + podman exec zammad-railsserver rails r "Setting.set('es_ssl_verify', false)" + become: true + become_user: "{{ podman_link_podman_rootless_user }}" + notify: + - "restart link.slice" + register: es_ssl_result + retries: 20 + delay: 5 + until: es_ssl_result.rc == 0 + +- name: Run OpenSearch setup script + ansible.builtin.shell: | + podman exec zammad-opensearch /bin/sh -c ' + if [ ! -f /tmp/.securityadmin_done ]; then + chmod +x /usr/share/opensearch/plugins/opensearch-security/tools/securityadmin.sh && \ + /usr/share/opensearch/plugins/opensearch-security/tools/securityadmin.sh \ + -cd /usr/share/opensearch/config/opensearch-security/ \ + -icl \ + -key /usr/share/opensearch/config/kirk-key.pem \ + -cert /usr/share/opensearch/config/kirk.pem \ + -cacert /usr/share/opensearch/config/root-ca.pem \ + -nhnv && \ + touch /tmp/.securityadmin_done + fi + ' + become: true + become_user: "{{ podman_link_podman_rootless_user }}" + register: securityadmin_scipt_result + retries: 20 + delay: 5 + until: securityadmin_scipt_result.rc == 0 + notify: + - "restart link.slice" + +- name: set up nginx + ansible.builtin.include_role: + name: irl.wip.podman_nginx + vars: + podman_nginx_frontend_network: frontend + podman_nginx_podman_rootless_user: "{{ podman_link_podman_rootless_user }}" + podman_nginx_primary_hostname: "{{ podman_link_web_hostname }}" + podman_nginx_systemd_service_slice: link.slice + podman_nginx_systemd_service_requires: ["zammad-nginx"] +# podman_nginx_additional_volumes: +# - src: "/home/{{ podman_cleaninsights_podman_rootless_user }}/matomo" +# dest: "/var/www/html" +# options: "ro" + +- name: create nginx configuration file + ansible.builtin.template: + src: nginx.conf + dest: "/home/{{ podman_link_podman_rootless_user }}/nginx/nginx.conf" + owner: "{{ podman_link_podman_rootless_user }}" + group: "{{ podman_link_podman_rootless_user }}" + mode: "0644" + become: true + notify: + - "restart link.slice" \ No newline at end of file diff --git a/roles/podman_link/templates/bridge-postgresql.container b/roles/podman_link/templates/bridge-postgresql.container new file mode 100644 index 0000000..bd7fcae --- /dev/null +++ b/roles/podman_link/templates/bridge-postgresql.container @@ -0,0 +1,11 @@ +[Container] +ContainerName=bridge-postgresql +EnvironmentFile=common-bridge.env +Image=registry.gitlab.com/digiresilience/link/link-stack/postgresql:3.1.0b2 +Volume=/home/{{ podman_link_podman_rootless_user }}/bridge-postgresql-data:/var/lib/postgresql/data +Network=link.network + + +[Service] +Restart=always +Slice=link.slice \ No newline at end of file diff --git a/roles/podman_link/templates/bridge-whatsapp.container b/roles/podman_link/templates/bridge-whatsapp.container new file mode 100644 index 0000000..247847d --- /dev/null +++ b/roles/podman_link/templates/bridge-whatsapp.container @@ -0,0 +1,17 @@ +[Unit] +Requires=bridge-postgresql.service link.service +After=bridge-postgresql.service link.service + +[Container] +ContainerName=bridge-whatsapp +Environment=BRIDGE_FRONTEND_URL=http://systemd-link:3000/link +EnvironmentFile=common-bridge.env +ExposeHostPort=5000 +Image=registry.gitlab.com/digiresilience/link/link-stack/bridge-whatsapp:3.1.0b2 +Volume=/home/{{ podman_link_podman_rootless_user }}/bridge-whatsapp-data:/home/node/baileys +Network=link.network + + +[Service] +Restart=always +Slice=link.slice \ No newline at end of file diff --git a/roles/podman_link/templates/bridge-worker.container b/roles/podman_link/templates/bridge-worker.container new file mode 100644 index 0000000..7f6f0ad --- /dev/null +++ b/roles/podman_link/templates/bridge-worker.container @@ -0,0 +1,13 @@ +[Unit] +Requires=bridge-postgresql.service link.service +After=bridge-postgresql.service link.service + +[Container] +ContainerName=bridge-worker +EnvironmentFile=common-bridge.env +Image=registry.gitlab.com/digiresilience/link/link-stack/bridge-worker:3.1.0b2 +Network=link.network + +[Service] +Restart=always +Slice=link.slice \ No newline at end of file diff --git a/roles/podman_link/templates/common-bridge.env b/roles/podman_link/templates/common-bridge.env new file mode 100644 index 0000000..ff147a4 --- /dev/null +++ b/roles/podman_link/templates/common-bridge.env @@ -0,0 +1,18 @@ +POSTGRES_USER={{ podman_link_postgres_link_user }} +POSTGRES_PASSWORD={{ podman_link_postgres_link_password }} +POSTGRES_DB={{ podman_link_postgres_link_database }} +NEXTAUTH_URL=https://{{ podman_link_web_hostname }}/link/api/auth +NEXTAUTH_SECRET={{ podman_link_nextauth_secret }} +GOOGLE_CLIENT_ID={{ podman_link_google_client_id }} +GOOGLE_CLIENT_SECRET={{ podman_link_google_client_secret }} +BRIDGE_FRONTEND_URL=http://link:3000 +BRIDGE_SIGNAL_URL=http://signal-cli-rest-api:8081 +BRIDGE_WHATSAPP_URL=http://bridge-whatsapp:5000 +DATABASE_NAME={{ podman_link_postgres_link_database }} +DATABASE_HOST=bridge-postgresql +DATABASE_USER={{ podman_link_postgres_link_user }} +DATABASE_PASSWORD={{ podman_link_postgres_link_password }} +DATABASE_PORT=5432 +DATABASE_URL=postgresql://{{ podman_link_postgres_link_user }}:{{ podman_link_postgres_link_password }}@bridge-postgresql/{{ podman_link_postgres_link_database}} +TZ=Etc/UTC + diff --git a/roles/podman_link/templates/common-zammad.env b/roles/podman_link/templates/common-zammad.env new file mode 100644 index 0000000..6cb0ff1 --- /dev/null +++ b/roles/podman_link/templates/common-zammad.env @@ -0,0 +1,13 @@ +POSTGRESQL_HOST=zammad-postgresql +POSTGRESQL_PASS={{ podman_link_postgres_zammad_password }} +POSTGRESQL_USER={{ podman_link_postgres_zammad_user }} +POSTGRESQL_DB={{ podman_link_postgres_zammad_database }} +POSTGRESQL_OPTIONS=?pool=50 +REDIS_URL=redis://default:{{ podman_link_zammad_redis_password }}@zammad-redis:6379 +MEMCACHE_SERVERS=zammad-memcached:11211 +ELASTICSEARCH_HOST=zammad-opensearch +ELASTICSEARCH_USER=admin +ELASTICSEARCH_PASS={{ podman_link_opensearch_password }} +ELASTICSEARCH_SCHEMA=https +ELASTICSEARCH_REINDEX=true +ELASTICSEARCH_SSL_VERIFY=false \ No newline at end of file diff --git a/roles/podman_link/templates/frontend.network b/roles/podman_link/templates/frontend.network new file mode 100644 index 0000000..379c059 --- /dev/null +++ b/roles/podman_link/templates/frontend.network @@ -0,0 +1,2 @@ +[Network] +NetworkName=frontend diff --git a/roles/podman_link/templates/link.container b/roles/podman_link/templates/link.container new file mode 100644 index 0000000..d9ce0b2 --- /dev/null +++ b/roles/podman_link/templates/link.container @@ -0,0 +1,21 @@ +[Unit] +Requires=bridge-postgresql.service +After=bridge-postgresql.service + +[Container] +ContainerName=link +Environment=ZAMMAD_VIRTUAL_HOST={{ podman_link_web_hostname }} +Environment=SETUP_MODE={{ podman_link_setup_mode }} +Environment=LEAFCUTTER_ENABLED={{ podman_link_leafcutter_enabled }} +Environment=LEAFCUTTER_DEFAULT_DASHBOARD_URL={{ podman_link_dashboard_url }} +Environment=ZAMMAD_API_TOKEN={{ podman_link_zammad_api_token }} +Environment=LINK_URL=https://localhost:3000/link +Environment=ZAMMAD_URL=http://zammad-nginx:8080 +EnvironmentFile=common-bridge.env +ExposeHostPort=3000 +Image=registry.gitlab.com/digiresilience/link/link-stack/link:3.1.0b2 +Network=link.network + +[Service] +Restart=always +Slice=link.slice \ No newline at end of file diff --git a/roles/podman_link/templates/link.network b/roles/podman_link/templates/link.network new file mode 100644 index 0000000..beae94f --- /dev/null +++ b/roles/podman_link/templates/link.network @@ -0,0 +1,2 @@ +[Network] +NetworkName=link diff --git a/roles/podman_link/templates/link.slice b/roles/podman_link/templates/link.slice new file mode 100644 index 0000000..fb3ff33 --- /dev/null +++ b/roles/podman_link/templates/link.slice @@ -0,0 +1,22 @@ +[Unit] +Description=Link Slice +Before=slices.target +Requires=zammad-init.service +Requires=zammad-memcached.service +Requires=zammad-postgresql.service +Requires=zammad-redis.service +Requires=zammad-railsserver.service +Requires=zammad-scheduler.service +Requires=zammad-websocket.service +Requires=zammad-opensearch.service +Requires=link.service +Requires=bridge-postgresql.service +Requires=zammad-nginx.service +Requires=signal-cli-rest-api.service +Requires=bridge-whatsapp.service +Requires=bridge-worker.service +Requires=opensearch-dashboards.service +Requires=nginx.service + +[Install] +WantedBy=default.target diff --git a/roles/podman_link/templates/nginx.conf b/roles/podman_link/templates/nginx.conf new file mode 100644 index 0000000..af60818 --- /dev/null +++ b/roles/podman_link/templates/nginx.conf @@ -0,0 +1,91 @@ +# If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the +# scheme used to connect to this server +map $http_x_forwarded_proto $proxy_x_forwarded_proto { + default $http_x_forwarded_proto; + '' $scheme; +} +# If we receive X-Forwarded-Port, pass it through; otherwise, pass along the +# server port the client connected to +map $http_x_forwarded_port $proxy_x_forwarded_port { + default $http_x_forwarded_port; + '' $server_port; +} +# If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any +# Connection header that may have been passed to this server +map $http_upgrade $proxy_connection { + default upgrade; + '' close; +} +# Apply fix for very long server names +server_names_hash_bucket_size 128; +# Default dhparam +# Set appropriate X-Forwarded-Ssl header +map $scheme $proxy_x_forwarded_ssl { + default off; + https on; +} +gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; +log_format vhost '$host $remote_addr - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent"'; +access_log off; +# HTTP 1.1 support +proxy_http_version 1.1; +proxy_buffering off; +proxy_set_header Host $http_host; +proxy_set_header Upgrade $http_upgrade; +proxy_set_header Connection $proxy_connection; +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 $proxy_x_forwarded_proto; +proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl; +proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port; +# Mitigate httpoxy attack (see README for details) +proxy_set_header Proxy ""; + +server { + listen 80; + listen [::]:80; + + server_name {{ podman_link_web_hostname }}; + server_tokens off; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://{{ podman_link_web_hostname }}$request_uri; + } +} + +upstream zammad { + server zammad-nginx:8080; +} + +server { + server_name {{ podman_link_web_hostname }}; + listen 80 ; + access_log /var/log/nginx/access.log vhost; + return 301 https://$host$request_uri; +} + +server { + server_name {{ podman_link_web_hostname }}; + listen 443 ssl; + listen [::]:443 ssl; + http2 on; + server_tokens off; + + ssl_certificate /etc/letsencrypt/live/{{ podman_link_web_hostname }}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/{{ podman_link_web_hostname }}/privkey.pem; + + + add_header Strict-Transport-Security "max-age=31536000" always; + add_header Referrer-Policy origin always; # make sure outgoing links don't show the URL to the Matomo instance + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + location / { + proxy_pass http://zammad; + } +} \ No newline at end of file diff --git a/roles/podman_link/templates/opensearch-config.yml b/roles/podman_link/templates/opensearch-config.yml new file mode 100644 index 0000000..26a3230 --- /dev/null +++ b/roles/podman_link/templates/opensearch-config.yml @@ -0,0 +1,36 @@ +_meta: + type: "config" + config_version: 2 + +config: + dynamic: + http: + anonymous_auth_enabled: false + xff: + enabled: true + remoteIpHeader: "x-forwarded-for" + internalProxies: ".*" + authc: + basic_internal_auth_domain: + description: "Authenticate via HTTP Basic against internal users database" + http_enabled: true + transport_enabled: true + order: 0 + http_authenticator: + type: basic + challenge: false + authentication_backend: + type: intern + proxy_auth_domain: + description: "Authenticate via proxy" + http_enabled: true + transport_enabled: true + order: 1 + http_authenticator: + type: proxy + challenge: false + config: + user_header: "x-forwarded-user" + roles_header: "x-forwarded-roles" + authentication_backend: + type: noop \ No newline at end of file diff --git a/roles/podman_link/templates/opensearch-dashboards.container b/roles/podman_link/templates/opensearch-dashboards.container new file mode 100644 index 0000000..9c2d42f --- /dev/null +++ b/roles/podman_link/templates/opensearch-dashboards.container @@ -0,0 +1,16 @@ +[Unit] +Requires=zammad-opensearch.service +After=zammad-opensearch.service + +[Container] +ContainerName=opensearch-dashboards +Environment=OPENSEARCH_USERNAME="kibanaserver" +Environment=OPENSEARCH_PASSWORD="kibanaserver" +Image=registry.gitlab.com/digiresilience/link/link-stack/opensearch-dashboards:3.1.0b2 +PublishPort=127.0.0.1:5601:5601 +#Volume=/home/{{ podman_link_podman_rootless_user }}/opensearch-dashboards-config.yml:/usr/share/opensearch-dashboards/config/opensearch_dashboards.yml +Network=link.network + +[Service] +Restart=always +Slice=link.slice \ No newline at end of file diff --git a/roles/podman_link/templates/signal-cli-rest-api.container b/roles/podman_link/templates/signal-cli-rest-api.container new file mode 100644 index 0000000..1b3b73e --- /dev/null +++ b/roles/podman_link/templates/signal-cli-rest-api.container @@ -0,0 +1,16 @@ +[Unit] +Requires=link.service +After=link.service + +[Container] +ContainerName=signal-cli-rest-api +Environment=MODE=native +Environment=PORT=8081 +ExposeHostPort=8081 +Image=registry.gitlab.com/digiresilience/link/link-stack/signal-cli-rest-api:3.1.0b2 +Volume=/home/{{ podman_link_podman_rootless_user }}/signal-cli-rest-api-data:/home/.local/share/signal-cli +Network=link.network + +[Service] +Restart=always +slice=link.slice \ No newline at end of file diff --git a/roles/podman_link/templates/zammad-database.yml b/roles/podman_link/templates/zammad-database.yml new file mode 100644 index 0000000..e655d15 --- /dev/null +++ b/roles/podman_link/templates/zammad-database.yml @@ -0,0 +1,9 @@ +production: + adapter: postgresql + database: {{ podman_link_postgres_zammad_database }} + pool: 50 + timeout: 5000 + encoding: utf8 + username: {{ podman_link_postgres_zammad_user }} + password: {{ podman_link_postgres_zammad_password }} + host: zammad-postgresql diff --git a/roles/podman_link/templates/zammad-init.container b/roles/podman_link/templates/zammad-init.container new file mode 100644 index 0000000..7de78a6 --- /dev/null +++ b/roles/podman_link/templates/zammad-init.container @@ -0,0 +1,21 @@ +[Unit] +Requires=zammad-postgresql.service +After=zammad-postgresql.service +Requires=zammad-redis.service +After=zammad-redis.service +Requires=zammad-memcached.service +After=zammad-memcached.service + +[Container] +ContainerName=zammad-init +EnvironmentFile=common-zammad.env +Exec=zammad-init +Image=registry.gitlab.com/digiresilience/link/link-stack/zammad:3.1.0b2 +Volume=/home/{{ podman_link_podman_rootless_user }}/zammad-config-nginx:/etc/nginx/sites-enabled +Volume=/home/{{ podman_link_podman_rootless_user }}/zammad-var:/opt/zammad/var +Volume=/home/{{ podman_link_podman_rootless_user }}/zammad-storage:/opt/zammad/storage:ro +Network=link.network + +[Service] +Restart=on-failure +Slice=link.slice diff --git a/roles/podman_link/templates/zammad-memcached.container b/roles/podman_link/templates/zammad-memcached.container new file mode 100644 index 0000000..d521fe8 --- /dev/null +++ b/roles/podman_link/templates/zammad-memcached.container @@ -0,0 +1,10 @@ +[Container] +ContainerName=zammad-memcached +Exec=memcached -m 256M +Image=registry.gitlab.com/digiresilience/link/link-stack/memcached:3.1.0b2 +Network=link.network +ExposeHostPort=11211 + +[Service] +Restart=always +Slice=link.slice \ No newline at end of file diff --git a/roles/podman_link/templates/zammad-nginx.container b/roles/podman_link/templates/zammad-nginx.container new file mode 100644 index 0000000..6778441 --- /dev/null +++ b/roles/podman_link/templates/zammad-nginx.container @@ -0,0 +1,18 @@ +[Unit] +Requires=zammad-railsserver.service +After=zammad-railsserver.service + +[Container] +ContainerName=zammad-nginx +EnvironmentFile=common-zammad.env +Exec=zammad-nginx +ExposeHostPort=8080 +Image=registry.gitlab.com/digiresilience/link/link-stack/zammad:3.1.0b2 +Volume=/home/{{ podman_link_podman_rootless_user }}/zammad-config-nginx:/etc/nginx/sites-enabled +Volume=/home/{{ podman_link_podman_rootless_user }}/zammad-var:/opt/zammad/var:ro +Network=link.network +Network=frontend.network + +[Service] +Restart=always +Slice=link.slice \ No newline at end of file diff --git a/roles/podman_link/templates/zammad-opensearch.container b/roles/podman_link/templates/zammad-opensearch.container new file mode 100644 index 0000000..9e0c058 --- /dev/null +++ b/roles/podman_link/templates/zammad-opensearch.container @@ -0,0 +1,24 @@ +[Container] +ContainerName=zammad-opensearch +Environment=discovery.type=single-node +Environment=plugins.security.ssl.transport.enforce_hostname_verification=false +Environment=plugins.security.ssl.transport.resolve_hostname=false +Environment=cluster.routing.allocation.disk.watermark.low=3gb +Environment=cluster.routing.allocation.disk.watermark.high=2gb +Environment=cluster.routing.allocation.disk.watermark.flood_stage=500mb +Environment=cluster.info.update.interval=1m +Environment=node.name=opensearch-node1 +Environment=bootstrap.memory_lock=true +Environment=OPENSEARCH_JAVA_OPTS="-Xms{{ podman_link_opensearch_memory_limit }}m -Xmx{{podman_link_opensearch_memory_limit }}m -XX:-HeapDumpOnOutOfMemoryError" +Environment=OPENSEARCH_INITIAL_ADMIN_PASSWORD={{ podman_link_opensearch_password }} +Environment=compatibility.override_main_response_version=true +Image=registry.gitlab.com/digiresilience/link/link-stack/opensearch:3.1.0b2 +PublishPort=127.0.0.1:9200:9200 +PublishPort=127.0.0.1:9600:9600 +Volume=/home/{{ podman_link_podman_rootless_user }}/opensearch-data:/usr/share/opensearch/data +Volume=/home/{{ podman_link_podman_rootless_user }}/opensearch-config.yml:/usr/share/opensearch/config/opensearch-security/config.yml +Network=link.network + +[Service] +Restart=always +Slice=link.slice \ No newline at end of file diff --git a/roles/podman_link/templates/zammad-postgresql.container b/roles/podman_link/templates/zammad-postgresql.container new file mode 100644 index 0000000..5fe6911 --- /dev/null +++ b/roles/podman_link/templates/zammad-postgresql.container @@ -0,0 +1,17 @@ +[Container] +ContainerName=zammad-postgresql +Environment=POSTGRES_PASSWORD={{ podman_link_postgres_zammad_password }} +Environment=POSTGRES_USER={{ podman_link_postgres_zammad_user }} +Environment=POSTGRES_DB={{ podman_link_postgres_zammad_database }} +Environment=POSTGRES_HOST_AUTH_METHOD=scram-sha-256 +Environment=POSTGRES_INITDB_ARGS=--auth-host=scram-sha-256 +Image=registry.gitlab.com/digiresilience/link/link-stack/postgresql:3.1.0b2 +Volume=/home/{{ podman_link_podman_rootless_user }}/postgresql-data:/var/lib/postgresql/data +Volume=/home/{{ podman_link_podman_rootless_user }}/zammad-data:/opt/zammad +Volume=/home/{{ podman_link_podman_rootless_user }}/zammad-backup:/var/tmp/zammad:ro +Network=link.network + +[Service] +Restart=always +Slice=link.slice + diff --git a/roles/podman_link/templates/zammad-railsserver.container b/roles/podman_link/templates/zammad-railsserver.container new file mode 100644 index 0000000..5037130 --- /dev/null +++ b/roles/podman_link/templates/zammad-railsserver.container @@ -0,0 +1,17 @@ +[Unit] +Requires=zammad-memcached.service zammad-redis.service zammad-postgresql.service +After=zammad-memcached.service zammad-redis.service zammad-postgresql.service + +[Container] +ContainerName=zammad-railsserver +EnvironmentFile=common-zammad.env +Exec=zammad-railsserver +Image=registry.gitlab.com/digiresilience/link/link-stack/zammad:3.1.0b2 +Volume=/home/{{ podman_link_podman_rootless_user }}/zammad-var:/opt/zammad/var +Volume=/home/{{ podman_link_podman_rootless_user }}/zammad-storage:/opt/zammad/storage +Volume=/home/{{ podman_link_podman_rootless_user }}/zammad-database.yml:/opt/zammad/config/database.yml:ro +Network=link.network + +[Service] +Restart=always +Slice=link.slice diff --git a/roles/podman_link/templates/zammad-redis.container b/roles/podman_link/templates/zammad-redis.container new file mode 100644 index 0000000..28bd9dd --- /dev/null +++ b/roles/podman_link/templates/zammad-redis.container @@ -0,0 +1,10 @@ +[Container] +ContainerName=zammad-redis +Environment=REDIS_PASSWORD={{ podman_link_zammad_redis_password }} +Image=registry.gitlab.com/digiresilience/link/link-stack/redis:3.1.0b2 +Volume=/home/{{ podman_link_podman_rootless_user }}/redis-data:/data +Network=link.network + +[Service] +Restart=always +Slice=link.slice \ No newline at end of file diff --git a/roles/podman_link/templates/zammad-scheduler.container b/roles/podman_link/templates/zammad-scheduler.container new file mode 100644 index 0000000..f993b53 --- /dev/null +++ b/roles/podman_link/templates/zammad-scheduler.container @@ -0,0 +1,16 @@ +[Unit] +Requires=zammad-memcached.service zammad-railsserver.service zammad-redis.service +After=zammad-memcached.service zammad-railsserver.service zammad-redis.service + +[Container] +ContainerName=zammad-scheduler +EnvironmentFile=common-zammad.env +Exec=zammad-scheduler +Image=registry.gitlab.com/digiresilience/link/link-stack/zammad:3.1.0b2 +Volume=/home/{{ podman_link_podman_rootless_user }}/zammad-var:/opt/zammad/var +Volume=/home/{{ podman_link_podman_rootless_user }}/zammad-storage:/opt/zammad/storage +Network=link.network + +[Service] +Restart=always +Slice=link.slice diff --git a/roles/podman_link/templates/zammad-websocket.container b/roles/podman_link/templates/zammad-websocket.container new file mode 100644 index 0000000..be46550 --- /dev/null +++ b/roles/podman_link/templates/zammad-websocket.container @@ -0,0 +1,16 @@ +[Unit] +Requires=zammad-memcached.service zammad-railsserver.service zammad-redis.service +After=zammad-memcached.service zammad-railsserver.service zammad-redis.service + +[Container] +ContainerName=zammad-websocket +EnvironmentFile=common-zammad.env +Exec=zammad-websocket +Image=registry.gitlab.com/digiresilience/link/link-stack/zammad:3.1.0b2 +Volume=/home/{{ podman_link_podman_rootless_user }}/zammad-var:/opt/zammad/var +Volume=/home/{{ podman_link_podman_rootless_user }}/zammad-storage:/opt/zammad/storage +Network=link.network + +[Service] +Restart=always +Slice=link.slice \ No newline at end of file diff --git a/roles/podman_nginx/defaults/main.yml b/roles/podman_nginx/defaults/main.yml deleted file mode 100644 index 5d6b76e..0000000 --- a/roles/podman_nginx/defaults/main.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -podman_nginx_additional_hostnames: [] -podman_nginx_certbot_testing: false -# podman_nginx_frontend_network: -podman_nginx_podman_rootless_user: nginx -# podman_nginx_primary_hostname: \ No newline at end of file diff --git a/roles/podman_nginx/handlers/main.yml b/roles/podman_nginx/handlers/main.yml deleted file mode 100644 index f1b17d9..0000000 --- a/roles/podman_nginx/handlers/main.yml +++ /dev/null @@ -1,18 +0,0 @@ ---- -- name: restart certbot-renew - ansible.builtin.systemd_service: - name: certbot-renew - state: started - scope: user - daemon_reload: true - become: true - become_user: "{{ podman_nginx_podman_rootless_user }}" - -- name: restart nginx - ansible.builtin.systemd_service: - name: nginx - state: restarted - scope: user - daemon_reload: true - become: true - become_user: "{{ podman_nginx_podman_rootless_user }}" diff --git a/roles/podman_nginx/tasks/main.yml b/roles/podman_nginx/tasks/main.yml deleted file mode 100644 index 7d3f3c8..0000000 --- a/roles/podman_nginx/tasks/main.yml +++ /dev/null @@ -1,110 +0,0 @@ ---- -- name: create service configuration directories - ansible.builtin.file: - path: "/home/{{ podman_nginx_podman_rootless_user }}/{{ item }}" - state: directory - owner: "{{ podman_nginx_podman_rootless_user }}" - group: "{{ podman_nginx_podman_rootless_user }}" - mode: "0755" - become: true - with_items: - - .config/systemd/user - - certbot/conf - - certbot/www - - nginx - -- name: install podman quadlet for rootless podman user - ansible.builtin.template: - src: "{{ item }}" - dest: "/home/{{ podman_nginx_podman_rootless_user }}/.config/containers/systemd/{{ item }}" - owner: "{{ podman_nginx_podman_rootless_user }}" - mode: "0400" - with_items: - - certbot-renew.container - - nginx.container - notify: - - "restart {{ item | split('.') | first }}" - become: true - -- name: install certbot renewal timer for rootless podman user - ansible.builtin.template: - src: "certbot-renew.timer" - dest: "/home/{{ podman_nginx_podman_rootless_user }}/.config/systemd/user/certbot-renew.timer" - owner: "{{ podman_nginx_podman_rootless_user }}" - mode: "0400" - become: true - -- name: verify quadlets are correctly defined - ansible.builtin.command: /usr/libexec/podman/quadlet -dryrun -user - register: podman_nginx_quadlet_result - ignore_errors: true - changed_when: false - become: true - become_user: "{{ podman_nginx_podman_rootless_user }}" - -- name: check if certificate exists - stat: - path: "/home/{{ podman_nginx_podman_rootless_user }}/certbot/conf/live/{{ podman_nginx_primary_hostname }}/fullchain.pem" - register: podman_nginx_cert_stat - become: yes - become_user: "{{ podman_nginx_podman_rootless_user }}" - -- name: create temporary nginx configuration (no https) - ansible.builtin.template: - src: nginx.conf - dest: "/home/{{ podman_nginx_podman_rootless_user }}/nginx/nginx.conf" - owner: "{{ podman_nginx_podman_rootless_user }}" - group: "{{ podman_nginx_podman_rootless_user }}" - mode: "0644" - become: true - when: podman_nginx_cert_stat.stat.exists == false - -- name: start nginx - ansible.builtin.systemd_service: - name: nginx - state: started - scope: user - daemon_reload: true - become: true - become_user: "{{ podman_nginx_podman_rootless_user }}" - -- name: run certbot container to create certificate - ansible.builtin.command: - cmd: > - podman run --name certbot-generate - --rm - --volume /home/{{ podman_nginx_podman_rootless_user }}/certbot/www:/var/www/certbot:rw - --volume /home/{{ podman_nginx_podman_rootless_user }}/certbot/conf:/etc/letsencrypt:rw - docker.io/certbot/certbot:latest - certonly - --register-unsafely-without-email - --agree-tos - --webroot - --webroot-path /var/www/certbot/ - -d "{{ podman_nginx_primary_hostname }}" - {% for hostname in podman_nginx_additional_hostnames %} -d "{{ hostname }}"{% endfor %} - {% if podman_nginx_certbot_testing %} --test-cert{% endif %} - when: podman_nginx_cert_stat.stat.exists == false - become: yes - become_user: "{{ podman_nginx_podman_rootless_user }}" - -- name: check if certificate exists - stat: - path: "/home/{{ podman_nginx_podman_rootless_user }}/certbot/conf/live/{{ podman_nginx_primary_hostname }}/fullchain.pem" - register: podman_nginx_cert_stat - become: yes - become_user: "{{ podman_nginx_podman_rootless_user }}" - -- name: ensure certificate exists now - ansible.builtin.assert: - that: - - podman_nginx_cert_stat.stat.exists - fail_msg: "Failed to get a Lets Encrypt certificate." - -- name: start certbot renewal timer - ansible.builtin.systemd_service: - name: "certbot-renew.timer" - state: started - scope: user - become: true - become_user: "{{ podman_nginx_podman_rootless_user }}" diff --git a/roles/podman_nginx/templates/certbot-renew.container b/roles/podman_nginx/templates/certbot-renew.container deleted file mode 100644 index 7d28fd4..0000000 --- a/roles/podman_nginx/templates/certbot-renew.container +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=Run certbot renew - -[Container] -AutoUpdate=registry -ContainerName=certbot-renew -Exec=renew -Image=docker.io/certbot/certbot:latest -Volume=/home/{{ podman_nginx_podman_rootless_user }}/certbot/www:/var/www/certbot -Volume=/home/{{ podman_nginx_podman_rootless_user }}/certbot/conf:/etc/letsencrypt - -[Service] -Restart=no diff --git a/roles/podman_nginx/templates/certbot-renew.timer b/roles/podman_nginx/templates/certbot-renew.timer deleted file mode 100644 index 65f32c1..0000000 --- a/roles/podman_nginx/templates/certbot-renew.timer +++ /dev/null @@ -1,9 +0,0 @@ -[Unit] -Description=Timer for certbot renewals - -[Timer] -OnCalendar=daily -Persistent=true - -[Install] -WantedBy=timers.target diff --git a/roles/podman_nginx/templates/nginx.conf b/roles/podman_nginx/templates/nginx.conf deleted file mode 100644 index aea3186..0000000 --- a/roles/podman_nginx/templates/nginx.conf +++ /dev/null @@ -1,17 +0,0 @@ -# {{ ansible_managed }} - -server { - listen 80; - listen [::]:80; - - server_name {{ podman_nginx_primary_hostname }}; - server_tokens off; - - location /.well-known/acme-challenge/ { - root /var/www/certbot; - } - - location / { - return 301 https://{{ podman_nginx_primary_hostname }}$request_uri; - } -} diff --git a/roles/podman_nginx/templates/nginx.container b/roles/podman_nginx/templates/nginx.container deleted file mode 100644 index fb8cafe..0000000 --- a/roles/podman_nginx/templates/nginx.container +++ /dev/null @@ -1,16 +0,0 @@ -[Container] -ContainerName=nginx -Image=docker.io/nginx:latest -{% if podman_nginx_frontend_network is defined %}Network={{ podman_nginx_frontend_network }}.network{% endif +%} -PublishPort=80:80 -PublishPort=443:443 -Volume=/home/{{ podman_nginx_podman_rootless_user }}/certbot/www:/var/www/certbot/:ro -Volume=/home/{{ podman_nginx_podman_rootless_user }}/certbot/conf/:/etc/letsencrypt/:ro -Volume=/home/{{ podman_nginx_podman_rootless_user }}/nginx:/etc/nginx/conf.d/:ro - -[Service] -RuntimeMaxSec=604800 -Restart=always - -[Install] -WantedBy=default.target diff --git a/roles/vps/tasks/main.yml b/roles/vps/tasks/main.yml deleted file mode 100644 index 759a183..0000000 --- a/roles/vps/tasks/main.yml +++ /dev/null @@ -1,35 +0,0 @@ ---- -# https://support.solusvm.com/hc/en-us/articles/21334950006807-How-to-install-Guest-Tools-manually-inside-a-VM-in-SolusVM-2 - -- name: install required packages - apt: - pkg: - - qemu-guest-agent - - cloud-init - - tuned - state: latest - cache_valid_time: 3600 - become: true - when: ansible_distribution == 'Debian' - -- name: install required packages - dnf: - name: - - qemu-guest-agent - - cloud-init - - tuned - state: latest - update_cache: true - become: true - when: ansible_distribution == 'AlmaLinux' - -- name: check tuned profile - command: tuned-adm active - register: vps_tuned_profile - become: true - changed_when: false - -- name: start tuned profile - shell: tuned-adm profile virtual-guest - become: true - when: "'virtual-guest' not in vps_tuned_profile.stdout"