diff --git a/inventories/production/group_vars/all.yml b/inventories/production/group_vars/all.yml index 3718d81..9acf3bf 100644 --- a/inventories/production/group_vars/all.yml +++ b/inventories/production/group_vars/all.yml @@ -126,6 +126,10 @@ forgejo_admin_password: "changeme_forgejo_admin" forgejo_admin_email: "admin@{{ base_domain }}" forgejo_ssh_port: 2222 +# Uptime Kuma +uptimekuma_domain: "status.{{ base_domain }}" +uptimekuma_version: "1" + # Automatisch automatisch_domain: "automate.{{ base_domain }}" automatisch_version: "latest" diff --git a/molecule/shared/vars.yml b/molecule/shared/vars.yml index 3f6b1a7..7f0134c 100644 --- a/molecule/shared/vars.yml +++ b/molecule/shared/vars.yml @@ -136,6 +136,11 @@ forgejo_admin_email: "admin@test.example.com" forgejo_ssh_port: 2222 forgejo_data_dir: /tmp/sovereign_test/forgejo +# Uptime Kuma +uptimekuma_domain: "status.test.example.com" +uptimekuma_version: "1" +uptimekuma_data_dir: /tmp/sovereign_test/uptimekuma + # Automatisch automatisch_domain: "automate.test.example.com" automatisch_version: "latest" diff --git a/playbooks/site.yml b/playbooks/site.yml index 5c6c078..38b8047 100644 --- a/playbooks/site.yml +++ b/playbooks/site.yml @@ -31,6 +31,8 @@ tags: [vaultwarden, passwords, vault] - role: forgejo tags: [forgejo, git, vcs] + - role: uptimekuma + tags: [uptimekuma, monitoring, uptime] - role: automatisch tags: [automatisch, automation, workflows] - role: twenty diff --git a/roles/uptimekuma/defaults/main.yml b/roles/uptimekuma/defaults/main.yml new file mode 100644 index 0000000..7eb5951 --- /dev/null +++ b/roles/uptimekuma/defaults/main.yml @@ -0,0 +1,3 @@ +--- +uptimekuma_data_dir: "{{ sovereign_base_dir }}/uptimekuma" +uptimekuma_domain: "status.{{ base_domain }}" diff --git a/roles/uptimekuma/handlers/main.yml b/roles/uptimekuma/handlers/main.yml new file mode 100644 index 0000000..8e0e64b --- /dev/null +++ b/roles/uptimekuma/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: restart uptimekuma + community.docker.docker_compose_v2: + project_src: "{{ uptimekuma_data_dir }}" + state: present + recreate: always + when: not (molecule_test_mode | default(false)) diff --git a/roles/uptimekuma/molecule/default/converge.yml b/roles/uptimekuma/molecule/default/converge.yml new file mode 100644 index 0000000..ce50b09 --- /dev/null +++ b/roles/uptimekuma/molecule/default/converge.yml @@ -0,0 +1,8 @@ +--- +- name: Converge + hosts: localhost + gather_facts: false + vars_files: + - ../../../molecule/shared/vars.yml + roles: + - role: uptimekuma diff --git a/roles/uptimekuma/molecule/default/molecule.yml b/roles/uptimekuma/molecule/default/molecule.yml new file mode 100644 index 0000000..5e0827d --- /dev/null +++ b/roles/uptimekuma/molecule/default/molecule.yml @@ -0,0 +1,21 @@ +--- +dependency: + name: galaxy + options: + requirements-file: requirements.yml +driver: + name: default +platforms: + - name: localhost + groups: + - sovereign +provisioner: + name: ansible + env: + ANSIBLE_ROLES_PATH: "${MOLECULE_PROJECT_DIRECTORY}/.." + inventory: + host_vars: + localhost: + ansible_connection: local +verifier: + name: ansible diff --git a/roles/uptimekuma/molecule/default/verify.yml b/roles/uptimekuma/molecule/default/verify.yml new file mode 100644 index 0000000..9b95dbb --- /dev/null +++ b/roles/uptimekuma/molecule/default/verify.yml @@ -0,0 +1,63 @@ +--- +- name: Verify uptimekuma role + hosts: localhost + gather_facts: false + vars: + uptimekuma_data_dir: /tmp/sovereign_test/uptimekuma + uptimekuma_domain: status.test.example.com + uptimekuma_version: "1" + + tasks: + - name: Check Uptime Kuma data directory exists + ansible.builtin.stat: + path: "/tmp/sovereign_test/uptimekuma" + register: data_dir_stat + + - name: Assert Uptime Kuma data directory is present + ansible.builtin.assert: + that: data_dir_stat.stat.isdir + fail_msg: "Data directory /tmp/sovereign_test/uptimekuma was not created" + + - name: Check docker-compose.yml exists + ansible.builtin.stat: + path: "/tmp/sovereign_test/uptimekuma/docker-compose.yml" + register: compose_stat + + - name: Assert docker-compose.yml was rendered + ansible.builtin.assert: + that: compose_stat.stat.exists + fail_msg: "docker-compose.yml was not rendered for uptimekuma" + + - name: Read docker-compose.yml + ansible.builtin.slurp: + src: "/tmp/sovereign_test/uptimekuma/docker-compose.yml" + register: compose_raw + + - name: Set compose content fact + ansible.builtin.set_fact: + compose: "{{ compose_raw.content | b64decode }}" + + - name: Assert Uptime Kuma image is present + ansible.builtin.assert: + that: "'louislam/uptime-kuma' in compose" + fail_msg: "louislam/uptime-kuma image not found in docker-compose.yml" + + - name: Assert Uptime Kuma domain traefik rule is present + ansible.builtin.assert: + that: "'Host(`status.test.example.com`)' in compose" + fail_msg: "Traefik rule for status.test.example.com not found in docker-compose.yml" + + - name: Assert Authentik forward auth middleware is present + ansible.builtin.assert: + that: "'outpost.goauthentik.io/auth/traefik' in compose" + fail_msg: "Authentik forward auth address not found in docker-compose.yml" + + - name: Assert GELF logging address is present + ansible.builtin.assert: + that: "'udp://127.0.0.1:12201' in compose" + fail_msg: "GELF logging address udp://127.0.0.1:12201 not found in docker-compose.yml" + + - name: Assert sovereign network is external + ansible.builtin.assert: + that: "'external: true' in compose" + fail_msg: "external: true not found in docker-compose.yml networks section" diff --git a/roles/uptimekuma/tasks/main.yml b/roles/uptimekuma/tasks/main.yml new file mode 100644 index 0000000..9bd0192 --- /dev/null +++ b/roles/uptimekuma/tasks/main.yml @@ -0,0 +1,22 @@ +--- +- name: Create Uptime Kuma directories + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: '0755' + loop: + - "{{ uptimekuma_data_dir }}" + - "{{ uptimekuma_data_dir }}/data" + +- name: Deploy Uptime Kuma docker-compose + ansible.builtin.template: + src: docker-compose.yml.j2 + dest: "{{ uptimekuma_data_dir }}/docker-compose.yml" + mode: '0644' + notify: restart uptimekuma + +- name: Start Uptime Kuma + community.docker.docker_compose_v2: + project_src: "{{ uptimekuma_data_dir }}" + state: present + when: not (molecule_test_mode | default(false)) diff --git a/roles/uptimekuma/templates/docker-compose.yml.j2 b/roles/uptimekuma/templates/docker-compose.yml.j2 new file mode 100644 index 0000000..442c5a3 --- /dev/null +++ b/roles/uptimekuma/templates/docker-compose.yml.j2 @@ -0,0 +1,32 @@ +services: + uptimekuma: + image: louislam/uptime-kuma:{{ uptimekuma_version }} + container_name: uptimekuma + restart: unless-stopped + volumes: + - {{ uptimekuma_data_dir }}/data:/app/data + labels: + - "traefik.enable=true" + - "traefik.http.routers.uptimekuma.rule=Host(`{{ uptimekuma_domain }}`)" + - "traefik.http.routers.uptimekuma.tls=true" + - "traefik.http.routers.uptimekuma.tls.certresolver=letsencrypt" + - "traefik.http.routers.uptimekuma.middlewares=uptimekuma-auth@docker" + - "traefik.http.services.uptimekuma.loadbalancer.server.port=3001" + # Authentik forward auth — protects the dashboard with Authentik SSO. + # Pre-requisite: create a Proxy Provider (Forward Auth, single application) + # in Authentik pointing to https://{{ uptimekuma_domain }}, then add it + # to the embedded outpost. + - "traefik.http.middlewares.uptimekuma-auth.forwardauth.address=https://{{ authentik_domain }}/outpost.goauthentik.io/auth/traefik" + - "traefik.http.middlewares.uptimekuma-auth.forwardauth.trustForwardHeader=true" + - "traefik.http.middlewares.uptimekuma-auth.forwardauth.authResponseHeaders=X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid,X-authentik-jwt,X-authentik-meta-jwks,X-authentik-meta-outpost,X-authentik-meta-provider,X-authentik-meta-app,X-authentik-meta-version" + networks: + - {{ sovereign_network_name }} + logging: + driver: gelf + options: + gelf-address: "udp://{{ graylog_host }}:{{ graylog_gelf_port }}" + tag: "uptimekuma" + +networks: + {{ sovereign_network_name }}: + external: true