--- /dev/null
+# Change log of
--- /dev/null
+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
+
+
+
--- /dev/null
+scripts.templates/SetRights
\ No newline at end of file
--- /dev/null
+[defaults]
+inventory = ./inventory
+
--- /dev/null
+# Ü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
--- /dev/null
+# 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.
--- /dev/null
+# 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.
+
--- /dev/null
+# 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.
+
--- /dev/null
+# 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.
+
+#
+
+
--- /dev/null
+[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
--- /dev/null
+---
+- 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
--- /dev/null
+---
+- 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]
--- /dev/null
+---
+
+- 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
--- /dev/null
+---
+- 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
--- /dev/null
+---
+- 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
+
--- /dev/null
+---
+# 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
--- /dev/null
+---
+- 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
--- /dev/null
+---
+- 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
--- /dev/null
+---
+
+- 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
--- /dev/null
+---
+- 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
+
+
+
+
+
+
--- /dev/null
+---
+# 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
+
--- /dev/null
+---
+- 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
--- /dev/null
+---
+- 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
--- /dev/null
+---
+- 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
+
--- /dev/null
+---
+- 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
--- /dev/null
+---
+# 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
+
--- /dev/null
+---
+- 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
--- /dev/null
+---
+- 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
--- /dev/null
+---
+- 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
+
--- /dev/null
+---
+- 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
--- /dev/null
+---
+- 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
--- /dev/null
+---
+- 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
--- /dev/null
+---
+- 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
--- /dev/null
+---
+# 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
--- /dev/null
+---
+# 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
--- /dev/null
+#! /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
--- /dev/null
+#! /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
--- /dev/null
+#! /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
--- /dev/null
+#! /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
+
+
--- /dev/null
+#! /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:])
--- /dev/null
+---
+# 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 }}"
+
--- /dev/null
+---
+# 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
+
+
+...
--- /dev/null
+---
+# 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"
+ - "#============================================="
+...
--- /dev/null
+---
+## 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 }}'
+
+...
--- /dev/null
+---
+# 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
+
--- /dev/null
+---
+# 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 }}"
+ - "#============================================="
+...
--- /dev/null
+---
+# 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
--- /dev/null
+---
+# 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
--- /dev/null
+---
+# 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
--- /dev/null
+# 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
--- /dev/null
+---
+# 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
--- /dev/null
+---
+# 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 }}"
+
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+---
+# 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
--- /dev/null
+---
+# 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 }}"
--- /dev/null
+# 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
--- /dev/null
+# 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"
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
+
--- /dev/null
+# 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'
+
--- /dev/null
+{% for domain in dkim_domains %}
+{{ dkim_selector }}._domainkey.{{ domain }} {{ domain }}:{{ dkim_selector }}:/etc/opendkim/keys/{{ domain }}/{{ dkim_selector }}.private
+{% endfor %}
--- /dev/null
+{% for domain in dkim_domains %}
+*@{{ domain }} {{ dkim_selector }}._domainkey.{{ domain }}
+{% endfor %}
--- /dev/null
+<html>
+<body>
+<h1>Welcome to {{hostname}}!</h1>
+</body
+</html>
\ No newline at end of file
--- /dev/null
+<?php
+phpinfo();
\ No newline at end of file
--- /dev/null
+# 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;
+}
+
--- /dev/null
+# 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;
+ }
+}
+
--- /dev/null
+# 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
--- /dev/null
+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.
--- /dev/null
+#! /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"
--- /dev/null
+#! /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
+
--- /dev/null
+#! /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
--- /dev/null
+#! /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)
+
--- /dev/null
+# 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
--- /dev/null
+---
+# 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
--- /dev/null
+---
+# 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"]
--- /dev/null
+# Ansible controlled: do not change on server manually
+127.0.0.1
+::1
+localhost
\ No newline at end of file
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+127.0.0.1
+::1
+localhost
\ No newline at end of file
--- /dev/null
+---
+# Facts (variables) of the firewall
+
+# execute "ip addr" on the remote server to findout the interface of the public zone:
+firewall_iface_public: enp5s0
--- /dev/null
+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;
--- /dev/null
+This directory contains all active NGINX configuration files from /etc/nginx/sites-available.
+They can be found via vars/webapps.yaml.
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+#
+# 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}
--- /dev/null
+# 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
+
--- /dev/null
+# 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}
--- /dev/null
+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
+
--- /dev/null
+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.
--- /dev/null
+# 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
--- /dev/null
+#! /bin/bash
+VERBOSE=-v
+QUIET=
+test "$1" = -q && VERBOSE=-q && QUIET=-q
+/usr/local/bin/CreateSysInfo $QUIET &
+/usr/local/bin/SaveDatabases $QUIET &
+
--- /dev/null
+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"
--- /dev/null
+---
+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
+
--- /dev/null
+---
+# Facts (variables) of the firewall
+firewall_iface_public: enp5s0
+
--- /dev/null
+---
+# 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
--- /dev/null
+vault_dba_password: TopSecret
\ No newline at end of file
--- /dev/null
+packages_list:
+ - certbot
+ - git
+ - unzip
+ - 7zip
+ - unrar-free
+ - htop
+ - smartmontools
--- /dev/null
+---
+# 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 }
+
--- /dev/null
+---
+# "/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
+
--- /dev/null
+---
+# 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
--- /dev/null
+webapps_list:
+ - dummy
+ db_name: dbdummy5
+ db_user: dummy5
+ db_password: NeverKnown5
+ directory: /srv/www/myapp5.example.com