From 068a8e2cd7bc93cc9a26f0395e0ba5bc047ab60f Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Wed, 20 Dec 2023 16:00:03 +0100
Subject: [PATCH 1/3] Add lego certificate service to ansible

---
 filter_plugins/acmedns_to_lego.py             | 18 ++++++++++
 node002.yml                                   |  2 ++
 roles/lego/files/hook.sh                      |  6 ++++
 roles/lego/files/lego.sh                      | 15 ++++++++
 roles/lego/files/lego@.timer                  | 10 ++++++
 roles/lego/files/node002/db.serguzim.me       | 16 +++++++++
 roles/lego/files/node002/registry.serguzim.me | 17 +++++++++
 roles/lego/tasks/config.yml                   | 19 ++++++++++
 roles/lego/tasks/lego.d.yml                   | 16 +++++++++
 roles/lego/tasks/main.yml                     | 35 +++++++++++++++++++
 roles/lego/tasks/systemd.yml                  | 23 ++++++++++++
 roles/lego/templates/lego@.service.j2         |  4 +++
 roles/lego/vars/main.yml                      | 31 ++++++++++++++++
 13 files changed, 212 insertions(+)
 create mode 100644 filter_plugins/acmedns_to_lego.py
 create mode 100644 roles/lego/files/hook.sh
 create mode 100755 roles/lego/files/lego.sh
 create mode 100644 roles/lego/files/lego@.timer
 create mode 100755 roles/lego/files/node002/db.serguzim.me
 create mode 100755 roles/lego/files/node002/registry.serguzim.me
 create mode 100644 roles/lego/tasks/config.yml
 create mode 100644 roles/lego/tasks/lego.d.yml
 create mode 100644 roles/lego/tasks/main.yml
 create mode 100644 roles/lego/tasks/systemd.yml
 create mode 100644 roles/lego/templates/lego@.service.j2
 create mode 100644 roles/lego/vars/main.yml

diff --git a/filter_plugins/acmedns_to_lego.py b/filter_plugins/acmedns_to_lego.py
new file mode 100644
index 0000000..76a24cd
--- /dev/null
+++ b/filter_plugins/acmedns_to_lego.py
@@ -0,0 +1,18 @@
+class FilterModule(object):
+    def filters(self):
+        return {
+            'acmedns_to_lego': self.acmedns_to_lego,
+        }
+
+    def acmedns_to_lego(self, acmedns_registered):
+        result = {}
+        for (key, value) in acmedns_registered.items():
+            result[key] = {
+                "fulldomain": value["subd"] + "." + value["host"],
+                "subdomain": value["subd"],
+                "username": value["user"],
+                "password": value["pass"],
+                "server_url": "https://" + value["host"]
+            }
+
+        return result
diff --git a/node002.yml b/node002.yml
index 6d4ddc3..476c5c3 100644
--- a/node002.yml
+++ b/node002.yml
@@ -6,6 +6,8 @@
       tags: [always]
     - role: backup
       tags: [backup]
+    - role: lego
+      tags: [lego, certificates]
     - role: caddy
       tags: [caddy, reverse-proxy, webserver]
       vars:
