diff --git a/roles/podman_cleaninsights/defaults/main.yml b/roles/podman_cleaninsights/defaults/main.yml deleted file mode 100644 index eeff222..0000000 --- a/roles/podman_cleaninsights/defaults/main.yml +++ /dev/null @@ -1,8 +0,0 @@ -podman_cleaninsights_podman_rootless_user: cleaninsights -podman_cleaninsights_web_hostname: "{{ inventory_hostname }}" -podman_cleaninsights_mysql_database: matomo -# podman_cleaninsights_mysql_password: -# podman_cleaninsights_mysql_root_password: -podman_cleaninsights_php_memory_limit: 2048M -podman_cleaninsights_mysql_user: matomo -podman_cleaninsights_matomo_token: "" \ No newline at end of file diff --git a/roles/podman_cleaninsights/tasks/main.yml b/roles/podman_cleaninsights/tasks/main.yml deleted file mode 100644 index 7550975..0000000 --- a/roles/podman_cleaninsights/tasks/main.yml +++ /dev/null @@ -1,113 +0,0 @@ ---- -- name: create service configuration directories - ansible.builtin.file: - path: "/home/{{ podman_cleaninsights_podman_rootless_user }}/{{ item }}" - state: directory - owner: "{{ podman_cleaninsights_podman_rootless_user }}" - group: "{{ podman_cleaninsights_podman_rootless_user }}" - mode: "0755" - become: true - with_items: - - mysql - - matomo - - -- name: install cleaninsights script - ansible.builtin.copy: - src: templates/cleaninsights.php - dest: "/home/{{ podman_cleaninsights_podman_rootless_user }}/matomo/cleaninsights.php" - owner: "{{ podman_cleaninsights_podman_rootless_user }}" - group: "{{ podman_cleaninsights_podman_rootless_user }}" - mode: "0755" - become: true - -- name: install cleaninsights configuration file - ansible.builtin.template: - src: "cleaninsights.ini.j2" - dest: "/home/{{ podman_cleaninsights_podman_rootless_user }}/matomo/cleaninsights.ini" - owner: "{{ podman_cleaninsights_podman_rootless_user }}" - group: "{{ podman_cleaninsights_podman_rootless_user }}" - mode: "0755" - become: true - -- name: install podman quadlet for rootless podman user - ansible.builtin.template: - src: "{{ item }}" - dest: "/home/{{ podman_cleaninsights_podman_rootless_user }}/.config/containers/systemd/{{ item }}" - owner: "{{ podman_cleaninsights_podman_rootless_user }}" - mode: "0400" - with_items: - - matomo.container - - mysql.container - - redis.container - notify: - - "restart {{ item | split('.') | first }}" - become: true - - -- name: install network quadlets for rootless podman user - ansible.builtin.template: - src: "{{ item }}" - dest: "/home/{{ podman_cleaninsights_podman_rootless_user }}/.config/containers/systemd/{{ item }}" - owner: "{{ podman_cleaninsights_podman_rootless_user }}" - mode: "0400" - with_items: - - frontend.network - - cleaninsights.network - become: true - -- name: verify quadlets are correctly defined - ansible.builtin.command: /usr/libexec/podman/quadlet -dryrun -user - register: podman_cleaninsights_quadlet_result - ignore_errors: true - changed_when: false - become: true - become_user: "{{ podman_cleaninsights_podman_rootless_user }}" - -- name: assert that the quadlet verification succeeded - ansible.builtin.assert: - that: - - podman_cleaninsights_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_cleaninsights_podman_rootless_user }}" - podman_nginx_primary_hostname: "{{ podman_cleaninsights_web_hostname }}" - podman_nginx_systemd_service_slice: cleaninsights.slice - podman_nginx_systemd_service_requires: ["matomo"] - 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_cleaninsights_podman_rootless_user }}/nginx/nginx.conf" - owner: "{{ podman_cleaninsights_podman_rootless_user }}" - group: "{{ podman_cleaninsights_podman_rootless_user }}" - mode: "0644" - become: true - -- name: install services slice for rootless podman user - ansible.builtin.template: - src: "cleaninsights.slice" - dest: "/home/{{ podman_cleaninsights_podman_rootless_user }}/.config/systemd/user/cleaninsights.slice" - owner: "{{ podman_cleaninsights_podman_rootless_user }}" - group: "{{ podman_cleaninsights_podman_rootless_user }}" - mode: "0655" - become: true - -- name: make sure services are started on boot - ansible.builtin.systemd_service: - name: "cleaninsights.slice" - enabled: true - state: started - daemon_reload: true - scope: user - become: true - become_user: "{{ podman_cleaninsights_podman_rootless_user }}" diff --git a/roles/podman_cleaninsights/templates/cleaninsights.ini.j2 b/roles/podman_cleaninsights/templates/cleaninsights.ini.j2 deleted file mode 100644 index 01ea4b4..0000000 --- a/roles/podman_cleaninsights/templates/cleaninsights.ini.j2 +++ /dev/null @@ -1,22 +0,0 @@ -; A Matomo token for authentication to enable retrograde setting of timestamps. -; Needs to be set for CIMP to work. See https://matomo.org/faq/general/faq_114/ -token_auth = '{{ podman_cleaninsights_matomo_token }}' - -; Allowed CORS scheme/domain/port tuples. Defaults to none allowed. -; cors[] = 'https://example.com:3000' - -; The base URL of your Matomo instance. Only needs to be set if CIMP is -; not installed inside the Matomo directory.ca -matomo_base_url = 'http://nginx' - -; Number of created tracking requests to send to Matomo at once. - chunk_size = 1000 - -; Number of seconds to wait before retry, if Matomo responded with an error. -delay_after_failure = 2 - -; Number of maximum retries on error. -max_attempts = 3 - -; Set to true to have a debug output in 'ciapi.log' in the same directory. -debug = true \ No newline at end of file diff --git a/roles/podman_cleaninsights/templates/cleaninsights.network b/roles/podman_cleaninsights/templates/cleaninsights.network deleted file mode 100644 index f95a29b..0000000 --- a/roles/podman_cleaninsights/templates/cleaninsights.network +++ /dev/null @@ -1,2 +0,0 @@ -[Network] -NetworkName=cleaninsights diff --git a/roles/podman_cleaninsights/templates/cleaninsights.php b/roles/podman_cleaninsights/templates/cleaninsights.php deleted file mode 100644 index d72a67a..0000000 --- a/roles/podman_cleaninsights/templates/cleaninsights.php +++ /dev/null @@ -1,341 +0,0 @@ -idsite < 1) { - debug('Malformed body. Last JSON error:' . json_last_error() . ' ' . json_last_error_msg()); - http_response_code(400); - exit(1); -} - - -// Build output for Matomo Tracking API and send in chunks. - -$output = []; - -$reporting_start = time(); -$reporting_end = 0; -$count = 0; -$result = false; - -if (property_exists($data, 'visits') && gettype($data->visits) === 'array') { - foreach ($data->visits as $visit) { - $count += renderVisit($visit); - } -} - -if (property_exists($data, 'events') && gettype($data->events) === 'array') { - foreach ($data->events as $event) { - $count += renderEvent($event); - } -} - -// Send the rest. -send(false); - -debug('result = ' . ($result ? 'success': 'failure') . ", count = $count"); - -if ($result === false) { - debug('There were no events and visits or the Matomo Tracking API refused all requests.'); - http_response_code(400); - exit(1); -} - - -// Instruct Matomo to re-analyse the complete period of this request -// on the next auto-archiving run. - -$start = new DateTime(); -$start->setTimestamp($reporting_start); - -$end = new DateTime(); -$end->setTimestamp($reporting_end); - -$query = http_build_query([ - 'module' => 'API', - 'method' => 'CoreAdminHome.invalidateArchivedReports', - 'idSites' => $data->idsite, - 'period' => 'range', - // Need to use `dates[]` instead of `dates`. Otherwise, some automagic happens in Matomo and breaks the call. - 'dates[]' => $start->format('Y-m-d') . ',' . $end->format('Y-m-d'), - 'token_auth' => $TOKEN_AUTH, -]); - -$result = callMatomo(null, $query); - -if ($result === false) { - debug('Matomo could not be triggered to reprocess data!'); -} -else { - debug('Matomo was triggered to reprocess data!'); -} - -http_response_code(204); - -exit(); - - -// Helper functions. - -function renderVisit(stdClass $visit): int { - $action_name = $visit->action_name; - - return render($visit->period_start, $visit->period_end, $visit->times, - function() use ($action_name) { - return ['action_name' => $action_name]; - }); -} - -function renderEvent(stdClass $event): int { - return render($event->period_start, $event->period_end, $event->times, - function() use ($event) { - $e = ['e_c' => $event->category, 'e_a' => $event->action]; - - if (property_exists($event, 'name') && $event->name) { - $e['e_n'] = $event->name; - } - - if (property_exists($event, 'value') && $event->value) { - $e['e_v'] = $event->value; - } - - return $e; - }); -} - -function render(int $period_start, int $period_end, int $times, callable $callback): int { - global $reporting_start, $reporting_end, $output; - - $reporting_start = min($reporting_start, $period_start); - $reporting_end = max($reporting_end, $period_end); - - $interval = ($period_end - $period_start) / $times; - - // Offset the first record by half an interval, so records are neatly "centered" in the period. - $period_start += $interval / 2; - - for ($i = 0; $i < $times; $i++) { - $request = $callback(); - - $request['cdt'] = $period_start + $interval * $i; - - $output[] = buildRequest($request); - - send(); - } - - return $times; -} - -function buildRequest(array &$request): string { - global $data; - - $request['idsite'] = $data->idsite; - $request['rec'] = 1; - if (property_exists($data, 'lang') && $data->lang) $request['lang'] = $data->lang; - if (property_exists($data, 'ua') && $data->ua) $request['ua'] = $data->ua; - - return '?' . http_build_query($request); -} - -function callMatomo(string $path = null, string $query = null, string $body = null): string { - global $MATOMO_BASE_URL, $MAX_ATTEMPTS, $DELAY_AFTER_FAILURE; - - $options = [ - 'method' => 'POST', - 'header' => 'Content-Type: application/json; charset=UTF-8']; - - if (isset($body)) { - $options['content'] = $body; - } - - $context = stream_context_create(['http' => $options]); - - $url = "$MATOMO_BASE_URL/"; - - if (isset($path)) { - $url .= $path; - } - - if (isset($query)) { - $url .= "?$query"; - } - - $counter = 1; - - debug("Call Matomo at $url"); - - do { - $result = file_get_contents($url, false, $context); - - $counter++; - - if ($result !== false || $counter > $MAX_ATTEMPTS) { - break; - } - - debug("Request error. Sleep $DELAY_AFTER_FAILURE seconds and retry!"); - - sleep($DELAY_AFTER_FAILURE); - - } while (true); - - return $result; -} - -function sendToMatomo(array &$output): bool { - global $TOKEN_AUTH; - - $body = json_encode([ - 'token_auth' => $TOKEN_AUTH, - 'requests' => $output]); - - if ($body === false) { - return false; - } - - $result = callMatomo("matomo.php", null, $body); - - debug("Called tracking API " . (!$result ? 'un' : '') . "successfully with " - . count($output) . " lines of data: " . substr($body, 0, 256)); - - return !!$result; -} - -function send(bool $onlyIfChunkSizeReached = true) { - global $output, $CHUNK_SIZE, $result; - - if (!$onlyIfChunkSizeReached || count($output) >= $CHUNK_SIZE) { - if (sendToMatomo($output)) { - // If at least one request succeeded, we consider this good. - // Otherwise, the client would re-send already processed data. - $result = true; - } - - // Remove already sent requests from memory again. - $output = []; - } -} - -function debug($message) { - global $DEBUG; - if (!$DEBUG) return; - - error_log(print_r($message, true)); -} \ No newline at end of file diff --git a/roles/podman_cleaninsights/templates/cleaninsights.slice b/roles/podman_cleaninsights/templates/cleaninsights.slice deleted file mode 100644 index 71c7a82..0000000 --- a/roles/podman_cleaninsights/templates/cleaninsights.slice +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=Cleaninsights Slice -Before=slices.target -Requires=matomo.service -Requires=nginx.service - -[Install] -WantedBy=default.target diff --git a/roles/podman_cleaninsights/templates/matomo.container b/roles/podman_cleaninsights/templates/matomo.container deleted file mode 100644 index fd9d168..0000000 --- a/roles/podman_cleaninsights/templates/matomo.container +++ /dev/null @@ -1,23 +0,0 @@ -[Unit] -Requires=mysql.service -After=mysql.service -Requires=redis.service -After=redis.service - -[Container] -ContainerName=matomo -Environment=MATOMO_DATABASE_HOST=mysql -Environment=PHP_MEMORY_LIMIT={{ podman_cleaninsights_php_memory_limit }} -Environment=MATOMO_DATABASE_DBNAME={{ podman_cleaninsights_mysql_database }} -Environment=MATOMO_DATABASE_PASSWORD={{ podman_cleaninsights_mysql_password }} -Environment=MATOMO_DATABASE_USERNAME={{ podman_cleaninsights_mysql_user }} -Image=docker.io/matomo:5-fpm -Volume=/home/{{ podman_cleaninsights_podman_rootless_user }}/matomo:/var/www/html -Volume=/home/{{ podman_cleaninsights_podman_rootless_user }}/matomo/cleaninsights.php:/var/www/html/cleaninsights.php:ro -Volume=/home/{{ podman_cleaninsights_podman_rootless_user }}/matomo/cleaninsights.ini:/var/www/html/cleaninsights.ini:ro -Network=cleaninsights.network -Network=frontend.network - -[Service] -Restart=always -Slice=cleaninsights.slice diff --git a/roles/podman_cleaninsights/templates/mysql.container b/roles/podman_cleaninsights/templates/mysql.container deleted file mode 100644 index 71e6dd6..0000000 --- a/roles/podman_cleaninsights/templates/mysql.container +++ /dev/null @@ -1,15 +0,0 @@ -[Container] -ContainerName=mysql -Environment=MYSQL_ROOT_PASSWORD={{ podman_cleaninsights_mysql_root_password }} -Environment=MYSQL_DATABASE={{ podman_cleaninsights_mysql_database }} -Environment=MYSQL_USER={{ podman_cleaninsights_mysql_user }} -Environment=MYSQL_PASSWORD={{ podman_cleaninsights_mysql_password }} -Image=docker.io/mysql:9 -PublishPort=127.0.0.1:3306:3306 -Volume=/home/{{ podman_cleaninsights_podman_rootless_user }}/mysql:/var/lib/mysql -Network=cleaninsights.network - -[Service] -Restart=always -Slice=cleaninsights.slice - diff --git a/roles/podman_cleaninsights/templates/nginx.conf b/roles/podman_cleaninsights/templates/nginx.conf deleted file mode 100644 index 3c20147..0000000 --- a/roles/podman_cleaninsights/templates/nginx.conf +++ /dev/null @@ -1,103 +0,0 @@ -# {{ ansible_managed }} - -upstream php-handler { - server matomo:9000; -} - -server { - listen 80; - listen [::]:80; - - server_name {{ podman_cleaninsights_web_hostname }}; - server_tokens off; - - location /.well-known/acme-challenge/ { - root /var/www/certbot; - } - - location / { - return 301 https://{{ podman_cleaninsights_web_hostname }}$request_uri; - } -} - -server { - listen 443 ssl; - listen [::]:443 ssl; - http2 on; - - server_name matomo {{ podman_cleaninsights_web_hostname }}; - server_tokens off; - - ssl_certificate /etc/letsencrypt/live/{{ podman_cleaninsights_web_hostname }}/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/{{ podman_cleaninsights_web_hostname }}/privkey.pem; - - add_header Strict-Transport-Security max-age=15768000 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; - - root /var/www/html/; # this is the default matomo image path - index index.php; - - location ~ ^/(index|matomo|cleaninsights|piwik|js/index|plugins/HeatmapSessionRecording/configs).php { - # regex to split $uri to $fastcgi_script_name and $fastcgi_path - try_files $fastcgi_script_name =404; - fastcgi_split_path_info ^(.+\.php)(/.+)$; - # Check that the PHP script exists before passing it - #try_files $fastcgi_script_name =404; - - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param PATH_INFO $fastcgi_path_info; - fastcgi_param HTTP_PROXY ""; # prohibit httpoxy: https://httpoxy.org/ - include fastcgi_params; - fastcgi_pass php-handler; - fastcgi_read_timeout 60s; - } - - # deny access to all other .php files - location ~* ^.+\.php$ { - return 403; - } - - location / { - try_files $uri $uri/ =404; - } - - # deny access to all cleaninsights.ini file - location ~ cleaninsights.ini { - return 403; - } - - # disable all access to the following directories - location ~ ^/(config|tmp|core|lang) { - return 403; # replace with 404 to not show these directories exist - } - - location ~ /\.ht { - return 403; - } - - location ~ js/container_.*_preview\.js$ { - expires off; - add_header Cache-Control 'private, no-cache, no-store'; - } - - location ~ \.(gif|ico|jpg|png|svg|js|css|htm|html|mp3|mp4|wav|ogg|avi|ttf|eot|woff|woff2)$ { - allow all; - # Cache images,CSS,JS and webfonts for an hour - # Increasing the duration may improve the load-time, but may cause old files to show after an Matomo upgrade - expires 1h; - add_header Pragma public; - add_header Cache-Control "public"; - } - - location ~ ^/(libs|vendor|plugins|misc|node_modules) { - deny all; - return 403; - } - - # properly display textfiles in root directory - location ~/(.*\.md|LEGALNOTICE|LICENSE) { - default_type text/plain; - } -} \ No newline at end of file diff --git a/roles/podman_cleaninsights/templates/redis.container b/roles/podman_cleaninsights/templates/redis.container deleted file mode 100644 index aa85fb3..0000000 --- a/roles/podman_cleaninsights/templates/redis.container +++ /dev/null @@ -1,9 +0,0 @@ -[Container] -ContainerName=redis -ExposeHostPort=6379 -Image=docker.io/redis:7.2-rc1-alpine -Network=cleaninsights.network - -[Service] -Restart=always -Slice=cleaninsights.slice 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_cleaninsights/handlers/main.yml b/roles/podman_link/handlers/main.yml similarity index 50% rename from roles/podman_cleaninsights/handlers/main.yml rename to roles/podman_link/handlers/main.yml index e0735da..19d7e1d 100644 --- a/roles/podman_cleaninsights/handlers/main.yml +++ b/roles/podman_link/handlers/main.yml @@ -1,26 +1,26 @@ -- name: restart mysql +- name: restart zammad-init ansible.builtin.systemd_service: - name: mysql + name: zammad-init state: restarted scope: user daemon_reload: true become: true - become_user: "{{ podman_cleaninsights_podman_rootless_user }}" + become_user: "{{ podman_link_podman_rootless_user }}" -- name: restart matomo +- name: restart zammad-postgresql ansible.builtin.systemd_service: - name: matomo + name: zammad-postgresql state: restarted scope: user daemon_reload: true become: true - become_user: "{{ podman_cleaninsights_podman_rootless_user }}" + become_user: "{{ podman_link_podman_rootless_user }}" -- name: restart redis +- name: restart link.slice ansible.builtin.systemd_service: - name: redis + name: link.slice state: restarted scope: user daemon_reload: true become: true - become_user: "{{ podman_cleaninsights_podman_rootless_user }}" + 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_cleaninsights/templates/frontend.network b/roles/podman_link/templates/frontend.network similarity index 100% rename from roles/podman_cleaninsights/templates/frontend.network rename to roles/podman_link/templates/frontend.network 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"