diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..f252fa3 --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,15 @@ +--- +lstu_api_url: https://framagit.org/api/v4/projects/5/releases/ +lstu_git_url: https://framagit.org/fiat-tux/hat-softwares/lstu.git +lstu_user: lstu +lstu_db: lstu +lstu_db_user: lstu +lstu_db_password: "{{ lookup('password', '/tmp/lstu_db_pw length=42 chars=ascii_letters,digits') }}" +lstu_monit_name: lstu +lstu_service: lstu.service +lstu_theme: default +lstu_cookie_secret: "zibuadoinckyg" +lstu: + path: /var/www/lstu + contact: 'admin[at]systemausfall.org' + diff --git a/files/lstu.conf b/files/lstu.conf new file mode 100644 index 0000000..f179844 --- /dev/null +++ b/files/lstu.conf @@ -0,0 +1,14 @@ +# Verwaltet durch Ansible + +proxy_http_version 1.1; +proxy_set_header Host $host; +proxy_set_header X-Real-IP $remote_addr; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + +# If you want to log the remote port of the file senders, you'll need that +proxy_set_header X-Remote-Port $remote_port; + +proxy_set_header X-Forwarded-Proto $scheme; + +# We expect the downstream servers to redirect to the right hostname, so don't do any rewrites here. +proxy_redirect off; diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..cd83756 --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,21 @@ +--- +- name: "get certificate" + ansible.builtin.command: dehydrated --cron -g + delegate_to: "{{ gateway_host }}" + +- name: reload nginx + ansible.builtin.service: + name: nginx + state: reloaded + delegate_to: "{{ gateway_host }}" + +- name: reload monit + ansible.builtin.service: + name: monit + state: reloaded + +- name: restart lstu + ansible.builtin.systemd: + name: lstu + state: restarted + daemon_reload: true diff --git a/meta/main.yml b/meta/main.yml new file mode 100644 index 0000000..db187c3 --- /dev/null +++ b/meta/main.yml @@ -0,0 +1,10 @@ +galaxy_info: + author: systemausfall.org + description: Role to install Ltsu + company: Sense.Lab e.V. + license: GPLv3 + min_ansible_version: "2.9" + platforms: + - name: Debian + versions: + - bullseye diff --git a/tasks/database.yml b/tasks/database.yml new file mode 100644 index 0000000..fd86500 --- /dev/null +++ b/tasks/database.yml @@ -0,0 +1,20 @@ +--- +- name: "database | Erstelle Datenbank" + ansible.builtin.mysql_db: + name: "{{ lstu_db }}" + state: present + login_unix_socket: "{{ mysql_socket }}" + login_user: root + delegate_to: "{{ database_host }}" + +- name: "database | Erstelle Benuzter" + ansible.builtin.mysql_user: + name: "{{ lstu_db_user }}" + password: "{{ lstu_db_password }}" + host: "{{ inventory_hostname }}" + priv: "{{ lstu_db }}.*:ALL" + update_password: on_create + state: present + login_unix_socket: "{{ mysql_socket }}" + login_user: root + delegate_to: "{{ database_host }}" diff --git a/tasks/gateway.yml b/tasks/gateway.yml new file mode 100644 index 0000000..b60c56e --- /dev/null +++ b/tasks/gateway.yml @@ -0,0 +1,27 @@ +--- +- name: "gateway | Domain zur Zertifikatsliste hinzufügen" + ansible.builtin.lineinfile: + path: /etc/dehydrated/domains.txt + insertafter: "^# systemausfall" + line: "{{ lstu.domain }}" + notify: get certificate + tags: gateway_cert + +- name: "gateway | Kopiere Konfigurations-Snippet" + ansible.builtin.copy: + src: lstu.conf + dest: /etc/nginx/snippets + mode: 0644 + +- name: "gateway | Proxy einrichten" + ansible.builtin.template: + src: nginx-rp-site.j2 + dest: "/etc/nginx/sites-available/{{ lstu.domain }}" + mode: 0644 + +- name: "gateway | Seite aktivieren" + ansible.builtin.file: + src: "/etc/nginx/sites-available/{{ lstu.domain }}" + dest: "/etc/nginx/sites-enabled/{{ lstu.domain }}" + state: link + notify: reload nginx diff --git a/tasks/lstu.yml b/tasks/lstu.yml new file mode 100644 index 0000000..5ca4532 --- /dev/null +++ b/tasks/lstu.yml @@ -0,0 +1,114 @@ +- name: "lstu | Ermittle aktuellen Tag" + ansible.builtin.shell: + cmd: curl -s "{{ lstu_api_url }}" | jq '.[]' | jq -r '.name' | head -1 + warn: false + changed_when: false + register: latest_tag + +- name: "lstu | Erstelle Verzeichnisse" + ansible.builtin.file: + path: "{{ lstu.path }}" + state: directory + owner: "{{ lstu_user }}" + group: "{{ lstu_user }}" + mode: 0755 + recurse: true + +- name: "lstu | Klone Git" + ansible.builtin.git: + repo: "{{ lstu_git_url }}" + dest: "{{ lstu.path }}" + force: true + version: "{{ latest_tag.stdout }}" + +- name: "lstu | Kopiere Konfiguration" + ansible.builtin.copy: + src: "{{ lstu.path }}/lstu.conf.template" + dest: "{{ lstu.path }}/lstu.conf" + mode: 0640 + remote_src: true + force: false + register: configuration + +- name: "lstu | Installiere Abhängigkeiten" + ansible.builtin.shell: + #cmd: carton install --deployment --without=test --without=sqlite --without=postgresql --without=ldap --without=htpasswd --without=cache + cmd: carton install --without=test --without=sqlite --without=postgresql --without=ldap --without=htpasswd --without=cache + chdir: "{{ lstu.path }}" + when: configuration.changed + +- name: "lstu | set ownership" + ansible.builtin.file: + path: "{{ lstu.path }}" + owner: "{{ lstu_user }}" + group: "{{ lstu_user }}" + recurse: true + + +- name: "lstu | edit config" + ansible.builtin.lineinfile: + path="{{ lstu.path }}/lstu.conf" + regexp="{{ item.regexp }}" + line="{{ item.line }}" + state=present + backup=no + backrefs=no + loop: + - { regexp: '^\s*#contact.*$', line: " contact => '{{ lstu.contact }}'," } + - { regexp: '^\s*#dbtype.*$', line: " dbtype => 'mysql'," } + - { regexp: '^\s*listen\s*=>.*$', line: " listen => ['http://0.0.0.0:8080']," } + - { regexp: '^\s*#proxy\s*=>.*$', line: " proxy => 1," } + - { regexp: '^\s*#really_delete_urls\s*=>.*$', line: " really_delete_urls => 1," } + - { regexp: '^\s*#secret\s*=>.*$', line: " secret => ['{{ lstu_cookie_secret }}']," } + - { regexp: '^\s*#adminpwd\s*=>.*$', line: " adminpwd => '{{ lstu.admin_passwd }}'," } + tags: lstu_conf + +- name: "lstu | edit config (db)" + ansible.builtin.blockinfile: + path: "{{ lstu.path }}/lstu.conf" + insertbefore: '^\s*#mysqldb.*$' + block: | + mysqldb => { + database => '{{ lstu_db }}', + host => '{{ database_host }}', + # optional, default is 3306 + #port => 3306, + user => '{{ lstu_db_user }}', + pwd => '{{ lstu_db_password }}', + # optional, default is 5 (set to 0 to disable persistent connections) + #max_connections => 5, + }, + + +- name: "lstu | Kopiere systemd-Unit" + ansible.builtin.template: + src: lstu.service.j2 + dest: "/etc/systemd/system/{{ lstu_service }}" + mode: 0644 + notify: restart lstu + +- name: "lstu | Prüfe Theme-Verzeichnis" + ansible.builtin.stat: + path: "{{ lstu.path }}/themes/{{ lstu_theme }}" + register: theme + + +# - name: "lstu | Kopiere Update-Skript" + # ansible.builtin.template: + # src: lstu-updater.j2 + # dest: "/usr/local/bin/lstu-updater" + # mode: 0755 + +# - name: "lstu | Cron für Auto-Updates" + # ansible.builtin.cron: + # name: lstu Aktualisierungen + # hour: "3" + # minute: "3" + # job: "chronic /usr/local/bin/lstu-updater" + +- name: "lstu | Monit-Überwachung" + ansible.builtin.template: + src: monit.j2 + dest: /etc/monit/conf-available/lstu + mode: 0644 + notify: reload monit diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..66e24ac --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,16 @@ +--- +- import_tasks: packages.yml + tags: packages + +- import_tasks: user.yml + tags: user + +- import_tasks: database.yml + tags: database + +- import_tasks: lstu.yml + tags: lstu + +- import_tasks: gateway.yml + tags: gateway + delegate_to: "{{ gateway_host }}" diff --git a/tasks/packages.yml b/tasks/packages.yml new file mode 100644 index 0000000..50b3608 --- /dev/null +++ b/tasks/packages.yml @@ -0,0 +1,18 @@ +--- +- name: "packages: Abhängigkeiten installieren" + ansible.builtin.apt: + pkg: + - carton + - curl + - monit + - make + - build-essential + - libssl-dev + - zlib1g-dev + - libpng-dev + - libmariadbd-dev + - jq + - libmojo-sqlite-perl + - libdbd-mysql-perl + - git + update_cache: yes diff --git a/tasks/user.yml b/tasks/user.yml new file mode 100644 index 0000000..d0c990e --- /dev/null +++ b/tasks/user.yml @@ -0,0 +1,6 @@ +--- +- name: "user: Systemkonto anlegen" + ansible.builtin.user: + name: "{{ lstu_user }}" + shell: /bin/bash + password_lock: true diff --git a/templates/lstu-updater.j2 b/templates/lstu-updater.j2 new file mode 100644 index 0000000..ee5ae97 --- /dev/null +++ b/templates/lstu-updater.j2 @@ -0,0 +1,41 @@ +#!/bin/bash +# {{ ansible_managed }} + +set -eu + +LSTU_PATH={{ lstu.path }} +INSTALLED_VERSION=$(cd $LSTU_PATH && git for-each-ref --sort=-taggerdate --count=1 --format '%(tag)' refs/tags) +LATEST_VERSION=$(curl -s {{ lstu_api_url }} | jq '.[]' | jq -r '.name' | head -1) +LSTU_CONF_TEMPLATE={{ lstu.path }}/lstu.conf.template + +if [ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]; then + echo "Lstu ist bereits aktuell." + exit 0 +else + echo "Deaktiviere Monitoring..." + monit unmonitor {{ lstu_monit_name }} + + echo "Stoppe Dienst..." + systemctl stop {{ lstu_service }} + + echo "Aktualisiere Git-Repository..." + cd "$LSTU_PATH" + git pull + git checkout tags/"$LATEST_VERSION" + + echo "Starte Dienst..." + systemctl start {{ lstu_service }} + sleep 10 + + echo "Aktiviere Monitoring..." + monit monitor {{ lstu_monit_name }} + sleep 10 + monit status {{ lstu_monit_name }} + + if [ "$(md5sum $LSTU_CONF_TEMPLATE)" == "$(cat $LSTU_CONF_TEMPLATE.md5)" ]; then + echo "Keine Aenderungen am Konfigurationstemplate" + else + echo "Es gibt Aenderungen an der lstu.conf.template." | mail -s "Lstu-Update: Nacharbeiten notwendig" lstu@admin.systemausfall.org + fi + md5sum "$LSTU_CONF_TEMPLATE" > "$LSTU_CONF_TEMPLATE".md5 +fi diff --git a/templates/lstu.service.j2 b/templates/lstu.service.j2 new file mode 100644 index 0000000..74dc270 --- /dev/null +++ b/templates/lstu.service.j2 @@ -0,0 +1,18 @@ +[Unit] +Description=Shortened URLs service +Documentation=https://framagit.org/fiat-tux/hat-softwares/lstu +Requires=network.target +After=network.target + +[Service] +Type=forking +User={{ lstu_user }} +RemainAfterExit=yes +WorkingDirectory={{ lstu.path }} +PIDFile={{ lstu.path }}/script/hypnotoad.pid +ExecStart=/usr/bin/carton exec hypnotoad script/lstu +ExecStop=/usr/bin/carton exec hypnotoad -s script/lstu +ExecReload=/usr/bin/carton exec hypnotoad script/lstu + +[Install] +WantedBy=multi-user.target diff --git a/templates/monit.j2 b/templates/monit.j2 new file mode 100644 index 0000000..853b083 --- /dev/null +++ b/templates/monit.j2 @@ -0,0 +1,4 @@ +check process lstu with pidfile {{ lstu.path }}/script/hypnotoad.pid + start program = "/usr/bin/systemctl start {{ lstu_service }}" + stop program = "/usr/bin/systemctl stop {{ lstu_service }}" + if failed host {{ ansible_default_ipv4.address }} port 8080 with timeout 15 seconds for 3 times within 4 cycles then restart diff --git a/templates/nginx-rp-site.j2 b/templates/nginx-rp-site.j2 new file mode 100644 index 0000000..080d318 --- /dev/null +++ b/templates/nginx-rp-site.j2 @@ -0,0 +1,18 @@ +server { + listen 80; + server_name {{ lstu.domain }}; + include snippets/letsencrypt.conf; + location / { return 301 https://$http_host$request_uri; } +} + +server { + listen 443 ssl http2; + server_name {{ lstu.domain }}; + ssl_certificate /var/lib/dehydrated/certs/{{ lstu.domain }}/fullchain.pem; + ssl_certificate_key /var/lib/dehydrated/certs/{{ lstu.domain }}/privkey.pem; + include /etc/nginx/snippets/add_headers.conf; + include /etc/nginx/snippets/proxy-transfer.conf; + include /etc/nginx/snippets/lstu.conf; + location = /robots.txt { root /data/lstu/; } + location / { proxy_pass http://{{ inventory_hostname }}:8080; } +} diff --git a/vars/main.yml b/vars/main.yml new file mode 100644 index 0000000..c835268 --- /dev/null +++ b/vars/main.yml @@ -0,0 +1,3 @@ +--- +# vars file for roles/lstu +mysql_socket: /var/run/mysqld/mysqld.sock