diff --git a/roles/lego/files/hook.sh b/roles/lego/files/hook.sh
new file mode 100644
index 0000000..b060634
--- /dev/null
+++ b/roles/lego/files/hook.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env sh
+
+cp -f "$LEGO_CERT_PATH" /certificates
+cp -f "$LEGO_CERT_KEY_PATH" /certificates
+
+exit 33 # special exit code to signal that the certificate has been updated
diff --git a/roles/lego/files/lego.sh b/roles/lego/files/lego.sh
new file mode 100755
index 0000000..f6a4a04
--- /dev/null
+++ b/roles/lego/files/lego.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env sh
+
+domain="$1"
+action="${2:-renew}"
+
+docker compose run --rm app \
+	--domains "$domain" \
+	"$action" \
+	"--$action-hook" "/config/hook.sh"
+
+if [ "$?" = "33" ] && [ -x "./lego.d/$domain" ];
+then
+	echo "Running hook for $domain"
+	"./lego.d/$domain"
+fi
diff --git a/roles/lego/files/lego@.timer b/roles/lego/files/lego@.timer
new file mode 100644
index 0000000..284347f
--- /dev/null
+++ b/roles/lego/files/lego@.timer
@@ -0,0 +1,10 @@
+[Unit]
+Description=Renew certificates
+
+[Timer]
+Persistent=true
+OnCalendar=*-*-* 01:15:00
+RandomizedDelaySec=2h
+
+[Install]
+WantedBy=timers.target
diff --git a/roles/lego/files/node002/db.serguzim.me b/roles/lego/files/node002/db.serguzim.me
new file mode 100755
index 0000000..09602b9
--- /dev/null
+++ b/roles/lego/files/node002/db.serguzim.me
@@ -0,0 +1,16 @@
+#!/usr/bin/env sh
+
+domain="db.serguzim.me"
+
+docker compose run --rm app "$1" "$domain"
+
+_install() {
+  install --owner=postgres --group=postgres --mode=600 \
+	  "/opt/services/_certificates/$domain.$1" \
+	  "/var/lib/postgresql/server.$1"
+}
+
+_install crt
+_install key
+
+sudo -u postgres pg_ctl -D /var/lib/postgres/data/ reload
diff --git a/roles/lego/files/node002/registry.serguzim.me b/roles/lego/files/node002/registry.serguzim.me
new file mode 100755
index 0000000..09e444c
--- /dev/null
+++ b/roles/lego/files/node002/registry.serguzim.me
@@ -0,0 +1,17 @@
+#!/usr/bin/env sh
+
+domain="registry.serguzim.me"
+
+docker compose run --rm app "$1" "$domain"
+
+_install() {
+  install --owner=root --group=root --mode=600 \
+	  "/opt/services/_certificates/$domain.$1" \
+	  "/opt/services/harbor/server.$1"
+}
+
+_install crt
+_install key
+
+export HARBOR_BUNDLE_DIR=/opt/services/harbor
+$HARBOR_BUNDLE_DIR/data/install.sh
diff --git a/roles/lego/tasks/config.yml b/roles/lego/tasks/config.yml
new file mode 100644
index 0000000..266efcb
--- /dev/null
+++ b/roles/lego/tasks/config.yml
@@ -0,0 +1,19 @@
+---
+- name: Set config path
+  ansible.builtin.set_fact:
+    config_path: "{{ (service_path, 'config') | path_join }}"
+- name: Create config directory
+  ansible.builtin.file:
+    path: "{{ config_path }}"
+    state: directory
+    mode: "0755"
+- name: Copy the acme-dns-accounts
+  ansible.builtin.template:
+    src: "json.j2"
+    dest: "{{ (config_path, 'acme-dns-accounts.json') | path_join }}"
+    mode: "0644"
+- name: Copy the hook script
+  ansible.builtin.copy:
+    src: "hook.sh"
+    dest: "{{ (config_path, 'hook.sh') | path_join }}"
+    mode: "0755"
diff --git a/roles/lego/tasks/lego.d.yml b/roles/lego/tasks/lego.d.yml
new file mode 100644
index 0000000..04acb4b
--- /dev/null
+++ b/roles/lego/tasks/lego.d.yml
@@ -0,0 +1,16 @@
+---
+- name: Set lego.d path
+  ansible.builtin.set_fact:
+    lego_d_path: "{{ (service_path, 'lego.d') | path_join }}"
+- name: Create lego.d directory
+  ansible.builtin.file:
+    path: "{{ lego_d_path }}"
+    state: directory
+    mode: "0755"
+- name: Copy the additional lego scripts
+  ansible.builtin.copy:
+    src: "{{ item }}"
+    dest: "{{ lego_d_path }}"
+    mode: "0755"
+  with_fileglob:
+    - "{{ ansible_facts.hostname }}/*"
diff --git a/roles/lego/tasks/main.yml b/roles/lego/tasks/main.yml
new file mode 100644
index 0000000..3dc6de1
--- /dev/null
+++ b/roles/lego/tasks/main.yml
@@ -0,0 +1,35 @@
+---
+- name: Set common facts
+  ansible.builtin.import_tasks: tasks/set-default-facts.yml
+
+- name: Deploy {{ svc.name }}
+  vars:
+    svc: "{{ lego_svc }}"
+    env: "{{ lego_env }}"
+    json: "{{ vault_acmedns_registered | acmedns_to_lego }}"
+    compose: "{{ lego_compose }}"
+  block:
+    - name: Import prepare tasks for common service
+      ansible.builtin.import_tasks: tasks/prepare-common-service.yml
+
+    - name: Create _certificates directory
+      ansible.builtin.file:
+        path: "{{ certificates_path }}"
+        state: directory
+        mode: "0755"
+
+    - name: Import tasks specific to the config directory
+      ansible.builtin.import_tasks: config.yml
+    - name: Import tasks specific to lego.d
+      ansible.builtin.import_tasks: lego.d.yml
+    - name: Import tasks specific to systemd
+      ansible.builtin.import_tasks: systemd.yml
+
+    - name: Copy the run script
+      ansible.builtin.copy:
+        src: "lego.sh"
+        dest: "{{ (service_path, 'lego.sh') | path_join }}"
+        mode: "0755"
+
+    - name: Import tasks create a service.env file
+      ansible.builtin.import_tasks: tasks/steps/template-service-env.yml
diff --git a/roles/lego/tasks/systemd.yml b/roles/lego/tasks/systemd.yml
new file mode 100644
index 0000000..21e99bf
--- /dev/null
+++ b/roles/lego/tasks/systemd.yml
@@ -0,0 +1,23 @@
+---
+- name: Copy the system service
+  ansible.builtin.template:
+    src: lego@.service.j2
+    dest: /etc/systemd/system/lego@.service
+    mode: "0644"
+  become: true
+- name: Copy the system timer
+  ansible.builtin.copy:
+    src: lego@.timer
+    dest: /etc/systemd/system/lego@.timer
+    mode: "0644"
+  become: true
+- name: Enable the system timer for {{ item }}
+  ansible.builtin.systemd_service:
+    name: lego@{{ item }}.timer
+    state: started
+    enabled: true
+    daemon_reload: true
+  loop:
+    - db.serguzim.me
+    - registry.serguzim.me
+  become: true
diff --git a/roles/lego/templates/lego@.service.j2 b/roles/lego/templates/lego@.service.j2
new file mode 100644
index 0000000..4b310f2
--- /dev/null
+++ b/roles/lego/templates/lego@.service.j2
@@ -0,0 +1,4 @@
+[Service]
+Type=oneshot
+ExecStart={{ service_path }}/lego.sh %i
+WorkingDirectory={{ service_path }}
diff --git a/roles/lego/vars/main.yml b/roles/lego/vars/main.yml
new file mode 100644
index 0000000..460fb79
--- /dev/null
+++ b/roles/lego/vars/main.yml
@@ -0,0 +1,31 @@
+---
+lego_svc:
+  name: lego
+
+lego_env:
+  ACME_DNS_API_BASE: https://{{ acme_dns.host }}
+  ACME_DNS_STORAGE_PATH: /config/acme-dns-accounts.json
+
+  LEGO_EMAIL: "{{ admin_email }}"
+  LEGO_PATH: /data
+
+lego_compose:
+  watchtower: false
+  network: false
+  image: goacme/lego
+  volumes:
+    - ./config:/config:ro
+    - "{{ certificates_path }}:/certificates"
+    - data:/data
+  file:
+    services:
+      app:
+        restart: never
+        network_mode: "host"
+        entrypoint:
+          - /lego
+          - --accept-tos
+          - --email={{ admin_email }}
+          - --dns=acme-dns
+    volumes:
+      data:

