]> gitweb.hamatoma.de Git - ansknife.git/commitdiff
Initial commit
authorHamatoma <author@hamatoma.de>
Mon, 5 May 2025 11:02:08 +0000 (13:02 +0200)
committerHamatoma <author@hamatoma.de>
Mon, 5 May 2025 11:02:08 +0000 (13:02 +0200)
110 files changed:
CHANGELOG.md [new file with mode: 0644]
README.md [new file with mode: 0644]
SetRights [new symlink]
ansible.cfg [new file with mode: 0644]
docu/de/01_überblick_ansible.md [new file with mode: 0644]
docu/de/05_yaml.md [new file with mode: 0644]
docu/de/10_einrichten_ansible_auf_workstation.md [new file with mode: 0644]
docu/de/20_einrichten_ansible_auf_server.md [new file with mode: 0644]
docu/de/50_neues_ansknife_projekt.md [new file with mode: 0644]
inventory [new file with mode: 0644]
playbooks.templates/i_10_basic.yaml [new file with mode: 0644]
playbooks.templates/i_11_user.yaml [new file with mode: 0644]
playbooks.templates/i_15_server_packages.yaml [new file with mode: 0644]
playbooks.templates/i_17_configuration.yaml [new file with mode: 0644]
playbooks.templates/i_20_nginx.yaml [new file with mode: 0644]
playbooks.templates/i_21_nginx_sites.yaml [new file with mode: 0644]
playbooks.templates/i_30_mariadb.yaml [new file with mode: 0644]
playbooks.templates/i_40_php8.2.yaml [new file with mode: 0644]
playbooks.templates/i_50_git_server.yaml [new file with mode: 0644]
playbooks.templates/i_60_postfix.yaml [new file with mode: 0644]
playbooks.templates/i_62_postfix_dkim.yaml [new file with mode: 0644]
playbooks.templates/i_70_webapps.yaml [new file with mode: 0644]
playbooks.templates/i_80_firewalld.yaml [new file with mode: 0644]
playbooks.templates/i_81_fail2ban.yaml [new file with mode: 0644]
playbooks.templates/i_99_test.yaml [new file with mode: 0644]
playbooks.templates/lets_create.yaml [new file with mode: 0644]
playbooks.templates/mysql_create_admin.yaml [new file with mode: 0644]
playbooks.templates/mysql_create_db_and_user.yaml [new file with mode: 0644]
playbooks.templates/nginx_create_site.yaml [new file with mode: 0644]
playbooks.templates/ssl_create_certificate.yaml [new file with mode: 0644]
playbooks.templates/webapp_backup.yaml [new file with mode: 0644]
playbooks.templates/webapp_create.yaml [new file with mode: 0644]
playbooks.templates/webapp_export.yaml [new file with mode: 0644]
playbooks.templates/webapp_import.yaml [new file with mode: 0644]
playbooks/create_project.yaml [new file with mode: 0644]
playbooks/labor.yaml [new file with mode: 0644]
resources/needed.tgz [new file with mode: 0644]
resources/rsh.tgz [new file with mode: 0644]
scripts.templates/AddPassword [new file with mode: 0755]
scripts.templates/CreatePlaybook [new file with mode: 0755]
scripts.templates/CreateTask [new file with mode: 0755]
scripts.templates/SetRights [new file with mode: 0755]
scripts/build_dkim_text.py [new file with mode: 0755]
tasks.templates/t_copy_wildcard.yaml [new file with mode: 0644]
tasks.templates/t_dkim.yaml [new file with mode: 0644]
tasks.templates/t_dkim_dns.yaml [new file with mode: 0644]
tasks.templates/t_dkim_keys.yaml [new file with mode: 0644]
tasks.templates/t_dmarc.yaml [new file with mode: 0644]
tasks.templates/t_dmarc_dns.yaml [new file with mode: 0644]
tasks.templates/t_firewalld_basic.yaml [new file with mode: 0644]
tasks.templates/t_firewalld_email.yaml [new file with mode: 0644]
tasks.templates/t_firewalld_http.yaml [new file with mode: 0644]
tasks.templates/t_firewalld_nfs.yaml [new file with mode: 0644]
tasks.templates/t_lets_create.yaml [new file with mode: 0644]
tasks.templates/t_link_wildcard.yaml [new file with mode: 0644]
tasks.templates/t_mysql_create_admin.yaml [new file with mode: 0644]
tasks.templates/t_mysql_create_db_and_user.yaml [new file with mode: 0644]
tasks.templates/t_nginx_create_site.yaml [new file with mode: 0644]
tasks.templates/t_spf.yaml [new file with mode: 0644]
tasks.templates/t_ssl_create_certificate.yaml [new file with mode: 0644]
tasks.templates/t_sysinfo_create.yaml [new file with mode: 0644]
tasks.templates/t_webapp_backup.yaml [new file with mode: 0644]
tasks.templates/t_webapp_create.yaml [new file with mode: 0644]
tasks.templates/t_webapp_export.yaml [new file with mode: 0644]
tasks.templates/t_webapp_import.yaml [new file with mode: 0644]
templates.fix/antispam/key.table [new file with mode: 0644]
templates.fix/antispam/signing.table [new file with mode: 0644]
templates.fix/nginx/hi1.txt [new file with mode: 0644]
templates.fix/nginx/hi2.txt [new file with mode: 0644]
templates.fix/nginx/index.html [new file with mode: 0644]
templates.fix/nginx/index.php [new file with mode: 0644]
templates.fix/nginx/letsencrypt.conf [new file with mode: 0644]
templates.fix/nginx/site.php [new file with mode: 0644]
templates.fix/nginx/test.site [new file with mode: 0644]
templates.fix/readme.txt [new file with mode: 0644]
templates.fix/scripts/ClearSwap [new file with mode: 0644]
templates.fix/scripts/CreateSysInfo [new file with mode: 0644]
templates.fix/scripts/SaveDatabases [new file with mode: 0755]
templates.fix/scripts/saveowner.py [new file with mode: 0755]
templates.install/project_ansible.md [new file with mode: 0644]
templates.install/project_cron.yaml [new file with mode: 0644]
templates.install/readme.txt [new file with mode: 0644]
templates.install/t_firewalld_local.yaml [new file with mode: 0644]
templates.local/antispam/ignore.hosts [new file with mode: 0644]
templates.local/antispam/opendkim.conf [new file with mode: 0644]
templates.local/antispam/opendmarc.conf [new file with mode: 0644]
templates.local/antispam/trusted.hosts [new file with mode: 0644]
templates.local/firewalld.yaml [new file with mode: 0644]
templates.local/nginx/http.conf [new file with mode: 0644]
templates.local/nginx/sites/readme.txt [new file with mode: 0644]
templates.local/postfix/aliases [new file with mode: 0644]
templates.local/postfix/email_forwarding/main.cf [new file with mode: 0644]
templates.local/postfix/email_forwarding/master.cf [new file with mode: 0644]
templates.local/postfix/send_only/main.cf [new file with mode: 0644]
templates.local/postfix/send_only/master.cf [new file with mode: 0644]
templates.local/postfix/virtual [new file with mode: 0644]
templates.local/readme.txt [new file with mode: 0644]
templates.local/scripts/CreateSysInfo.conf [new file with mode: 0644]
templates.local/scripts/ExampleBackup [new file with mode: 0644]
templates.local/users.yaml [new file with mode: 0644]
templates.vars/common.yaml [new file with mode: 0644]
templates.vars/dkim.yaml [new file with mode: 0644]
templates.vars/firewalld.yaml [new file with mode: 0644]
templates.vars/mysql.yaml [new file with mode: 0644]
templates.vars/mysql_vault.yaml [new file with mode: 0644]
templates.vars/packages.yaml [new file with mode: 0644]
templates.vars/php.yaml [new file with mode: 0644]
templates.vars/ssl-certificate.yaml [new file with mode: 0644]
templates.vars/users.yaml [new file with mode: 0644]
templates.vars/webapps.yaml [new file with mode: 0644]

diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644 (file)
index 0000000..51ee0d2
--- /dev/null
@@ -0,0 +1 @@
+# Change log of 
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..5f92e30
--- /dev/null
+++ b/README.md
@@ -0,0 +1,37 @@
+Project ansknife
+
+# Description
+A collection of predefined tasks, playbooks, scripts and other.
+
+With that project you can create a server definition project in a short time.
+
+# The Project Name
+ansknife is the short form of Ansible swiss knive
+
+# Preconditions
+- we support usage of NGINX, MARIADB, PHP, LetsEncrypt
+
+# Glossar
+- webapp: A web application: a webserver application with PHP source in a directory tree, MariaDB database and Nginx configuration
+
+# Creation of a New Server Definition Project
+The server name may be "colibri", we take the name also for the new project.
+- go to the base directory, e.g. /home/workspace/ansible/
+- clone the ansknife project: /home/workspace/ansible/ansknife will be created
+- start a playbook to create the project colibri: /home/workspace/ansible/colibri will be created
+```
+BASE=/home/workspace/ansible
+GIT_REP=ssh://myserver.com/git/repo
+PROJ=colibri
+cd $BASE
+git clone $GIT_REP/ansknife.git
+cd ansknife
+ansible-playbook playbook/create_project -e project=$PROJ
+cd ../$PROJ
+./SetRights
+```
+- edit all files in $BASE/colibri/vars: configure your project by choosing valid/meaningful variable values
+- edit $BASE/colibri/README.md, $BASE/colibri/CHANGELOG.md, inventory 
+
+
+
diff --git a/SetRights b/SetRights
new file mode 120000 (symlink)
index 0000000..c319c58
--- /dev/null
+++ b/SetRights
@@ -0,0 +1 @@
+scripts.templates/SetRights
\ No newline at end of file
diff --git a/ansible.cfg b/ansible.cfg
new file mode 100644 (file)
index 0000000..930e818
--- /dev/null
@@ -0,0 +1,3 @@
+[defaults]
+inventory = ./inventory
+
diff --git a/docu/de/01_überblick_ansible.md b/docu/de/01_überblick_ansible.md
new file mode 100644 (file)
index 0000000..db3d208
--- /dev/null
@@ -0,0 +1,37 @@
+# Überblick Ansible
+
+Ansible ist ein Computerkonfigurationswerkzeug.
+
+## Begriffe (Glossar)
+
+atomare Task
+: Eine atomare Task ist eine Aufgabe, die nicht mehr unterteilt werden kann
+: Ansible liefert viele vordefinierte atomare Tasks mit.
+: Beispiel: die Task "copy"
+: Jede Taskt hat spezifische Parameter, die die Aufgabe genau festlegen
+
+komplexe Task
+: eine komplexe Task ist eine Folge von atomaren Tasks, definiert in genau einer Datei
+
+Playbook
+: Ein Playbook ist eine Datei, in der eine zu lösende komplexe Aufgabe komplett beschrieben ist
+: Im Playbook können atomare Tasks verwendet werden
+: Im Playbook können komplexe Tasks statisch eingefügt ("include_task") oder dynamisch eingefügt werden ("import_task")
+
+Fact
+: Ein "Fact" ist eine Variable.
+: Eine Variable wird definiert (siehe folgendes Kapitel)
+: Facts können ihren Wert ändern, z.B. mit set_fact
+: Eine Variable kann in Playbooks, atomaren und komplexen Tasks verwendet werden, mit der Syntax {{ fact_name }}
+
+
+## Facts (Variablen)
+
+In Ansible werden Variablen mit "facts" bezeichnet.
+
+# Definition
+Es gibt viele Möglichkeiten, Facts zu definieren:
+- direkt im Playbook: Dort gibt es den Bereich: -vars:
+```
+--- 
+- im Playbook und ein
\ No newline at end of file
diff --git a/docu/de/05_yaml.md b/docu/de/05_yaml.md
new file mode 100644 (file)
index 0000000..5b3ba25
--- /dev/null
@@ -0,0 +1,62 @@
+# Einführung in YAML
+
+Die Konfigurierung von Ansible erfolgt mit dem Dateiformat "YAML".
+
+Das ist eine gut lesbare, kommentierbare Textdatei.
+
+## Die wichtigsten Elemente:
+
+```
+---
+# das ist ein Kommentar
+# die obigen "---" leiten die YAML-Datei ein. Das ist optional
+
+# Es folgt eine Liste mit dem Namen "hosts" und den Elementen "mars", "venus", "saturn"
+hosts:
+  - mars
+  - venus
+  - saturn
+
+# Die Liste kann auch so geschrieben werden:
+hosts: [mars, venus, saturn]
+
+# Es folgt eine Struktur namens "users" mit benannten Elementen "name" und "user_id":
+# Der Werte für den Schlüssel "jonny" ist 101, der für den Schlüssel "eve" ist 102:
+users:
+  jonny: 101
+  eve: 102
+```
+Das kann auch in verkürzter Form formuliert werden:
+```
+users: { jonny: 101, eve: 102 }
+```
+'''Wichtig''':
+
+## Einrückungen
+
+Die Hierarchie der Definitionen mittels Einrückung muss strikt eingehalten werden: immer 2 Leerzeichen je Einrückungsschritt.
+
+## Strings
+Ein String wird entweder mit "<inhalt>" oder mit '<inhalt>' geschrieben.
+Wenn der Inhalt keine Leerzeichen und keine Sonderzeichen enthält, können die Anführungszeichen weggelassen werden.
+```
+user: [jonny, eve]
+```
+ist nur die Abkürzung von 
+```
+"user": ["jonny", "eve"]
+```
+
+## Mehrzeilige Strings
+Es können auch mehrzeilige Werte eingetragen werden:
+```
+content: |
+  Diese Zeile gehört zu content
+  Auch diese 2.te Zeile gehört dazu
+  Das ist die letzte Zeile, erkennbar an der geringeren Einrückung der Folgezeile
+name: 123
+```
+'''Merke''': 
+- "|" leitet den mehrzeilen Wert ein
+- Die Einrückung vor "Diese Zeile..." gehört nicht zum Inhalt.
+- Das Ende der Einrückung wird durch eine Zeile mit geringeren Einrücktiefe bestimmt.
diff --git a/docu/de/10_einrichten_ansible_auf_workstation.md b/docu/de/10_einrichten_ansible_auf_workstation.md
new file mode 100644 (file)
index 0000000..b928e67
--- /dev/null
@@ -0,0 +1,50 @@
+# Einrichten einer Ansible-Umgebung auf einer Workstation mit GUI
+
+# Systemvoraussetzungen
+Diese Beschreibung bezieht sich auf eine Linuxumgebung eines Debian-Systems.
+
+## Paketinstallation
+```
+apt install ansible openssh
+```
+## Installation Entwicklungsumgebung
+Es ist sinnvoll, die Entwicklungsumgebung "Microsoft Visual Code" zu verwenden.
+
+- Gehe auf https://code.visualstudio.com/download
+- Lade das *.deb-Paket herunter
+```
+dpkg -i code*.deb
+```
+
+## SSH vorbereiten
+Ansible verwendet SSH, um mit den entfernten Server zu kommunizieren. Daher muss ein SSH-Zugriff ermöglicht werden.
+
+### Erstellen eines Zertifikats
+```
+ssh-keygen -t rsa -b 4096
+```
+Es wird ein Passwort abgefragt, mit dem dann das Zertifikat geschützt ist.
+
+'''Wichtig''': Unbedingt ein Passwort vergeben, da der Zugriff auf den entfernten Server mit Root-Rechten erfolgt, also wirklich alles offenlegt.
+
+## Verwendung von ssh-agent
+
+Damit das Passwort des Zertifikats nur einmal pro Session eingeben werden muss, kann das Programm ssh-agent verwendet werden. Dieses wird normalerweise automatisch gestartet.
+
+Hier ein Test, ob es läuft:
+```
+ps aux | grep ssh-agent
+'''
+Wird hier ein Prozess angezeigt, läuft ssh-agent.
+
+Wenn nicht:
+```
+eval $(ssh-agent)
+```
+
+Der Schlüssel wird so "angemeldet":
+```
+ssh-add
+```
+Hier wird das Passwort des Zertifikats abgefragt. Dieses ist dann bis zum Ende der Session "gültig", d.h. es wird nicht mehr benötigt.
+
diff --git a/docu/de/20_einrichten_ansible_auf_server.md b/docu/de/20_einrichten_ansible_auf_server.md
new file mode 100644 (file)
index 0000000..847ba36
--- /dev/null
@@ -0,0 +1,75 @@
+# Einrichten von Ansible auf dem entfernten Server
+
+## Installation
+```
+apt install ansible openssh
+```
+
+## Einrichten des Benutzers ansadm
+- Für den Zugriff der Workstations wird der Benutzer ansadm mit festgelegter Id angelegt
+  - Am besten kein Passwort vergeben: Wenn das Passwort abgefragt wird, "x" eingeben. 
+  - Wenn die Bestätigung abgefragt wird, "Y" eingeben
+  - Bei "Nochmal versuchen?" mit N antworten
+- Es wird der automatische Zugang mittels Zertifikat für den Benutzer der Workstation erlaubt
+- Dies geschieht in der Datei authorized_keys.
+```
+adduser ansadm --uid=260
+DIR=/home/ansadm/.ssh
+mkdir $DIR
+chmod 700 $DIR
+touch $DIR/authorized_keys
+chmod 600 $DIR/authorized_keys
+chown ansadm -R $DIR
+```
+
+### sudo ermöglichen
+Dazu muss die Datei /etc/sudoers ediert werden:
+Als Editor ist vi oder nano möglich:
+```
+nano /etc/sudoers
+```
+- Ans Ende der Datei blättern (Bild runter)
+- Oberhalb die Zeile "root ALL..." suchen
+- Nach dieser Zeile eintragen:
+```
+ansadm ALL=NOPASSWD: ALL
+```
+- Strg-o drücken (Speichern)
+- Strg-x drücken (Beenden)
+
+### Ermitteln des öffentlichen Schlüssels:
+Auf der Workstation rocket mit dem Benutzer jonny:
+```
+USER=jonny
+cat /home/$USER/.ssh/*.pub
+```
+Es wird ausgegeben:
+```
+ssh-rsa AAAAB3NzaC1yc2EA ... BCr8AEZIj jonny@rocket
+```
+
+## Eintrag eines Workstation-Benutzers für den Zugang zu ansadm
+Für den Beispielsbenutzer jonny, der sich automatisch als ansadm anmelden will, muss ein Eintrag in die Datei /home/ansadm/.ssh/authorized_keys gemacht werden.
+
+Dazu wird der öffentliche Schlüssel einfach in die Datei authorized_keys eingetragen.
+```
+echo >>/home/ansadm/.ssh/authorized_keys "ssh-rsa AAAAB3NzaC1yc2EA ... BCr8AEZIj jonny@rocket"
+Dazu wird der öffentliche Schlüssel einfach in die Datei authorized_keys eingetragen.
+```
+
+## Test für Funktion
+Um zu testen, ob der Zugriff von der Workstation auf den Server rocket funktioniert:
+
+'''Auf der Workstation:'''
+
+Es muss jetzt ein Anmelden als User ansadm möglich sein:
+```
+ssh ansadm@rocket
+```
+
+Ansible testen:
+```
+ansible -i inventory rocket -m ping
+```
+Das setzt voraus, dass schon ein Projekt für den Server rocket aufgesetzt wurde, mit dem Inventory namens inventory.
+
diff --git a/docu/de/50_neues_ansknife_projekt.md b/docu/de/50_neues_ansknife_projekt.md
new file mode 100644 (file)
index 0000000..80d4571
--- /dev/null
@@ -0,0 +1,117 @@
+# Einrichten eines neuen Ansible-Projekts mittels ansknife
+
+Das Projekt ansknife bietet die Möglichkeit, schnell ein vollständiges Ansible-Projekt aufzusetzen, indem es eine funktionierende Struktur erzeugen kann, in der nur projektspezifische Dinge angepasst werden müssen.
+
+## Installation des Projekts ansknife
+
+```
+BASE=/home/ws/ansible
+cd $BASE
+git clone <ansknife_repository>
+```
+Es wird damit das Verzeichnis $BASE/ansknife erstellt.
+
+## Erstellen des Projekts rocket
+Mit dem Projekt rocket soll eine Ansible-Konfiguration für den Beispielserver "rocket" erstellt werden.
+
+Dieses Projekt muss '''immer''' als Nachbarverzeichnis zu $BASE/ansknife sein, wegen der relativen symbolischen Links. Das geschieht automatisch mit der folgenden Installation.
+```
+BASE=/home/ws/ansible
+cd $BASE/ansknife
+ansible-playbook playbook/create_project \
+  -e project=rocket \
+  -e postfix_mode=send_only \
+  -e ansible_base=$BASE
+```
+Hier wird die einfachste Variante der postfix-Installation gewählt. Weitere Varianten siehe 60_postfix_varianten.md
+
+Das Ergebnis des obigen Aufrufs ist ein Nachbarverzeichnis von ansknife namens rocket:
+```
+cd $BASE/rocket
+code .
+```
+Damit wird die Entwicklungsumgebung im richtigen Projekt gestartet.
+
+## Anpassen des Projekts rocket
+Anpassungen müssen in folgenden Unterverzeichnissen durchgeführt werden:
+- vars
+- templates.local
+- tasks
+- playbooks
+
+### Anpassungen in rocket/vars
+Es müssen alle Dateien angeschaut werden und projektspezifische Anpassungen vorgenommen werden.
+
+#### vars/common.yaml
+Unbedingt überprüfen/anpassen:
+- remote_www_directory: "/home/www"
+  - Das ist das Verzeichnis, das dann mit /srv/www erreicht wird.
+- postfix_host: "hero.infeos.it"
+  - Das muss eine gültige DNS-Adresse sein
+- postfix_domain: "infeos.it"
+- postfix_receipient_email: "mail.rocket@example.com"
+  - Das wird als Absenderadresse für Mails von postfix benutzt, z.B. für "Post kann nicht zugestellt werden".
+webmaster_email: "web.hero@hamatoma.de"
+  - Das wird als Absenderadresse für Mails vom System benutzt, z.B. wenn ein Cronjob missglückt
+
+### vars/user.yaml
+Hier werden die Benutzer auf dem Server rocket eingetragen.
+Benutzer, die schon im System angelegt sind, müssen die ID bekommen, die sie schon haben.
+```
+id jonny
+```
+Alle neuen User sollten Ids > 1500 haben, damit sie nicht mit den automatisch generierten User-Ids kollidieren.
+
+Wenn ein User auch root-Rechte bekommen soll, dann auch in die zweite Liste "user_sudo_members" eintragen.
+
+#### vars/mysql.yaml
+Hier wird ein Administrator definiert, der Zugriff auf alle MySql-Datenbanken hat.
+Hier normalerweise nichts ändern.
+
+#### vars/mysql_vault.yaml
+Hier muss ein Passwort für den Adminstrator eingetragen werden.
+
+#### vars/firewall
+Änderung nur, wenn die Firewall auch benutzt wird.
+
+Dann muss hier das Interface eingetragen werden, mit dem das Internet erreichbar ist:
+
+```
+ip addr
+```
+Listet alle Interfaces auf, normalerweise ist es eines von den Ethernet-Interfaces, die mit "en" beginnen, z.B. "enp5s0". Erkennbar ist das an der öffentlichen IP-Adresse, hier "178.64.33.44"
+
+Ausgabe:
+```
+1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
+    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+    inet 127.0.0.1/8 scope host lo
+       valid_lft forever preferred_lft forever
+    inet6 ::1/128 scope host noprefixroute 
+       valid_lft forever preferred_lft forever
+2: enp5s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
+    link/ether a0:36:bc:cb:94:be brd ff:ff:ff:ff:ff:ff
+    inet 178.64.33.44/26 brd 178.63.21.63 scope global enp5s0
+       valid_lft forever preferred_lft forever
+    inet6 2a01:2f8:141:45b9::2/64 scope global 
+       valid_lft forever preferred_lft forever
+    inet6 fe80::a236:bcfc:fecc:95be/64 scope link 
+       valid_lft forever preferred_lft forever
+2: enp7s2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
+    link/ether a0:36:bc:cb:94:be brd ff:ff:ff:ff:ff:ff
+    inet 10.100.100.39/26 brd 178.63.21.63 scope global enp5s0
+       valid_lft forever preferred_lft forever
+    inet6 2a01:2f8:131:55b9::2/64 scope global 
+       valid_lft forever preferred_lft forever
+    inet6 fe80::a232:b8fc:fecc:95b1/64 scope link 
+       valid_lft forever preferred_lft forever
+```
+
+#### vars/php.yaml
+
+### Anpassungen in rocket/templates.local
+Es müssen alle Dateien angeschaut werden und projektspezifische Anpassungen vorgenommen werden.
+
+# 
+
+
diff --git a/inventory b/inventory
new file mode 100644 (file)
index 0000000..3d37642
--- /dev/null
+++ b/inventory
@@ -0,0 +1,7 @@
+[hosts]
+localhost
+[hosts:vars]
+ansible_python_interpreter=/usr/bin/python3
+ansible_ssh_common_args=-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
+ansible_user=ansadm
+ansible_become=yes
diff --git a/playbooks.templates/i_10_basic.yaml b/playbooks.templates/i_10_basic.yaml
new file mode 100644 (file)
index 0000000..f063cf4
--- /dev/null
@@ -0,0 +1,44 @@
+---
+- name: Playbook to prepare the system
+  hosts: all
+  vars:
+    hostname: "{{ inventory_hostname | regex_search('[0-9a-zA-Z_]+')}}"
+  tasks:
+    - name: Prepare /media/trg directory
+      ansible.builtin.file: path=/media/trg state=directory
+    - name: Prepare /media/tmp directory
+      ansible.builtin.file: path=/media/trg state=directory
+    - name: Prepare /media/fs.cave directory
+      ansible.builtin.file: path=/media/fs.cave state=directory
+    - name: Prepare /media/fs.sys directory
+      ansible.builtin.file: path=/media/fs.sys state=directory
+    - name: Prepare /usr/local/bin directory
+      ansible.builtin.file: path=/usr/local/bin state=directory
+    - name: Prepare /usr/local/bin/local directory
+      ansible.builtin.file: path=/usr/local/bin/local state=directory
+    - name: Prepare /usr/share/pyrshell directory
+      ansible.builtin.file: path=/usr/share/pyrshell state=directory
+    - name: Symbolic link to local directory
+      ansible.builtin.file: src=/usr/local/bin/local dest=/usr/local/bin/{{hostname}} state=link
+    - name: Unpack a tar into /usr/local/bin/local
+      unarchive: src=../resources/needed.tgz dest=/usr/local/bin
+    - name: Symbolic link to /p
+      ansible.builtin.file: src=/usr/local/bin/std.profile dest=/p state=link
+    - name: Unpack a tar into /usr/share/pyrshell
+      unarchive: src=../resources/rsh.tgz dest=/usr/share/pyrshell
+    - name: Create configuration directory
+      ansible.builtin.file: path=/etc/config state=directory mode=0700
+    - name: Create webapp.d
+      ansible.builtin.file: path=/etc/config/webapps.d state=directory mode=0700
+    - name: Create directories in /media
+      ansible.builtin.file: path=/media/{{ item }} state=directory mode=0700
+      with_items: [src, tmp, trg]
+    - name: install standard packages
+      apt:
+        name: "{{ item }}"
+        state: present
+        update_cache: true
+        cache_valid_time: 3600
+      with_items: [htop, rsync, sudo, curl, iotop, jnettop, ssl-cert, ca-certificates, zram-tools, nfs-kernel-server, tmux]
+
+        
\ No newline at end of file
diff --git a/playbooks.templates/i_11_user.yaml b/playbooks.templates/i_11_user.yaml
new file mode 100644 (file)
index 0000000..4f44da7
--- /dev/null
@@ -0,0 +1,65 @@
+---
+- name: Playbook to create the users
+  hosts: all
+  vars:
+    hostname: "{{ inventory_hostname | regex_search('[0-9a-zA-Z_]+')}}"
+  vars_files:
+    - ../vars/common.yaml
+    - ../vars/users.yaml
+  tasks:
+    # === human users
+    - name: Create human users
+      ansible.builtin.user: 
+        name: "{{ item.key }}"
+        state: present 
+        uid: "{{ item.value }}"
+      with_dict: "{{ user_humans }}"
+    - name: Enable sudo for human users
+      ansible.builtin.lineinfile:
+        path: /etc/sudoers
+        state: present
+        regexp: '^{{ item }}\s+ALL'
+        insertafter: '^root\s+ALL'
+        line: '{{ item }} ALL=NOPASSWD: ALL'
+      with_items: "{{ user_sudo_members }}"
+
+    # === system users, used for rsync communication with other sites
+    - name: Create user bupsrv
+      ansible.builtin.user: name=bupsrv state=present uid=201
+    - name: Create user bupsupply
+      ansible.builtin.user: name=bupsupply state=present uid=202
+    - name: Create ssh directory for bupsupply
+      ansible.builtin.file: path=/home/bupsupply/.ssh state=directory group=bupsupply owner=bupsupply mode=0700
+    - name: Create authorized_keys for bupsupply
+      ansible.builtin.file: path=/home/bupsupply/.ssh/authorized_keys state=touch group=bupsupply owner=bupsupply mode=0600
+    - name: Create user bupwiki
+      ansible.builtin.user: name=bupwiki state=present uid=203
+    - name: Create user buptmp
+      ansible.builtin.user: name=buptmp state=present uid=204
+    - name: Create ssh directory for buptmp
+      ansible.builtin.file: path=/home/buptmp/.ssh state=directory group=buptmp owner=buptmp mode=0700
+    - name: Create authorized_keys for buptmp
+      ansible.builtin.file: path=/home/buptmp/.ssh/authorized_keys state=touch group=buptmp owner=buptmp mode=0600
+    - name: Create user extdata
+      ansible.builtin.user: name=extdata state=present uid=211
+    - name: Create user extcloud
+      ansible.builtin.user: name=extcloud state=present uid=212
+    - name: Create user extbup
+      ansible.builtin.user: name=extbup state=present uid=213
+    - name: Create ssh directory for ext* users
+      ansible.builtin.file: 
+        path: /home/{{ item }}/.ssh 
+        state: directory 
+        group: "{{ item }}" 
+        owner: "{{ item }}" 
+        mode: "0700"
+      with_items: [extdata, extcloud, extbup]
+    - name: Create keys for the ext* users
+      ansible.builtin.openssh_keypair:
+        path: /home/{{ item }}/.ssh/id_rsa
+        type: rsa
+        owner: "{{ item }}"
+        group: "{{ item }}"
+        mode: '0600'
+        passphrase: ''
+      with_items: [extdata, extcloud, extbup]
diff --git a/playbooks.templates/i_15_server_packages.yaml b/playbooks.templates/i_15_server_packages.yaml
new file mode 100644 (file)
index 0000000..c16e231
--- /dev/null
@@ -0,0 +1,19 @@
+---
+
+- name: Playbook to install server packages
+  hosts: all
+  vars:
+    hostname: "{{ inventory_hostname | regex_search('[0-9a-zA-Z_]+')}}"
+  vars_files:
+    - ../vars/common.yaml
+    - ../vars/packages.yaml
+  tasks:
+    - name: install server packages
+      apt:
+        name: "{{ item }}"
+        state: present
+        update_cache: true
+        cache_valid_time: 3600
+      with_items: "{{ packages_list }}"
+
+        
\ No newline at end of file
diff --git a/playbooks.templates/i_17_configuration.yaml b/playbooks.templates/i_17_configuration.yaml
new file mode 100644 (file)
index 0000000..b0be4ca
--- /dev/null
@@ -0,0 +1,26 @@
+---
+- name: Playbook to configure some system settings in configuration files
+  hosts: all
+  vars_files:
+    - ../vars/common.yaml
+  tasks:
+    - name: limit the total size of journald logs
+      lineinfile:
+        dest: /etc/systemd/journald.conf
+        regexp: ^#?\s*SystemMaxUse=
+        line: SystemMaxUse={{ systemd_journal_system_max_use }}
+      notify:
+        - restart systemd-journald
+
+    - name: limit the size of each journald log file
+      lineinfile:
+        dest: /etc/systemd/journald.conf
+        regexp: ^#\s*SystemMaxFileSize=
+        line: SystemMaxFileSize={{ systemd_journal_system_max_file_size }}
+      notify:
+        - restart systemd-journald
+  handlers:
+    - name: restart systemd-journald
+      ansible.builtin.systemd:
+        name: systemd-journald
+        state: restarted
\ No newline at end of file
diff --git a/playbooks.templates/i_20_nginx.yaml b/playbooks.templates/i_20_nginx.yaml
new file mode 100644 (file)
index 0000000..a404f0c
--- /dev/null
@@ -0,0 +1,61 @@
+---
+- name: Install and configure NGINX
+  hosts: all
+  become: yes
+
+  vars:
+    user: www-data
+    hostname: "{{ inventory_hostname }}"
+    log_name: "{{ inventory_hostname | regex_search('[0-9a-zA-Z_]+') }}"
+  vars_files:
+    - ../vars/common.yaml
+    - ../vars/ssl-certificate.yaml
+  tasks:
+    - name: Install nginx
+      ansible.builtin.apt:
+        name: nginx
+        state: latest
+        update_cache: yes
+    - name: Prepare letsencrypt home directory
+      ansible.builtin.file: path=/srv/www/letsencrypt/.well-known/acme-challenge state=directory
+    - name: Add test file1
+      ansible.builtin.copy: src=../templates.fix/nginx/hi1.txt dest=/srv/www/letsencrypt/.well-known/
+    - name: Add test file2
+      ansible.builtin.copy: src=../templates.fix/nginx/hi2.txt dest=/srv/www/letsencrypt/.well-known/acme-challenge/hi2.txt
+    - name: Prepare letsencrypt
+      ansible.builtin.copy:
+        src: ../templates.fix/nginx/letsencrypt.conf
+        dest: /etc/nginx/snippets
+    - name: add HTTP-variables
+      ansible.builtin.copy:
+        src: ../templates.local/nginx/http.conf
+        dest: /etc/nginx/snippets
+    - name: create a www directory
+      ansible.builtin.file: path={{ remote_www_directory }} state=directory owner=root group=www-data  
+    - name: create the /srv/www link
+      ansible.builtin.file: src={{ remote_www_directory }} dest=/srv/www state=link
+    - name: Ensure nginx is running
+      ansible.builtin.systemd:
+        name: nginx
+        state: started
+        enabled: yes
+    - name: create a test virtual hosts
+      ansible.builtin.template: 
+        src: ../templates.fix/nginx/test.site
+        dest: /etc/nginx/sites-available/{{hostname}}
+    - name: activate by link in sites-enabled
+      ansible.builtin.file:
+        src: /etc/nginx/sites-available/{{hostname}}
+        dest: /etc/nginx/sites-enabled/{{hostname}}
+        state: link
+    - name: create a ssh-certificate
+      ansible.builtin.command: openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/{{hostname}}.key -out /etc/ssl/certs/{{hostname}}.pem -subj "/C={{ssl_country}}/ST={{ssl_state}}/L={{ssl_locality}}/O={{ssl_organization}}/CN={{hostname}}"
+      args:
+        creates: /etc/ssl/private/{{hostname}}.key
+    - name: create a document root
+      ansible.builtin.file: dest=/srv/www/{{hostname}} state=directory owner=www-data group=www-data
+    - name: create a test index.html
+      template: src=../templates.fix/nginx/index.html dest=/srv/www/{{hostname}}/index.html
+    - name: create a test index.php
+      ansible.builtin.copy: src=../templates.fix/nginx/index.php dest=/srv/www/{{hostname}}/index.php
+
diff --git a/playbooks.templates/i_21_nginx_sites.yaml b/playbooks.templates/i_21_nginx_sites.yaml
new file mode 100644 (file)
index 0000000..de6704d
--- /dev/null
@@ -0,0 +1,25 @@
+---
+# This playbook maintains the NGINX configuration files in /etc/nginx//sites_available
+- name: Installs and configures the sites served by 
+  hosts: all
+  vars_files:
+    - ../vars/common.yaml
+    - ../vars/webapps.yaml
+  tasks:
+    - name: Set the NGINX configuration files
+      copy:
+        src: "../templates.local/nginx/sites/{{ item.webapp_name }}"
+        dest: "/etc/nginx/sites-available/"
+        owner: root
+        group: root
+        mode: 0644
+      with_list: "{{ webapps_list }}"
+      notify:
+        - reload nginx
+  handlers:
+    - name: reload nginx
+      service:
+        name: nginx
+        state: reloaded
+        enabled: yes
\ No newline at end of file
diff --git a/playbooks.templates/i_30_mariadb.yaml b/playbooks.templates/i_30_mariadb.yaml
new file mode 100644 (file)
index 0000000..6bc7362
--- /dev/null
@@ -0,0 +1,16 @@
+---
+- name: Playbook to install and configure mariadb
+  hosts: all
+  tasks:
+    - name: Install mariadb
+      ansible.builtin.apt:
+        name:
+          - "mariadb-server"
+          - "python3-mysqldb"
+        state: latest
+        update_cache: yes
+    - name: Ensure mariadb is running
+      ansible.builtin.systemd:
+        name: mysqld
+        state: started
+        enabled: yes
\ No newline at end of file
diff --git a/playbooks.templates/i_40_php8.2.yaml b/playbooks.templates/i_40_php8.2.yaml
new file mode 100644 (file)
index 0000000..5ae3231
--- /dev/null
@@ -0,0 +1,54 @@
+---
+- name: Playbook to install and configure PHP (all versions from 7.4)
+  # optional facts:
+  # php_version: the wanted version. Default: "8.2"
+  hosts: all
+  vars:
+    - php_version: "8.2"
+  vars_files:
+    - ../vars/php.yaml
+  pre_tasks:
+    - name:
+      ansible.builtin.apt:
+        name: gpg
+        state: present
+        update_cache: true
+    - name: add packages.sury.org (Debian case)
+      block:
+        - name: add gpg repo key
+          ansible.builtin.apt_key:
+            url:  'https://packages.sury.org/php/apt.gpg'
+            state: present
+
+        - name: add apt repo
+          ansible.builtin.apt_repository:
+            repo:  'deb https://packages.sury.org/php/ {{ ansible_distribution_release|lower }} main'
+            state: present
+            filename: php
+      when: ansible_distribution == 'Debian'
+  tasks:
+    - name: Install PHP {{php_version}} and common modules
+      ansible.builtin.apt:
+        name: "{{ item }}"
+        state: present
+        update_cache: true
+        cache_valid_time: 3600
+      with_items: "{{ php_packages + php_additional_packages }}"
+    - name: Define PHP variables in php.ini
+      ansible.builtin.ini_file:
+        dest: /etc/php/{{php_version}}/fpm/php.ini 
+        section: "{{ item.section }}"
+        option: "{{ item.option }}"
+        value: "{{ item.value }}"
+      with_items:
+        "{{ php_ini_settings }}"
+    - name: Ensure PHP-FPM is running
+      ansible.builtin.systemd:
+        name: php{{php_version}}-fpm
+        state: started
+        enabled: yes
+    - name: Ensure Redis is running
+      ansible.builtin.systemd:
+        name: redis-server
+        state: started
+        enabled: yes
\ No newline at end of file
diff --git a/playbooks.templates/i_50_git_server.yaml b/playbooks.templates/i_50_git_server.yaml
new file mode 100644 (file)
index 0000000..7cf1393
--- /dev/null
@@ -0,0 +1,39 @@
+---
+
+- name: Playbook to install and configure a git server
+  hosts: all
+  vars:
+    git_user: git
+  tasks:
+    - name: install packages
+      apt:
+        name: git
+        state: present
+    - name: create git user
+      user:
+        name: "{{ git_user }}"
+        state: present
+        shell: /usr/bin/git-shell
+        home: /home/git
+    - name: Prepare SSH directory for git
+      file: 
+        path: /home/git/.ssh 
+        state: directory 
+        group: "{{git_user}}"
+        owner: "{{git_user}}"
+        mode: "0700"
+    - name: Prepare authorized_keys
+      file: 
+        path: /home/git/.ssh/authorized_keys
+        state: touch
+        group: "{{git_user}}"
+        owner: "{{git_user}}"
+        mode: "0600"
+    - name: Prepare repository directory
+      file: 
+        path: /home/git/repo 
+        state: directory
+        group: "{{git_user}}"
+        owner: "{{git_user}}"
+
+        
\ No newline at end of file
diff --git a/playbooks.templates/i_60_postfix.yaml b/playbooks.templates/i_60_postfix.yaml
new file mode 100644 (file)
index 0000000..b782279
--- /dev/null
@@ -0,0 +1,89 @@
+---
+- name: Installs the postfix mail server
+  # Needed facts (variables):
+  hosts: all
+  vars:
+    postfix_domain: "{{ inventory_hostname }}"
+  vars_files:
+    - ../vars/common.yaml
+  tasks:
+    - name: install the packages
+      ansible.builtin.package:
+        name: "{{ item }}"
+        state: present
+      with_items:
+        - postfix
+        - mailutils
+    - name: build the main.cf
+      ansible.builtin.copy:
+        src: "../templates.local/postfix/send_only/{{ item }}"
+        dest: "/etc/postfix/{{ item }}"
+        mode: 0640
+        owner: root
+        group: root
+      with_items:
+        - main.cf
+        - master.cf
+      notify:
+        - restart postfix
+    - name: create alias
+      template:
+        src: "../templates.local/postfix/aliases"
+        dest: "/etc/aliases"
+        mode: 0640
+        owner: root
+        group: root
+      notify:
+        - restart postfix
+    - name: create virtual alias
+      template:
+        src: "../templates.local/postfix/virtual"
+        dest: "/etc/postfix/virtual"
+        mode: 0640
+        owner: root
+        group: root
+      when: postfix_mode == "email_forwarding"
+      notify:
+        - restart postfix
+    - name: create the mailname file
+      ansible.builtin.copy:
+        content: |
+          {{ postfix_host }}
+        dest: "/etc/mailname"
+        mode: 0644
+        owner: root
+        group: root
+      notify:
+        - restart postfix
+    - name: create a script to activate the virtual alias
+      ansible.builtin.copy:
+        content: |
+          #! /bin/bash
+          postmap virtual
+        dest: "/etc/postfix/Activate"
+        mode: 0700
+        owner: root
+        group: root
+      when: postfix_mode == "email_forwarding"
+      notify:
+        - postalias
+    - name: Ensure postfix is running
+      ansible.builtin.systemd:
+        name: postfix
+        state: started
+        enabled: yes
+  handlers: 
+    - name: restart postfix
+      ansible.builtin.service:
+        name: postfix
+        state: reloaded
+    - name: postalias
+      shell: postalias /etc/aliases
+    - name: postmap
+      shell: postmap /etc/postfix/virtual
+
+     
+
+
+
+
diff --git a/playbooks.templates/i_62_postfix_dkim.yaml b/playbooks.templates/i_62_postfix_dkim.yaml
new file mode 100644 (file)
index 0000000..169064f
--- /dev/null
@@ -0,0 +1,38 @@
+---
+# Installs the the spam reducing staff for postfix: SPF DKIM DMARYC
+# Needed facts (variables):
+# -
+# Needed role installation:
+- hosts: all
+  vars_files:
+    - ../vars/common.yaml
+    - ../vars/antispam.yaml
+  tasks:  
+    - name: install the packages
+      ansible.builtin.package:
+        name: "{{ item }}"
+        state: present
+      with_items: [ postfix-policyd-spf-python, opendkim, opendkim-tools, opendmarc]
+    # ====== SPF ======
+    - name: Configure the SPF staff
+      ansible.builtin.include_tasks: ../tasks/t_spf_configuration.yaml
+    # ====== DKIM ======
+    - name: Configure the DKIM staff
+      ansible.builtin.include_tasks: ../tasks/t_dkim.yaml
+    # ====== DMARC ======
+    - name: Configure the DMARC staff
+      ansible.builtin.include_tasks: ../tasks/t_dmarc.yaml
+  handlers:
+    - name: restart opendkim
+      ansible.builtin.systemd:
+        name: opendkim
+        state: restarted
+    - name: restart opendmarc
+      ansible.builtin.systemd:
+        name: opendmarc
+        state: restarted
+    - name: restart postfix
+      ansible.builtin.systemd:
+        name: postfix
+        state: restarted      
+
diff --git a/playbooks.templates/i_70_webapps.yaml b/playbooks.templates/i_70_webapps.yaml
new file mode 100644 (file)
index 0000000..1244cb3
--- /dev/null
@@ -0,0 +1,22 @@
+---
+- name: Install and configure web applications
+  hosts: all
+  vars_files:
+    - ../vars/common.yaml
+    - ../vars/ssl-certificate.yaml
+    - ../vars/webapps_vault.yaml
+  tasks:
+    - name: Prepare state.infeos.net
+      ansible.builtin.set_fact:
+        webapp_name: "state.infeos.net"
+        domain: "{{ webapp_name }}"
+        shortname: state_infeos
+        mysql_vault.yaml: swstateinfeos
+        db_type: mysql
+        db_host: localhost
+        db_user: swstateinfeos
+        db_password: "{{ vault_db_password }}"
+        php_version: "8.3"
+    - debug:
+    - name: create web application for {{ webapp_name }}
+      include_tasks: ../tasks/t_webapp_create.yaml
diff --git a/playbooks.templates/i_80_firewalld.yaml b/playbooks.templates/i_80_firewalld.yaml
new file mode 100644 (file)
index 0000000..0805023
--- /dev/null
@@ -0,0 +1,40 @@
+---
+- name: Installs the firewall firewalld
+# Precondition: ansible-galaxy collection install ansible.posix
+  hosts: all
+  become: true
+  vars_files:
+    - ../vars/common.yaml
+    - ../vars/firewalld.yaml
+  tasks:
+    - name: ensure firewalld is installed
+      ansible.builtin.apt:
+        name: firewalld
+        state: present
+
+    - name: allow ssh
+      ansible.posix.firewalld:
+        service: ssh
+        zone: public
+        permanent: true
+        state: enabled
+        immediate: yes
+    - name: ensure firewalld is running and enabled
+      ansible.builtin.service:
+        name: firewalld
+        state: started
+        enabled: true
+
+    - name: set interface to public zone
+      ansible.posix.firewalld:
+        interface: "{{ firewall_iface_public }}"
+        zone: public
+        permanent: true
+        state: enabled
+        immediate: yes
+
+    - name: define host specific firewall rules
+      ansible.builtin.include_tasks: "../tasks/t_firewalld_local.yaml"
+  handlers:
+    - name: firewall reload
+      ansible.builtin.command: firewall-cmd --reload
\ No newline at end of file
diff --git a/playbooks.templates/i_81_fail2ban.yaml b/playbooks.templates/i_81_fail2ban.yaml
new file mode 100644 (file)
index 0000000..d30863e
--- /dev/null
@@ -0,0 +1,45 @@
+---
+- name: Installs the fail2ban package to protect the server from brute-force attacks
+  hosts: all
+  become: true
+  vars_files:
+    - ../vars/common.yaml
+  tasks:
+    - name: ensure fail2ban is installed
+      ansible.builtin.apt:
+        name: fail2ban
+        state: present
+    - name: Ensure fail2ban is running
+      ansible.builtin.systemd:
+        name: fail2ban
+        state: started
+        enabled: yes
+    - set_fact:
+        email_sender: "{{ webmaster_email }}"
+        ban_time_sec: 3600
+
+    - name: configure jail.conf
+      ansible.builtin.lineinfile:
+        dest: /etc/fail2ban/jail.conf
+        regexp: "{{ item.rexpr }}"
+        line: "{{ item.line }}"
+      loop:
+        - { rexpr: '^sender\s*=', line: 'sender = {{ email_sender }}' }
+        - { rexpr: '^bantime\s*=', line: 'bantime = {{ ban_time_sec }}' }
+        - { rexpr: '^banaction = iptables-multiport$', line: 'banaction = firewallcmd-ipset' }
+      notify:
+        - restart fail2ban
+    - name: configure /etc/fail2ban/jail.local
+      ansible.builtin.copy:
+        content: |
+          [DEFAULT]
+          banaction = firewallcmd-ipset
+        dest: /etc/fail2ban/jail.local
+      notify:
+        - restart fail2ban
+  handlers:
+    - name: restart fail2ban
+      ansible.builtin.systemd:
+        name: fail2ban
+        state: restarted
+
diff --git a/playbooks.templates/i_99_test.yaml b/playbooks.templates/i_99_test.yaml
new file mode 100644 (file)
index 0000000..cd7de04
--- /dev/null
@@ -0,0 +1,30 @@
+---
+- name: Install and configure with letsencrypt
+  hosts: localhost
+  become: yes
+
+  vars:
+    user: www-data
+    hostname: "{{ inventory_hostname }}"
+    log_name: "{{ inventory_hostname | regex_search('[0-9a-zA-Z_]+') }}"
+    webapp_name: myapp5.example.com
+  vars_files:
+    - ../vars/webapps.yaml
+  tasks:
+    - name: Test regexpr
+      lineinfile:
+        dest: /tmp/wk.conf
+        regexp: '^{{ item }}\s*=\s*Fail'
+        line: "{{ item }} = False"
+      with_items: [x, y]
+    - name: Test Dateiänderung
+      ansible.builtin.copy:
+        content: |
+          Line 1
+          Line 2
+          Line 3 added
+        dest: /tmp/wk.yaml
+    - set_fact:
+        target_file: "/home/tmp/xxx.yyy"
+    - debug: 
+        msg: "{{ '/tmp/' + (target_file | basename) }}"
\ No newline at end of file
diff --git a/playbooks.templates/lets_create.yaml b/playbooks.templates/lets_create.yaml
new file mode 100644 (file)
index 0000000..e2e8e7f
--- /dev/null
@@ -0,0 +1,16 @@
+---
+# Creates a letsencrypt certificate for a domain 
+# needed facts (variables) from commandline: (e.g. -e domain=example.com
+# domain: the site domain name
+- hosts: all
+  vars_files:
+    - ../vars/common.yaml
+    - ../vars/ssl-certificate.yaml
+  tasks:
+    - name: Check pre-requisites
+      fail: 
+        msg: "The variable 'domain' must be defined: use -e domain=mydomain.com"
+      when: domain is not defined or domain == ""
+    - name: create certificate for {{ domain }}
+      ansible.builtin.include_tasks: ../tasks/t_lets_create.yaml
+
diff --git a/playbooks.templates/mysql_create_admin.yaml b/playbooks.templates/mysql_create_admin.yaml
new file mode 100644 (file)
index 0000000..11d51c9
--- /dev/null
@@ -0,0 +1,9 @@
+---
+- name: Creates the MySQL administrator with all privileges for all databases
+  hosts: all
+  vars_files:
+    - ../vars/mysql_vault.yaml
+    - ../vars/mysql.yaml
+  tasks:
+    - name: Create the database adminstrator {{dba_name}}
+      import_tasks : ../tasks/t_mysql_create_admin.yaml
diff --git a/playbooks.templates/mysql_create_db_and_user.yaml b/playbooks.templates/mysql_create_db_and_user.yaml
new file mode 100644 (file)
index 0000000..035db8d
--- /dev/null
@@ -0,0 +1,14 @@
+---
+- name: Create MySQL database and user for a web application
+# needed facts (variables) from commandline (e.g. -e db_name=webapp):
+# db_name: name of the database
+# db_host: the ip or name of the host hosting mysql. Normally: localhost
+# db_user: name of the database user
+# db_password: password of the database user
+  hosts: all
+  vars_files:
+    - ../vars/mysql_vault.yaml
+    - ../vars/mysql.yaml
+  tasks:
+    - name: Create the database adminstrator {{dba_name}}
+      import_tasks : ../tasks/t_mysql_db_and_user.yaml
diff --git a/playbooks.templates/nginx_create_site.yaml b/playbooks.templates/nginx_create_site.yaml
new file mode 100644 (file)
index 0000000..3eaa2c5
--- /dev/null
@@ -0,0 +1,22 @@
+---
+- name: Creates the NGINX configuration file for a PHP site
+  # needed facts (variables) from commandline (e.g. -e domain=example.com -e force=true):
+  # domain: the site domain name
+  # optional facts:
+  # document_root: the document root of the site without path, e.g. example.com. Default: domain
+  # shortname: the short name of the site. Used for log file names
+  # php_version: the PHP version to use. Default: 8.3
+  # force: if true, the site will be created even if it already exists. Default: false
+  hosts: all
+  vars_files:
+    - ../vars/common.yaml
+    - ../vars/ssl-certificate.yaml
+  tasks:
+    - name: Check pre-requisites
+      fail: msg="The variable 'domain' must be defined and not empty."
+      when: domain is not defined or domain == ""
+    - name: create certificate for {{ domain }}
+      import_tasks: ../tasks/t_ssl_create_certificate.yaml
+    - name: Create the NGINX configuration for  {{domain}}
+      import_tasks: ../tasks/t_nginx_create_site.yaml
+
diff --git a/playbooks.templates/ssl_create_certificate.yaml b/playbooks.templates/ssl_create_certificate.yaml
new file mode 100644 (file)
index 0000000..4571676
--- /dev/null
@@ -0,0 +1,14 @@
+---
+- name: Creates a X509 certificate for a domain, needed for a letsencrypt certificate
+  # needed facts (variables) from the commandline: (e.g. ansible-playbook -e "domain=example.com")
+  # - domain: the domain name for the certificate
+  hosts: all
+  vars_files:
+    - ../vars/common.yaml
+    - ../vars/ssl-certificate.yaml
+  tasks:
+  - name: test pre-requisites
+    fail: msg="missing fact! domain. Use -e domain=..."
+    when: domain is not defined
+  - name: Creates a X509 certificate for {{domain}}
+    import_tasks : ../tasks/t_ssl_create_certificate.yaml
diff --git a/playbooks.templates/webapp_backup.yaml b/playbooks.templates/webapp_backup.yaml
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/playbooks.templates/webapp_create.yaml b/playbooks.templates/webapp_create.yaml
new file mode 100644 (file)
index 0000000..cd8253e
--- /dev/null
@@ -0,0 +1,20 @@
+---
+- name: Creates a database, a database user of a web application.
+  # Stores the data in configuration files (remote and local)
+  # needed facts (variables) from the command line: (e.g. -e webapp_name=webapp):
+  # - webapp_name: name of the web application
+  # - db_name: name of the database
+  # - db_host: the ip or name of the host hosting mysql. Normally: localhost
+  # - db_user: name of the databasded user
+  # - db_password: password of the database user
+  hosts: all
+  vars_files:
+    - ../vars/common.yaml
+    - ../vars/mysql_vault.yaml
+    - ../vars/mysql.yaml
+  tasks:
+    - name: test pre-requisites
+      fail: msg="missing facts! webapp_name, db_name, db_user, db_password. Use -e webapp_name=..."
+      when: webapp_name is not defined or db_name is not defined or db_user is not defined or db_password is not defined
+    - name: create the web application
+      import_tasks : ../tasks/t_webapp_create.yaml
diff --git a/playbooks.templates/webapp_export.yaml b/playbooks.templates/webapp_export.yaml
new file mode 100644 (file)
index 0000000..ebc5acc
--- /dev/null
@@ -0,0 +1,19 @@
+---
+- name: Dumps a database of a web application.
+  # needed facts (variables) from the command line:
+  # - webapp_name: name of the web application
+  # - target_file: name of the file where the database will be exported to. May be *.sql or *.sql.gz
+  hosts: all
+  vars:
+    fetch: false
+  vars_files:
+    - ../vars/common.yaml
+    - ../vars/mysql_vault.yaml
+    - ../vars/mysql.yaml
+    - ../vars/webapps.yaml
+  tasks:
+    - name: test pre-requisites
+      fail: msg="missing facts! webapp_name, target_file. Use -e webapp_name=..."
+      when: webapp_name is not defined or target_file is not defined
+    - name: create the web application
+      import_tasks : ../tasks/t_webapp_export.yaml
diff --git a/playbooks.templates/webapp_import.yaml b/playbooks.templates/webapp_import.yaml
new file mode 100644 (file)
index 0000000..0c7a424
--- /dev/null
@@ -0,0 +1,22 @@
+---
+- name: Loads a database dump into the database of a web application.
+  # needed facts (variables) from the command line: (e.g. -e webapp_name=webapp)
+  # - webapp_name: name of the web application
+  # - target_file: name of the file where the database will be exported to. May be *.sql or *.sql.gz
+  # - webapps_list: the list defined in ../vars/webapps.yaml
+  hosts: all
+  vars:
+    backup: true
+    backup_file: "/tmp/{{ webapp_name }}.{{ now(fmt='%s') }}.sql.gz"
+  vars_files:
+    - ../vars/common.yaml
+    - ../vars/mysql_vault.yaml
+    - ../vars/mysql.yaml
+    - ../vars/webapps.yaml
+  tasks:
+    - name: test pre-requisites
+      fail: msg="missing facts! webapp_name, source_file. Use -e webapp_name=..."
+      when: webapp_name is not defined or source_file is not defined
+
+    - name: create the web application
+      import_tasks : ../tasks/t_webapp_import.yaml
diff --git a/playbooks/create_project.yaml b/playbooks/create_project.yaml
new file mode 100644 (file)
index 0000000..4f0fa98
--- /dev/null
@@ -0,0 +1,136 @@
+---
+# Create a new project directory structure for an Ansible project.
+# needed facts (variables) from the commandline:
+# -e project=your_project_name
+# -e postfix_mode=none|send_only|email_forwarding
+# -e base_ansible=$(dirname $(realpath $(pwd)))
+- hosts: localhost
+  become: true
+  tasks:
+    - name: check needed command line (variables
+      ansible.builtin.fail: 
+        msg: "missing project or postfix_mode or base_ansible.
+          Example: -e project=your_project_name
+          Example: -e postfix_mode=none|send_only|email_forwarding
+          Example: -e base_ansible=$(dirname $(realpath $(pwd)))
+          Recommanded: -e base_ansible=$(dirname $(realpath $(pwd)))"
+      when: project is not defined or postfix_mode is not defined or base_ansible is not defined
+    - name: Check if project variable is defined
+      ansible.builtin.fail: 
+        msg: "The 'project' variable is not defined. -e project=your_project_name"
+      when: project is not defined
+    - name: Prepare base directory for {{ project }} in {{ base_ansible }}
+      ansible.builtin.file: path={{ base_ansible }}/{{ project }} state=directory
+    - name: create sub directories for {{ project }}
+      ansible.builtin.file: path={{ base_ansible }}/{{ project }}/{{ item }} state=directory
+      with_items: [playbooks, docu, resources, roles, scripts, templates.local, tasks, vars]
+    - name: create the links into the resources directory
+      ansible.builtin.file:
+        src: "../../ansknife/resources/{{ item }}"
+        dest: "{{ base_ansible }}/{{ project }}/resources/{{ item }}"
+        force: yes
+        state: link
+      with_items: [needed.tgz, rsh.tgz]
+    - name: build link to the templates.fix directory
+      ansible.builtin.file:
+        src: "../ansknife/templates.fix"
+        dest: "{{ base_ansible }}/{{ project }}/templates.fix"
+        force: yes
+        state: link
+      delegate_to: localhost
+    - name: create the templates base directory
+      ansible.builtin.file:
+        path: "{{ base_ansible }}/{{ project }}/templates.local"
+        state: directory
+    - name: copy the filetree
+      ansible.builtin.shell: "cp -a {{ base_ansible }}/ansknife/templates.local/* {{ base_ansible }}/{{ project }}/templates.local/"
+      delegate_to: localhost
+    - name: remove superflous sub file tree antispam
+      ansible.builtin.file:
+        path: "{{ base_ansible }}/{{ project }}/templates.local/antispam"
+        state: absent
+      when: postfix_mode != 'email_forwarding'
+      delegate_to: localhost
+    - name: remove superflous sub file postfix/email_forwarding
+      ansible.builtin.file:
+        path: "{{ base_ansible }}/{{ project }}/templates.local/postfix/email_forwarding"
+        state: absent
+      when: postfix_mode != 'email_forwarding'
+      delegate_to: localhost
+    - name: prepare facts for templates
+      ansible.builtin.copy:
+        src: '../templates.vars/'
+        dest: "{{ base_ansible }}/{{ project }}/vars"
+      delegate_to: localhost
+    - name: Prepare facts for links in playbooks
+      ansible.builtin.set_fact:
+        src_dir: '../playbooks.templates'
+        src_relative: '../../ansknife/playbooks.templates'
+        dest_dir: "{{ base_ansible }}/{{ project }}/playbooks"
+        pattern: "*.yaml"
+      delegate_to: localhost
+    - name: Create links in playbooks
+      import_tasks: ../tasks.templates/t_link_wildcard.yaml
+      delegate_to: localhost
+    - name: Prepare facts for links in tasks
+      ansible.builtin.set_fact:
+        src_dir: '../tasks.templates'
+        src_relative: '../../ansknife/tasks.templates'
+        dest_dir: "{{ base_ansible }}/{{ project }}/tasks"
+        pattern: "*.yaml"
+      delegate_to: localhost
+    - name: Create links in tasks
+      ansible.builtin.import_tasks: ../tasks.templates/t_link_wildcard.yaml
+    - name: Prepare facts for links in scripts
+      set_fact:
+        src_dir: "../scripts.templates"
+        src_relative: '../../ansknife/scripts.templates'
+        dest_dir: "{{ base_ansible }}/{{ project }}/scripts"
+        pattern: "*"
+      delegate_to: localhost
+    - name: Create links in scripts
+      ansible.builtin.import_tasks: ../tasks.templates/t_link_wildcard.yaml
+    - name: create a README file
+      copy:
+        dest: "{{ base_ansible }}/{{ project }}/README.md"
+        content: |
+          Project {{ project }}
+          This is the README file for the {{ project }} project.
+          # Description
+          This project defines the software of the server
+      delegate_to: localhost
+    - name: create a CHANGELOG file
+      ansible.builtin.copy:
+        dest: "{{ base_ansible }}/{{ project }}/CHANGELOG.md"
+        content: |
+          # V0.1.0
+          - Initial version
+      delegate_to: localhost
+    - name: create a the inventory (host definition)
+      ansible.builtin.copy:
+        dest: "{{ base_ansible }}/{{ project }}/inventory"
+        content: |
+          [hosts]
+          {{ project }}.example.com
+          [hosts:vars]
+          ansible_python_interpreter=/usr/bin/python3
+          ansible_ssh_common_args=-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
+          ansible_user=ansadm
+          ansible_become=yes
+      delegate_to: localhost
+    - name: create a the ansible configuration file
+      ansible.builtin.copy:
+        dest: "{{ base_ansible }}/{{ project }}/ansible.cfg"
+        content: |
+          [defaults]
+          inventory = ./inventory
+      delegate_to: localhost
+    - name: create project specific files
+      ansible.builtin.template:
+        src: "../templates.install/{{ item.src }}"
+        dest: "{{ base_ansible }}/{{ project }}/{{ item.path }}/{{ item.dest }}"
+      with_items:
+        - { src: project_ansible.md, dest: "{{ project }}_ansible", path: docu }
+        - { src: project_cron.yaml, dest: "{{ project }}_cron", path: playbooks }
+        - { src: t_firewalld_local.yaml, dest: "t_firewalld_local.yaml", path: tasks }
+      delegate_to: localhost
diff --git a/playbooks/labor.yaml b/playbooks/labor.yaml
new file mode 100644 (file)
index 0000000..5c3dbcd
--- /dev/null
@@ -0,0 +1,14 @@
+---
+# Create a new project directory structure for an Ansible project.
+# needed facts (variables) from the commandline:
+- hosts: localhost
+  become: true
+  tasks:
+    - name: prepare base target directory
+      ansible.builtin.file: 
+        path: /tmp/ansknife
+        state: directory
+    - name: copy a file tree
+      ansible.builtin.copy:
+        src: ../templates.local/
+        dest: /tmp/ansknife/
\ No newline at end of file
diff --git a/resources/needed.tgz b/resources/needed.tgz
new file mode 100644 (file)
index 0000000..a389986
Binary files /dev/null and b/resources/needed.tgz differ
diff --git a/resources/rsh.tgz b/resources/rsh.tgz
new file mode 100644 (file)
index 0000000..3ff6405
Binary files /dev/null and b/resources/rsh.tgz differ
diff --git a/scripts.templates/AddPassword b/scripts.templates/AddPassword
new file mode 100755 (executable)
index 0000000..c5ad692
--- /dev/null
@@ -0,0 +1,23 @@
+#! /bin/bash
+# Ansible controlled: do not change on remote server
+
+NAME=$1
+VALUE=$2
+PW_SAFE=resources/vaults.yaml
+function usage() {
+    echo "Usage: $0 NAME VALUE"
+    echo "Add a password to the keyring"
+    echo "  NAME: The name of the password"
+    echo "  VALUE: The value of the password"
+    echo "Example: $0 dba_password mysecret"
+    echo "+++ $*"
+}
+if [ -z "$VALUE" ]; then
+    Usage "missing VALUE"
+else
+  if [ ! -f "$PW_SAFE" ]; then
+    echo "= creating a password safe: $PW_SAFE"
+    ansible-vault create $PW_SAFE
+  fi
+  ansible-vault encrypt_string >$PW_SAFE "$VALUE" --name "$NAME" 
+fi
diff --git a/scripts.templates/CreatePlaybook b/scripts.templates/CreatePlaybook
new file mode 100755 (executable)
index 0000000..e1d92eb
--- /dev/null
@@ -0,0 +1,17 @@
+#! /bin/bash
+NAME=$1
+DIR_KNIFE=../ansknife/playbooks.templates
+if [ -z "$NAME" ]; then
+  echo "Usage: CreatePlaybook NAME"
+  echo "Example: CreateTask db_create"
+  echo "+++ missing NAME"
+else
+  touch $DIR_KNIFE/$NAME.yaml
+  cd playbooks
+  ln -s ../$DIR_KNIFE/$NAME.yaml .
+  cd ..
+  ls -ld $DIR_KNIFE/$NAME.yaml
+  sudo ./SetRights
+  cd ../ansknife/
+  sudo ./SetRights
+fi
diff --git a/scripts.templates/CreateTask b/scripts.templates/CreateTask
new file mode 100755 (executable)
index 0000000..643fe8b
--- /dev/null
@@ -0,0 +1,17 @@
+#! /bin/bash
+NAME=$1
+DIR_KNIFE=../ansknife/tasks.templates
+if [ -z "$NAME" ]; then
+  echo "Usage: CreateTask NAME"
+  echo "Example: CreateTask db_create"
+  echo "+++ missing NAME"
+else
+  touch $DIR_KNIFE/$NAME.yaml
+  cd tasks
+  ln -s ../$DIR_KNIFE/$NAME.yaml .
+  cd ..
+  ls -ld $DIR_KNIFE/$NAME.yaml
+  sudo ./SetRights
+  cd ../ansknife/
+  sudo ./SetRights
+fi
diff --git a/scripts.templates/SetRights b/scripts.templates/SetRights
new file mode 100755 (executable)
index 0000000..a0788ed
--- /dev/null
@@ -0,0 +1,9 @@
+#! /bin/bash
+chown -R wk *
+chmod -R g+rw *
+if [ -d playbooks.templates ]; then
+  echo "protecting templates.local from changing"
+  chmod -R uog-w templates.local
+fi
+
+
diff --git a/scripts/build_dkim_text.py b/scripts/build_dkim_text.py
new file mode 100755 (executable)
index 0000000..33e271a
--- /dev/null
@@ -0,0 +1,17 @@
+#! /usr/bin/python3
+import sys
+def main(args):
+    filename = args[0]
+    with open(filename, "r") as fp:
+        lines = fp.read().split('\n')
+        # 20250419._domainkey  IN      TXT     ( "v=DKIM1; h=sha256; k=rsa; "
+           #   "p=MIIBIjANBgkqh...QoSuyvjwoNEUNO"
+           #   "dfsOuTKBzmchQ4Z...c2jwIDAQAB" )  ; ----- DKIM key 20250419 for f-r-e-i.de
+        info = lines[0].split('"')[1]
+        info += lines[1].split('"')[1]
+        info += lines[2].split('"')[1]
+        print(info)
+
+if __name__ == '__main__':
+    #main(["/tmp/example.txt"])
+    main(sys.argv[1:])
diff --git a/tasks.templates/t_copy_wildcard.yaml b/tasks.templates/t_copy_wildcard.yaml
new file mode 100644 (file)
index 0000000..846ccbe
--- /dev/null
@@ -0,0 +1,13 @@
+---
+# Copys files specified by a wildcard pattern from the source directory to the destination directory.
+# needed facts (variables):
+#   - src_dir: source directory
+#   - dest_dir: destination directory
+#   - pattern: wildcard pattern
+- name: Copy files matching {{ pattern }} from {{ src_dir }} to {{dest_dir}}
+  ansible.builtin.copy:
+    src: "{{ item }}"
+    dest: "{{ dest_dir }}"
+  with_fileglob:
+      - "{{ src_dir }}/{{ pattern }}"
+
diff --git a/tasks.templates/t_dkim.yaml b/tasks.templates/t_dkim.yaml
new file mode 100644 (file)
index 0000000..0c9aade
--- /dev/null
@@ -0,0 +1,74 @@
+---
+# Configures DKIM
+# needed facts (variables):
+# dkim_domains: a list of domains handled by DKIM
+# dkim_opendkim_config_dir: normally /etc/opendkim
+# dkim_config_ansible.builtin.file: normally /etc/opendkim.conf
+# dkim_selector: normally the date yyyymmdd
+# dkim_user: the user running opendkim
+# dkim_group: the user group running opendkim
+# dkim_socket_port: port of the socket of opendkim
+
+
+- name: add user to group postfix
+  ansible.builtin.user:
+    name: '{{ dkim_user }}'
+    groups: postfix
+    append: yes
+- name: ensure existence of /var/spool/postfix/opendkim
+  ansible.builtin.file:
+    path: /var/spool/postfix/opendkim
+    state: directory
+    recurse: true
+    owner: "{{ dkim_user }}"
+    group: "{{ dkim_group }}"
+
+- name: opendkim directory present
+  ansible.builtin.file:
+    path: '{{ dkim_opendkim_config_dir }}/keys'
+    owner: "{{ dkim_user }}"
+    state: directory
+    group: "{{ dkim_group }}"
+    mode: 0750
+
+- name: opendkim trusted.hosts present
+  ansible.builtin.template:
+    src: ../templates.local/antispam/trusted.hosts
+    dest: '{{ dkim_opendkim_config_dir }}/trusted.hosts'
+  notify:
+   - restart opendkim
+
+- name: configure opendkim in {{ dkim_config_file }}
+  ansible.builtin.copy:
+    src: ../templates.local/antispam/opendkim.conf
+    dest: "{{ dkim_config_file }}"
+  notify:
+   - restart opendkim
+
+- name: installing tables
+  ansible.builtin.template:
+    src: "../templates.fix/antispam/{{ item }}"
+    dest: '{{ dkim_opendkim_config_dir }}/{{item}}'
+  notify:
+   - restart opendkim
+  with_items: [key.table, signing.table]
+
+- name: generate opendkim keys
+  ansible.builtin.include_tasks: "../tasks/t_dkim_keys.yaml"
+  loop: "{{ dkim_domains }}"
+  loop_control:
+    loop_var: dkim_domain
+
+- name: ensure opendkim is running
+  service:
+    name: opendkim
+    state: started
+    enabled: yes
+- name: display DNS staff and tests
+  ansible.builtin.include_tasks: "../tasks/t_dkim_dns.yaml"
+  loop: "{{ dkim_domains }}"
+  loop_control:
+    loop_var: dkim_domain
+
+
+...
diff --git a/tasks.templates/t_dkim_dns.yaml b/tasks.templates/t_dkim_dns.yaml
new file mode 100644 (file)
index 0000000..8b7eb9f
--- /dev/null
@@ -0,0 +1,31 @@
+---
+# Show the DNS records to configure with dkim publik key
+# needed facts (variables):
+# dkim_domain: the domain to configure
+# dkim_opendkim_config_dir: normally /etc/opendkim
+# dkim_selector: normally the date yyyymmdd
+- name: store hostname and name of the public key file
+  ansible.builtin.set_fact:
+    hostname: "{{ inventory_hostname }}"
+    public_key: "{{ dkim_opendkim_config_dir }}/keys/{{ dkim_domain }}/{{ dkim_selector }}.txt"
+- name: fetch the remote key file to the (local) control host
+  fetch:
+    src: "{{ public_key }}" 
+    dest: "/tmp"
+- name: start the local script to find out the related info
+  script: "../scripts/build_dkim_text.py /tmp/{{ hostname }}{{ public_key }}"
+  register: dns_text
+  delegate_to: localhost
+- name: display DKIM public key DNS record
+  debug:
+    msg:
+      - "#============================================="
+      - "zone: {{ dkim_selector }}._domainkey.{{ dkim_domain }}"
+      - "type: TXT"
+      - "content: {{ dns_text.stdout | regex_replace('\n', '')}}"
+      - "#============================================="
+      - "Test:"
+      - "host -t txt {{ dkim_selector }}._domainkey.{{ dkim_domain }}._domainkey"
+      - "opendkim-testkey -d {{ dkim_domain }} -s {{ dkim_selector }} -vvv"
+      - "#============================================="
+...
diff --git a/tasks.templates/t_dkim_keys.yaml b/tasks.templates/t_dkim_keys.yaml
new file mode 100644 (file)
index 0000000..ba35e97
--- /dev/null
@@ -0,0 +1,34 @@
+---
+## DKIM: Generates a key for a given domain
+# dkim_domain: the domain to configure
+# dkim_opendkim_config_dir: normally /etc/opendkim
+# dkim_selector: normally the date yyyymmdd
+# dkim_user: the user running DKIM
+# dkim_group: the group running DKIM
+# dkim_rsa_keylen: the count of bits of the RSA key
+
+- name: creates domain's keys directory
+  file: 
+    path: "{{ dkim_opendkim_config_dir }}/keys/{{ dkim_domain }}"
+    state: directory
+    recurse: true
+
+- name: ensure signing key is present
+  stat: 
+    path: "{{ dkim_opendkim_config_dir }}/keys/{{ dkim_domain }}/{{ dkim_selector }}.private"
+    get_checksum: false
+  register: dkim_key
+
+- name: generate signing key
+  command: opendkim-genkey -b {{ dkim_rsa_keylen }} -s {{ dkim_selector }} -d {{ dkim_domain }} -D {{ dkim_opendkim_config_dir }}/keys/{{ dkim_domain }}
+  when: not dkim_key.stat.exists
+  notify:
+  - restart opendkim
+
+- name: ensure signing key owner
+  file:
+    path: "{{ dkim_opendkim_config_dir }}/keys/{{ dkim_domain }}/{{ dkim_selector }}.private"
+    owner: '{{ dkim_user }}'
+    group: '{{ dkim_group }}'
+
+...
diff --git a/tasks.templates/t_dmarc.yaml b/tasks.templates/t_dmarc.yaml
new file mode 100644 (file)
index 0000000..3f016eb
--- /dev/null
@@ -0,0 +1,54 @@
+---
+# Configures DMARC
+# needed facts (variables):
+# dkim_domains: a list of domains handled by DKIM
+# dmarc_config_dir: normally /etc/opendmarc
+# dmarc_config_file: normally /etc/opendmarc.conf
+# dmarc_user: the user running opendmarc
+# dmarc_group: the user group running opendmarc
+
+- name: add user to group postfix
+  ansible.builtin.user:
+    name: '{{ dmarc_user }}'
+    groups: postfix
+    append: yes
+- name: ensure existence of /var/spool/postfix/opendmarc
+  file:
+    path: /var/spool/postfix/opendmarc
+    state: directory
+    recurse: true
+    owner: "{{ dmarc_user }}"
+    group: "{{ dmarc_group }}"
+    mode: "0750"
+- name: configure opendmarc in {{ dmarc_config_file }}
+  ansible.builtin.copy:
+    src: ../templates.local/antispam/opendmarc.conf
+    dest: "{{ dmarc_config_file }}"
+  notify:
+   - restart opendmarc
+- name: "create configuration directory {{ dmarc_config_dir }}" 
+  ansible.builtin.file:
+    path: "{{ dmarc_config_dir }}"
+    state: directory
+    owner: "{{ dmarc_user }}"
+    group: "{{ dmarc_group }}"
+    mode: "0750"
+
+- name: create ignore.hosts
+  ansible.builtin.copy:
+    src: ../templates.local/antispam/ignore.hosts
+    dest: "{{ dmarc_config_dir }}/ignore.hosts"
+  notify:
+   - restart opendmarc
+- name: download suffix list
+  get_url: 
+    url: https://publicsuffix.org/list/public_suffix_list.dat
+    dest: "{{ dmarc_config_dir }}"
+    owner: "{{ dmarc_user }}"
+    group: "{{ dmarc_group }}"
+- name: display DNS staff and tests
+  ansible.builtin.include_tasks: "../tasks/t_dmarc_dns.yaml"
+  loop: "{{ dkim_domains }}"
+  loop_control:
+    loop_var: dkim_domain
+
diff --git a/tasks.templates/t_dmarc_dns.yaml b/tasks.templates/t_dmarc_dns.yaml
new file mode 100644 (file)
index 0000000..0bc19ec
--- /dev/null
@@ -0,0 +1,17 @@
+---
+# Show the DNS records to configure DMARC
+# needed facts (variables):
+# dkim_domain: the domain to configure
+# dmarc_email_report: receipient email addres for reports
+- name: display DMARC public key DNS record
+  debug:
+    msg:
+      - "#============================================="
+      - "zone: _dmarc.{{ dkim_domain }}"
+      - "type: TXT"
+      - "content: v=DMARC1; p=reje; sp=reje; adkim; aspf; pct=1; rua=mailto:{{ dmarc_email_report }}; ruf=mailto:{{ dmarc_email_report }}; fo=1;"
+      - "#============================================="
+      - "Test:"
+      - "host -t txt _dmarc.{{ dkim_domain }}"
+      - "#============================================="
+...
diff --git a/tasks.templates/t_firewalld_basic.yaml b/tasks.templates/t_firewalld_basic.yaml
new file mode 100644 (file)
index 0000000..50a0621
--- /dev/null
@@ -0,0 +1,21 @@
+---
+# Allow HTTP and HTTPS
+# needed facts (variables):
+#
+- name: allow http
+  ansible.posix.firewalld:
+    service: http
+    zone: public
+    permanent: true
+    state: enabled
+    immediate: yes
+  notify: firewall reload
+
+- name: allow https
+  ansible.posix.firewalld:
+    service: https
+    zone: public
+    permanent: true
+    state: enabled
+    immediate: yes
+  notify: firewall reload
diff --git a/tasks.templates/t_firewalld_email.yaml b/tasks.templates/t_firewalld_email.yaml
new file mode 100644 (file)
index 0000000..98fe999
--- /dev/null
@@ -0,0 +1,21 @@
+---
+# Allow SMTP and IMAP
+# needed facts (variables):
+#
+- name: Erlaube HTTP (Port 80) in der Zone public
+  ansible.posix.firewalld:
+    service: smtp
+    zone: public
+    permanent: true
+    state: enabled
+    immediate: yes
+  notify: firewall reload
+
+- name: allow imap
+  ansible.posix.firewalld:
+    service: imaps
+    zone: public
+    permanent: true
+    state: enabled
+    immediate: yes
+  notify: firewall reload
diff --git a/tasks.templates/t_firewalld_http.yaml b/tasks.templates/t_firewalld_http.yaml
new file mode 100644 (file)
index 0000000..50a0621
--- /dev/null
@@ -0,0 +1,21 @@
+---
+# Allow HTTP and HTTPS
+# needed facts (variables):
+#
+- name: allow http
+  ansible.posix.firewalld:
+    service: http
+    zone: public
+    permanent: true
+    state: enabled
+    immediate: yes
+  notify: firewall reload
+
+- name: allow https
+  ansible.posix.firewalld:
+    service: https
+    zone: public
+    permanent: true
+    state: enabled
+    immediate: yes
+  notify: firewall reload
diff --git a/tasks.templates/t_firewalld_nfs.yaml b/tasks.templates/t_firewalld_nfs.yaml
new file mode 100644 (file)
index 0000000..b7bdd7b
--- /dev/null
@@ -0,0 +1,19 @@
+# Allow NFS access only from known hosts
+# needed facts (variables):
+# firewall_nfs_hosts: a list of IPs of the allowed hosts
+- name: Allow TCP port 111 from known hosts
+  ansible.posix.firewalld:
+    zone: public
+    rich_rule: 'rule family="ipv4" source address="{{ item }}" port protocol="tcp" port="111" accept'
+    permanent: true
+    state: enabled
+  with_items: "{{ firewall_nfs_hosts }}"
+  notify: firewall reload
+
+- name: Drop TCP port 111 from all other sources
+  ansible.posix.firewalld:
+    zone: public
+    rich_rule: 'rule family="ipv4" port protocol="tcp" port="111" drop'
+    permanent: true
+    state: enabled
+  notify: firewall reload
diff --git a/tasks.templates/t_lets_create.yaml b/tasks.templates/t_lets_create.yaml
new file mode 100644 (file)
index 0000000..1704b99
--- /dev/null
@@ -0,0 +1,13 @@
+---
+# Creates a letsencrypt certificate for the given domain
+# needed facts (variables):
+#   - domain: the domain to create the certificate for
+#   - webmaster_email: the email address to use for the certificate
+- name: Check pre-requisites
+  fail: msg="The variable 'domain' must be defined and not empty."
+  when: domain is not defined or domain == ""
+- name: create a letsencrypt certificate for {{ domain }}
+  shell:
+    cmd: "certbot certonly -a webroot --webroot-path=/srv/www/letsencrypt -d {{ domain }} --email {{ webmaster_email }} --agree-tos --non-interactive"
+  args:
+    creates: "/etc/letsencrypt/live/{{ domain }}"
\ No newline at end of file
diff --git a/tasks.templates/t_link_wildcard.yaml b/tasks.templates/t_link_wildcard.yaml
new file mode 100644 (file)
index 0000000..818c9ed
--- /dev/null
@@ -0,0 +1,16 @@
+---
+# Copys files specified by a wildcard pattern from the source directory to the destination directory.
+# needed facts (variables):
+#   - src_dir: source directory
+#   - src_relative: source directory relative link target
+#   - dest_dir: destination directory
+#   - pattern: wildcard pattern
+- name: Link files matching {{ pattern }} from {{ src_dir }} to {{dest_dir}}
+  #command: ln -s {{ src_relative }}/{{ item | basename}} {{ dest_dir }}/{{ item | basename }}
+  ansible.builtin.file:
+    src: "{{ src_relative }}/{{ item | basename }}"
+    dest: "{{ dest_dir }}/{{ item | basename }}"
+    state: link
+  with_fileglob:
+    - "{{ src_dir }}/{{ pattern }}"
+
diff --git a/tasks.templates/t_mysql_create_admin.yaml b/tasks.templates/t_mysql_create_admin.yaml
new file mode 100644 (file)
index 0000000..83d9cda
--- /dev/null
@@ -0,0 +1,10 @@
+# needed facts (variables):
+# dba_name: name of the database user with all privileges
+# dba_password: password of the database user with all privileges
+
+- name: Create the database adminstrator {{dba_name}}
+  community.mysql.mysql_user:
+    name: "{{ dba_name }}"
+    password: "{{ dba_password }}"
+    priv: "*.*:ALL,GRANT"
+    state: present
diff --git a/tasks.templates/t_mysql_create_db_and_user.yaml b/tasks.templates/t_mysql_create_db_and_user.yaml
new file mode 100644 (file)
index 0000000..888f64a
--- /dev/null
@@ -0,0 +1,24 @@
+# needed facts:
+# db_name: name of the database
+# db_host: the ip or name of the host hosting mysql. Normally: localhost
+# db_user: name of the database user
+# db_password: password of the database user
+# webapp_name: name of the web application
+# dba_name: name of the database user with all privileges
+# dba_password: password of the database user with all privileges
+- name: Create the database {{db_name}}
+  community.mysql.mysql_db:
+    name: "{{ db_name }}"
+    login_host: "{{ db_host | default('localhost') }}"
+    login_user: "{{ dba_name }}"
+    login_password: "{{ dba_password }}"
+    state: present
+- name: Create the database user {{db_user}}
+  community.mysql.mysql_user:
+    name: "{{ db_user }}"
+    host: "{{ db_host | default('localhost') }}"
+    password: "{{ db_password }}"
+    login_user: "{{ dba_name }}"
+    login_password: "{{ dba_password }}"
+    priv: "{{ db_name }}.*:ALL,GRANT"
+    state: present
diff --git a/tasks.templates/t_nginx_create_site.yaml b/tasks.templates/t_nginx_create_site.yaml
new file mode 100644 (file)
index 0000000..b64b337
--- /dev/null
@@ -0,0 +1,43 @@
+---
+# Creates the nginx site configuration file (sites-available)
+# needed facts (variables):
+# domain: the site domain name
+# document_root: the document root of the site without path, e.g. example.com. Default: domain
+# shortname: the short name of the site. Used for log file names
+# force: if true, the site will be created even if it already exists. Default: false
+- name: Set default values for document_root
+  ansible.builtin.set_fact:
+    document_root: "{{ domain }}"
+  when: document_root is not defined
+- name: Set default values for shortname
+  ansible.builtin.set_fact:
+    shortname: "{{ domain }}"
+  when: name is not defined
+- name: Set default values for php_version
+  ansible.builtin.set_fact:
+    php_version: "8.3"
+  when: php_version is not defined
+- name: Set default values for force
+  ansible.builtin.set_fact:
+    force: 
+  when: force is not defined
+- name: check if configuration already exists
+  ansible.builtin.stat:
+    path: /etc/nginx/sites-available/{{ domain }}
+  register: site_config
+- name: abort if configuration already exists
+  ansible.builtin.fail:
+    msg: "The nginx site configuration file /etc/nginx/sites-available/{{ domain }} already exists. Please remove it first."
+  when: not force and site_config.stat.exists
+- name: Create nginx site configuration of {{ domain }}
+  ansible.builtin.template:
+    src: ../templates.fix/nginx/site.php
+    dest: /etc/nginx/sites-available/{{ domain }}
+    owner: root
+    group: root
+    mode: '0644'
+- name: Create symlink to sites-enabled
+  ansible.builtin.file:
+    src: /etc/nginx/sites-available/{{ domain }}
+    dest: /etc/nginx/sites-enabled/{{ domain }}
+    state: link
diff --git a/tasks.templates/t_spf.yaml b/tasks.templates/t_spf.yaml
new file mode 100644 (file)
index 0000000..bf50d1e
--- /dev/null
@@ -0,0 +1,33 @@
+---
+# Installs the SPF staff for postfix
+# needed facts (variables):
+# dkim_domains: the list of email domains
+- name: intro
+  debug: 
+    msg: "domains: {{ dkim_domains }}"
+- name: Adapt policyd-spf.conf
+  lineinfile:
+    dest: /etc/postfix-policyd-spf-python/policyd-spf.conf
+    regexp: "^{{ item }}\\s*=\\s*Fail"
+    line: "{{ item }} = False"
+  notify:
+    - restart postfix
+  with_items: [ HELO_reject, Mail_From_reject]
+- name: Display DNS configuration
+  debug:
+    msg: 
+      - "*************************************************************"
+      - "zone: {{ item }}"
+      - "type: TXT"
+      - "content: 'v=spf1 mx -all' deny other"
+      - "content: 'v=spf1 mx ~all' allow other"
+      - "*************************************************************"
+  with_items: "{{ dkim_domains }}"
+- name: Display test command
+  debug:
+    msg: 
+      - "*************************************************************"
+      - "Test it with:"
+      - "host -t txt {{ item }}"
+      - "*************************************************************"
+  with_items: "{{ dkim_domains }}"
diff --git a/tasks.templates/t_ssl_create_certificate.yaml b/tasks.templates/t_ssl_create_certificate.yaml
new file mode 100644 (file)
index 0000000..5d9550c
--- /dev/null
@@ -0,0 +1,13 @@
+# Creates a X509 certificate for a domain, needed for a letsencrypt certificate.
+# needed facts (variables):
+# - domain: the domain name for the certificate
+# - ssl_country: the country code for the certificate
+# - ssl_state: the state for the certificate
+# - ssl_locality: the locality for the certificate
+# - ssl_organization: the organization for the certificate
+# - ssl_lifetime: the lifetime of the certificate in days
+# - ssl_rsa_key_size: the size of the RSA key for the certificate
+- name: create a ssh-certificate
+  ansible.builtin.command: openssl req -x509 -nodes -days {{ssl_lifetime}} -newkey rsa:{{ssl_rsa_key_size}} -keyout /etc/ssl/private/{{domain}}.key -out /etc/ssl/certs/{{domain}}.pem -subj "/C={{ssl_country}}/ST={{ssl_state}}/L={{ssl_locality}}/O={{ssl_organization}}/CN={{domain}}"
+  args:
+    creates: /etc/ssl/private/{{domain}}.key
diff --git a/tasks.templates/t_sysinfo_create.yaml b/tasks.templates/t_sysinfo_create.yaml
new file mode 100644 (file)
index 0000000..0ad8391
--- /dev/null
@@ -0,0 +1,8 @@
+# Installs a script saving structure meta data and configuration and a cron job to start it every night
+# needed facts (variables):
+# hostname: the name of the remote server
+- name: install the script
+  ansible.builtin.copy:
+    src: ../template.local/scripts/CreateSysInfo
+    dest: "/usr/local/bin/{{ hostname }}"
+    mode: "0755"
diff --git a/tasks.templates/t_webapp_backup.yaml b/tasks.templates/t_webapp_backup.yaml
new file mode 100644 (file)
index 0000000..91b0893
--- /dev/null
@@ -0,0 +1,26 @@
+# Create a backup of a web application:
+# - Dumps a database of a web application
+# - Syncronizes the directory with the web application with an local directory
+# needed facts:
+# webapp_name: name of the web application
+# webapp_backup_directory: a local directory which will be syncronized with the web application
+# webapps_list: the list defined in ../vars/webapps.yaml
+# fetch: true: the file will be fetched to the local machine
+- ansible.builtin.set_fact:
+    db_name: "{{ webapps_list | selectattr('webapp_name', 'equalto', webapp_name) | map(attribute='db_name') | first }}"
+    db_host: "{{ webapps_list | selectattr('webapp_name', 'equalto', webapp_name) | map(attribute='db_host') | first }}"
+    db_user: "{{ webapps_list | selectattr('webapp_name', 'equalto', webapp_name) | map(attribute='db_user') | first }}"
+    db_password: "{{ webapps_list | selectattr('webapp_name', 'equalto', webapp_name) | map(attribute='db_password') | first }}"
+    document_root: "{{ webapps_list | selectattr('webapp_name', 'equalto', webapp_name) | map(attribute='directory') | first }}"
+    target_file: "{{ document_root }}/db/{{ webapp_name }}.sql.gz"
+- name: Dump the database {{ db_host }}.{{ db_name }} into {{ document_root }}
+  ansible.builtin.import_tasks: t_webapp_export.yaml
+- name: Synchronize the web application {{ webapp_name }} with {{ webapp_backup_directory }}
+  ansible.posix.synchronize:
+    mode: push
+    src: "{{ document_root }}"
+    dest: "{{ webapp_backup_directory }}/{{ webapp_name }}"
+    rsync_opts:
+      - "--delete"
+      - "--exclude=.git"
+  
\ No newline at end of file
diff --git a/tasks.templates/t_webapp_create.yaml b/tasks.templates/t_webapp_create.yaml
new file mode 100644 (file)
index 0000000..21c8cac
--- /dev/null
@@ -0,0 +1,47 @@
+# Create a web application: database, db user, db password, configuration file (remote and local)
+# needed facts:
+# db_name: name of the database
+# db_host: ip or name fo the host hosting the database server: normally "localhost"
+# db_user: name of the database user
+# db_password: password of the database user
+# webapp_name: name of the web application
+# dba_name: name of the database user with all privileges
+# dba_password: password of the database user with all privileges
+# remote_webapps_directory: the access data will be stored there, e.g. /etc/ansknife/webapp.d
+# local_webapps_directory: the access data will be stored there, e.g. ../webapp.d
+#
+# optional facts:
+# db_host: ip or name fo the host hosting the database server: normally "localhost"
+
+- name: 'check if the webapp is already defined'
+  ansible.builtin.stat:
+    path: "{{remote_webapps_directory}}/{{ webapp_name }}.yaml" 
+  register: webapp
+- name: 'stop if the webapp already defined'
+  ansible.builtin.fail: msg="webapp {{ webapp_name }} is already defined"
+  when: webapp.stat.exists
+- name: 'stop on missing facts'
+  ansible.builtin.fail: msg="missing facts! webapp_name, db_name, db_user, db_password. Use -e webapp_name=..., -e db_name=..., -e db_user=..., -e db_password=..."
+  when: webapp_name is not defined or db_name is not defined or db_user is not defined or db_password is not defined
+- name: Create the database {{ db_host }}.{{ db_name }} and db user {{ db_user }}
+  ansible.builtin.import_tasks: ../tasks/t_mysql_create_db_and_user.yaml
+- name: Store data in remote configuration file
+  ansible.builtin.copy:
+    content: |
+      db_name={{ db_name }}
+      db_type={{ db_type | default('mysql') }}
+      db_host={{ db_host | default('localhost') }}
+      db_user={{ db_user }}
+      db_password={{ db_password }}
+      directory=/srv/www/{{ webapp_name }}
+      exclude_dirs=
+    dest: "{{remote_webapps_directory}}/{{ webapp_name }}.conf"
+    mode: 0600
+    owner: root
+    group: root
+- name: append it to the webapps list
+  ansible.builtin.lineinfile:
+    path: ../vars/webapps.yaml
+    line: "  - webapp_name: '{{ webapp_name }}'\n    db_name: '{{ db_name }}'\n    db_type: '{{ db_type | default('mysql') }}'\n    db_host: '{{ db_host | default('localhost') }}'\n    db_user: '{{ db_user }}'\n    db_password: '{{ db_password }}'\n    directory: '/srv/www/{{ webapp_name }}'"
+    create: yes
+  delegate_to: localhost
diff --git a/tasks.templates/t_webapp_export.yaml b/tasks.templates/t_webapp_export.yaml
new file mode 100644 (file)
index 0000000..c452c5c
--- /dev/null
@@ -0,0 +1,34 @@
+# Dumps a database of a web application into a file
+# needed facts:
+# webapp_name: name of the web application
+# target_file: the file where the database will be exported to. May be *.sql or *.sql.gz
+# webapps_list: the list defined in ../vars/webapps.yaml
+# fetch: true: the file will be fetched to the local machine
+- set_fact:
+    db_name: "{{ webapps_list | selectattr('webapp_name', 'equalto', webapp_name) | map(attribute='db_name') | first }}"
+    db_type: "{{ webapps_list | selectattr('webapp_name', 'equalto', webapp_name) | map(attribute='db_type') | first }}"
+    db_host: "{{ webapps_list | selectattr('webapp_name', 'equalto', webapp_name) | map(attribute='db_host') | first }}"
+    db_user: "{{ webapps_list | selectattr('webapp_name', 'equalto', webapp_name) | map(attribute='db_user') | first }}"
+    db_password: "{{ webapps_list | selectattr('webapp_name', 'equalto', webapp_name) | map(attribute='db_password') | first }}"
+    compressed: "{{ target_file is regex('\\.sql\\.') }}"
+    remote_file: "{{ target_file }}"
+- set_fact:
+    remote_file: "{{ '/tmp/' + (target_file | basename) }}"
+  when: fetch | bool
+- name: Dump the database {{ db_host }}.{{ db_name }} to {{ target_file }}
+  community.mysql.mysql_db:
+    state: dump
+    name: "{{ db_name }}"
+    host: "{{ db_host }}"
+    login_user: "{{ db_user }}"
+    login_password: "{{ db_password }}"
+    target: "{{ remote_file }}"
+    single_transaction: true
+    pipefail: "{{ compressed }}"
+- name: Fetch the database dump {{ target_file }} to local machine
+  ansible.builtin.fetch:
+    src: "{{ remote_file }}"
+    dest: "{{ target_file }}"
+    flat: true
+  when: fetch | bool
+
diff --git a/tasks.templates/t_webapp_import.yaml b/tasks.templates/t_webapp_import.yaml
new file mode 100644 (file)
index 0000000..b0f6251
--- /dev/null
@@ -0,0 +1,34 @@
+# Imports a database dump into the database of a web application
+# needed facts:
+# webapp_name: name of the web application
+# source_file: the database dump file. May be *.sql or *.sql.gz
+# backup: if true, the database will be dumped before importing
+# backup_file: the name of the backup file
+# webapps_list: the list defined in ../vars/webapps.yaml
+- set_fact:
+    db_name: "{{ webapps_list | selectattr('webapp_name', 'equalto', webapp_name) | map(attribute='db_name') | first }}"
+    db_type: "{{ webapps_list | selectattr('webapp_name', 'equalto', webapp_name) | map(attribute='db_type') | first }}"
+    db_host: "{{ webapps_list | selectattr('webapp_name', 'equalto', webapp_name) | map(attribute='db_host') | first }}"
+    db_user: "{{ webapps_list | selectattr('webapp_name', 'equalto', webapp_name) | map(attribute='db_user') | first }}"
+    db_password: "{{ webapps_list | selectattr('webapp_name', 'equalto', webapp_name) | map(attribute='db_password') | first }}"
+    compressed: "{{ source_file is regex('\\.sql\\.$') }}"
+- name: Backup the current database {{db_name}} to {{ backup_file }}
+  community.mysql.mysql_db:
+    state: dump
+    name: "{{ db_name }}"
+    login_user: "{{ db_user }}"
+    login_password: "{{ db_password }}"
+    target: "{{ backup_file }}"
+    single_transaction: true
+    pipefail: "{{ compressed }}"
+  when: backup and db_type == 'mysql' | bool
+- name: Import {{ source_file }} into {{db_name}}
+  community.mysql.mysql_db:
+    state: import
+    name: "{{ db_name }}"
+    host: "{{ 'db_host' }}"
+    login_user: "{{ db_user }}"
+    login_password: "{{ db_password }}"
+    target: "{{ source_file }}"
+  when: db_type == 'mysql'
+
diff --git a/templates.fix/antispam/key.table b/templates.fix/antispam/key.table
new file mode 100644 (file)
index 0000000..58d41bc
--- /dev/null
@@ -0,0 +1,3 @@
+{% for domain in dkim_domains %}
+{{ dkim_selector }}._domainkey.{{ domain }} {{ domain }}:{{ dkim_selector }}:/etc/opendkim/keys/{{ domain }}/{{ dkim_selector }}.private
+{% endfor %}
diff --git a/templates.fix/antispam/signing.table b/templates.fix/antispam/signing.table
new file mode 100644 (file)
index 0000000..f7d5949
--- /dev/null
@@ -0,0 +1,3 @@
+{% for domain in dkim_domains %}
+*@{{ domain }} {{ dkim_selector }}._domainkey.{{ domain }}
+{% endfor %}
diff --git a/templates.fix/nginx/hi1.txt b/templates.fix/nginx/hi1.txt
new file mode 100644 (file)
index 0000000..877acc4
--- /dev/null
@@ -0,0 +1 @@
+Hi 1!
diff --git a/templates.fix/nginx/hi2.txt b/templates.fix/nginx/hi2.txt
new file mode 100644 (file)
index 0000000..e42aa2a
--- /dev/null
@@ -0,0 +1 @@
+Hi 2!
diff --git a/templates.fix/nginx/index.html b/templates.fix/nginx/index.html
new file mode 100644 (file)
index 0000000..0e0cbca
--- /dev/null
@@ -0,0 +1,5 @@
+<html>
+<body>
+<h1>Welcome to {{hostname}}!</h1>
+</body
+</html>
\ No newline at end of file
diff --git a/templates.fix/nginx/index.php b/templates.fix/nginx/index.php
new file mode 100644 (file)
index 0000000..bfd863b
--- /dev/null
@@ -0,0 +1,2 @@
+<?php
+phpinfo();
\ No newline at end of file
diff --git a/templates.fix/nginx/letsencrypt.conf b/templates.fix/nginx/letsencrypt.conf
new file mode 100644 (file)
index 0000000..2fa4cd7
--- /dev/null
@@ -0,0 +1,13 @@
+# Ansible controlled: do not change on remote server
+
+location ^~ /.well-known/acme-challenge/ {
+    default_type "text/plain";
+    root /srv/www/letsencrypt;
+}
+# Hide /acme-challenge subdirectory and return 404 on all requests.
+# It is somewhat more secure than letting Nginx return 403.
+# Ending slash is important!
+location = /.well-known/acme-challenge/ {
+    return 404;
+}
+
diff --git a/templates.fix/nginx/site.php b/templates.fix/nginx/site.php
new file mode 100644 (file)
index 0000000..ee896e6
--- /dev/null
@@ -0,0 +1,57 @@
+# Ansible controlled: do not change on remote server
+server {
+    listen 80;
+    listen [::]:80;
+    server_name {{ domain }};
+    include snippets/letsencrypt.conf;
+    root /srv/www/{{ document_root }};
+    location / {
+      return 301 https://{{ domain }}$request_uri;  # enforce https
+    }
+  }
+  
+server {
+  listen 443 ssl http2;
+  listen [::]:443 ssl http2;
+  server_name {{ domain }};
+  access_log /var/log/nginx/a_{{ shortname }}.log;
+  error_log /var/log/nginx/e_{{ shortname }}.log;
+  
+  #ssl_certificate     /etc/letsencrypt/live/{{ domain }}/fullchain.pem;
+  #ssl_certificate_key /etc/letsencrypt/live/{{ domain }}/privkey.pem;
+  ssl_certificate /etc/ssl/certs/{{ domain }}.pem;
+  ssl_certificate_key /etc/ssl/private/{{ domain }}.key;
+  
+  # Path to the root of your installation
+  root /srv/www/{{ document_root }};
+  autoindex off;
+  client_max_body_size 1m; # set max upload size
+  fastcgi_buffers 64 4K;
+  
+  index index.html;
+  
+  location = /robots.txt {
+     allow all;
+     log_not_found off;
+     access_log off;
+  }
+  location / {
+    allow all;
+  }
+  # Optional: set long EXPIRES header on static assets
+  location ~* ^.+\.(jpg|jpeg|gif|bmp|ico|png|css|js|swf)$ {
+    expires 30d;
+    # Optional: Don't log access to assets
+    access_log off;
+  }
+  location ~ ^(.+?\.php)(/.*)?$ {
+    try_files $1 = 404;
+  
+    include fastcgi_params;
+    fastcgi_param SCRIPT_FILENAME $document_root$1;
+    fastcgi_param PATH_INFO $2;
+    fastcgi_param HTTPS on;
+    fastcgi_pass unix:/run/php/php{{ php_version }}-fpm.sock;
+  }
+} 
diff --git a/templates.fix/nginx/test.site b/templates.fix/nginx/test.site
new file mode 100644 (file)
index 0000000..84f0494
--- /dev/null
@@ -0,0 +1,58 @@
+# Ansible controlled: do not change on remote server
+server {
+    listen 80;
+    listen [::]:80;
+    server_name {{hostname}} hamatoma.de;
+    include snippets/letsencrypt.conf;
+    server_name {{hostname}};
+    root /srv/www/{{hostname}};
+    location / {
+      return 301 https://{{hostname}}$request_uri;  # enforce https
+    }
+  }
+  
+server {
+  listen 443 ssl http2;
+  listen [::]:443 ssl http2;
+  server_name {{hostname}};
+  access_log /var/log/nginx/a_{{log_name}}.log;
+  error_log /var/log/nginx/e_{{log_name}}.log;
+  
+  #ssl_certificate     /etc/letsencrypt/live/latest/fullchain.pem;
+  #ssl_certificate_key /etc/letsencrypt/live/latest/privkey.pem;
+  ssl_certificate /etc/ssl/certs/{{hostname}}.pem;
+  ssl_certificate_key /etc/ssl/private/{{hostname}}.key;
+  
+  # Path to the root of your installation
+  root /home/www/{{hostname}};
+  autoindex off;
+  client_max_body_size 1m; # set max upload size
+  fastcgi_buffers 64 4K;
+  
+  index index.html;
+  
+  location = /robots.txt {
+     allow all;
+     log_not_found off;
+     access_log off;
+  }
+  location / {
+    allow all;
+  }
+  # Optional: set long EXPIRES header on static assets
+  location ~* ^.+\.(jpg|jpeg|gif|bmp|ico|png|css|js|swf)$ {
+    expires 30d;
+    # Optional: Don't log access to assets
+    access_log off;
+  }
+  location ~ ^(.+?\.php)(/.*)?$ {
+    try_files $1 = 404;
+  
+    include fastcgi_params;
+    fastcgi_param SCRIPT_FILENAME $document_root$1;
+    fastcgi_param PATH_INFO $2;
+    fastcgi_param HTTPS on;
+    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
+  }
+} 
+  
\ No newline at end of file
diff --git a/templates.fix/readme.txt b/templates.fix/readme.txt
new file mode 100644 (file)
index 0000000..89c8c5f
--- /dev/null
@@ -0,0 +1,2 @@
+Objective: This directory contains templates that can be used in all projects without modification.
+Therefore, the files should only be linked within the project, not copied.
diff --git a/templates.fix/scripts/ClearSwap b/templates.fix/scripts/ClearSwap
new file mode 100644 (file)
index 0000000..faa711d
--- /dev/null
@@ -0,0 +1,16 @@
+#! /bin/bash
+LOG=/var/log/local/clearswap.log
+VERBOSE=-v
+test "$1" = -q && VERBOSE=
+PATH=/bin:/usr/bin:/usr/sbin
+function Log(){
+        local msg=$(date "+%Y.%m.%d-%H:%M:%S: $1")
+        test -n "$VERBOSE" && echo $msg
+        echo >>$LOG "$msg"
+}
+
+Log "swapoff:"
+info=$(swapoff -a)
+Log "logoff result: $info"
+info=$(swapon -a)
+Log "logon result: $info"
diff --git a/templates.fix/scripts/CreateSysInfo b/templates.fix/scripts/CreateSysInfo
new file mode 100644 (file)
index 0000000..6dba4d4
--- /dev/null
@@ -0,0 +1,131 @@
+#! /bin/bash
+# Ansible controlled: do not change on remote server
+VERBOSE=-v
+export PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin
+test "$1" = -q && VERBOSE=
+. /usr/local/bin/local/CreateSysInfo.conf
+function Packages(){
+  test "$VERBOSE" = -v && echo "= packages"
+  dpkg -l | sort > $DIR_INFO/packets.installed.txt
+  apt-mark showmanual | sort >$DIR_INFO/packets.manually.txt
+  cp /var/log/apt/history.log $DIR_INFO/apt.history.txt
+}
+function Mount(){
+  test "$VERBOSE" = -v && echo "= mount"
+  mount | grep /dev/ | egrep -v "devpts|hugetlbfs|tmpfs|mqueue" > $DIR_INFO/mount.txt
+}
+function Devices(){
+  test "$VERBOSE" = -v && echo "= device info"
+  blkid > $DIR_INFO/blkid.txt
+  lsblk > $DIR_INFO/lsblk.txt
+  free > $DIR_INFO/free.txt
+  df -h | grep -v docker > $DIR_INFO/df.txt
+  test "$VERBOSE" = -v && echo "= btrfs"
+  rm -f $DIR_INFO/btrfs.subvols.txt
+  if [ -n "$BTRFS_FS" ]; then
+    for fs in $BTRFS_FS; do
+      if [ -d $fs ]; then
+        echo "= $fs: >>$DIR_INFO/btrfs.subvols.txt"
+        btrfs subvol list $fs | grep -v snapshot >> $DIR_INFO/btrfs.subvols.txt
+      fi
+    done
+  fi
+  test "$VERBOSE" = -v && echo "= fdisk"
+  fdisk -l > $DIR_INFO/fdisk.txt
+}
+function SoftwareRaid(){
+  if [ "$HAS_SOFT_RAID" = yes ]; then
+    cat /proc/mdstat > $DIR_INFO/mdstat.txt
+  fi
+}
+function SmartMonitor(){
+  local fn=$DIR_INFO/smartmon.txt
+  date >$fn
+  for dev in $SMART_MON_DEVS; do
+    echo "============ $dev" >>$fn
+    smartctl --all /dev/$dev >>$fn
+  done  
+}
+function SystemD(){
+  test "$VERBOSE" = -v && echo "= systemctl"
+  systemctl list-unit-files > $DIR_INFO/systemd.files.txt
+  systemctl list-units > $DIR_INFO/systemd.units.txt
+}
+function Trees(){
+  test "$VERBOSE" = -v && echo "= directories"
+  for dir in etc usr/local/bin var/spool/cron/crontabs var/log boot; do
+    name=${dir//\//_}
+    cd /$dir
+    test -n "$VERBOSE" && echo "= $(pwd)"
+    opt=
+    trg=$DIR_INFO/$dir
+    mkdir -p $trg
+    #test $dir = var/log && opt=--newer-mtime=$(date --date='yesterday' +%Y%m%d)
+    #tar czf $DIR_INFO/$name.tgz . $opt 
+    test $dir = var/log && opt="--exclude=journal --exclude=*.log.*gz"
+    test $dir = etc && opt="--exclude=.git --exclude=sysinit.target.wants --exclude=multi-user.target.wants"
+    rsync -au $VERBOSE --delete-excluded --delete $opt ./ $trg
+    test "$VERBOSE" = -v && echo "= chown $dir"
+    find $trg -uid 0 -exec chown bupsupply "{}" \; >/dev/null 2>&1
+  done
+  if [ $MK_HOME_TAR = yes ]; then
+    test "$VERBOSE" = -v && echo "= home"
+    pushd /home >/dev/null 2>&1
+    tar czf $DIR_INFO/home.tgz $EXCLUDE_HOME --exclude-tag-all=$TAG_FILE .
+    popd >/dev/null 2>&1
+    test "$VERBOSE" = -v && ls -ld $DIR_INFO/home.tgz
+  fi
+}
+function Zfs(){
+  if [ $USE_ZFS = yes ]; then
+    test "$VERBOSE" = -v && echo "= ZFS"
+    local fn=$DIR_INFO/zfs.info.txt
+    echo "== zfs list" >$fn
+    zfs list >>$fn
+    echo "== zpool list" >>$fn
+    zpool list >>$fn
+    echo "== zpool status" >>$fn
+    zpool status >>$fn
+    echo "== zfs list -t snapshot" >>$fn
+    zfs list -t snapshot >>$fn
+  fi
+}
+function DoIt(){
+  local debug=
+  $debug Packages
+  $debug Mount
+  $debug Devices
+  $debug SystemD
+  $debug Trees
+  $debug Zfs
+  $debug SoftwareRaid
+  $debug SmartMonitor
+}
+function ShowConfig(){
+  echo "= Example of /usr/local/bin/local/CreateSysInfo.conf:"
+  echo "# This file will be included into CreateSysInfo
+DIR_INFO=/srv/www/sys.info/example.host
+# a blank separated list of mount points for devices formatted with BTRFS, e.g. "/media/fs.cache /media/fs.system"
+BTRFS_FS=
+# a blank separated list of devices that should be controlled by smartmon, e.g. 'sda sdb'
+SMART_MON_DEVS='nvme0n1'
+# set to yes if mdadm manages software raid devices
+HAS_SOFT_RAID=no
+MK_HOME_TAR=yes
+USE_ZFS=no
+# a blank separated list of '--exclude=<dir>' entries. That listed subdirs will not be saved in the TAR archive
+EXCLUDE_HOME='--exclude=jails'
+# each subdirectory of /home containing that file will not be saved as TAR archive
+TAG_FILE=.do.not.save.as.home
+"
+}
+if [ ! -f /usr/local/bin/local/CreateSysInfo.conf  ]; then
+  echo "+++ missing /usr/local/bin/local/CreateSysInfo.conf"
+  ShowConfig
+elif [ -z "$DIR_INFO" -o ! -d "$DIR_INFO" ]; then
+  echo "+++ missing DIR_INFO"
+  ShowConfig
+else
+  DoIt
+fi
+
diff --git a/templates.fix/scripts/SaveDatabases b/templates.fix/scripts/SaveDatabases
new file mode 100755 (executable)
index 0000000..11e1720
--- /dev/null
@@ -0,0 +1,26 @@
+#! /bin/bash
+BASE_DIR=/srv/www
+BASE_WEBAPPS=/etc/ansknife/webapps.d
+VERBOSE=-v
+test "$1" = -q && VERBOSE=
+PATH=/bin:/usr/bin
+function SaveOne(){
+    local domain=$1
+    if [ -e $BASE_WEBAPPS/$domain.conf ]; then
+        . $BASE_WEBAPPS/$domain.conf
+        if [ -n "$db_name" -a "$db_type" = 'mysql' ]; then
+            local sql=$BASE_DIR/$domain/db/$domain.sql.gz
+            mysqldump -h $db_host -u $db_user "-p$db_password" $db_name | gzip > $sql
+            test -n "$VERBOSE" && ls -ld $sql
+        fi
+    fi
+}
+function SaveAll(){
+    cd $BASE_DIR
+    for domain in *; do
+        if [ -d $BASE_DIR/$domain/db ]; then
+          SaveOne $domain
+        fi
+    done
+}
+SaveAll
\ No newline at end of file
diff --git a/templates.fix/scripts/saveowner.py b/templates.fix/scripts/saveowner.py
new file mode 100755 (executable)
index 0000000..3ce0699
--- /dev/null
@@ -0,0 +1,225 @@
+#! /usr/bin/python3
+# -*- coding: utf-8 -*- 
+
+import sys
+import os
+import os.path
+import pwd
+import grp
+import time
+import stat
+import re
+
+class DirectoryInfo:
+    def __init__(self, owner: int, group: int):
+        self.owner = owner
+        self.group = group
+        self.nodes = {}
+    def addNode(self, node: str, owner: int, group: int):
+        self.nodes[node] = (owner, group)
+
+    def hasNode(self, node: str):
+        rc = node in self.nodes
+        return rc
+    
+class Traverser:
+    def __init__(self, directory: str, verbose: bool):
+        self.start = time.time()
+        self.verbose = verbose
+        self.startDirectory = directory
+        self.fileOwners = '.owner.list'
+        self.ownerList = []
+        self.ownerOfId = {}
+        self.groupOfId = {}
+        self.linesOwnerList = None
+        self.directoryInfos = {}
+        self.first = True
+
+    def adaptOneDirectory(self, directory: str, parentOwner: int, parentGroup: int):
+        dirs = []
+        if self.first:
+            relativePath = '.'
+            self.first = False
+        else:
+            relativePath = self.startDirectory[len(directory):]
+        try:
+            statInfo = os.lstat(directory)
+            directoryInfo = self.directoryInfos[relativePath] if relativePath in self.directoryInfos else None
+            if directoryInfo is not None:
+                parentOwner = directoryInfo.owner
+                parentGroup = directoryInfo.group
+            if parentOwner != statInfo.st_uid or parentGroup != statInfo.st_gid:
+                os.chown(directory, parentOwner, parentGroup)
+                if self.verbose:
+                    print(f'= {directory}')
+            nodes = os.listdir(directory)
+            for node in nodes:
+                fn = os.path.join(directory, node)
+                statInfo = os.lstat(fn)
+                if stat.S_ISDIR(statInfo.st_mode):
+                    dirs.append(node)
+                if directoryInfo is None:
+                    owner = parentOwner
+                    group = parentGroup
+                elif node in directoryInfo.nodes:
+                    owner, group = directoryInfo.nodes[node]
+                else:
+                    owner = parentOwner
+                    group = parentGroup
+                if owner != statInfo.st_uid or group != statInfo.st_gid:
+                    os.chown(fn, owner, group)
+                    #print(f'  {owner} {group} {node}')
+            for node in dirs:
+                self.adaptOneDirectory(os.path.join(directory, node), parentOwner, parentGroup)
+        except OSError as e:
+            if self.verbose:
+                print(f'+++ Error: {e}')
+        
+        
+    def buildDirectoryInfos(self):
+        regexDir = re.compile(r'^= (\d+) (\d+) (.*)$')
+        regexNode = re.compile(r'^(\d+) (\d+) (.*)$')
+        currentDir = None
+        startDir = self.startDirectory
+        dirs = 0
+        nodes = 0
+        for line in self.linesOwnerList:
+            match = regexNode.match(line)
+            if match is not None:
+                node = match.group(3)
+                fn = os.path.join(startDir, node)
+                currentDir.addNode(node, int(match.group(1)), int(match.group(2)))
+                nodes += 1
+            else:
+                match = re.match(regexDir, line)
+                if match is not None:
+                    currentName = match.group(3)
+                    currentDir = DirectoryInfo(int(match.group(1)), int(match.group(2)))
+                    self.directoryInfos[currentName] = currentDir
+                    #os.chown(currentDir, int(match.group(1)), int(match.group(2)))
+                    dirs += 1   
+                elif line.startswith('u') or line.startswith('g') or line == '':
+                    break
+                else:
+                    print(f'+++ unknown line: {line}')
+        if self.verbose:
+            print(f'== directories: {dirs} nodes: {nodes}')
+
+    def dumpOwnersGroups(self):
+        for owner in self.ownerOfId:
+            self.ownerList.append(f'u{owner} {self.ownerOfId[owner]}')
+        for group in self.groupOfId:
+            self.ownerList.append(f'g{group} {self.groupOfId[group]}')
+
+    def extractOwnersAndGroups(self):
+        regexOwner = re.compile(r'^u(\d+) (\S+)')
+        regexGroup = re.compile(r'^g(\d+) (\S+)')
+        for ix in range(len(self.linesOwnerList) - 1, 0, -1):
+            line = self.linesOwnerList[ix]
+            match = regexOwner.match(line)
+            if match is not None:
+                self.ownerOfId[int(match.group(1))] = match.group(2)
+            else:
+                match = regexGroup.match(line)
+                if match is not None:
+                    self.groupOfId[int(match.group(1))] = match.group(2)
+                elif line != '':
+                    break
+        if self.verbose:
+            print(f'= {len(self.ownerOfId)} owners and {len(self.groupOfId)} groups found')
+
+    def readOwnerList(self):
+        fn = os.path.join(self.startDirectory, self.fileOwners)
+        with open(fn, 'r') as fp:
+            self.linesOwnerList = fp.read().split('\n')
+
+    def save(self):
+        fn = os.path.join(self.startDirectory, self.fileOwners)
+        with open(fn, 'w') as fp:
+            for line in self.ownerList:
+                fp.write(line + '\n')
+        if self.verbose:
+            print(f'== Saved {len(self.ownerList)} lines to {self.fileOwners}')
+            print(f'== Time taken: {time.time() - self.start:.2f} seconds')
+
+    def storeOneDirectory(self, directory: str):
+        dirs = []
+        try:
+            statInfo = os.lstat(directory)
+            parentOwner = statInfo.st_uid
+            parentGroup = statInfo.st_gid
+            nodes = os.listdir(directory)
+            firstChange = True
+            if self.first:
+                self.first = False
+                firstChange = False
+                msg = f'= {parentOwner} {parentGroup} .'
+                firstChange = False
+                if self.verbose:
+                    print(msg)
+                self.ownerList.append(msg)
+            for node in nodes:
+                fn = os.path.join(directory, node)
+                statInfo = os.lstat(fn)
+                if stat.S_ISDIR(statInfo.st_mode):
+                    dirs.append(node)
+                owner = statInfo.st_uid
+                group = statInfo.st_gid
+                if owner != parentOwner or group != parentGroup:
+                    if firstChange:
+                        ix = len(self.startDirectory)
+                        relativePath = '.' if ix == len(directory) else directory[ix+1:]
+                        msg = f'= {parentOwner} {parentGroup} {relativePath}'
+                        firstChange = False
+                        if self.verbose:
+                            print(msg)
+                        self.ownerList.append(msg)
+                    self.ownerList.append(f'{owner} {group} {node}')
+                    if owner not in self.ownerOfId:
+                        self.ownerOfId[owner] = pwd.getpwuid(owner).pw_name
+                    if group not in self.groupOfId:
+                        self.groupOfId[group] = grp.getgrgid(group).gr_name
+        except OSError as e:
+            if self.verbose:
+                print(f'+++ Error: {e}')
+        for node in dirs:
+            self.storeOneDirectory(os.path.join(directory, node))
+
+def usage(msg: str):
+    print(f'''Usage: saveowner.py [-q] [--restore] DIRECTORY
+  Save or restore the owner and group of all files in the filetree of DIRECTORY
+  Note: the stored data are only differences from the parent directory
+Options:
+-q        Be quiet
+--restore Restore the ownership of DIRECTORY by the data in DIRECTORY/.owner.list
+          If not given the ownership is stored in DIRECTORY/.owner.list
++++ {msg}
+''')
+    sys.exit(9)
+def main(args):
+    quiet = len(args) > 0 and args[0] == '-q'
+    if quiet:
+        args = args[1:]
+    doRestore = len(args) > 0 and args[0] == '--restore'
+    if doRestore:
+        args = args[1:]
+    if len(args) < 1:
+        usage('missing DIRECTORY')
+    else:
+        app = Traverser(args[0], not quiet)
+        if doRestore:
+            app.readOwnerList()
+            app.extractOwnersAndGroups()
+            app.buildDirectoryInfos()
+            app.adaptOneDirectory(app.startDirectory, None, None)
+        else:
+            app.storeOneDirectory(args[0])
+            app.dumpOwnersGroups()
+            app.save()
+
+if __name__ == '__main__':
+    args = sys.argv[1:]
+    #args = sys.argv[1:] if len(sys.argv) > 1 else ['--restore', '/tmp/etc']
+    #args = sys.argv[1:] if len(sys.argv) > 1 else ['/tmp/etc']
+    main(args)
+    
diff --git a/templates.install/project_ansible.md b/templates.install/project_ansible.md
new file mode 100644 (file)
index 0000000..98218b7
--- /dev/null
@@ -0,0 +1,61 @@
+# Beschreibung der Ansible-Struktur von {{ project }}
+
+## Wartung
+### Benutzer
+* neuer Benutzer: Änderung in vars/users.yaml
+```
+ansible-playbook playbooks/i_11_user.yaml
+```
+### Webauftritte
+Es wird eine neue Website xxx.example.net benötigt:
+```
+ansible-playbook webapp_create.yaml -e domain=xxx.example.net -e db_name=dbexample -e db_user=dbexample -e db_password=xxx
+```
+### Firewall-Änderung
+Änderung in vars/firewalld.yaml eintragen.
+```
+ansible-playbook playbooks/i_80_firewalld.yaml
+```
+
+### Email-Server-Änderung
+- Alias-Änderung: Änderung in templates.local/postfix/alias.yaml eintragen
+- Postfix-Änderung in main.cf: Änderung in templates.local/postfix/send_only/main.cf eintragen.
+- Postfix-Änderung in master.cf: Änderung in templates.local/postfix/send_only/master.cf eintragen.
+```
+ansible-playbook playbooks/i_60_postfix.yaml
+```
+
+## PHP-Konfiguration-Änderung
+- Änderung in vars/php.yaml eintragen
+```
+for VERS in 7.4 8.2 8.3; do
+  ansible-playbook playbooks/i_40_php8.2.yaml -e php_version=$VERS
+done
+```
+
+## Neues Software-Paket
+- Änderung in vars/packages.yaml
+```
+ansible-playbook playbooks/i_15_server_packages.yaml
+```
+
+## Installation
+- Am besten zuerst das Kapitel "Wartung" durchgehen, dort sind die Dateien aufgeführt, in denen konfiguriert wird.
+- Nie in templates.fix konfigurieren: 
+  - das wird vom Paket ansknife bereitgestellt und das sind nur symbolische Links
+  - die Dateien sind schreibgeschützt
+
+### Benutzte Playbooks
+- i_10_basic.yaml
+- i_11_user.yaml
+- i_15_server.yaml
+- i_17_configuration.yaml
+- i_20_configuration.yaml
+- i_30_mariadb.yaml
+- i_40_php8.2.yaml
+  - i_40_php8.2.yaml -e php_vers=7.4
+  - i_40_php8.2.yaml -e php_vers=8.3
+- i_60_postfix.yaml
+- i_80_firewalld.yaml
+- i_81_fail2ban.yaml
+- mysql_create_admin.yaml
diff --git a/templates.install/project_cron.yaml b/templates.install/project_cron.yaml
new file mode 100644 (file)
index 0000000..b8e9d1a
--- /dev/null
@@ -0,0 +1,44 @@
+---
+# Defines the cron jobs
+- hosts: all
+  tasks:
+    - name: Creates the script for clearing the swap devices
+      ansible.builtin.copy:
+        src: ../templates.fix/scripts/ClearSwap
+        dest: /usr/local/bin/ClearSwap
+        owner: root
+        group: root
+        mode: 0744
+    - name: Cron job for clearing the swap devices 
+      ansible.builtin.cron:
+        name: clear the swap files
+        #weekday: "2"
+        minute: "42"
+        hour: "23"
+        user: root
+        job: "/usr/local/bin/ClearSwap -q"
+        cron_file: ClearSwap
+    - name: Save the ownership of a file tree in the file .owner.list
+      ansible.builtin.cron:
+        name: store the ownership of a file tree
+        minute: "39"
+        hour: "23"
+        user: root
+        job: "/usr/local/bin/host.example.com/SaveOwner.sh -q"
+        cron_file: SaveOwner
+    - name: Build a backup of the /etc/letsencrypt directory
+      ansible.builtin.cron:
+        name: builds the backup
+        minute: "19"
+        hour: "0"
+        user: root
+        job: "/usr/local/bin/host.example.com/exampleBackup -q"
+        cron_file: exampleBackup
+    - name: Save the web applications (filetree and database) into a tar
+      ansible.builtin.cron:
+        name: Save the web applications
+        minute: "19"
+        hour: "0"
+        user: root
+        job: "/usr/local/bin/SaveWebapps -q"
+        cron_file: SaveWebapps
diff --git a/templates.install/readme.txt b/templates.install/readme.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/templates.install/t_firewalld_local.yaml b/templates.install/t_firewalld_local.yaml
new file mode 100644 (file)
index 0000000..a4c87a5
--- /dev/null
@@ -0,0 +1,12 @@
+---
+# Defines the specific firewall rules for example.com
+#
+- name: allow HTTP and HTTPS
+  ansible.builtin.include_tasks: "../tasks/t_firewalld_http.yaml"
+- name: allow SMTP and IMAP
+  ansible.builtin.include_tasks: ../tasks/t_firewalld_email.yaml
+- name: allow NFS
+  ansible.builtin.include_tasks: ../tasks/t_firewalld_nfs.yaml
+  vars:
+    # example1 example2
+    firewall_nfs_hosts: ["1.2.3.4", "4.3.2.1"]
diff --git a/templates.local/antispam/ignore.hosts b/templates.local/antispam/ignore.hosts
new file mode 100644 (file)
index 0000000..d15cd1d
--- /dev/null
@@ -0,0 +1,4 @@
+# Ansible controlled: do not change on server manually
+127.0.0.1
+::1
+localhost
\ No newline at end of file
diff --git a/templates.local/antispam/opendkim.conf b/templates.local/antispam/opendkim.conf
new file mode 100644 (file)
index 0000000..61329f8
--- /dev/null
@@ -0,0 +1,20 @@
+# normally no need for changes
+UserID                  opendkim:opendkim
+UMask                   002
+PidFile                 /var/run/opendkim/opendkim.pid
+SOCKET                  local:/var/spool/postfix/opendkim/opendkim.sock
+Mode                    sv
+Domain                  *
+#Selector               mail
+Canonicalization        relaxed/relaxed
+SignatureAlgorithm      rsa-sha256
+OversignHeaders         From
+AutoRestart             yes
+AutoRestartRate         10/1h
+SigningTable            refile:/etc/opendkim/signing.table
+KeyTable                /etc/opendkim/key.table
+ExternalIgnoreList      refile:/etc/opendkim/trusted.hosts
+InternalHosts           refile:/etc/opendkim/trusted.hosts
+Syslog                  yes
+SyslogSuccess           yes
+LogWhy                  yes
diff --git a/templates.local/antispam/opendmarc.conf b/templates.local/antispam/opendmarc.conf
new file mode 100644 (file)
index 0000000..130c98e
--- /dev/null
@@ -0,0 +1,20 @@
+# Ansible controlled: do not change on server manually
+AuthservID              "{{ hostname }}"
+TrustedAuthservIDs      "{{ hostname }}"
+UMask                   0002
+UserID                  opendmarc
+AutoRestart             true
+Socket                  local:/var/spool/postfix/opendmarc/opendmarc.sock
+RejectFailures          true
+IgnoreMailFrom          "{{ domain }}"
+IgnoreHosts             /etc/opendmarc/ignore.hosts
+PublicSuffixList        /etc/opendmarc/public_suffix_list.dat
+SoftwareHeader          false
+FailureReports          true
+FailureReportsSentBy    "no-reply.dmarc.reports@{{ domain_email }}"
+#FailureReportsBcc
+BaseDirectory           /var/run/opendmarc
+PidFile                 /var/run/opendmarc/opendmarc.pid
+HistoryFile             /var/run/opendmarc/opendmarc.dat
+Syslog                  true
+SyslogFacility          mail
diff --git a/templates.local/antispam/trusted.hosts b/templates.local/antispam/trusted.hosts
new file mode 100644 (file)
index 0000000..3f4908e
--- /dev/null
@@ -0,0 +1,3 @@
+127.0.0.1
+::1
+localhost
\ No newline at end of file
diff --git a/templates.local/firewalld.yaml b/templates.local/firewalld.yaml
new file mode 100644 (file)
index 0000000..f988c4b
--- /dev/null
@@ -0,0 +1,5 @@
+---
+# Facts (variables) of the firewall
+
+# execute "ip addr" on the remote server to findout the interface of the public zone:
+firewall_iface_public: enp5s0
diff --git a/templates.local/nginx/http.conf b/templates.local/nginx/http.conf
new file mode 100644 (file)
index 0000000..0eed601
--- /dev/null
@@ -0,0 +1,9 @@
+client_max_body_size 512M;
+## Detect when HTTPS is used
+map $scheme $fastcgi_https {
+    default off;
+    https on;
+}
+fastcgi_read_timeout 3600s;
+fastcgi_request_buffering off;
+error_log /var/log/nginx/error.log;
diff --git a/templates.local/nginx/sites/readme.txt b/templates.local/nginx/sites/readme.txt
new file mode 100644 (file)
index 0000000..3d77b05
--- /dev/null
@@ -0,0 +1,2 @@
+This directory contains all active NGINX configuration files from /etc/nginx/sites-available.
+They can be found via vars/webapps.yaml.
diff --git a/templates.local/postfix/aliases b/templates.local/postfix/aliases
new file mode 100644 (file)
index 0000000..2ba0231
--- /dev/null
@@ -0,0 +1,9 @@
+# ansible controlled: do not change manually on remote server!
+#
+postmaster: root
+devnull: /dev/null
+mailer-daemon: root
+webmaster: root
+www: root
+security: root
+root: root@example.com
diff --git a/templates.local/postfix/email_forwarding/main.cf b/templates.local/postfix/email_forwarding/main.cf
new file mode 100644 (file)
index 0000000..c2e9cb2
--- /dev/null
@@ -0,0 +1,31 @@
+# Ansible controlled! Do not change on remote host
+myorigin = /etc/mailname
+smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
+biff = no
+append_dot_mydomain = no
+#delay_warning_time = 4h
+readme_directory = no
+compatibility_level = 3.6
+
+
+#smtpd_tls_cert_file=/etc/letsencrypt/live/host.example.com/fullchain.pem
+#smtpd_tls_key_file=/etc/letsencrypt/live/host.example.com/privkey.pem
+smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
+smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
+smtpd_tls_security_level=may
+
+smtp_tls_CApath=/etc/ssl/certs
+smtp_tls_security_level=may
+smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
+
+smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
+myhostname = host.example.com
+alias_maps = hash:/etc/aliases
+alias_database = hash:/etc/aliases
+mydestination = $myhostname, localhost.example.com, localhost
+relayhost = 
+mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
+mailbox_size_limit = 0
+recipient_delimiter = +
+inet_interfaces = all
+inet_protocols = all
diff --git a/templates.local/postfix/email_forwarding/master.cf b/templates.local/postfix/email_forwarding/master.cf
new file mode 100644 (file)
index 0000000..fd0d581
--- /dev/null
@@ -0,0 +1,137 @@
+#
+# Postfix master process configuration file.  For details on the format
+# of the file, see the master(5) manual page (command: "man 5 master" or
+# on-line: http://www.postfix.org/master.5.html).
+#
+# Do not forget to execute "postfix reload" after editing this file.
+#
+# ==========================================================================
+# service type  private unpriv  chroot  wakeup  maxproc command + args
+#               (yes)   (yes)   (no)    (never) (100)
+# ==========================================================================
+smtp      inet  n       -       y       -       -       smtpd
+#smtp      inet  n       -       y       -       1       postscreen
+#smtpd     pass  -       -       y       -       -       smtpd
+#dnsblog   unix  -       -       y       -       0       dnsblog
+#tlsproxy  unix  -       -       y       -       0       tlsproxy
+# Choose one: enable submission for loopback clients only, or for any client.
+#127.0.0.1:submission inet n -   y       -       -       smtpd
+#submission inet n       -       y       -       -       smtpd
+#  -o syslog_name=postfix/submission
+#  -o smtpd_tls_security_level=encrypt
+#  -o smtpd_sasl_auth_enable=yes
+#  -o smtpd_tls_auth_only=yes
+#  -o smtpd_reject_unlisted_recipient=no
+#     Instead of specifying complex smtpd_<xxx>_restrictions here,
+#     specify "smtpd_<xxx>_restrictions=$mua_<xxx>_restrictions"
+#     here, and specify mua_<xxx>_restrictions in main.cf (where
+#     "<xxx>" is "client", "helo", "sender", "relay", or "recipient").
+#  -o smtpd_client_restrictions=
+#  -o smtpd_helo_restrictions=
+#  -o smtpd_sender_restrictions=
+#  -o smtpd_relay_restrictions=
+#  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
+#  -o milter_macro_daemon_name=ORIGINATING
+# Choose one: enable submissions for loopback clients only, or for any client.
+#127.0.0.1:submissions inet n  -       y       -       -       smtpd
+#submissions     inet  n       -       y       -       -       smtpd
+#  -o syslog_name=postfix/submissions
+#  -o smtpd_tls_wrappermode=yes
+#  -o smtpd_sasl_auth_enable=yes
+#  -o smtpd_reject_unlisted_recipient=no
+#     Instead of specifying complex smtpd_<xxx>_restrictions here,
+#     specify "smtpd_<xxx>_restrictions=$mua_<xxx>_restrictions"
+#     here, and specify mua_<xxx>_restrictions in main.cf (where
+#     "<xxx>" is "client", "helo", "sender", "relay", or "recipient").
+#  -o smtpd_client_restrictions=
+#  -o smtpd_helo_restrictions=
+#  -o smtpd_sender_restrictions=
+#  -o smtpd_relay_restrictions=
+#  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
+#  -o milter_macro_daemon_name=ORIGINATING
+#628       inet  n       -       y       -       -       qmqpd
+pickup    unix  n       -       y       60      1       pickup
+cleanup   unix  n       -       y       -       0       cleanup
+qmgr      unix  n       -       n       300     1       qmgr
+#qmgr     unix  n       -       n       300     1       oqmgr
+tlsmgr    unix  -       -       y       1000?   1       tlsmgr
+rewrite   unix  -       -       y       -       -       trivial-rewrite
+bounce    unix  -       -       y       -       0       bounce
+defer     unix  -       -       y       -       0       bounce
+trace     unix  -       -       y       -       0       bounce
+verify    unix  -       -       y       -       1       verify
+flush     unix  n       -       y       1000?   0       flush
+proxymap  unix  -       -       n       -       -       proxymap
+proxywrite unix -       -       n       -       1       proxymap
+smtp      unix  -       -       y       -       -       smtp
+relay     unix  -       -       y       -       -       smtp
+        -o syslog_name=postfix/$service_name
+#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
+showq     unix  n       -       y       -       -       showq
+error     unix  -       -       y       -       -       error
+retry     unix  -       -       y       -       -       error
+discard   unix  -       -       y       -       -       discard
+local     unix  -       n       n       -       -       local
+virtual   unix  -       n       n       -       -       virtual
+lmtp      unix  -       -       y       -       -       lmtp
+anvil     unix  -       -       y       -       1       anvil
+scache    unix  -       -       y       -       1       scache
+postlog   unix-dgram n  -       n       -       1       postlogd
+#
+# ====================================================================
+# Interfaces to non-Postfix software. Be sure to examine the manual
+# pages of the non-Postfix software to find out what options it wants.
+#
+# Many of the following services use the Postfix pipe(8) delivery
+# agent.  See the pipe(8) man page for information about ${recipient}
+# and other message envelope options.
+# ====================================================================
+#
+# maildrop. See the Postfix MAILDROP_README file for details.
+# Also specify in main.cf: maildrop_destination_recipient_limit=1
+#
+maildrop  unix  -       n       n       -       -       pipe
+  flags=DRXhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
+#
+# ====================================================================
+#
+# Recent Cyrus versions can use the existing "lmtp" master.cf entry.
+#
+# Specify in cyrus.conf:
+#   lmtp    cmd="lmtpd -a" listen="localhost:lmtp" proto=tcp4
+#
+# Specify in main.cf one or more of the following:
+#  mailbox_transport = lmtp:inet:localhost
+#  virtual_transport = lmtp:inet:localhost
+#
+# ====================================================================
+#
+# Cyrus 2.1.5 (Amos Gouaux)
+# Also specify in main.cf: cyrus_destination_recipient_limit=1
+#
+#cyrus     unix  -       n       n       -       -       pipe
+#  flags=DRX user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}
+#
+# ====================================================================
+# Old example of delivery via Cyrus.
+#
+#old-cyrus unix  -       n       n       -       -       pipe
+#  flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}
+#
+# ====================================================================
+#
+# See the Postfix UUCP_README file for configuration details.
+#
+uucp      unix  -       n       n       -       -       pipe
+  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
+#
+# Other external delivery methods.
+#
+ifmail    unix  -       n       n       -       -       pipe
+  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
+bsmtp     unix  -       n       n       -       -       pipe
+  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
+scalemail-backend unix -       n       n       -       2       pipe
+  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
+mailman   unix  -       n       n       -       -       pipe
+  flags=FRX user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py ${nexthop} ${user}
diff --git a/templates.local/postfix/send_only/main.cf b/templates.local/postfix/send_only/main.cf
new file mode 100644 (file)
index 0000000..039aa8a
--- /dev/null
@@ -0,0 +1,32 @@
+# Ansible controlled! Do not change on remote host
+smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
+biff = no
+append_dot_mydomain = no
+#delay_warning_time = 4h
+readme_directory = no
+compatibility_level = 3.6
+
+
+smtpd_tls_cert_file=/etc/letsencrypt/live/host.example.com/fullchain.pem
+smtpd_tls_key_file=/etc/letsencrypt/live/host.example.com/privkey.pem
+#smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
+#smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
+smtpd_tls_security_level=may
+
+smtp_tls_CApath=/etc/ssl/certs
+smtp_tls_security_level=may
+smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
+
+smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
+myhostname = host.example.com
+alias_maps = hash:/etc/aliases
+alias_database = hash:/etc/aliases
+myorigin = /etc/mailname
+mydestination = $myhostname, localhost.example.com, localhost
+relayhost =
+mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
+mailbox_size_limit = 0
+recipient_delimiter = +
+inet_interfaces = all
+inet_protocols = all
+
diff --git a/templates.local/postfix/send_only/master.cf b/templates.local/postfix/send_only/master.cf
new file mode 100644 (file)
index 0000000..248aa99
--- /dev/null
@@ -0,0 +1,138 @@
+# Ansible controlled! Do not change on remote host
+#
+# Postfix master process configuration file.  For details on the format
+# of the file, see the master(5) manual page (command: "man 5 master" or
+# on-line: http://www.postfix.org/master.5.html).
+#
+# Do not forget to execute "postfix reload" after editing this file.
+#
+# ==========================================================================
+# service type  private unpriv  chroot  wakeup  maxproc command + args
+#               (yes)   (yes)   (no)    (never) (100)
+# ==========================================================================
+smtp      inet  n       -       y       -       -       smtpd
+#smtp      inet  n       -       y       -       1       postscreen
+#smtpd     pass  -       -       y       -       -       smtpd
+#dnsblog   unix  -       -       y       -       0       dnsblog
+#tlsproxy  unix  -       -       y       -       0       tlsproxy
+# Choose one: enable submission for loopback clients only, or for any client.
+#127.0.0.1:submission inet n -   y       -       -       smtpd
+#submission inet n       -       y       -       -       smtpd
+#  -o syslog_name=postfix/submission
+#  -o smtpd_tls_security_level=encrypt
+#  -o smtpd_sasl_auth_enable=yes
+#  -o smtpd_tls_auth_only=yes
+#  -o smtpd_reject_unlisted_recipient=no
+#     Instead of specifying complex smtpd_<xxx>_restrictions here,
+#     specify "smtpd_<xxx>_restrictions=$mua_<xxx>_restrictions"
+#     here, and specify mua_<xxx>_restrictions in main.cf (where
+#     "<xxx>" is "client", "helo", "sender", "relay", or "recipient").
+#  -o smtpd_client_restrictions=
+#  -o smtpd_helo_restrictions=
+#  -o smtpd_sender_restrictions=
+#  -o smtpd_relay_restrictions=
+#  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
+#  -o milter_macro_daemon_name=ORIGINATING
+# Choose one: enable submissions for loopback clients only, or for any client.
+#127.0.0.1:submissions inet n  -       y       -       -       smtpd
+#submissions     inet  n       -       y       -       -       smtpd
+#  -o syslog_name=postfix/submissions
+#  -o smtpd_tls_wrappermode=yes
+#  -o smtpd_sasl_auth_enable=yes
+#  -o smtpd_reject_unlisted_recipient=no
+#     Instead of specifying complex smtpd_<xxx>_restrictions here,
+#     specify "smtpd_<xxx>_restrictions=$mua_<xxx>_restrictions"
+#     here, and specify mua_<xxx>_restrictions in main.cf (where
+#     "<xxx>" is "client", "helo", "sender", "relay", or "recipient").
+#  -o smtpd_client_restrictions=
+#  -o smtpd_helo_restrictions=
+#  -o smtpd_sender_restrictions=
+#  -o smtpd_relay_restrictions=
+#  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
+#  -o milter_macro_daemon_name=ORIGINATING
+#628       inet  n       -       y       -       -       qmqpd
+pickup    unix  n       -       y       60      1       pickup
+cleanup   unix  n       -       y       -       0       cleanup
+qmgr      unix  n       -       n       300     1       qmgr
+#qmgr     unix  n       -       n       300     1       oqmgr
+tlsmgr    unix  -       -       y       1000?   1       tlsmgr
+rewrite   unix  -       -       y       -       -       trivial-rewrite
+bounce    unix  -       -       y       -       0       bounce
+defer     unix  -       -       y       -       0       bounce
+trace     unix  -       -       y       -       0       bounce
+verify    unix  -       -       y       -       1       verify
+flush     unix  n       -       y       1000?   0       flush
+proxymap  unix  -       -       n       -       -       proxymap
+proxywrite unix -       -       n       -       1       proxymap
+smtp      unix  -       -       y       -       -       smtp
+relay     unix  -       -       y       -       -       smtp
+        -o syslog_name=postfix/$service_name
+#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
+showq     unix  n       -       y       -       -       showq
+error     unix  -       -       y       -       -       error
+retry     unix  -       -       y       -       -       error
+discard   unix  -       -       y       -       -       discard
+local     unix  -       n       n       -       -       local
+virtual   unix  -       n       n       -       -       virtual
+lmtp      unix  -       -       y       -       -       lmtp
+anvil     unix  -       -       y       -       1       anvil
+scache    unix  -       -       y       -       1       scache
+postlog   unix-dgram n  -       n       -       1       postlogd
+#
+# ====================================================================
+# Interfaces to non-Postfix software. Be sure to examine the manual
+# pages of the non-Postfix software to find out what options it wants.
+#
+# Many of the following services use the Postfix pipe(8) delivery
+# agent.  See the pipe(8) man page for information about ${recipient}
+# and other message envelope options.
+# ====================================================================
+#
+# maildrop. See the Postfix MAILDROP_README file for details.
+# Also specify in main.cf: maildrop_destination_recipient_limit=1
+#
+maildrop  unix  -       n       n       -       -       pipe
+  flags=DRXhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
+#
+# ====================================================================
+#
+# Recent Cyrus versions can use the existing "lmtp" master.cf entry.
+#
+# Specify in cyrus.conf:
+#   lmtp    cmd="lmtpd -a" listen="localhost:lmtp" proto=tcp4
+#
+# Specify in main.cf one or more of the following:
+#  mailbox_transport = lmtp:inet:localhost
+#  virtual_transport = lmtp:inet:localhost
+#
+# ====================================================================
+#
+# Cyrus 2.1.5 (Amos Gouaux)
+# Also specify in main.cf: cyrus_destination_recipient_limit=1
+#
+#cyrus     unix  -       n       n       -       -       pipe
+#  flags=DRX user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}
+#
+# ====================================================================
+# Old example of delivery via Cyrus.
+#
+#old-cyrus unix  -       n       n       -       -       pipe
+#  flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}
+#
+# ====================================================================
+#
+# See the Postfix UUCP_README file for configuration details.
+#
+uucp      unix  -       n       n       -       -       pipe
+  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
+#
+# Other external delivery methods.
+#
+ifmail    unix  -       n       n       -       -       pipe
+  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
+bsmtp     unix  -       n       n       -       -       pipe
+  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
+scalemail-backend unix -       n       n       -       2       pipe
+  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
+mailman   unix  -       n       n       -       -       pipe
+  flags=FRX user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py ${nexthop} ${user}
diff --git a/templates.local/postfix/virtual b/templates.local/postfix/virtual
new file mode 100644 (file)
index 0000000..771d803
--- /dev/null
@@ -0,0 +1,6 @@
+root {{ postfix_receipient_email }}
+webmaster {{ postfix_receipient_email }}
+#ada ada@miller.com
+#all@example.com jonny@miller.com ada@miller.com
+#@example.com info@miller.com
+
diff --git a/templates.local/readme.txt b/templates.local/readme.txt
new file mode 100644 (file)
index 0000000..bb20bd9
--- /dev/null
@@ -0,0 +1,2 @@
+Objective: This directory contains templates that are specific to the project and must be adapted for each project.
+Therefore, the files may only be copied into the project, not linked.
diff --git a/templates.local/scripts/CreateSysInfo.conf b/templates.local/scripts/CreateSysInfo.conf
new file mode 100644 (file)
index 0000000..8b1c26f
--- /dev/null
@@ -0,0 +1,14 @@
+# This file will be included into CreateSysInfo
+DIR_INFO=/srv/www/sys.info/example.com
+# a blank separated list of mount points formatted with BTRFS, e.g. "/media/fs.cache /media/fs.system"
+BTRFS_FS=""
+# a blank separated list of devices that should be controlled by smartmon, e.g. 'sda sdb'
+SMART_MON_DEVS='nvme0n1'
+# set to yes if mdadm manages software raid devices
+HAS_SOFT_RAID=no
+MK_HOME_TAR=yes
+USE_ZFS=no
+# a blank separated list of "--exclude=<dir>" entries. That listed subdirs will not be saved in the TAR archive
+EXCLUDE_HOME="--exclude=jails"
+# each subdirectory of /home containing that file will not be saved as TAR archive
+TAG_FILE=.do.not.save.as.home
diff --git a/templates.local/scripts/ExampleBackup b/templates.local/scripts/ExampleBackup
new file mode 100644 (file)
index 0000000..2085655
--- /dev/null
@@ -0,0 +1,7 @@
+#! /bin/bash
+VERBOSE=-v
+QUIET=
+test "$1" = -q && VERBOSE=-q && QUIET=-q
+/usr/local/bin/CreateSysInfo $QUIET &
+/usr/local/bin/SaveDatabases $QUIET &
+
diff --git a/templates.local/users.yaml b/templates.local/users.yaml
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/templates.vars/common.yaml b/templates.vars/common.yaml
new file mode 100644 (file)
index 0000000..9079a81
--- /dev/null
@@ -0,0 +1,12 @@
+configuration_directory: /etc/ansknife
+remote_webapps_directory: "{{ configuration_directory }}/webapps.d"
+local_webapps_directory: "../webapps"
+remote_www_directory: "/home/www"
+# the system log files have maximal that size
+systemd_journal_system_max_use: 200M
+systemd_journal_system_max_file_size: 50M
+postfix_host: "example.infeos.it"
+postfix_domain: "infeos.it"
+postfix_mode: send_only
+postfix_receipient_email: "mail@example.com"
+webmaster_email: "web@example.com"
diff --git a/templates.vars/dkim.yaml b/templates.vars/dkim.yaml
new file mode 100644 (file)
index 0000000..c61e6f3
--- /dev/null
@@ -0,0 +1,8 @@
+---
+dkim_domains: ["example.com"]
+dkim_opendkim_config_dir: /etc/opendkim
+dkim_config_file: /etc/opendkim.conf
+dkim_selector: "email"
+dkim_user: opendkim
+dkim_group: opendkim
+
diff --git a/templates.vars/firewalld.yaml b/templates.vars/firewalld.yaml
new file mode 100644 (file)
index 0000000..60af0e5
--- /dev/null
@@ -0,0 +1,4 @@
+---
+# Facts (variables) of the firewall
+firewall_iface_public: enp5s0
+
diff --git a/templates.vars/mysql.yaml b/templates.vars/mysql.yaml
new file mode 100644 (file)
index 0000000..00573f9
--- /dev/null
@@ -0,0 +1,8 @@
+---
+# Creation of the central password file:
+# echo "top_secret_password" > resources/.vaults
+# Creation of the encrypted vault file:
+# ansible-vault encrypt_string --vault-password-file resources/.vault --name 'vault_dba_password' --stdin-name 'vault_dba_password' | tee vars/mysql_vault.yaml
+# Find the password file: ANSIBLE_VAULT_PASSWORD_FILE=resources/.vault
+dba_name: dba
+dba_password: "{{ vault_dba_password }}"
\ No newline at end of file
diff --git a/templates.vars/mysql_vault.yaml b/templates.vars/mysql_vault.yaml
new file mode 100644 (file)
index 0000000..5241a79
--- /dev/null
@@ -0,0 +1 @@
+vault_dba_password: TopSecret
\ No newline at end of file
diff --git a/templates.vars/packages.yaml b/templates.vars/packages.yaml
new file mode 100644 (file)
index 0000000..360b9c6
--- /dev/null
@@ -0,0 +1,8 @@
+packages_list:
+  - certbot
+  - git
+  - unzip
+  - 7zip
+  - unrar-free
+  - htop
+  - smartmontools
diff --git a/templates.vars/php.yaml b/templates.vars/php.yaml
new file mode 100644 (file)
index 0000000..a9bcf1d
--- /dev/null
@@ -0,0 +1,44 @@
+---
+# php.yaml:
+# Defines variables for the PHP role.
+# This file is used to set up the PHP environment and configuration.
+# needed variables: php_version
+
+php_packages:
+  - php{{php_version}}-common
+  - php{{php_version}}-curl
+  - php{{php_version}}-fpm
+  - php{{php_version}}-gd
+  - php{{php_version}}-igbinary
+  - php{{php_version}}-imagick
+  - php{{php_version}}-imap
+  - php{{php_version}}-intl
+  - php{{php_version}}-mbstring
+  - php{{php_version}}-memcached
+  - php{{php_version}}-msgpack
+  - php{{php_version}}-mysql
+  - php{{php_version}}-opcache
+  - php{{php_version}}-phpdbg
+  - php{{php_version}}-readline
+  - php{{php_version}}-redis
+  - php{{php_version}}-xdebug
+  - php{{php_version}}-xml
+  - php{{php_version}}-zip
+php_additional_packages:
+  - redis-server
+  - imagemagick
+
+php_ini_settings:
+  - { section: "DEFAULT", option: "memory_limit", value: "512M" }
+  - { section: "DEFAULT", option: "upload_max_filesize", value: "512M" }
+  - { section: "DEFAULT", option: "max_file_uploads", value: 100 }
+  - { section: "DEFAULT", option: "post_max_size", value: "512M" }
+  - { section: "DEFAULT", option: "max_execution_time", value: 600 }
+  - { section: "DEFAULT", option: "max_input_time", value: 600 }
+  - { section: "DEFAULT", option: "default_socket_timeout", value: 600 }
+  - { section: "Session", option: "session.save_handler", value: "redis" }
+  - { section: "Session", option: "session.save_path", value: "tcp://127.0.0.1:6379" }
+  - { section: "opcache", option: "opcache.enable", value: 1 }
+  - { section: "opcache", option: "opcache.memory_consumption", value: 512 }
+  - { section: "opcache", option: "opcache.interned_strings_buffer", value: 256 }
+
diff --git a/templates.vars/ssl-certificate.yaml b/templates.vars/ssl-certificate.yaml
new file mode 100644 (file)
index 0000000..7f42064
--- /dev/null
@@ -0,0 +1,9 @@
+---
+# "/C=DE/ST=NRW/L=Bochum/O=IT/CN={{hostname}}"
+ssl_country: DE
+ssl_state: Bavaria
+ssl_locality: Munich
+ssl_organization: 'Example company Limited'
+ssl_lifetime: 365
+ssl_rsa_key_size: 2048
+
diff --git a/templates.vars/users.yaml b/templates.vars/users.yaml
new file mode 100644 (file)
index 0000000..4d74e5d
--- /dev/null
@@ -0,0 +1,9 @@
+---
+# human users: use user ids > 1500 to avoid conflicts with ad hoc users (1000-1499)
+user_humans:
+  jonny: 1000
+  admin: 1501
+# the following users can use sudo to receive root rights
+user_sudo_members:
+  - jonny
+  - admin
diff --git a/templates.vars/webapps.yaml b/templates.vars/webapps.yaml
new file mode 100644 (file)
index 0000000..4825c48
--- /dev/null
@@ -0,0 +1,6 @@
+webapps_list:
+  - dummy
+    db_name: dbdummy5
+    db_user: dummy5
+    db_password: NeverKnown5
+    directory: /srv/www/myapp5.example.com