From 05014af7442d3dece27b6fde98124240fbdfdecc Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Sat, 23 Dec 2023 19:25:42 +0100
Subject: [PATCH 2/3] Add ntfy service

---
 node002.yml               |  2 ++
 roles/ntfy/tasks/main.yml | 12 +++++++++
 roles/ntfy/vars/main.yml  | 55 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 69 insertions(+)
 create mode 100644 roles/ntfy/tasks/main.yml
 create mode 100644 roles/ntfy/vars/main.yml

diff --git a/node002.yml b/node002.yml
index 476c5c3..0a9cb22 100644
--- a/node002.yml
+++ b/node002.yml
@@ -40,6 +40,8 @@
       tags: [influxdb, sensors, monitoring]
     - role: jellyfin
       tags: [jellyfin, media]
+    - role: ntfy
+      tags: [ntfy, notifications, push]
     - role: reitanlage_oranienburg
       tags: [reitanlage-oranienburg, website]
     - role: synapse
diff --git a/roles/ntfy/tasks/main.yml b/roles/ntfy/tasks/main.yml
new file mode 100644
index 0000000..4026612
--- /dev/null
+++ b/roles/ntfy/tasks/main.yml
@@ -0,0 +1,12 @@
+---
+- name: Set common facts
+  ansible.builtin.import_tasks: tasks/set-default-facts.yml
+
+- name: Deploy {{ svc.name }}
+  vars:
+    svc: "{{ ntfy_svc }}"
+    compose: "{{ ntfy_compose }}"
+    env: "{{ ntfy_env }}"
+  block:
+    - name: Import tasks to deploy common service
+      ansible.builtin.import_tasks: tasks/deploy-common-service.yml
diff --git a/roles/ntfy/vars/main.yml b/roles/ntfy/vars/main.yml
new file mode 100644
index 0000000..b881fe4
--- /dev/null
+++ b/roles/ntfy/vars/main.yml
@@ -0,0 +1,55 @@
+---
+ntfy_svc:
+  name: ntfy
+  domain: push.serguzim.me
+  port: 80
+
+ntfy_env:
+  TZ: "{{ timezone }}"
+
+  NTFY_BASE_URL: "https://{{ ntfy_svc.domain }}"
+
+  NTFY_CACHE_FILE: /var/cache/ntfy/cache.db
+  NTFY_CACHE_DURATION: "12h"
+
+  NTFY_BEHIND_PROXY: true
+
+  NTFY_AUTH_FILE: /var/lib/ntfy/user.db
+  NTFY_AUTH_DEFAULT_ACCESS: "deny-all"
+
+  NTFY_ATTACHMENT_CACHE_DIR: "/var/cache/ntfy/attachments"
+  NTFY_ATTACHMENT_TOTAL_SIZE_LIMIT: "5G"
+  NTFY_ATTACHMENT_FILE_SIZE_LIMIT: "15M"
+  NTFY_ATTACHMENT_EXPIRY_DURATION: "3h"
+
+  NTFY_KEEPALIVE_INTERVAL: "45s"
+  NTFY_MANAGER_INTERVAL: "60m"
+
+  NTFY_ENABLE_SIGNUP: false
+  NTFY_ENABLE_LOGIN: true
+  NTFY_ENABLE_RESERVATIONS: true
+
+  NTFY_GLOBAL_TOPIC_LIMIT: 15000
+
+  NTFY_VISITOR_SUBSCRIPTION_LIMIT: 30
+  NTFY_VISITOR_REQUEST_LIMIT_BURST: 60
+  NTFY_VISITOR_REQUEST_LIMIT_REPLENISH: "5s"
+  NTFY_VISITOR_ATTACHMENT_TOTAL_SIZE_LIMIT: "100M"
+  NTFY_VISITOR_ATTACHMENT_DAILY_BANDWIDTH_LIMIT: "500M"
+
+  NTFY_ENABLE_METRICS: true
+
+ntfy_compose:
+  watchtower: true
+  image: binwiederhier/ntfy
+  volumes:
+    - cache:/var/cache/ntfy
+    - data:/var/lib/ntfy
+  file:
+    services:
+      app:
+        command:
+          - serve
+    volumes:
+      cache:
+      data:

From 8a861e080b587eb1be40c67b1643ebc72c1a43b8 Mon Sep 17 00:00:00 2001
From: Tobias Reisinger <tobias@msrg.cc>
Date: Sun, 24 Dec 2023 01:26:18 +0100
Subject: [PATCH 3/3] Refactor telegraf prometheus input

---
 roles/telegraf/templates/telegraf.conf.j2 | 11 +++++++++--
 roles/telegraf/vars/main.yml              |  9 ++++++---
 2 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/roles/telegraf/templates/telegraf.conf.j2 b/roles/telegraf/templates/telegraf.conf.j2
index 9b4c404..654b933 100644
--- a/roles/telegraf/templates/telegraf.conf.j2
+++ b/roles/telegraf/templates/telegraf.conf.j2
@@ -18,12 +18,19 @@
 
 [[inputs.prometheus]]
   urls = [
-  {%- for url in svc.prometheus.urls -%}
+  {%- for url in svc.prometheus_unprotected.urls -%}
     "{{ url }}",
   {%- endfor -%}
   ]
 
-  bearer_token_string = "{{ svc.prometheus.bearer_token }}"
+[[inputs.prometheus]]
+  urls = [
+  {%- for url in svc.prometheus_protected.urls -%}
+    "{{ url }}",
+  {%- endfor -%}
+  ]
+
+  bearer_token_string = "{{ svc.prometheus_protected.bearer_token }}"
 
 [[inputs.postgresql]]
   address = "postgres://{{ svc.postgresql.user }}:{{ svc.postgresql.pass }}@{{ svc.postgresql.host }}:{{ svc.postgresql.port }}/{{ svc.postgresql.database }}?sslmode=verify-full"
diff --git a/roles/telegraf/vars/main.yml b/roles/telegraf/vars/main.yml
index 887c8b7..a110aa0 100644
--- a/roles/telegraf/vars/main.yml
+++ b/roles/telegraf/vars/main.yml
@@ -6,12 +6,15 @@ telegraf_svc:
     token: "{{ vault_telegraf.influxdb_token }}"
     organization: serguzim.net
     bucket: metrics
-  prometheus:
+  prometheus_unprotected:
+    urls:
+      - https://matrix.msrg.cc/_synapse/metrics
+      - https://push.serguzim.me/metrics
+      - https://tick.serguzim.me/metrics
+  prometheus_protected:
     urls:
       - https://ci.serguzim.me/metrics
       - https://git.serguzim.me/metrics
-      - https://matrix.msrg.cc/_synapse/metrics
-      - https://tick.serguzim.me/metrics
     bearer_token: "{{ vault_metrics_token }}"
   postgresql:
     user: "{{ vault_telegraf.db.user }}"