Einrichtung Server / PHP / Datenbank und Co.

Übersicht

Mini-PC / NUC mit dem Betriebssystem Ubuntu-Server und Xfce als Workflow / Arbeitstier einrichten ;o)

Linux Umgebung / Upgrade & Autoclean

				
					sudo apt-get update && sudo apt-get upgrade && sudo apt-get dist-upgrade && sudo apt-get autoremove && sudo apt-get autoclean
				
			

Fehlerberichte auswerten

				
					export PATH="$PATH:/sbin"
sudo grep -i -e fail -e error -e corrupt /var/log/syslog
sudo grep -Ei 'warn|fail|error|debug|firmware|missing|not found|fault|conflict|corrupt|disable|support|not available' /var/log/syslog
sudo journalctl -p 3 -xb
sudo dmesg
# syslog löschen
sudo tee /var/log/syslog </dev/null
				
			

Pakete installieren

				
					# Install essential packages and update the locate database
apt install -y \
    mc \                  # Midnight Commander: Ein textbasierter Dateimanager für die einfache Navigation und Dateiverwaltung
    locate \              # Schnellersuchen von Dateien anhand ihres Namens
    net-tools \           # Netzwerkwerkzeuge (z.B. ifconfig, netstat) für Netzwerkverwaltung und -diagnose
    nmap \                # Netzwerkscanner zur Erkennung von Hosts und Diensten sowie zur Sicherheitsüberprüfung
    build-essential \     # Notwendige Pakete zum Kompilieren von Software (Compiler, Bibliotheken usw.)
    && updatedb           # Aktualisiert die Datenbank, die von 'locate' verwendet wird, um die Dateisuche zu beschleunigen

				
			
				
					# Tastatur Problem NoMaschine 
# https://linux.die.net/man/1/setxkbmap
setxkbmap -model pc105 -layout de
				
			

WIFI-. Hotspot *optional

				
					# https://github.com/lakinduakash/linux-wifi-hotspot 
# For ubuntu - package outdated due to lost GPG keys
sudo add-apt-repository ppa:lakinduakash/lwh
sudo apt install linux-wifi-hotspot
				
			

MariaDB Server & Client installieren

				
					# 1. MariaDB Server und Client installieren
sudo apt-get -y install mariadb-server mariadb-client

# 2. MariaDB Dienst aktivieren und sofort starten
sudo systemctl enable --now mariadb.service

# 3. Status des MariaDB Dienstes überprüfen
sudo systemctl status mariadb.service

# 4. Prüfen, ob MariaDB Dienst aktiviert ist
sudo systemctl is-enabled mariadb.service

# 5. MariaDB als root ohne Passwort konfigurieren
# Melde dich als root bei MariaDB an
sudo mysql <<EOF
USE mysql;

# Setze das Passwort für 'root'@'localhost' auf leer
ALTER USER 'root'@'localhost' IDENTIFIED BY '';

# Setze das Passwort für 'mysql'@'localhost' auf leer
ALTER USER 'mysql'@'localhost' IDENTIFIED BY '';

EOF

# 6. Einen neuen Benutzer 'app_root' ohne Einschränkungen erstellen
sudo mysql <<EOF
# Erstelle Benutzer 'app_root' mit Passwort 'password' für lokale Verbindungen
CREATE USER 'app_root'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'app_root'@'localhost' WITH GRANT OPTION;

# Erstelle Benutzer 'app_root' mit Passwort 'password' für alle Hosts
CREATE USER 'app_root'@'%' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'app_root'@'%' WITH GRANT OPTION;

# Privilegien aktualisieren
FLUSH PRIVILEGES;

# Anzeigen der vergebenen Rechte für 'app_root'
SHOW GRANTS FOR 'app_root'@'localhost';
SHOW GRANTS FOR 'app_root'@'%';
EOF

# 7. Bei Problemen: MariaDB-Datenbank aktualisieren
sudo mysql_upgrade -u root --force

# 8. Status des MariaDB Dienstes erneut überprüfen
sudo systemctl status mariadb

				
			

PHP / Apache 2

				
					# 1. Apache2 installieren
# Aktualisiert die Paketliste und installiert den Apache-Webserver.
sudo apt-get update
sudo apt-get -y install apache2

# 2. PHP PPA hinzufügen
# Fügt das PHP-Repository von Ondřej Surý hinzu, um Zugriff auf mehrere PHP-Versionen zu erhalten.
sudo add-apt-repository ppa:ondrej/php
# Drücke Enter, wenn du dazu aufgefordert wirst.

# 3. Paketliste aktualisieren
# Aktualisiert die Paketliste nach dem Hinzufügen des neuen Repositories.
sudo apt-get update

# 4. PHP-Versionen und benötigte Module installieren
# Installiert PHP-Versionen 8.1 bis 8.4 sowie die notwendigen PHP-Module.

# PHP 8.1 installieren
sudo apt-get install -y php8.1-fpm php8.1-cli php8.1-dev php8.1-pgsql php8.1-sqlite3 php8.1-gd \
php8.1-curl php8.1-memcached php8.1-imap php8.1-mysql php8.1-mbstring php8.1-xml \
php8.1-imagick php8.1-zip php8.1-bcmath php8.1-soap php8.1-intl php8.1-readline \
php8.1-common php8.1-pspell php8.1-tidy php8.1-xsl php8.1-opcache php8.1-apcu

# PHP 8.2 installieren
sudo apt-get install -y php8.2-fpm php8.2-cli php8.2-dev php8.2-pgsql php8.2-sqlite3 php8.2-gd \
php8.2-curl php8.2-memcached php8.2-imap php8.2-mysql php8.2-mbstring php8.2-xml \
php8.2-imagick php8.2-zip php8.2-bcmath php8.2-soap php8.2-intl php8.2-readline \
php8.2-common php8.2-pspell php8.2-tidy php8.2-xsl php8.2-opcache php8.2-apcu

# PHP 8.3 installieren
sudo apt-get install -y php8.3-fpm php8.3-cli php8.3-dev php8.3-pgsql php8.3-sqlite3 php8.3-gd \
php8.3-curl php8.3-memcached php8.3-imap php8.3-mysql php8.3-mbstring php8.3-xml \
php8.3-imagick php8.3-zip php8.3-bcmath php8.3-soap php8.3-intl php8.3-readline \
php8.3-common php8.3-pspell php8.3-tidy php8.3-xsl php8.3-opcache php8.3-apcu

# PHP 8.4 installieren
sudo apt-get install -y php8.4-fpm php8.4-cli php8.4-dev php8.4-pgsql php8.4-sqlite3 php8.4-gd \
php8.4-curl php8.4-memcached php8.4-imap php8.4-mysql php8.4-mbstring php8.4-xml \
php8.4-imagick php8.4-zip php8.4-bcmath php8.4-soap php8.4-intl php8.4-readline \
php8.4-common php8.4-pspell php8.4-tidy php8.4-xsl php8.4-opcache php8.4-apcu

# 5. Standard-PHP-Version konfigurieren
# Ermöglicht die Auswahl der gewünschten PHP-Version als Standard.
sudo update-alternatives --config php
# Beispielausgabe:
# Es gibt 4 Auswahlmöglichkeiten für die Alternative php (welche /usr/bin/php bereitstellen).
#
#   Auswahl    Pfad             Priorität Status
# ------------------------------------------------------------
# * 0          /usr/bin/php8.4   84        automatischer Modus
#   1          /usr/bin/php8.1   81        manueller Modus
#   2          /usr/bin/php8.2   82        manueller Modus
#   3          /usr/bin/php8.3   83        manueller Modus
#   4          /usr/bin/php8.4   84        manueller Modus
#
# Drücke die entsprechende Nummer und dann Enter, um die gewünschte PHP-Version auszuwählen.

# 6. Apache-Module aktivieren
# Aktiviert die notwendigen Apache-Module für die PHP-Verarbeitung.
sudo a2enmod proxy_fcgi setenvif

# 7. PHP-FPM Konfiguration für Apache aktivieren
# Aktiviert die PHP-FPM-Konfiguration für die gewünschte PHP-Version (hier PHP 8.3).
sudo a2enconf php8.3-fpm
# Ersetze 'php8.3-fpm' durch die gewünschte PHP-Version, falls nötig.

# 8. Apache neu starten
# Startet den Apache-Webserver neu, um die Änderungen zu übernehmen.
sudo systemctl restart apache2

# 9. Memcached und zugehörige Tools installieren
# Installiert Memcached sowie die zugehörigen Tools für Caching-Zwecke.
sudo apt-get install -y memcached libmemcached-tools

sudo mcedit /etc/memcached.conf

# Lausche nur auf IPv4 localhost
-l 127.0.0.1
#-l ::1  // ausdokumentieren IPv6

sudo netstat -tulnp | grep 11211
tcp        0      0 127.0.0.1:11211         0.0.0.0:*               LISTEN      33355/memcached

# 10. Dienste überprüfen
# Überprüft den Status der installierten Dienste, um sicherzustellen, dass alles ordnungsgemäß läuft.
sudo systemctl status apache2 --no-pager
sudo systemctl status php8.3-fpm --no-pager
sudo systemctl status memcached --no-pager
# Ersetze 'php8.3-fpm' durch die von dir verwendete PHP-Version, falls erforderlich

				
			

mod_http2 Modul installieren und aktivieren

				
					# Apache-Module aktivieren
sudo a2enmod ssl                  # SSL-Unterstützung
sudo a2enmod http2               # HTTP/2-Unterstützung
sudo a2enmod proxy_fcgi setenvif # Für PHP-FPM
sudo a2enmod proxy               # Für Mailhog
sudo a2enmod proxy_http          # Für Mailhog
sudo a2enmod substitute          # Für HTML-Modifikationen

# PHP-Module konfigurieren
sudo a2dismod php8.4             # Apache PHP-Modul deaktivieren
sudo a2enconf php8.4-fpm         # PHP-FPM aktivieren

# MPM-Module umstellen
sudo a2dismod mpm_prefork        # Prefork deaktivieren
sudo a2enmod mpm_event          # Event-MPM aktivieren

# Dienste aktivieren und neu starten
sudo systemctl enable php8.4-fpm
sudo systemctl enable apache2

# Konfiguration testen und neu starten
sudo apache2ctl configtest
sudo systemctl restart php8.4-fpm
sudo systemctl restart apache2

# Schnellcheck HTTP/2
curl -v --http2 https://web.test 2>&1 | grep -E '(accepted h2|using HTTP/2|^< HTTP/2)'


* ALPN: server accepted h2
* using HTTP/2
< HTTP/2 200



				
			

Adminer

				
					# Kurze Installationsanleitung für Adminer auf Ubuntu mit Apache

# 1. Adminer installieren
sudo apt update
sudo apt install -y adminer

# 2. Adminer Apache Konfiguration erstellen
sudo bash -c 'cat > /etc/adminer/adminer.conf <<EOL
Alias /adminer /usr/share/adminer/adminer
EOL'

# 3. Symlink zur Apache Konfigurationsverzeichnis erstellen und aktivieren
sudo ln -s /etc/adminer/adminer.conf /etc/apache2/conf-available/adminer.conf
sudo a2enconf adminer

# 4. Apache neu laden und Status prüfen
sudo systemctl reload apache2
sudo systemctl status apache2 --no-pager

# 5. Adminer PHP bearbeiten, um Login ohne Passwort zu ermöglichen
# ⚠️ Warnung: Dies ist unsicher und nur für Entwicklungsumgebungen geeignet
sudo sed -i "/login($_e,\$F)/c\login($_e,\$F){return true;}" /usr/share/adminer/adminer.php

# 6. Browser öffnen und Adminer aufrufen
# Gehe zu http://your_server_ip/adminer
echo "Adminer ist unter http://your_server_ip/adminer erreichbar. Logge dich als root ohne Passwort ein."

# Optional: Automatisch die Firewall anpassen (wenn UFW aktiviert ist)
# sudo ufw allow 'Apache Full'

# Abschluss
echo "Adminer erfolgreich installiert und konfiguriert."




				
			

rc-local

				
					# 1. Erstellen der rc-local.service Datei
sudo bash -c 'cat > /etc/systemd/system/rc-local.service << EOF
[Unit]
Description=/etc/rc.local Compatibility
ConditionPathExists=/etc/rc.local

[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
StandardOutput=tty
RemainAfterExit=yes
SysVStartPriority=99

[Install]
WantedBy=multi-user.target
EOF'

# 2. Erstellen der /etc/rc.local Datei
sudo bash -c 'cat > /etc/rc.local << EOF
#!/bin/bash
#
# rc.local
# Dieses Skript wird am Ende jedes Mehrbenutzer-Runlevels ausgeführt.
# Stellen Sie sicher, dass das Skript mit "exit 0" bei Erfolg oder einem anderen
# Wert bei Fehler endet.

# IP-Adresse anzeigen
_IP=\$(hostname -I) || true
if [ "\$_IP" ]; then
  printf "My IP address is %s\n" "\$_IP"
fi

export PATH="\$PATH:/sbin"

###########################################
## PHP-Skript beim Start ausführen       ##
###########################################

# /bin/sleep 5 && php /home/web/www/domains/meine_anwendung.php

exit 0
EOF'

# 3. Setzen der Ausführungsrechte
sudo chmod a+x /etc/rc.local

# 4. Aktivieren und Starten des rc-local Dienstes
sudo systemctl enable rc-local.service
sudo systemctl start rc-local.service

# 5. Überprüfen des Dienststatus
systemctl status rc-local.service

				
			

Virtuell Hosts

				
					# 1. Öffne die Apache-Konfigurationsdatei mit mcedit
mcedit /etc/apache2/conf-available/all-virtuell-host.conf

# 2. Zeige den Inhalt der Konfigurationsdatei an
cat /etc/apache2/conf-available/all-virtuell-host.conf

# Inhalt der Konfigurationsdatei:
###########################################
### hier werden unsere *.conf includiert ##
###########################################
IncludeOptional /var/www/html/*/apache/*.conf

# 3. Aktiviere das Apache-Modul vhost_alias
sudo a2enmod vhost_alias

# 4. Starte Apache neu, um die Änderungen zu übernehmen
sudo systemctl restart apache2

# 5. Überprüfe die Apache-Konfiguration auf Fehler
sudo apachectl configtest

# 6. Aktiviere die benutzerdefinierte virtuelle Host-Konfiguration
sudo a2enconf all-virtuell-host.conf

# 7. Lade die Apache-Konfiguration neu
sudo systemctl reload apache2

				
			

SSL - Einrichtung

				
					# 1. Installiere benötigte Tools für SSL
sudo apt-get -y install libnss3-tools

# 2. Erstelle Verzeichnis für SSL-Zertifikate und wechsle hinein
mkdir -p /home/ssl
cd /home/ssl

# 3. Lade mkcert herunter
wget -O mkcert https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64

# 4. Mache mkcert ausführbar und verschiebe es in /usr/local/bin
chmod +x mkcert
sudo mv mkcert /usr/local/bin

# 5. Installiere CA-Zertifikate und konfiguriere mkcert
sudo apt-get install -y ca-certificates
sudo update-ca-certificates
mkcert -install

# 6. Aktiviere das SSL-Modul in Apache und starte Apache neu
sudo a2enmod ssl
sudo service apache2 restart

# 7. Setze ServerName auf localhost
echo "ServerName localhost" | sudo tee /etc/apache2/conf-available/servername.conf

# 8. Aktiviere die ServerName-Konfiguration und lade Apache neu
sudo a2enconf servername
sudo service apache2 reload

# 9. Kopiere das Root-CA-Zertifikat in das Samba-Verzeichnis
cp ~/.local/share/mkcert/rootCA.pem /home/web/www/html/SSL-Zertifikat

# 10. Anleitung zum Importieren des Zertifikats in Windows:
#    a. Speichere die Datei rootCA.pem auf deinem Windows-Desktop.
#    b. Öffne den Zertifikatsmanager:
#       - Drücke Win + R, gib certmgr.msc ein und drücke Enter.
#    c. Importiere das Zertifikat:
#       - Navigiere zu: Vertrauenswürdige Stammzertifizierungsstellen > Zertifikate.
#       - Rechtsklick auf Zertifikate > Alle Aufgaben > Importieren.
#       - Folge dem Import-Assistenten:
#         i. Wähle die Datei rootCA.pem aus dem Samba-Verzeichnis (\\192.169.1.165\web-nuc-www\SSL-Zertifikat).
#         ii. Bestätige den Import in den Speicher "Vertrauenswürdige Stammzertifizierungsstellen".
#         iii. Schließe den Assistenten nach erfolgreichem Import.

				
			

IP 4 erzwingen

				
					# 1. GRUB konfigurieren, um IPv6 zu deaktivieren
sudo nano /etc/default/grub
# Füge 'ipv6.disable=1' zur GRUB_CMDLINE_LINUX-Zeile hinzu, z.B.:
# GRUB_CMDLINE_LINUX="... ipv6.disable=1"
sudo update-grub

# 2. Sysctl-Konfiguration zur Deaktivierung von IPv6 erstellen
sudo bash -c 'cat <<EOF > /etc/sysctl.d/disable-ipv6.conf
# Deaktiviert IPv6 für alle Interfaces
net.ipv6.conf.all.disable_ipv6 = 1
# Deaktiviert IPv6 für Standard-Interfaces
net.ipv6.conf.default.disable_ipv6 = 1
# Deaktiviert IPv6 für das Loopback-Interface
net.ipv6.conf.lo.disable_ipv6 = 1
EOF'

# 3. Sysctl-Einstellungen anwenden
sudo sysctl -p /etc/sysctl.d/disable-ipv6.conf

# 4. IPv6-Kernelmodul blacklisten
sudo bash -c 'echo "blacklist ipv6" > /etc/modprobe.d/blacklist-ipv6.conf'

# 5. System neu starten, um alle Änderungen zu übernehmen
sudo reboot

# 6. prüfen

# Überprüfe die Netzwerkschnittstellen erneut
ip a | grep inet6

# Überprüfe die aktiven Sysctl-Einstellungen für IPv6
sysctl -a | grep ipv6 | grep disable_ipv6

# Überprüfe die GRUB-Konfiguration auf korrekte Einträge
cat /etc/default/grub | grep ipv6.disable

# Überprüfe die Blacklist-Datei für IPv6
cat /etc/modprobe.d/blacklist-ipv6.conf

# IPv6-Kernelmodul geladen 
lsmod | grep ipv6




				
			

Ich verwende ausschließlich IPv4 in meiner Testumgebung, um den Datenverkehr präzise überwachen und kontrollieren zu können. IPv4 ermöglicht mir eine klare Übersicht über alle Kommunikationsprozesse in meinem Netzwerk, was die Verwaltung und Sicherheit erheblich vereinfacht.

Während IPv6 zweifellos die Zukunft der Internetprotokollierung darstellt und langfristig notwendig ist, gibt es legitime Gründe, warum manche  Benutzer vorerst weiterhin IPv4 nutzen möchten ;o)

				
					# Zeige alle aktiven IPv4-Verbindungen und lauschenenden Ports
netstat -4 -tuln

# Alternative zu netstat: Zeige alle IPv4-Verbindungen und lauschenenden Ports mit ss
ss -4 -tuln

# Zeige die IPv4-Adressen aller Netzwerkinterfaces
ip -4 addr show

# Zeige die IPv4-Routing-Tabelle
ip -4 route show

# Pinge eine IPv4-Adresse, um die Konnektivität zu überprüfen
ping 8.8.8.8

# Traceroute zur Verfolgung des Pfads zu einer IPv4-Adresse
traceroute -4 google.com

# Zeige detaillierte Netzwerkstatistiken für IPv4
netstat -s -4

# Überprüfe offene IPv4-Ports und die zugehörigen Prozesse
sudo netstat -tulpn -4

# Verwende ifconfig (falls installiert) zur Anzeige der IPv4-Konfiguration
ifconfig | grep 'inet '

# Zeige die aktuelle IPv4-Adresszuweisung für alle Interfaces
hostname -I | awk '{print $1}'

# Überwache in Echtzeit die Netzwerkverbindungen für IPv4
watch -n 1 "netstat -4 -an | grep ESTABLISHED"

				
			

Website einrichten

				
					# ----------------------------------------------
# 1. Verzeichnisstruktur für die Website erstellen
# ----------------------------------------------
mkdir -p /var/www/html/web.test/{dist,zugang,ssl,apache,doku,logs}
mkdir -p /var/www/html/web.test/logs/{apache,php}

# ----------------------------------------------
# 2. SSL-Zertifikate mit mkcert erstellen
# ----------------------------------------------
cd /var/www/html/web.test/ssl
mkcert web.test '*.web.test'

# ----------------------------------------------
# 3. Notwendige Apache-Module aktivieren
# ----------------------------------------------
sudo a2enmod substitute
sudo a2enmod filter

# ----------------------------------------------
# 4. Virtuelle Host-Konfiguration erstellen
# ----------------------------------------------
cd /var/www/html/web.test/apache/

# Erstelle die Datei web.test.conf mit den folgenden Inhalten
sudo bash -c 'cat > web.test.conf <<EOF
################################################################################################
##	Für Änderungen muss beides neu gestartet werden dann zieht  SetEnv PHP_ADMIN_VALUE
##
##	apachectl configtest
##
##	systemctl restart php8.4-fpm & service apache2 restart
##	
##	systemctl enable mailhog
##	
##	http://localhost:8025/
##
## 
##
################################################################################################

<VirtualHost *:80>
   DocumentRoot /var/www/html/web.test/dist/
   ServerName web.test
   ServerAlias www.web.test
   LogFormat "%v %l %u %t \\"%r\\" %>s %b" comonvhost
   CustomLog /var/www/html/web.test/logs/apache/port_80_access_log comonvhost

    # Permanente Weiterleitung von HTTP zu HTTPS
    Redirect permanent / https://www.web.test
</VirtualHost>

<VirtualHost *:443>
   DocumentRoot /var/www/html/web.test/dist/
   ServerName web.test
   ServerAlias www.web.test
   SSLEngine on
   SSLCertificateFile /var/www/html/web.test/ssl/web.test+1.pem
   SSLCertificateKeyFile /var/www/html/web.test/ssl/web.test+1-key.pem
   SetEnv LOCAL true



    <IfModule proxy_fcgi_module>
        # Setzt PHP-Admin-Umgebungsvariablen für FastCGI
        ProxyFCGISetEnvIf "true" PHP_ADMIN_VALUE " upload_max_filesize=1G; \n post_max_size=500M; \n memory_limit=1G; \n max_input_time=300;"
    </IfModule>


   LogFormat "%v %l %u %t \\"%r\\" %>s %b" comonvhost
   CustomLog /var/www/html/web.test/logs/apache/port_443_access_log comonvhost
   <Directory "/var/www/html/web.test/dist">
       Options Indexes FollowSymLinks MultiViews
       AllowOverride all
       Require all granted
   </Directory>
   <FilesMatch \.php\$>
      # Für Apache Version 2.4.10 und höher
      # SetHandler "proxy:unix:/run/php/php8.1-fpm.sock|fcgi://localhost"
      SetHandler "proxy:unix:/run/php/php8.4-fpm.sock|fcgi://localhost"
   </FilesMatch>
   AddOutputFilterByType SUBSTITUTE text/html
   Substitute "s|</head>|<script charset=\'UTF-8\' type=\'text/javascript\'>document.title =\'OFFLINE :: \' + document.title </script></head>|inq"
</VirtualHost>
EOF'

# ----------------------------------------------
# 5. Apache-Konfiguration testen
# ----------------------------------------------
sudo apachectl configtest

# ----------------------------------------------
# 6. Virtuelle Host-Konfiguration aktivieren und Apache neu laden
# ----------------------------------------------
sudo a2enconf all-virtuell-host.conf
sudo systemctl reload apache2

# ----------------------------------------------
# 7. Testdateien erstellen
# ----------------------------------------------
# Erstelle eine PHP-Info-Datei
sudo bash -c 'cat >/var/www/html/web.test/dist/info.php <<EOF
<?php
phpinfo();
?>
EOF'

# Erstelle eine einfache index.html
sudo bash -c 'cat >/var/www/html/web.test/dist/index.html <<EOF
<h1>hallo Server</h1>
EOF'

# ----------------------------------------------
# 8. Apache-Webserver neu starten
# ----------------------------------------------
sudo systemctl restart apache2

# ----------------------------------------------
# 9. Hosts-Datei bearbeiten
# ----------------------------------------------
# Öffne die Hosts-Datei mit mcedit
mcedit /etc/hosts

# Alternativ: Füge die Zeile direkt hinzu
sudo bash -c 'echo "127.0.1.1 web.test" >> /etc/hosts'

# Zeige den Inhalt der Hosts-Datei an
cat /etc/hosts

# Beispielausgabe:
# 127.0.0.1 localhost
# 127.0.1.1 nucneu
# 127.0.1.1 web.test

# ----------------------------------------------
# 10. Apache-Konfiguration erneut testen und neu laden
# ----------------------------------------------
sudo apachectl configtest
sudo systemctl reload apache2

				
			

Apache Virtual Host incl. Sicherheitsheader

				
					################################################################################################
# Apache Virtual Host Konfiguration für web.test
# Version: 1.3
# Datum: 19.12.2024
#
# Wichtige Befehle für die Verwaltung:
# - Apache Syntax Test: apachectl configtest
# - Services neu starten: systemctl restart php8.4-fpm && systemctl restart apache2
# - Mailhog aktivieren: systemctl enable mailhog
# - Mailhog Webinterface: https://web.test/mailhog/
#
# Voraussetzungen:
# - Apache 2.4.58 oder höher
# - PHP 8.4-FPM
# - Mailhog installiert
# - SSL-Zertifikate generiert mit mkcert
# - Aktivierte Apache-Module:
#   * ssl
#   * http2
#   * proxy_fcgi
#   * proxy
#   * proxy_http
#   * substitute
#   * headers
#   * rewrite
#   * mpm_event (WICHTIG: nicht mpm_prefork!)
################################################################################################

# ======================================================================
# 1. HTTP (Port 80) Konfiguration - Redirect auf HTTPS mit www
# ======================================================================
<VirtualHost *:80>
    # Basiseinstellungen für HTTP
    DocumentRoot /var/www/html/web.test/dist
    ServerName web.test
    ServerAlias www.web.test

    # HTTP Access Logging
    LogFormat "%v %l %u %t \"%r\" %>s %b" comonvhost
    CustomLog /var/www/html/web.test/logs/apache/port_80_access_log comonvhost

    # Permanente Weiterleitung aller HTTP-Anfragen auf HTTPS mit www
    RewriteEngine On

    # Bedingung 1: HTTPS ist deaktiviert
    RewriteCond %{HTTPS} off [OR]

    # Bedingung 2: Der Hostname beginnt nicht mit 'www.'
    RewriteCond %{HTTP_HOST} !^www\. [NC]

    # Regel: Weiterleitung zur 'https://www.'-Version der aktuellen Domain
    RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [L,R=301]
</VirtualHost>

# ======================================================================
# 2. HTTPS (Port 443) Hauptkonfiguration
# ======================================================================
<VirtualHost *:443>
    ################################################################################################
    # Basis Server-Konfiguration
    ################################################################################################

    # Pfad zum Webroot ohne abschließenden Slash
    DocumentRoot /var/www/html/web.test/dist

    # Hauptdomain für den Server
    ServerName web.test

    # Alternative Domains (z.B. mit www)
    ServerAlias www.web.test

    ################################################################################################
    # SSL/TLS Konfiguration
    ################################################################################################

    # Aktiviert SSL/TLS
    SSLEngine on

    # Pfade zu den SSL-Zertifikaten (erstellt mit mkcert)
    SSLCertificateFile /var/www/html/web.test/ssl/web.test+1.pem
    SSLCertificateKeyFile /var/www/html/web.test/ssl/web.test+1-key.pem

    # SSL/TLS Sicherheitseinstellungen
    # Deaktiviert veraltete, unsichere SSL/TLS Versionen
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1

    # Konfiguriert sichere Cipher-Suites
    SSLCipherSuite HIGH:!aNULL:!MD5:!3DES

    # Lässt Server die Cipher-Reihenfolge bestimmen
    SSLHonorCipherOrder on

    # Deaktiviert SSL-Kompression (Schutz gegen CRIME-Angriffe)
    SSLCompression off

    ################################################################################################
    # Umgebungsvariablen
    ################################################################################################

    # Kennzeichnet diese Installation als lokale Entwicklungsumgebung
    SetEnv LOCAL true

    # Setzt Environment-Variables für /wp-admin/
    SetEnvIf Request_URI "^/wp-admin/" WP_ADMIN=true

    ################################################################################################
    # HTTP/2 Optimierungen
    ################################################################################################

    # Aktiviert HTTP/2 und HTTP/1.1 als Fallback
    Protocols h2 http/1.1

    # Ermöglicht direkte HTTP/2-Verbindungen
    H2Direct on

    # Erzwingt moderne TLS-Versionen für HTTP/2
    H2ModernTLSOnly on

    # Aktiviert HTTP/2 Server Push
    H2Push on

    # Konfiguriert die Server Push Priorität
    H2PushPriority * after

    # Maximale Anzahl gleichzeitiger HTTP/2 Streams
    H2MaxSessionStreams 100

    ################################################################################################
    # PHP-FPM Konfiguration via FastCGI
    ################################################################################################

    <IfModule proxy_fcgi_module>
        # Setzt PHP-Admin-Umgebungsvariablen für FastCGI
        ProxyFCGISetEnvIf "true" PHP_ADMIN_VALUE "sendmail_path=/usr/local/bin/mhsendmail\nupload_max_filesize=1G\npost_max_size=500M\nmemory_limit=1G\nmax_input_time=300"
    </IfModule>

    ################################################################################################
    # Mailhog Proxy-Konfiguration
    ################################################################################################

    # Leitet /mail/ Anfragen an Mailhog weiter
    ProxyPass /mail/ http://127.0.0.1:8080/
    ProxyPassReverse /mail/ http://127.0.0.1:8080/

    # Mailhog Zugriffskonfiguration
    <Location /mail/>
        Require all granted
    </Location>

    # Mailhog Proxy-Zugriffsrechte
    <Proxy "http://127.0.0.1:8080/*">
        Require all granted
    </Proxy>

    ################################################################################################
    # Logging-Konfiguration
    ################################################################################################

    # Definiert das Log-Format mit vHost-Informationen
    LogFormat "%v %l %u %t \"%r\" %>s %b" comonvhost

    # Speicherort und Name der Access-Log-Datei
    CustomLog /var/www/html/web.test/logs/apache/port_443_access_log comonvhost

    ################################################################################################
    # Sicherheitsheader setzen
    ################################################################################################

    <IfModule headers_module>
        # Schutz vor Cross-Site Scripting (XSS)
        Header set X-XSS-Protection "1; mode=block"

        # Verhindert, dass die Seite in Frames von anderen Domains eingebettet wird
        Header set X-Frame-Options "SAMEORIGIN"

        # Verhindert MIME-Typ Sniffing
        Header set X-Content-Type-Options "nosniff"

        # Entfernt den X-Powered-By-Header, um Informationen über die verwendete Technologie zu verbergen
        Header always unset X-Powered-By

        # Beschränkt Cross-Domain-Policies
        Header set X-Permitted-Cross-Domain-Policies "none"

        # Strenge Referrer-Policy
        Header set Referrer-Policy "strict-origin-when-cross-origin"

        # Erzwingt die Nutzung von HTTPS für alle zukünftigen Anfragen
        Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

        # Optimiert das Caching-Verhalten
        Header append Vary "Accept-Encoding, User-Agent, Referer"

        # CORS-Header: Erlaubt Anfragen von allen Ursprüngen
        Header set Access-Control-Allow-Origin "*"
        Header set Access-Control-Allow-Methods "GET, POST, OPTIONS"
        Header set Access-Control-Allow-Headers "Origin, Content-Type, Accept"

        # Berechtigungs-Politik zur Kontrolle der Nutzung von Browser-Features
        Header set Permissions-Policy "camera=(), fullscreen=(self), geolocation=(*), microphone=(self), autoplay=(self), payment=(), usb=(), vibrate=(), notifications=(self), accelerometer=(self), gyroscope=(self)"

        # Content-Security-Policy (CSP) für die gesamte Website
        Header set Content-Security-Policy "default-src 'self' https://maps.google.com https://www.google.com; \
        img-src 'self' data: https://s.w.org https://images.dmca.com https://secure.gravatar.com https://www.youtube.com https://maps.google.com https://translate.google.com https://fonts.gstatic.com https://www.gstatic.com https://www.google.com https://translate.googleapis.com; \
        font-src 'self' data: https://fonts.gstatic.com; \
        connect-src 'self' https://t.clarity.ms https://wid.cert-bund.de https://www.youtube.com https://maps.google.com https://translate.google.com https://translate.googleapis.com https://translate-pa.googleapis.com; \
        style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com https://www.gstatic.com https://translate.google.com https://maps.google.com; \
        script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://ajax.googleapis.com https://images.dmca.com https://www.googletagmanager.com https://www.youtube.com https://maps.google.com https://translate.google.com https://translate.googleapis.com https://www.google-analytics.com https://www.clarity.ms; \
        script-src-elem 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://www.google-analytics.com https://www.clarity.ms https://www.googletagmanager.com https://www.gstatic.com https://images.dmca.com https://maps.google.com https://translate.google.com https://translate.googleapis.com https://translate-pa.googleapis.com; \
        object-src 'none'; \
        worker-src 'self' blob:; \
        frame-src 'self' https://www.youtube.com https://www.youtube-nocookie.com https://maps.google.com https://translate.google.com https://www.google.com; \
        frame-ancestors 'self'; \
        form-action 'self'; \
        base-uri 'self';"

        # Hinzugefügte Sicherheitsheader

        # Schutz vor bestimmten Dateidownload-Angriffen in IE
        Header set X-Download-Options "noopen"

        # Implementiert Certificate Transparency (Expect-CT)
        Header always set Expect-CT "max-age=86400, enforce, report-uri='https://web.test/report-ct'"

        # Cross-Origin Resource Policy (CORP)
        Header set Cross-Origin-Resource-Policy "same-origin"

        # Clear-Site-Data Header
        Header set Clear-Site-Data "\"cache\", \"cookies\", \"storage\", \"executionContexts\""

        # Entfernen der Sicherheitsheader für /wp-admin/
        Header unset Content-Security-Policy env=WP_ADMIN
        Header unset X-Frame-Options env=WP_ADMIN
        Header unset X-Content-Type-Options env=WP_ADMIN
        Header unset Referrer-Policy env=WP_ADMIN
        Header unset Strict-Transport-Security env=WP_ADMIN
        Header unset Permissions-Policy env=WP_ADMIN
    </IfModule>

    ################################################################################################
    # Sicherheitsheader für das Verzeichnis "/lala/" deaktivieren
    ################################################################################################

    <LocationMatch "^/lala/">
        # Entfernen der Sicherheitsheader für alle Anfragen an "/lala/"
        Header unset Content-Security-Policy
        Header unset X-Frame-Options
        Header unset X-Content-Type-Options
        Header unset Referrer-Policy
        Header unset Strict-Transport-Security
        Header unset Permissions-Policy
    </LocationMatch>

    ################################################################################################
    # Verzeichnis-Konfiguration für das Webroot
    ################################################################################################

    <Directory "/var/www/html/web.test/dist">
        # Verzeichnisoptionen:
        # - Indexes: Verzeichnisauflistung
        # - FollowSymLinks: Symbolische Links erlaubt
        # - MultiViews: Content-Negotiation
        Options Indexes FollowSymLinks MultiViews

        # Erlaubt .htaccess Dateien
        AllowOverride All

        # Gewährt generellen Zugriff
        Require all granted

        # Beschränkung der erlaubten HTTP-Methoden
        <LimitExcept GET POST HEAD>
            Require all denied
        </LimitExcept>
    </Directory>

    ################################################################################################
    # PHP Dateiverarbeitung
    ################################################################################################

    <FilesMatch \.php$>
        # Verbindung zum PHP-FPM Socket
        SetHandler "proxy:unix:/run/php/php8.4-fpm.sock|fcgi://localhost"
    </FilesMatch>

    ################################################################################################
    # HTML-Modifikation für Offline-Kennzeichnung
    ################################################################################################

    # Aktiviert Substitution für HTML-Dokumente
    AddOutputFilterByType SUBSTITUTE text/html

    # Fügt JavaScript für "OFFLINE" im Titel ein
    Substitute "s|</head>|<script charset='UTF-8' type='text/javascript'>document.title ='OFFLINE :: ' + document.title;</script></head>|inq"

</VirtualHost>

################################################################################################
# Erforderliche Verzeichnisstruktur:
# /var/www/html/web.test/
# ├── dist/                 # DocumentRoot
# ├── logs/
# │   └── apache/           # Log-Dateien
# └── ssl/                  # SSL-Zertifikate
#
# Erforderliche Berechtigungen:
# - dist: www-data:www-data (755)
# - logs: www-data:www-data (755)
# - ssl: root:root (600)
################################################################################################

				
			

Zusammenfassung der Sicherheitsmerkmale

  1. HTTPS-Weiterleitung mit www-Präfix
  2. Sichere SSL/TLS-Einstellungen
  3. HTTP/2 Optimierungen
  4. PHP-FPM Integration
  5. Mailhog Proxy-Konfiguration
  6. Umfassende Sicherheitsheader:
    • X-XSS-Protection
    • X-Frame-Options
    • X-Content-Type-Options
    • X-Permitted-Cross-Domain-Policies
    • Referrer-Policy
    • Strict-Transport-Security (HSTS)
    • Vary
    • Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers
    • Permissions-Policy
    • Content-Security-Policy (CSP)
    • X-Download-Options
    • Expect-CT
    • Cross-Origin-Resource-Policy (CORP)
    • Clear-Site-Data
  7. Globale Sicherheitskonfiguration (ServerSignature, ServerTokens, TraceEnable)
  8. Ausnahmen für spezifische Verzeichnisse (/wp-admin/ und /lalal/)
  9. Beschränkung der erlaubten HTTP-Methoden auf GET, POST und HEAD

Bindfs

Bindfs kurz erklärt

Bindfs ist ein FUSE-basiertes Dateisystem für Linux, das es ermöglicht, ein bestehendes Verzeichnis an einem anderen Ort im Dateisystem zu mounten. Dabei können Benutzer- und Gruppenbesitz sowie Dateiberechtigungen flexibel angepasst werden, ohne die Originaldateien oder -verzeichnisse zu verschieben. Dies erlaubt eine einfache Verwaltung von Zugriffsrechten für verschiedene Benutzer oder Gruppen, ohne dass Root-Rechte erforderlich sind.

Hauptfunktionen von Bindfs:

  • Neuzuordnung von Besitzrechten: Ändert den Besitzer und die Gruppe der gemounteten Dateien und Verzeichnisse.
  • Anpassung von Berechtigungen: Legt spezifische Zugriffsrechte fest, die vom ursprünglichen Dateisystem unabhängig sind.
  • Benutzerfreundlichkeit: Ermöglicht das Mounten ohne Root-Zugriff, was die Sicherheit erhöht und die Verwaltung vereinfacht.

Anwendungsbeispiele:

  • Gemeinsame Nutzung von Verzeichnissen zwischen verschiedenen Benutzern mit unterschiedlichen Berechtigungen.
  • Anpassung von Zugriffsrechten für Webserver-Verzeichnisse, ohne die ursprünglichen Besitzverhältnisse zu ändern.
  • Temporäres Überlagern von Dateisystemen für spezifische Anwendungsfälle.

Bindfs ist besonders nützlich in Szenarien, in denen flexible und sichere Zugriffssteuerungen auf Dateisystemebene erforderlich sind, ohne die Struktur oder die Berechtigungen der Originaldateien dauerhaft zu verändern.  

Samba und Bindfs: Effiziente Kombination für Dateifreigaben und Zugriffssteuerung unter Linux

Samba ist eine weit verbreitete Software-Suite, die es Linux- und Unix-Systemen ermöglicht, Dateien und Drucker im Netzwerk freizugeben und mit Windows-Clients zu interagieren. Bindfs hingegen ist ein FUSE-basiertes Dateisystem, das flexible Anpassungen von Benutzer- und Gruppenbesitzrechten sowie Dateiberechtigungen ermöglicht, ohne die Originaldateien zu verschieben. Die Kombination von Samba und Bindfs bietet eine leistungsstarke Lösung zur Verwaltung von Dateifreigaben mit granularer Zugriffskontrolle.

Vorteile der Kombination von Samba und Bindfs

  1. Flexible Berechtigungsverwaltung: Bindfs ermöglicht die Neuzuordnung von Besitzrechten und Berechtigungen, die Samba dann an die Netzwerkbenutzer weitergeben kann, ohne die ursprünglichen Dateiberechtigungen zu ändern.
  2. Sicherheitsverbesserung: Durch die Trennung der Originaldateisystemberechtigungen und der durch Bindfs gesetzten Berechtigungen können Sicherheitsrichtlinien präziser umgesetzt werden.
  3. Einfache Verwaltung: Administratoren können komplexe Berechtigungsszenarien umsetzen, ohne tief in die Samba-Konfiguration einsteigen zu müssen.
  4. Keine Root-Rechte erforderlich: Bindfs ermöglicht das Anpassen von Berechtigungen ohne Root-Zugriff, was die Sicherheit erhöht und die Verwaltung vereinfacht.

Anwendungsbeispiele

  • Webserver-Verzeichnisse freigeben: Ein Webserver-Verzeichnis kann mit Bindfs so gemountet werden, dass es für verschiedene Samba-Benutzer unterschiedliche Berechtigungen aufweist.
  • Gemeinsame Arbeitsbereiche: In Teams, in denen verschiedene Benutzergruppen unterschiedliche Zugriffsebenen benötigen, kann Bindfs verwendet werden, um maßgeschneiderte Berechtigungen bereitzustellen.
  • Temporäre Freigaben: Temporäre Projekte oder Dateien können sicher und flexibel freigegeben werden, ohne die bestehenden Dateisystemstrukturen zu beeinträchtigen.

Systemd und Mount Units

Systemd ist das Init-System und der Service-Manager in vielen modernen Linux-Distributionen. Es übernimmt die Verwaltung von Systemdiensten, Mount Points und anderen Systemressourcen. Ein wesentlicher Bestandteil davon sind Mount Units, die das Einbinden von Dateisystemen steuern.

Systemd-fstab-Generator

  • Automatische Erstellung: Der systemd-fstab-generator liest die Einträge aus der Datei /etc/fstab bei jedem Systemstart.
  • Temporäre Units: Basierend auf diesen Einträgen erstellt der Generator temporäre Mount Units und speichert sie im Verzeichnis /run/systemd/generator/.
  • Verwaltung mit systemctl: Diese generierten Mount Units können anschließend wie jede andere systemd-Unit mit dem Befehl systemctl verwaltet werden. Beispielsweise können Sie Mount Points starten, stoppen oder ihren Status überprüfen.

Vorteile der Verwendung von systemd Mount Units

  1. Integration und Abhängigkeiten: Mount Units profitieren von der nahtlosen Integration in das systemd-Ökosystem, wodurch Abhängigkeiten zwischen verschiedenen Diensten und Mount Points einfacher verwaltet werden können.
  2. Parallele Initialisierung: Systemd kann Mount Points parallel mounten, was den Boot-Prozess beschleunigt.
  3. Einheitliches Management: Durch die Verwaltung aller Mount Points über systemd erhalten Administratoren ein einheitliches Werkzeugset zur Kontrolle und Überwachung.
  4. Flexibilität: Neben den automatisch generierten Units aus /etc/fstab können auch benutzerdefinierte Mount Units erstellt werden, um spezifische Anforderungen zu erfüllen.
				
					# ----------------------------------------------
# 1. Besitz und Berechtigungen für /var/www anpassen
# ----------------------------------------------
# Ändert den Besitzer von /var/www rekursiv zu www-data
sudo chown -R www-data:www-data /var/www/

# Setzt das Set-GID-Bit für alle Verzeichnisse in /var/www, damit neue Dateien die Gruppenberechtigung erben
sudo find /var/www -type d -exec chmod g+s {} +

# ----------------------------------------------
# 2. Benutzer 'web' erstellen und Bindfs installieren
# ----------------------------------------------
# Erstellt einen neuen Benutzer namens 'web'
sudo adduser web

# Installiert bindfs, ein FUSE-basiertes Dateisystem zum Anpassen von Dateiberechtigungen
sudo apt-get -y install bindfs

# ----------------------------------------------
# 3. Verzeichnis für den Web-Benutzer erstellen
# ----------------------------------------------
# Erstellt das Verzeichnis /home/web/www
sudo mkdir -p /home/web/www

# Ändert den Besitzer des Verzeichnisses zu 'web'
sudo chown web:web /home/web/www

# Setzt die Berechtigungen des Verzeichnisses auf 755
sudo chmod 755 /home/web/www

# ----------------------------------------------
# 4. Bindfs in /etc/fstab eintragen
# ----------------------------------------------
# Öffnet die fstab-Datei mit mcedit zur Bearbeitung
mcedit /etc/fstab

# Füge die folgende Zeile am Ende der Datei hinzu, um bindfs für den 'web' Benutzer zu konfigurieren
# ###### web user ######
# bindfs#/var/www /home/web/www fuse force-user=web,force-group=web,create-for-user=www-data,create-for-group=www-data,create-with-perms=0755,chgrp-ignore,chown-ignore,chmod-ignore 0 0

# ----------------------------------------------
# 5. Änderungen anwenden und Verzeichnis neu mounten
# ----------------------------------------------
# Öffnet das Verzeichnis für systemd-Generatoren mit mc (Midnight Commander)
mc /run/systemd/generator/

# Entmountet das Verzeichnis /home/web/www, falls es bereits gemountet ist
sudo umount /home/web/www

# Überprüft, welche Prozesse das Verzeichnis /home/web/www nutzen
lsof /home/web/www

# ----------------------------------------------
# 6. Konfiguration abschließen
# ----------------------------------------------
# Mountet alle Dateisysteme in der fstab neu
sudo mount -a

# Überprüft den Status des neuen Mounts
df -h | grep /home/web/www

# Optional: Startet den bindfs-Dienst neu, falls benötigt
sudo systemctl restart bindfs.service

				
			
# bindfs#/var/www /home/web/www fuse force-user=web,force-group=web,create-for-user=www-data,create-for-group=www-data,create-with-perms=0755,chgrp-ignore,chown-ignore,chmod-ignore 0 0

Was wird erreicht:

  1. Bindfs-Mount:

    • Bindfs bindet das Quellverzeichnis /var/www an den Zielpfad /home/web/www.
  2. Benutzer- und Gruppenzuweisung:

    • force-user=web & force-group=web: Alle Dateien und Verzeichnisse im gemounteten Pfad erscheinen dem Benutzer web und der Gruppe web, unabhängig von den Originalbesitzrechten.
  3. Erstellungseinstellungen:

    • create-for-user=www-data & create-for-group=www-data: Neue Dateien und Verzeichnisse, die in /home/web/www erstellt werden, gehören dem Benutzer und der Gruppe www-data.
    • create-with-perms=0755: Neue Dateien erhalten die Berechtigungen 0755 (rwxr-xr-x).
  4. Ignorieren von Änderungen:

    • chgrp-ignore, chown-ignore, chmod-ignore: Änderungen an Gruppen, Besitzern und Berechtigungen durch andere Prozesse werden ignoriert, wodurch die festgelegten Einstellungen konstant bleiben.
  5. FUSE-Dateisystem:

    • fuse: Verwendet das FUSE (Filesystem in Userspace)-Framework, um die Anpassungen ohne Root-Rechte zu ermöglichen.

Vorteile:

  • Sichere Zugriffskontrolle: Der Benutzer web hat kontrollierten Zugriff auf /var/www über /home/web/www, ohne die Originalbesitzrechte zu verändern.
  • Einfache Verwaltung: Ermöglicht das Anpassen von Zugriffsrechten ohne Root-Zugriff.
  • Konsistente Berechtigungen: Neue Dateien und Änderungen behalten die festgelegten Benutzer-, Gruppen- und Berechtigungseinstellungen bei.

Zusammenfassung:

Diese fstab-Zeile konfiguriert Bindfs, um das Verzeichnis /var/www unter /home/web/www mit angepassten Benutzer- und Gruppenrechten zu mounten. Dabei wird der Zugriff für den Benutzer web vereinfacht und gleichzeitig sichergestellt, dass neue Dateien den Webserver-Benutzer www-data korrekt zugewiesen bekommen. Dies ermöglicht eine flexible und sichere Verwaltung der Zugriffsrechte ohne Änderungen an den Originaldateien oder Root-Berechtigungen.

Samba

				
					# ----------------------------------------------
# 1. Samba installieren
# Installiert Samba, Samba-Common und smbclient für Dateifreigaben
# ----------------------------------------------
sudo apt-get install samba samba-common smbclient -y

# ----------------------------------------------
# 2. Status der Samba-Dienste überprüfen
# Überprüft, ob die Samba-Dienste smbd und nmbd laufen
# ----------------------------------------------
sudo systemctl status smbd.service
sudo systemctl status nmbd.service

# ----------------------------------------------
# 3. Samba-Dienste neu starten
# Startet die Samba-Dienste neu, um sicherzustellen, dass alle Änderungen übernommen werden
# ----------------------------------------------
sudo systemctl restart smbd.service
sudo systemctl restart nmbd.service

# ----------------------------------------------
# 4. Sicherung der bestehenden Samba-Konfiguration
# Sichert die aktuelle smb.conf-Datei, bevor eine neue erstellt wird
# ----------------------------------------------
sudo mv /etc/samba/smb.conf /etc/samba/smb.conf_sicher

# ----------------------------------------------
# 5. Neue Samba-Konfiguration erstellen
# Erstellt eine neue smb.conf mit den gewünschten Einstellungen
# ----------------------------------------------
sudo bash -c 'cat > /etc/samba/smb.conf <<EOF
[global]
    workgroup = WORKGROUP
    netbios name = NUC-SERVER
    security = user

[web-nuc-www]
    comment = web-nuc-local
    path = /home/web/www/html
    browsable = yes
    writable = yes
    guest ok = yes
    read only = no
    force user = web
EOF'

# ----------------------------------------------
# 6. Samba-Konfiguration testen
# Überprüft die neue smb.conf auf Syntaxfehler und Validität
# ----------------------------------------------
testparm -s

# ----------------------------------------------
# 7. Samba-Dienste neu starten, um die neue Konfiguration zu übernehmen
# ----------------------------------------------
sudo service smbd restart && sudo service nmbd restart

# ----------------------------------------------
# 8. Samba-Benutzer 'web' hinzufügen
# Erstellt einen neuen Samba-Benutzer 'web' und setzt das Passwort auf 'web'
# ----------------------------------------------
sudo smbpasswd -a web
# Folge den Aufforderungen:
# New SMB password: web
# Retype new SMB password: web
# Added user web.

# ----------------------------------------------
# 9. Überprüfen, welche Samba-Ports geöffnet sind
# Zeigt alle aktiven Samba-bezogenen Ports und die zugehörigen Prozesse an
# ----------------------------------------------
netstat -tulpn | egrep "samba|smbd|nmbd|winbind"
# Beispielausgabe:
# tcp        0      0 0.0.0.0:139             0.0.0.0:*               LISTEN      1322/smbd
# tcp        0      0 0.0.0.0:445             0.0.0.0:*               LISTEN      1322/smbd
# tcp6       0      0 :::139                  :::*                    LISTEN      1322/smbd
# tcp6       0      0 :::445                  :::*                    LISTEN      1322/smbd
# udp        0      0 192.169.1.255:137       0.0.0.0:*                           1302/nmbd
# udp        0      0 192.169.1.165:137       0.0.0.0:*                           1302/nmbd
# udp        0      0 0.0.0.0:137             0.0.0.0:*                           1302/nmbd
# udp        0      0 192.169.1.255:138       0.0.0.0:*                           1302/nmbd
# udp        0      0 192.169.1.165:138       0.0.0.0:*                           1302/nmbd
# udp        0      0 0.0.0.0:138             0.0.0.0:*                           1302/nmbd

				
			
				
					# Der smbd-Dienst kann keine Sockets öffnen 
# Zunächst die Netzwerkabhängigkeiten in systemd konfigurieren
sudo systemctl edit smbd.service

# Ausdokumentieren / Hinzufügen Sie folgende:
[Unit]
After=network-online.target 
Wants=network-online.target

# Dann die Dienste neu starten
sudo systemctl daemon-reload
sudo systemctl restart smbd.service nmbd.service

# Interface-Konfiguration in Samba
# optimiert auf Geschwindigkeit 

sudo mcedit /etc/samba/smb.conf


[global]
    # Basis-Einstellungen
    netbios name = NUC-SERVER
    server role = standalone server
    security = USER

    # Zugriffskontrolle
    hosts allow = 127.0.0.0/8 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12
    hosts deny = ALL

    # IP-Protokoll-Einstellungen
    disable netbios = no
    dns proxy = no

    # Beschränkung auf Loopback und die spezifischen Subnetze
    interfaces = lo enx0050b6174810 wlo1 127.0.0.0/8 192.168.1.0/24 10.0.0.0/8 172.16.0.0/12
    bind interfaces only = yes

    # Performance-Optimierungen
    use sendfile = yes
    aio read size = 1
    aio write size = 1
    max xmit = 65535
    deadtime = 15

    # Protokoll-Optimierungen
    server min protocol = SMB3
    max protocol = SMB3

    # Opportunistic Locking
    oplocks = yes
    level2 oplocks = yes
    kernel oplocks = yes
    posix locking = no
    strict locking = no
    veto oplock files = /*.mdb/*.ldb/*.suo/

    # Deaktivierung unnötiger Dienste
    disable spoolss = yes
    smb encrypt = off

    # Feinabstimmung der Performance-Optionen
    strict allocate = no
    strict sync = no

    # Optional: Socket-Optionen (nur wenn nötig)
    # socket options = TCP_NODELAY

    # Logging (optional, zur Fehlerdiagnose)
    log level = 3

[web-nuc-www]
    comment = web-nuc-local
    path = /home/web/www/html
    force user = web
    guest ok = yes
    read only = no
    strict sync = no
    strict locking = no
    block size = 1048576
    create mask = 0666
    directory mask = 0777
    veto files = /._*/.DS_Store/


sudo systemctl restart smbd.service nmbd.service

				
			

PHP-FPM tuning

				
					mcedit /etc/php/8.*/fpm/pool.d/www.conf

pm.max_requests = 500
pm = dynamic
pm.max_children = 12 
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.start_servers = 6
				
			

MYSQL PHP-Tuning

				
					<?php

function getSystemMemoryMB() {
    $totalMemory = trim(shell_exec("grep 'MemTotal' /proc/meminfo | awk '{print $2}'"));
    return intval($totalMemory / 1024); // Konvertiere von KB in MB
}

function getCPUCores() {
    $cpuCores = trim(shell_exec("nproc"));
    return intval($cpuCores);
}

function isSSD() {
    // Diese Funktion ist stark vereinfacht und prüft nur, ob ein SSD-Merkmal vorhanden ist.
    // Für eine genaue Erkennung müsste man spezifischer auf das Speichersystem eingehen.
    $rootDrive = trim(shell_exec("df / | tail -1 | cut -d' ' -f1"));
    $isSSD = trim(shell_exec("cat /sys/block/${rootDrive}/queue/rotational"));
    return $isSSD == "0"; // 0 bedeutet SSD, 1 bedeutet HDD
}

function calculateConfigValues() {
    // Dynamische Ermittlung der Werte
    $totalMemoryMB = getSystemMemoryMB();
    $cpuCores = getCPUCores();
    $usingSSD = isSSD();

    // InnoDB Einstellungen
    $innodbBufferPoolSize = intdiv($totalMemoryMB * 70, 100); // 70% des Gesamtspeichers
    $innodbBufferPoolInstances = min(64, max(1, intdiv($innodbBufferPoolSize, 1024))); // 1 Instanz pro 1GB, max 64
    $innodbLogFileSize = $usingSSD ? 1024 : 512; // 1GB für SSDs, sonst 512MB
    $innodbFlushMethod = $usingSSD ? 'O_DIRECT_NO_FSYNC' : 'O_DIRECT';
    $innodbFlushLogAtTrxCommit = 2;
    $innodbIOCapacity = $usingSSD ? 2000 : 200;
    $innodbIOCapacityMax = $usingSSD ? 4000 : 2000;

    // Verbindungseinstellungen
    $maxConnections = 150;
    $threadCacheSize = 100;
    $threadHandling = 'pool-of-threads';

    // Caching und temporäre Tabellen
    $tableOpenCache = 4000;
    $openFilesLimit = 8000;
    $tmpTableSize = 256;
    $maxHeapTableSize = 256;
    $queryCacheSize = 0; // Query Cache ist in neueren Versionen deaktiviert
    $queryCacheType = 0;

    // Logging
    $slowQueryLog = 1;
    $slowQueryLogFile = '/var/log/mysql/mysql-slow.log';
    $longQueryTime = 2;

    // Sortierung und Joins
    $joinBufferSize = 8; // 8M empfohlen, aber je nach Anwendungsfall anzupassen
    $sortBufferSize = 4; // 4M empfohlen
    $readRndBufferSize = 4; // 4M empfohlen

    // Generiere das Konfigurations-Template mit den berechneten Werten
    return "
[mysqld]
port = 3306
socket = /var/run/mysqld/mysqld.sock
datadir = /var/lib/mysql
default_storage_engine = InnoDB
user = mysql
bind_address = 127.0.0.1
max_connections = $maxConnections
innodb_buffer_pool_size = ${innodbBufferPoolSize}M
innodb_log_file_size = ${innodbLogFileSize}M
innodb_buffer_pool_instances = $innodbBufferPoolInstances
innodb_flush_method = $innodbFlushMethod
innodb_flush_log_at_trx_commit = $innodbFlushLogAtTrxCommit
query_cache_size = ${queryCacheSize}M
query_cache_type = $queryCacheType
thread_cache_size = $threadCacheSize
thread_handling = $threadHandling
table_open_cache = $tableOpenCache
open_files_limit = $openFilesLimit
tmp_table_size = ${tmpTableSize}M
max_heap_table_size = ${maxHeapTableSize}M
slow_query_log = $slowQueryLog
slow_query_log_file = '$slowQueryLogFile'
long_query_time = $longQueryTime
skip-log-bin
innodb_io_capacity = $innodbIOCapacity
innodb_io_capacity_max = $innodbIOCapacityMax
join_buffer_size = ${joinBufferSize}M
sort_buffer_size = ${sortBufferSize}M
read_rnd_buffer_size = ${readRndBufferSize}M
";
}

echo calculateConfigValues();

exit;


// Generiert die Konfigurationsdatei und speichert sie temporär
$configContent = calculateConfigValues();
$file = tempnam(sys_get_temp_dir(), 'my_cnf_');
file_put_contents($file, $configContent);

// Zur Sicherheit, Berechtigungen so setzen, dass nur der Besitzer lesen kann
chmod($file, 0600);

// Header für den Download
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="my.cnf"');
header('Content-Length: ' . filesize($file));

// Datei zum Download senden und temporäre Datei löschen
readfile($file);
unlink($file);

?>

				
			

MariaDB / MySql

				
					# ----------------------------------------------
# 1. MariaDB-Konfigurationsdatei bearbeiten
# Öffnet die Tuningskonfigurationsdatei mit dem Editor mcedit
# ----------------------------------------------
mcedit /etc/mysql/conf.d/tuning-my.cnf

# ----------------------------------------------
# 2. Füge die folgenden Einstellungen hinzu
# Diese Einstellungen optimieren die Leistung von MariaDB
# ----------------------------------------------
# [mysqld]
max_connections = 150
innodb_buffer_pool_size = 5511M
innodb_log_file_size = 512M
#innodb_buffer_pool_instances = 5
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit = 2
query_cache_size = 0M
query_cache_type = 0
thread_cache_size = 100
thread_handling = pool-of-threads
table_open_cache = 4000
open_files_limit = 8000
tmp_table_size = 256M
max_heap_table_size = 256M
slow_query_log = 1
slow_query_log_file = '/var/log/mysql/mysql-slow.log'
long_query_time = 2
skip-log-bin
innodb_io_capacity = 200
innodb_io_capacity_max = 2000
join_buffer_size = 8M
sort_buffer_size = 4M
read_rnd_buffer_size = 4M

# ----------------------------------------------
# 3. Setze die Berechtigungen der Konfigurationsdatei
# Stellt sicher, dass die Datei die richtigen Berechtigungen hat
# ----------------------------------------------
sudo chmod 644 /etc/mysql/conf.d/tuning-my.cnf

# ----------------------------------------------
# 4. Konfiguration testen
# Überprüft die MariaDB-Konfiguration auf Fehler
# ----------------------------------------------
sudo mysqld --verbose --help | grep -A1 "Default options"
sudo systemctl restart mysql

# ----------------------------------------------
# 5. Log-Verzeichnis erstellen
# Erstellt das Verzeichnis für die MariaDB-Logs und setzt die richtigen Eigentümer
# ----------------------------------------------
sudo mkdir -p /var/log/mysql
sudo chown mysql:mysql /var/log/mysql

# ----------------------------------------------
# 6. Log-Datei erstellen
# Erstellt die Datei für langsame Abfragen und setzt die richtigen Berechtigungen
# ----------------------------------------------
sudo touch /var/log/mysql/mysql-slow.log
sudo chown mysql:mysql /var/log/mysql/mysql-slow.log
sudo chmod 644 /var/log/mysql/mysql-slow.log

# ----------------------------------------------
# 7. MariaDB neu starten
# Startet den MariaDB-Dienst neu, damit die Änderungen wirksam werden
# ----------------------------------------------
sudo systemctl restart mariadb

				
			
				
					mcedit /etc/mysql/mariadb.conf.d/50-server.cnf
bind-address = 0.0.0.0
				
			

Tor

				
					# ----------------------------------------------
# 1. System aktualisieren und Tor installieren
# Aktualisiert die Paketliste und installiert Tor
# ----------------------------------------------
sudo apt update
sudo apt install tor -y

# ----------------------------------------------
# 2. Verzeichnisse für mehrere Tor-Instanzen erstellen
# Erstellt separate Datenverzeichnisse für jede Tor-Instanz
# ----------------------------------------------
sudo mkdir -p /var/lib/tor1 /var/lib/tor2 /var/lib/tor3

# ----------------------------------------------
# 3. Besitz und Berechtigungen setzen
# Setzt den Besitzer auf debian-tor und Berechtigungen auf 700
# ----------------------------------------------
sudo chown -R debian-tor:debian-tor /var/lib/tor1 /var/lib/tor2 /var/lib/tor3
sudo chmod -R 700 /var/lib/tor1 /var/lib/tor2 /var/lib/tor3

# ----------------------------------------------
# 4. Tor-Konfigurationsdateien erstellen
# Erstellt separate torrc-Dateien für jede Tor-Instanz
# ----------------------------------------------

# Torrc für die erste Instanz
sudo bash -c "cat <<EOF > /etc/tor/torrc.1
SocksPort 9050
ControlPort 9051
DataDirectory /var/lib/tor1
RunAsDaemon 1
CookieAuthentication 1
Log notice file /var/log/tor/tor1.log
EOF"

# Torrc für die zweite Instanz
sudo bash -c "cat <<EOF > /etc/tor/torrc.2
SocksPort 9060
ControlPort 9061
DataDirectory /var/lib/tor2
RunAsDaemon 1
CookieAuthentication 1
Log notice file /var/log/tor/tor2.log
EOF"

# Torrc für die dritte Instanz
sudo bash -c "cat <<EOF > /etc/tor/torrc.3
SocksPort 9070
ControlPort 9071
DataDirectory /var/lib/tor3
RunAsDaemon 1
CookieAuthentication 1
Log notice file /var/log/tor/tor3.log
EOF"

# ----------------------------------------------
# Erklärung der Einstellungen:
# ----------------------------------------------
# SocksPort: Der Port, auf dem Tor SOCKS-Verbindungen akzeptiert.
# ControlPort: Der Port für die Tor-Kontrollschnittstelle.
# DataDirectory: Das Verzeichnis, in dem Tor Daten speichert.
# RunAsDaemon 1: Startet Tor als Hintergrundprozess.
# CookieAuthentication 1: Aktiviert die Authentifizierung mittels Cookies.
# Log notice file: Gibt an, wohin Tor Protokolle schreibt.

# ----------------------------------------------
# 5. Tor-Dienst neu starten
# Startet den Tor-Dienst neu, um die neuen Konfigurationen zu laden
# ----------------------------------------------
sudo systemctl restart tor

# ----------------------------------------------
# 6. Status des Tor-Dienstes überprüfen
# Überprüft, ob der Tor-Dienst aktiv ist
# ----------------------------------------------
sudo systemctl status tor

# ----------------------------------------------
# 7. Überprüfen, ob Tor auf Port 9050 läuft
# Zeigt an, ob Tor auf dem angegebenen Port lauscht
# ----------------------------------------------
sudo lsof -i :9050

# ----------------------------------------------
# 8. Überprüfen, ob Tor korrekt funktioniert
# Verwendet curl, um die Tor-Verbindung zu testen
# ----------------------------------------------
curl -x socks5h://localhost:9050 -s https://check.torproject.org/api/ip
# Erwartete Ausgabe: {"IsTor":true,"IP":"<Your_Tor_IP>"}

# ----------------------------------------------
# 9. Zusätzliche Tor-Instanz manuell starten
# Startet die zweite Tor-Instanz mit torrc.2
# ----------------------------------------------
sudo -u debian-tor tor -f /etc/tor/torrc.2

# ----------------------------------------------
# 10. Überprüfen, ob weitere Tor-Ports laufen
# Listet alle aktiven Tor-Ports auf
# ----------------------------------------------
sudo netstat -tuln | grep -E '9050|9060|9070'

# ----------------------------------------------
# 11. Tor-Dienste beenden
# Beendet alle laufenden Tor-Prozesse
# ----------------------------------------------
pidof tor | xargs kill

# ----------------------------------------------
# 12. Überprüfen, ob die Tor-Ports weiterhin laufen
# Stellt sicher, dass die Ports nicht mehr belegt sind
# ----------------------------------------------
sudo netstat -tuln | grep ':9050\|:9060\|:9070'

# ----------------------------------------------
# 13. Überprüfen der Verzeichnisberechtigungen
# Zeigt die Berechtigungen der Tor-Daten- und Log-Verzeichnisse an
# ----------------------------------------------
sudo ls -ld /var/lib/tor1 /var/lib/tor2 /var/lib/tor3
sudo ls -ld /var/log/tor

# ----------------------------------------------
# 14. Tor-Konfiguration validieren
# Überprüft die Konfigurationsdatei auf Gültigkeit
# ----------------------------------------------
sudo -u debian-tor tor -f /etc/tor/torrc.1 --verify-config
# Erwartete Ausgabe: Configuration was valid

				
			

MailHog

				
					https://github.com/mailhog/MailHog
https://gist.github.com/dipenparmar12/4e6cd50d8d1303d5e914742f62659116


# System aktualisieren und Go installieren
sudo apt-get update
sudo apt-get -y install golang git apache2

# MailHog mit Go installieren
go install github.com/mailhog/MailHog@latest

# MailHog in /usr/local/bin kopieren und ausführbar machen
sudo cp ~/go/bin/MailHog /usr/local/bin/MailHog
sudo chmod +x /usr/local/bin/MailHog

# Service-Datei erstellen
sudo tee /etc/systemd/system/mailhog.service > /dev/null <<EOL
[Unit]
Description=MailHog
After=network.target

[Service]
ExecStart=/usr/local/bin/MailHog \
  -api-bind-addr 127.0.0.1:8080 \
  -ui-bind-addr 127.0.0.1:8080 \
  -smtp-bind-addr 127.0.0.1:1025

[Install]
WantedBy=multi-user.target
EOL

# Service laden, aktivieren und starten
sudo systemctl daemon-reload
sudo systemctl enable mailhog
sudo systemctl start mailhog

# Status prüfen
sudo systemctl status mailhog

# Notwendige Module aktivieren
sudo a2enmod proxy proxy_http ssl headers
sudo systemctl restart apache2

# Virtuellen Host konfigurieren
sudo tee /etc/apache2/sites-available/web.test.conf > /dev/null <<EOL
<VirtualHost *:80>
   ServerName web.test
   ServerAlias www.web.test
   DocumentRoot /var/www/html/web.test/dist/
   Redirect permanent / https://www.web.test
</VirtualHost>

<VirtualHost *:443>
   ServerName web.test
   ServerAlias www.web.test
   DocumentRoot /var/www/html/web.test/dist/

   SSLEngine on
   SSLCertificateFile /var/www/html/web.test/ssl/web.test+1.pem
   SSLCertificateKeyFile /var/www/html/web.test/ssl/web.test+1-key.pem

   SetEnv PHP_ADMIN_VALUE "sendmail_path = /usr/local/bin/mhsendmail"

   <IfModule proxy_fcgi_module>
       ProxyFCGISetEnvIf "true" PHP_ADMIN_VALUE " upload_max_filesize=1G; post_max_size=500M; memory_limit=1G; max_input_time=300;"
   </IfModule>

   # Proxy für MailHog
   ProxyPass /mail/ http://127.0.0.1:8080/
   ProxyPassReverse /mail/ http://127.0.0.1:8080/

   <Location /mail/>
       Require all granted
   </Location>

   <Proxy "http://127.0.0.1:8080/*">
       Require all granted
   </Proxy>

   <Directory "/var/www/html/web.test/dist">
       Options Indexes FollowSymLinks MultiViews
       AllowOverride all
       Require all granted
   </Directory>

   <FilesMatch \.php$>
      SetHandler "proxy:unix:/run/php/php8.4-fpm.sock|fcgi://localhost"
   </FilesMatch>

   AddOutputFilterByType SUBSTITUTE text/html
   Substitute "s|</head>|<script>document.title='OFFLINE :: ' + document.title;</script></head>|i"
</VirtualHost>
EOL

# Virtuellen Host aktivieren und Apache neu starten
sudo a2ensite web.test.conf
sudo apachectl configtest && sudo systemctl restart apache2

# mhsendmail mit Go installieren
go install github.com/mailhog/mhsendmail@latest

# Symlinks erstellen
sudo ln -s ~/go/bin/mhsendmail /usr/local/bin/mhsendmail
sudo ln -s /usr/local/bin/mhsendmail /usr/local/bin/sendmail
sudo ln -s /usr/local/bin/mhsendmail /usr/local/bin/mail

# Ausführungsrechte setzen
sudo chmod +x /usr/local/bin/mhsendmail

# Hilfe anzeigen zur Überprüfung
sudo -u www-data /usr/local/bin/mhsendmail --help

# Logs überwachen (in zwei Terminals ausführen)
sudo tail -f /var/log/apache2/error.log
sudo journalctl -u php8.4-fpm -f

# Test-E-Mail senden
echo -e "Subject: Test\r\nFrom: webmaster@web.test\r\n\r\nTest E-Mail" | /usr/local/bin/mhsendmail test@example.com

# MailHog UI im Browser aufrufen
# Gehe zu http://web.test/mail/ oder https://www.web.test/mail/

# Dienste bei Änderungen neu starten
sudo systemctl restart php8.4-fpm
sudo systemctl restart apache2

####################################################################

Prüfen:

sudo tail -f /var/log/apache2/error.log
sudo journalctl -u php8.4-fpm -f


ls -l /usr/local/bin/mhsendmail
ls -l /usr/local/bin/sendmail
ls -l /usr/local/bin/mail

lrwxrwxrwx 1 root root 26 Dez 18 14:14 /usr/local/bin/mhsendmail -> /opt/mhsendmail/mhsendmail
lrwxrwxrwx 1 root root 25 Dez 18 14:14 /usr/local/bin/sendmail -> /usr/local/bin/mhsendmail
lrwxrwxrwx 1 root root 25 Dez 18 14:14 /usr/local/bin/mail -> /usr/local/bin/mhsendmail

echo -e "Subject: Test\r\nFrom: webmaster@web.test\r\n\r\nTest E-Mail" | /usr/local/bin/mhsendmail test@example.com

<?php
$to = 'test@example.com';
$subject = 'Test E-Mail';
$message = 'Dies ist eine Test-E-Mail.';
$headers = 'From: webmaster@web.test' . "\r\n" .
           'Reply-To: webmaster@web.test' . "\r\n" .
           'X-Mailer: PHP/' . phpversion();

if(mail($to, $subject, $message, $headers)) {
    echo 'E-Mail erfolgreich gesendet.';
} else {
    echo 'Fehler beim Senden der E-Mail.';
}
?>


Source
From: webmaster@web.test
Message-ID: Rr1C6ddwNObdZi5x5n4maJOCj08xninquIobdwXi8jM=@mailhog.example
Received: from localhost by mailhog.example (MailHog)
          id Rr1C6ddwNObdZi5x5n4maJOCj08xninquIobdwXi8jM=@mailhog.example; Wed, 18 Dec 2024 13:31:55 +0000
Return-Path: <webmaster@web.test>
Subject: Manuelle Test-E-Mail

Test E-Mail von mhsendmail



				
			

Composer

				
					# 1. Composer-Installer herunterladen und ausführen
curl -sS https://getcomposer.org/installer | php

# 2. Composer global verfügbar machen, indem die composer.phar verschoben wird
sudo mv composer.phar /usr/local/bin/composer

# 3. Überprüfe die Composer-Version, um sicherzustellen, dass die Installation erfolgreich war
sudo -u www-data composer --version

# 4. Wechsle in das gewünschte Projektverzeichnis
cd /var/www/html/web.test/dist/


				
			
				
					# 1. Schreibrechte für die Gruppe www-data hinzufügen
sudo chmod g+w /var/www/html/web.test/dist/

# 2. Berechtigungen überprüfen
ls -ld /var/www/html/web.test/dist/

# Optional: Besitz auf www-data ändern
sudo chown -R www-data:www-data /var/www/html/web.test/dist/

# 3. Composer-Projekt als www-data initialisieren
sudo -u www-data composer init

# 4. Falls nötig, manuell composer.json erstellen und Abhängigkeiten installieren
sudo -u www-data mcedit /var/www/html/web.test/dist/composer.json

{
    "name": "www-data/dist",
    "version": "1.0.0",
    "require": {
        "cocur/slugify": "^4.6"
    },
    "autoload": {
        "psr-4": {
            "WwwData\\Dist\\": "src/"
        }
    }
}

# 5. installieren
sudo -u www-data composer install

# 6. Autoloading optimieren
sudo -u www-data composer dump-autoload -o

# Neue Abhängigkeit hinzufügen
sudo -u www-data composer require vendor/package
sudo -u www-data composer update

# Bedeutung von -vvv:
# -v: Zeigt detaillierte Informationen über den Installationsprozess.
# -vv: Zeigt noch ausführlichere Debugging-Informationen.
# -vvv: Maximale Ausführlichkeit, einschließlich aller Debugging-Informationen.
# ist jederzeit nachträglich ausführbar
sudo -u www-data composer install -vvv

# Falls der Debug-Modus Fehler zeigt, kannst du folgende Schritte unternehmen:

# Composer Cache leeren:
sudo -u www-data composer clear-cache

# Abhängigkeiten neu installieren:
sudo -u www-data composer install --no-cache -vvv

# Autoload-Dateien neu generieren:
sudo -u www-data composer dump-autoload -o -vvv

# Überprüfen der Composer-Version: Stelle sicher, dass du die neueste Composer
sudo composer self-update


				
			

Docker installieren

				
					# Schritt 1: Alte Docker-Versionen deinstallieren
apt-get purge -y docker docker-engine docker.io containerd runc

# Schritt 2: Paketliste aktualisieren und notwendige Pakete installieren
apt-get update
apt-get install -y ca-certificates curl gnupg lsb-release

# Schritt 3: Docker's offizielles GPG-Schlüssel hinzufügen
mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# Schritt 4: Docker Repository einrichten
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null

# Schritt 5: Docker Engine, CLI und Buildx Plugin installieren
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Schritt 6: Überprüfen der Docker-Installation
docker --version
docker buildx version

# Schritt 7: Buildx Builder erstellen und aktivieren
docker buildx create --name mybuilder --use
docker buildx inspect --bootstrap

# Schritt 8: Projektverzeichnis vorbereiten
cd ~/playwright-docker/

# Schritt 9: Dockerfile erstellen oder aktualisieren
cat <<EOF > Dockerfile
# Verwende das Playwright Docker-Image in der Version v1.49.1-jammy
FROM mcr.microsoft.com/playwright:v1.49.1-jammy

# Setze das Arbeitsverzeichnis
WORKDIR /usr/src/app

# Kopiere package.json und package-lock.json (falls vorhanden)
COPY package*.json ./

# Installiere Node.js-Abhängigkeiten
RUN npm install

# Kopiere den restlichen Anwendungscode
COPY . .

# Exponiere den Port, auf dem die App läuft
EXPOSE 3000

# Definiere den Startbefehl
CMD ["node", "index.js"]
EOF

# Schritt 10: package.json erstellen oder aktualisieren
cat <<EOF > package.json
{
  "name": "playwright-service",
  "version": "1.0.0",
  "description": "Ein einfacher Playwright-Service mit Express.js",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "express": "^4.17.1",
    "playwright": "1.49.1"
  }
}
EOF

# (Optional) Schritt 11: index.js erstellen, falls noch nicht vorhanden
cat <<EOF > index.js
const express = require('express');
const { chromium } = require('playwright');

const app = express();
const port = 3000;

// Middleware zum Parsen von JSON-Anfragen
app.use(express.json());

// POST-Endpunkt zum Scrapen einer Webseite
app.post('/scrape', async (req, res) => {
    const { url } = req.body;
    if (!url) {
        return res.status(400).json({ error: 'URL ist erforderlich' });
    }

    try {
        // Starte den Browser
        const browser = await chromium.launch({
            headless: true, // Headless-Modus aktivieren
            args: ['--no-sandbox', '--disable-setuid-sandbox'] // Notwendig für Docker-Container
        });
        const page = await browser.newPage();
        
        // Navigiere zur angegebenen URL
        await page.goto(url, { waitUntil: 'networkidle' });
        
        // Extrahiere den Seitentitel
        const title = await page.title();
        
        // Schließe den Browser
        await browser.close();

        // Sende den Titel als Antwort zurück
        res.json({ title });
    } catch (error) {
        console.error('Fehler beim Scrapen:', error);
        res.status(500).json({ 
            error: 'Fehler beim Scrapen der Webseite',
            details: error.message // Detaillierte Fehlermeldung
        });
    }
});

// Starte den Server und höre auf allen Interfaces (0.0.0.0)
app.listen(port, '0.0.0.0', () => {
    console.log(`Playwright-Service läuft unter http://localhost:${port}`);
});
EOF

# Schritt 12: .dockerignore erstellen, um unnötige Dateien auszuschließen
cat <<EOF > .dockerignore
node_modules
npm-debug.log
Dockerfile
.dockerignore
EOF

# Schritt 13: Docker-Image bauen
docker buildx build --platform linux/amd64 -t playwright-service:latest --load .

# Schritt 14: Existierenden Container entfernen (falls vorhanden)
docker rm -f playwright-container

# Schritt 15: Neuen Docker-Container starten
docker run -d --name playwright-container -p 3000:3000 --restart unless-stopped playwright-service:latest
# Schritt 15.1:  Ressourcenlimits setzen: Begrenze die CPU- und Speicherressourcen des Containers, um eine Überlastung zu verhindern.
docker run -d --name playwright-container -p 3000:3000 --restart unless-stopped \
    --memory="512m" --cpus="1.0" playwright-service:latest

# Schritt 16: Container-Logs überwachen, um sicherzustellen, dass der Server läuft
docker logs -f playwright-container

# Schritt 17: Service mit curl testen
curl -X POST http://localhost:3000/scrape \
     -H "Content-Type: application/json" \
     -d '{"url": "https://example.com"}'

				
			
				
					# *** Docker Kurzanleitung: Container verwalten und bearbeiten ***

# *** Container-Management ***

# 1. Auflisten aller Container
docker ps                  # Zeigt laufende Container
docker ps -a               # Zeigt alle Container, auch gestoppte

# 2. Starten und Stoppen von Containern
docker start <container>   # Startet einen gestoppten Container
docker stop <container>    # Stoppt einen laufenden Container

# 3. Löschen von Containern
docker rm <container>      # Entfernt einen gestoppten Container
docker rm -f <container>   # Erzwingt das Entfernen eines laufenden Containers

# 4. Überprüfen der Container-Logs
docker logs <container>    # Zeigt die Logs eines Containers
docker logs -f <container> # Zeigt die Logs in Echtzeit

# 5. Zugriff auf die Bash-Shell im Container
docker exec -it <container> /bin/bash  # Öffnet eine Bash-Shell im Container
exit                                  # Verlasse die Shell

# *** Arbeiten mit Dateien ***

# 6. Bearbeiten von Dateien im Container
docker exec -it <container> /bin/bash # Betritt den Container
cd /usr/src/app                       # Wechsle ins Arbeitsverzeichnis
nano index.js                         # Bearbeite die Datei mit nano
exit                                  # Verlasse die Shell

# 7. Bearbeiten von Dateien auf dem Host
nano ~/playwright-docker/index.js     # Bearbeite index.js direkt auf dem Host

# 8. Dateien kopieren
docker cp <host-path> <container>:<container-path>  # Vom Host in den Container
docker cp <container>:<container-path> <host-path> # Vom Container auf den Host

# *** Docker-Image-Management ***

# 9. Docker-Images auflisten
docker images                        # Zeigt alle verfügbaren Images

# 10. Erstellen eines Docker-Images
docker build -t <image-name>:<tag> . # Baut ein Image aus einem Dockerfile

# 11. Löschen von Docker-Images
docker rmi <image-id>                # Entfernt ein Image
docker image prune -f                # Entfernt nicht verwendete Images

# *** Container basierend auf einem neuen Image starten ***

# 12. Container starten
docker run -d --name <container-name> -p 3000:3000 <image-name>:<tag>
# -d: Hintergrundmodus
# --name: Containername
# -p: Portweiterleitung

# *** Testen und Debuggen ***

# 13. Testen des Services im Container
curl -X POST http://localhost:3000/scrape \
     -H "Content-Type: application/json" \
     -d '{"url": "https://example.com"}'

# 14. Überprüfen der Systemressourcen
docker system df                      # Zeigt Docker-Ressourcennutzung
docker system prune -a --volumes -f   # Entfernt ungenutzte Ressourcen

# *** Aktualisieren von Dateien ***

# 15. Änderungen an index.js im Container testen
docker exec -it <container> /bin/bash # Betritt den Container
cd /usr/src/app                       # Wechsle ins Arbeitsverzeichnis
nano index.js                         # Bearbeite index.js
node index.js                         # Teste die Änderungen
exit                                  # Verlasse den Container

# 16. Änderungen auf dem Host umsetzen
nano ~/playwright-docker/index.js     # Bearbeite die Datei
docker build -t <image-name>:<tag> .  # Neues Image erstellen
docker rm -f <container-name>         # Alten Container entfernen
docker run -d --name <container-name> -p 3000:3000 <image-name>:<tag> # Neuen Container starten

# *** Playwright-spezifische Befehle ***

# 17. Playwright-Browser installieren (innerhalb des Containers)
docker exec -it <container> /bin/bash # Betritt den Container
npx playwright install                # Installiere Browser
npx playwright install-deps           # Installiere fehlende Abhängigkeiten
exit                                  # Verlasse den Container

# *** Fehlerbehebung und Debugging ***

# 18. Überprüfen der Logs eines Containers
docker logs <container>
docker logs -f <container>            # Logs in Echtzeit

# 19. Aktivieren von Docker BuildKit (für effiziente Builds)
export DOCKER_BUILDKIT=1              # Temporär
echo '{"features": {"buildkit": true}}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker         # Permanent aktivieren

# 20. Speicherplatz freigeben
docker container prune -f             # Entfernt gestoppte Container
docker image prune -a -f              # Entfernt ungenutzte Images
docker volume prune -f                # Entfernt ungenutzte Volumes
docker system prune -a --volumes -f   # Entfernt ungenutzte Ressourcen

				
			

Bind-Mount-Verzeichnis - Docker & Nodemon

				
					# 1. Docker-Volume-Verzeichnis vorbereiten
# Erstellt das Hauptverzeichnis und setzt Berechtigungen
sudo mkdir -p /var/docker
sudo chown -R www-data:www-data /var/docker
sudo chmod -R 775 /var/docker
sudo find /var/docker -type d -exec chmod g+s {} +

# 2. Bind-Mount-Verzeichnis einrichten
sudo mkdir -p /home/docker
sudo chown web:web /home/docker
sudo chmod 755 /home/docker

# 3. bindfs in /etc/fstab konfigurieren
# Fügen Sie folgende Zeile in /etc/fstab ein:
bindfs#/var/docker /home/docker fuse force-user=web,force-group=web,create-for-user=www-data,create-for-group=www-data,create-with-perms=0775,chgrp-ignore,chown-ignore,chmod-ignore 0 0

# 4. Mount aktivieren und prüfen
sudo umount -l /home/docker
lsof /home/docker
sudo mount -a && systemctl daemon-reload
df -h | grep /home/docker

# 5. Samba-Konfiguration
# Fügen Sie in /etc/samba/smb.conf hinzu:
[docker-nuc]
        block size = 1048576
        comment = docker-nuc-local
        create mask = 0666
        directory mask = 0777
        force user = web
        guest ok = Yes
        path = /home/docker
        read only = No
        veto files = /._*/.DS_Store/

# Samba-Dienste neustarten
sudo service smbd restart && sudo service nmbd restart

# 6. Playwright-Docker-Projekt einrichten
# Verzeichnis erstellen und Berechtigungen setzen
sudo mkdir -p /var/docker/playwright-docker
sudo chown www-data:www-data /var/docker/playwright-docker
sudo chmod 775 /var/docker/playwright-docker
sudo bash -c 'chmod -R 775 /var/docker/ && find /var/docker -type d -exec chmod g+s {} +'

# 7. Docker-Compose-Datei erstellen
cat <<EOF > /var/docker/playwright-docker/docker-compose.yml
version: '2'

services:
  playwright:
    build: 
      context: .
      dockerfile: Dockerfile
    command: sh -c "npm install && nodemon --legacy-watch"
    ports:
      - "3000:3000"
    volumes:
      - .:/usr/src/app:cached
    environment:
      NODE_ENV: development
      CHOKIDAR_USEPOLLING: "true"
EOF

# 8. Dockerfile erstellen
cat <<EOF > /var/docker/playwright-docker/Dockerfile
FROM mcr.microsoft.com/playwright:v1.49.1-jammy

WORKDIR /usr/src/app

# Installiere nodemon global
RUN npm install -g nodemon

# Kopiere package.json und package-lock.json
COPY package*.json ./

# Installiere Node.js-Abhängigkeiten
RUN npm install

# Kopiere den restlichen Anwendungscode
COPY . .

# Exponiere den Port
EXPOSE 3000
EOF

# 9. Package.json erstellen
cat <<EOF > /var/docker/playwright-docker/package.json
{
  "name": "playwright-service",
  "version": "1.0.0",
  "description": "Ein einfacher Playwright-Service mit Express.js",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "express": "^4.17.1",
    "playwright": "^1.49.1"
  }
}
EOF

# 10. index.js erstellen
cat <<EOF > /var/docker/playwright-docker/index.js
const express = require('express');
const { chromium } = require('playwright');
const app = express();
const port = 3000;

// Middleware zum Parsen von JSON-Anfragen
app.use(express.json());

// POST-Endpunkt zum Scrapen einer Webseite
app.post('/scrape', async (req, res) => {
    const { url } = req.body;
    if (!url) {
        return res.status(400).json({ error: 'URL ist erforderlich' });
    }

    try {
        // Starte den Browser
        const browser = await chromium.launch({
            headless: true,
            args: ['--no-sandbox', '--disable-setuid-sandbox']
        });
        const page = await browser.newPage();
        
        await page.goto(url, { waitUntil: 'networkidle' });
        const title = await page.title();
        await browser.close();

        res.json({ test: title });
    } catch (error) {
        console.error('Fehler beim Scrapen:', error);
        res.status(500).json({ 
            error: 'Fehler beim Scrapen der Webseite',
            details: error.message
        });
    }
});

app.listen(port, '0.0.0.0', () => {
    console.log(`Playwright-Service läuft unter http://localhost:${port}`);
});
EOF

# 11. Docker-Container neu aufsetzen
docker-compose down
docker system prune -f
rm -rf node_modules
rm -f package-lock.json
docker-compose build --no-cache
docker-compose up -d

# 12. Testen des Services
curl -X POST http://localhost:3000/scrape \
     -H "Content-Type: application/json" \
     -d '{"url": "https://example.com"}'

##########################################################################

Optional:


# 8.1 nodemon.json erstellen (NEU)
cat <<EOF > /var/docker/playwright-docker/nodemon.json
{
  "verbose": true,
  "watch": [
    "/usr/src/app/"
  ],
  "ignore": ["*.test.js", "logs/*", "node_modules/*"],
  "ext": "js,json,css,html",
  "delay": "500ms"
}
EOF


1.) Die nodemon.json wird durch zwei zusammenwirkende Komponenten geladen:

In der docker-compose.yml durch den Befehl:

sh -c "npm install && nodemon --legacy-watch"

2.) Durch das Volume-Mounting in der docker-compose.yml:


- .:/usr/src/app:cached
  
Dies macht die nodemon.json im Container unter /usr/src/app/nodemon.json verfügbar.

docker-compose logs

zum überprüfen


###############################################################################


# Testen der Samba-Freigabe
smbclient -L //localhost -U web

# Prüfen der Docker-Container-Logs
docker-compose logs -f

# Prüfen der nodemon-Funktionalität durch Ändern einer Datei
echo "// Test-Kommentar" >> /var/docker/playwright-docker/index.js

# Prüfen ob die Mount-Punkte leer sind vor dem Einrichten
ls -la /home/docker
ls -la /var/docker

# Benutzer zur Docker-Gruppe hinzufügen falls noch nicht geschehen
sudo usermod -aG docker web

# Docker-Status
docker ps
docker-compose ps

# Filesystem-Status
mount | grep docker
df -h | grep docker

# Logs
tail -f /var/log/samba/log.smbd
journalctl -u docker

# Reset falls nötig
sudo umount -l /home/docker
docker-compose down -v
				
			

WordPress Install

				
					# Navigiere zum Zielverzeichnis
cd /var/www/html/web.test/dist

# Lade die neueste deutsche Version von WordPress herunter
wget https://de.wordpress.org/latest-de_DE.tar.gz

# Entpacke das heruntergeladene Archiv
tar xfz latest-de_DE.tar.gz

# Verschiebe alle Dateien aus dem wordpress-Ordner ins aktuelle Verzeichnis
rsync -av --remove-source-files wordpress/ ./

# Lösche leere Verzeichnisse im wordpress-Ordner
find wordpress -type d -empty -delete

# Entferne den nun leeren wordpress-Ordner
# rmdir wordpress

# Lösche unnötige Dateien, einschließlich des Standard-Plugins "Hello"
rm -f latest-de_DE.tar.gz readme.html license.txt liesmich.html wp-content/plugins/hello.php

# Setze die Dateiberechtigungen auf 644
find /var/www/html/web.test/dist/ -type f -print0 | xargs -0 chmod 644

# Setze die Verzeichnisberechtigungen auf 755
find /var/www/html/web.test/dist/ -type d -print0 | xargs -0 chmod 755

# Ändere den Besitz aller Dateien und Verzeichnisse auf den Benutzer und die Gruppe 'www-data'
sudo chown -R www-data:www-data /var/www/html/web.test/dist

# Wechsle zum Plugins-Verzeichnis von WordPress
cd wp-content/plugins/

# Überprüfe, ob 'unzip' installiert ist, und installiere es bei Bedarf
if ! command -v unzip &> /dev/null
then
    echo "'unzip' wird installiert..."
    sudo apt-get update
    sudo apt-get install -y unzip
fi

# Lade die Plugins herunter
wget https://downloads.wordpress.org/plugin/code-snippets.zip && \
wget https://downloads.wordpress.org/plugin/duplicate-page.zip && \
wget https://downloads.wordpress.org/plugin/duplicate-menu.zip && \
wget https://downloads.wordpress.org/plugin/elementor.zip && \
wget https://downloads.wordpress.org/plugin/wps-hide-login.zip && \
wget https://downloads.wordpress.org/plugin/wps-limit-login.zip && \
wget https://downloads.wordpress.org/plugin/seo-by-rank-math.zip && \
wget https://downloads.wordpress.org/plugin/wp-sweep.zip && \
wget https://downloads.wordpress.org/plugin/user-role-editor.zip && \
wget https://downloads.wordpress.org/plugin/query-monitor.zip && \
wget https://downloads.wordpress.org/plugin/permalink-manager.zip && \
wget https://downloads.wordpress.org/plugin/ewww-image-optimizer.zip && \
wget https://downloads.wordpress.org/plugin/svg-support.zip && \
wget https://downloads.wordpress.org/plugin/plugin-load-filter.zip && \
wget https://downloads.wordpress.org/plugin/wp-super-cache.zip && \
wget https://downloads.wordpress.org/plugin/adminimize.zip && \
wget https://downloads.wordpress.org/plugin/webp-express.zip && \
wget https://downloads.wordpress.org/plugin/google-language-translator.zip && \
wget https://downloads.wordpress.org/plugin/ai-assistant-elementor.zip

# Entpacke alle heruntergeladenen Zip-Dateien
unzip '*.zip'

# Entferne die Zip-Dateien nach dem Entpacken
rm *.zip

# Db anlegen  
mysql
CREATE DATABASE db_local_web_test;
exit;

# ======================================================================
# Abschließende Schritte
# ======================================================================

sudo chown -R www-data:www-data /var/www/html/web.test/dist
sudo find /var/www/html/web.test/dist -type d -exec chmod 755 {} \;
sudo find /var/www/html/web.test/dist -type f -exec chmod 644 {} \;

# Zeige den Inhalt der Hosts-Datei an
cat /etc/hosts

sudo bash -c 'echo "127.0.1.1 web.test" >> /etc/hosts'
sudo bash -c 'echo "127.0.1.1 www.web.test" >> /etc/hosts'

# Überprüfe die Apache Konfiguration auf Syntaxfehler
apachectl configtest

# Starte die notwendigen Services neu
systemctl restart php8.4-fpm
systemctl restart apache2

# Wechsle zurück zum Verzeichnis der WordPress-Distribution
cd /var/www/html/web.test/dist

# Zeige eine detaillierte Liste der Dateien und Verzeichnisse an
ls -la /var/www/html/web.test/dist

# *optional Ändere den Besitz für FTP-Zugriff auf online Server
chown -R ftp-user:psaserv /Path/online-Server



				
			

WSL2 Debian - Snippets

				
					# ================================
# Debian Installation und Konfiguration unter Windows WSL
# ================================

# 1. Standardbenutzer auf root setzen
debian config --default-user root

# 2. Zugriffsrechte für einen Windows-Ordner gewähren
icacls D:\<example_folder> /grant "chris:(OI)(CI)(F)"

# 3. System aktualisieren und aufrüsten
apt-get update && sudo apt-get upgrade && sudo apt-get dist-upgrade && sudo apt-get autoremove && sudo apt-get autoclean

# 4. Netzwerkverbindungen anzeigen
netstat -tulpn

# 5. Wichtige Pakete installieren
apt install -y \
    mc \
    locate \
    net-tools \
    nmap \
    dnsutils \
    build-essential \
    && updatedb

# 6. Umgebungsvariablen bearbeiten
mcedit /etc/environment

# Füge folgende Zeile hinzu oder bearbeite sie:
PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/usr/local/sbin:/sbin"

# 7. Änderungen anwenden
source /etc/environment

# 8. MariaDB Server und Client installieren
sudo apt-get -y install mariadb-server mariadb-client

# 9. MariaDB Dienst aktivieren und starten
systemctl enable --now mariadb.service

# 10. Status des MariaDB Dienstes überprüfen
systemctl status mariadb.service
systemctl is-enabled mariadb.service

# 11. MariaDB Konfiguration bearbeiten, um Remote-Zugriff zu erlauben
mcedit /etc/mysql/mariadb.conf.d/50-server.cnf
# Ändere die Zeile:
# bind-address = 0.0.0.0

# 12. mkcert Konfiguration bereinigen und unter Benutzer 'nutzer' installieren
cd home/nutzer/.local/share/mkcert
# Lösche das root-Verzeichnis
# Installiere mkcert unter dem Benutzer 'nutzer'

# 13. MySQL Benutzer konfigurieren
mysql

# Führe folgende SQL-Befehle aus:
ALTER USER 'root'@'localhost' IDENTIFIED BY '';
ALTER USER 'mysql'@'localhost' IDENTIFIED BY '';

CREATE USER 'root'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION;
CREATE USER 'root'@'%' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
QUIT;

# 14. IP-Adressen anzeigen
ip a

# 15. Verbindung zur MySQL-Datenbank herstellen
mysql -u wsl_root -p -h 192.168.99.2

# 16. Apache2 installieren
apt-get -y install apache2

# 17. wget installieren
apt-get -y install wget

# 18. PHP von Ondrej's PPA installieren
# a) Benötigte Pakete installieren
sudo apt install apt-transport-https lsb-release

# b) Signierschlüssel herunterladen
sudo wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg

# c) Ondrej's Repository hinzufügen
sudo sh -c 'echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'

# d) Paketlisten aktualisieren
sudo apt update 

# e) PHP PPA hinzufügen
sudo add-apt-repository ppa:ondrej/php # Drücke Enter, wenn gefragt

# f) Paketlisten erneut aktualisieren
sudo apt update

# g) PHP 8.1 und Erweiterungen installieren
sudo apt-get -y install php8.1 php8.1-mysql php8.1-curl php8.1-gd php8.1-zip php8.1-fpm php8.1-cli php8.1-opcache php8.1-mbstring php8.1-xml libapache2-mod-php8.1

# h) PHP 8.2 und Erweiterungen installieren
sudo apt-get -y install php8.2 php8.2-mysql php8.2-curl php8.2-gd php8.2-zip php8.2-fpm php8.2-cli php8.2-opcache php8.2-mbstring php8.2-xml libapache2-mod-php8.2

# i) PHP-Version konfigurieren
sudo update-alternatives --config php

# 19. Apache-Module aktivieren und PHP-FPM konfigurieren
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.1-fpm
sudo systemctl restart apache2

# 20. Weitere Pakete installieren
apt-get install ca-certificates apt-transport-https software-properties-common -y

# 21. Netzwerkdienste überprüfen
sudo netstat -tupln

# Beispielausgabe:
# Active Internet connections (only servers)
# Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
# tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN      189/mariadbd
# tcp6       0      0 :::80                   :::*                    LISTEN      17685/apache2
# udp        0      0 127.0.0.1:323           0.0.0.0:*                           -
# udp6       0      0 ::1:323                 :::*                                -

# 22. Adminer installieren
apt install adminer

# 23. Adminer Konfiguration bearbeiten
mcedit /etc/adminer/adminer.conf

# Inhalt von /etc/adminer/adminer.conf:
Alias /adminer /usr/share/adminer/adminer

# 24. Adminer in Apache einbinden
cd /etc/apache2/conf-available
ln -s /etc/adminer/adminer.conf adminer.conf
a2enconf adminer
systemctl reload apache2

# 25. Apache-Status überprüfen
systemctl status apache2

# Hinweis: Ohne Passwort als root einloggen

# 26. Adminer anpassen, um ohne Passwort zu ermöglichen
mcedit /usr/share/adminer/adminer/include/adminer.inc.php

# Beispielhafte Anpassung in adminer.inc.php:
# function login($login, $password) {
#     /*
#     if ($password == "") {
#         return lang('Adminer does not support accessing a database without a password, <a href="https://www.adminer.org/en/password/" %s rel="nofollow noopener" target="_blank">more information.', target_blank());
#     }
#     */
#     return true;
# }

# 27. WSL-Konfiguration überprüfen
cat /etc/wsl.conf

# Beispielinhalt von /etc/wsl.conf:
[boot]
systemd = true
command="ip addr add 10.10.100.2/24 broadcast 10.10.100.255 dev eth0 label eth0:1;netsh interface ip add address \"vEthernet (WSL)\" 10.10.100.1 255.255.255.0;"

[user]
default=root

[automount]
#enabled = false

[network]
generateResolvConf = false

[interop]
appendWindowsPath=false

# 28. WSL neu starten und als root einloggen
# Beende WSL
wsl --terminate debian

# Schalte WSL komplett aus
wsl --shutdown

# Starte Debian WSL als root
wsl -d Debian -u root

# 29. rc-local Dienst konfigurieren für Kompatibilität
mcedit /etc/systemd/system/rc-local.service

# Inhalt von /etc/systemd/system/rc-local.service:
[Unit]
 Description=/etc/rc.local Compatibility
 ConditionPathExists=/etc/rc.local

[Service]
 Type=forking
 ExecStart=/etc/rc.local start
 TimeoutSec=0
 StandardOutput=tty
 RemainAfterExit=yes
 SysVStartPriority=99

[Install]
 WantedBy=multi-user.target

# 30. /etc/rc.local erstellen und bearbeiten
mcedit /etc/rc.local

# Inhalt von /etc/rc.local:
#!/bin/bash
#
# rc.local
#
# Dieses Skript wird am Ende jedes Multiuser-Runlevels ausgeführt.
# Stelle sicher, dass das Skript mit "exit 0" bei Erfolg endet oder einen anderen
# Wert bei Fehlern zurückgibt.
#
# Um dieses Skript zu aktivieren oder zu deaktivieren, ändere einfach die Ausführungsbits.
#
# Standardmäßig tut dieses Skript nichts.

# IP-Adresse anzeigen
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

export PATH="$PATH:/sbin"

#########################
## Domains / IPs        ##
#########################

/bin/sleep 5 && php /home/web/www/domains/domain-ip-web-route.php

exit 0

# 31. rc.local ausführbar machen und Dienst aktivieren/starten
sudo chmod a+x /etc/rc.local
systemctl enable rc-local.service
systemctl start rc-local.service
systemctl status rc-local.service

# 32. Apache Virtual Hosts konfigurieren
mcedit /etc/apache2/conf-available/all-virtuell-host.conf

# Inhalt von /etc/apache2/conf-available/all-virtuell-host.conf:
###########################################
### hier werden unsere *.conf includiert ##
###########################################

IncludeOptional /var/www/html/*/apache/*.conf

# 33. Apache Module aktivieren und Konfigurationen laden
sudo a2enmod vhost_alias
systemctl restart apache2
sudo apachectl configtest
sudo a2enconf all-virtuell-host.conf
sudo systemctl reload apache2

# 34. SSL einrichten
# a) libnss3-tools installieren
sudo apt install libnss3-tools

# b) SSL-Verzeichnis erstellen
mkdir -p /home/ssl
cd /home/ssl

# c) mkcert herunterladen und installieren
wget -O mkcert https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64
chmod +x mkcert
sudo mv mkcert /usr/local/bin
sudo apt-get install -y ca-certificates
sudo update-ca-certificates
mkcert -install

# d) SSL-Modul in Apache aktivieren und Apache neu starten
sudo a2enmod ssl
sudo service apache2 restart

# 35. ServerName in Apache festlegen
mcedit /etc/apache2/conf-available/servername.conf

# Alternativ über Kommandozeile:
echo "ServerName LAPTOP" | sudo tee /etc/apache2/conf-available/servername.conf
sudo a2enconf servername
sudo service apache2 reload

# 36. IPv4 erzwingen und IPv6 deaktivieren
mcedit /etc/sysctl.d/99-sysctl.conf

# Füge folgende Zeilen hinzu:
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

# Alternativ über Kommandozeile:
echo "net.ipv6.conf.all.disable_ipv6=1" | sudo tee /etc/sysctl.d/disable-ipv6.conf

# 37. Sysctl Einstellungen anwenden und IPv6 überprüfen
sudo sysctl -p
sudo ifconfig | grep inet6

# 38. Webseite einrichten
# a) Verzeichnisse erstellen
mkdir -p /var/www/html/web.test/{dist,zugang,ssl,apache,doku,logs}
mkdir -p /var/www/html/web.test/logs/{apache,php}

# b) SSL-Zertifikate erstellen
cd /var/www/html/web.test/ssl
mkcert web.test '*.web.test'

# c) Apache Module für Substitution und Filter aktivieren
a2enmod substitute
a2enmod filter

# d) Apache Konfigurationsverzeichnis betreten
cd /var/www/html/web.test/apache/

# e) Virtual Host Konfiguration erstellen
mcedit web.test.conf

# f) Apache Konfiguration testen und neu laden
sudo apachectl configtest
sudo systemctl reload apache2

# 39. Testseiten erstellen
# a) PHP Testseite
sudo bash -c 'cat >/var/www/html/web.test/dist/index.php <<EOF
<?php
echo "<pre>";
print_r($_SERVER);
echo "</pre>";
phpinfo();
?>
EOF'

# b) HTML Testseite
sudo bash -c 'cat >/var/www/html/web.test/dist/index.html <<EOF
hallo
EOF'

# 40. Apache neu starten, um Änderungen zu übernehmen
systemctl restart apache2

# 41. Zusätzliche Testseite erstellen
sudo bash -c 'cat >/var/www/html/webserver.test/dist/index.php <<EOF
<?php
echo "<pre>";
print_r($_SERVER);
echo "</pre>";
phpinfo();
?>
EOF'

# 42. Apache Konfiguration erneut testen und neu laden
sudo apachectl configtest
sudo systemctl reload apache2

# 43. Windows PowerShell: Physische Laufwerke auflisten
# Öffne PowerShell und führe folgenden Befehl aus:
GET-CimInstance -query "SELECT * from Win32_DiskDrive"

# Beispielausgabe:
# DeviceID           Caption                          Partitions Size         Model
# --------           -------                          ---------- ----         -----
# \\.\PHYSICALDRIVE1  USB  SanDisk 3.2Gen1 USB Device 1          494199498240  USB  SanDisk 3.2Gen1 USB Device
# \\.\PHYSICALDRIVE0 KXG5AZNV512G TOSHIBA             3          512105932800 KXG5AZNV512G TOSHIBA

# Hinweis: Der DeviceID-Pfad ist wichtig, z.B. \\.\PHYSICALDRIVE1

# 44. Physisches Laufwerk in WSL mounten
wsl --mount \\.\PHYSICALDRIVE1

# 45. Statische IP-Adresse hinzufügen
netsh interface ip add address "vEthernet (WSL)" 10.10.100.1 255.255.255.0





				
			
				
					# ==========================================
# Netzwerk-Konfiguration für WSL unter Windows
# ==========================================

# 1. Anzeigen der aktuellen IPv4-Konfiguration aller Netzwerkadapter
netsh interface ipv4 show config

# 2. Hinzufügen einer statischen IP-Adresse zur Netzwerkschnittstelle "vEthernet (WSL)"
netsh interface ip add address "vEthernet (WSL)" 10.10.100.1 255.255.255.0

# 3. Hinzufügen einer statischen IP-Adresse zur Netzwerkschnittstelle "vEthernet (WSL (Hyper-V firewall))"
netsh interface ip add address "vEthernet (WSL (Hyper-V firewall))" 10.10.100.1 255.255.255.0

				
			
				
					# ==========================================
# Nextcloud 21 mit PHP 8.0 nachrüsten und optimieren
# ==========================================

# Referenzen:
# https://www.c-rieger.de/nextcloud-21-mit-php-8-0-nachruesten/
# https://addictedtocode.de/wordpress-und-webentwicklung/nutzung-und-optimierung-des-apcu-und-opcache/

# 1. PHP und benötigte Erweiterungen installieren
apt install -y php-common php8.1-{fpm,gd,mysql,curl,xml,zip,intl,mbstring,bz2,ldap,apcu,bcmath,gmp,imagick,igbinary,redis,cli,common,opcache,readline} imagemagick

# 2. Konfigurationsdateien sichern
cp /etc/php/8.1/fpm/pool.d/www.conf /etc/php/8.1/fpm/pool.d/www.conf.bak
cp /etc/php/8.1/fpm/php-fpm.conf /etc/php/8.1/fpm/php-fpm.conf.bak
cp /etc/php/8.1/cli/php.ini /etc/php/8.1/cli/php.ini.bak
cp /etc/php/8.1/fpm/php.ini /etc/php/8.1/fpm/php.ini.bak
cp /etc/php/8.1/fpm/php-fpm.conf /etc/php/8.1/fpm/php-fpm.conf.bak
cp /etc/php/8.1/mods-available/apcu.ini /etc/php/8.1/mods-available/apcu.ini.bak
cp /etc/ImageMagick-6/policy.xml /etc/ImageMagick-6/policy.xml.bak

# 3. PHP-FPM Dienst neu starten
systemctl restart php8.1-fpm.service

# 4. Verfügbare RAM und durchschnittliche FPM-Prozesse berechnen
AvailableRAM=$(awk '/MemAvailable/ {printf "%d", $2/1024}' /proc/meminfo)
AverageFPM=$(ps --no-headers -o 'rss,cmd' -C php-fpm8.1 | awk '{ sum+=$1 } END { printf ("%d\n", sum/NR/1024,"M") }')
FPMS=$((AvailableRAM/AverageFPM))
PMaxSS=$((FPMS*2/3))
PMinSS=$((PMaxSS/2))
PStartS=$(((PMaxSS+PMinSS)/2))

# 5. PHP-FPM Konfiguration optimieren
sed -i "s/;env\[HOSTNAME\] = /env[HOSTNAME] = /" /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/;env\[TMP\] = /env[TMP] = /" /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/;env\[TMPDIR\] = /env[TMPDIR] = /" /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/;env\[TEMP\] = /env[TEMP] = /" /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/;env\[PATH\] = /env[PATH] = /" /etc/php/8.1/fpm/pool.d/www.conf
sed -i 's/pm.max_children =.*/pm.max_children = '$FPMS'/' /etc/php/8.1/fpm/pool.d/www.conf
sed -i 's/pm.start_servers =.*/pm.start_servers = '$PStartS'/' /etc/php/8.1/fpm/pool.d/www.conf
sed -i 's/pm.min_spare_servers =.*/pm.min_spare_servers = '$PMinSS'/' /etc/php/8.1/fpm/pool.d/www.conf
sed -i 's/pm.max_spare_servers =.*/pm.max_spare_servers = '$PMaxSS'/' /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/;pm.max_requests =.*/pm.max_requests = 1000/" /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/allow_url_fopen =.*/allow_url_fopen = 1/" /etc/php/8.1/fpm/php.ini

# 6. PHP CLI Konfiguration optimieren
sed -i "s/output_buffering =.*/output_buffering = 'Off'/" /etc/php/8.1/cli/php.ini
sed -i "s/max_execution_time =.*/max_execution_time = 3600/" /etc/php/8.1/cli/php.ini
sed -i "s/max_input_time =.*/max_input_time = 3600/" /etc/php/8.1/cli/php.ini
sed -i "s/post_max_size =.*/post_max_size = 10240M/" /etc/php/8.1/cli/php.ini
sed -i "s/upload_max_filesize =.*/upload_max_filesize = 10240M/" /etc/php/8.1/cli/php.ini
sed -i "s/;date.timezone.*/date.timezone = Europe\/Berlin/" /etc/php/8.1/cli/php.ini

# 7. PHP-FPM php.ini Konfiguration optimieren
sed -i "s/memory_limit = 128M/memory_limit = 512M/" /etc/php/8.1/fpm/php.ini
sed -i "s/output_buffering =.*/output_buffering = 'Off'/" /etc/php/8.1/fpm/php.ini
sed -i "s/max_execution_time =.*/max_execution_time = 3600/" /etc/php/8.1/fpm/php.ini
sed -i "s/max_input_time =.*/max_input_time = 3600/" /etc/php/8.1/fpm/php.ini
sed -i "s/post_max_size =.*/post_max_size = 10240M/" /etc/php/8.1/fpm/php.ini
sed -i "s/upload_max_filesize =.*/upload_max_filesize = 10240M/" /etc/php/8.1/fpm/php.ini
sed -i "s/;date.timezone.*/date.timezone = Europe\/Berlin/" /etc/php/8.1/fpm/php.ini
sed -i "s/;session.cookie_secure.*/session.cookie_secure = True/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.enable=.*/opcache.enable=1/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.enable_cli=.*/opcache.enable_cli=1/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.memory_consumption=.*/opcache.memory_consumption=128/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.interned_strings_buffer=.*/opcache.interned_strings_buffer=16/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.max_accelerated_files=.*/opcache.max_accelerated_files=10000/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.revalidate_freq=.*/opcache.revalidate_freq=1/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.save_comments=.*/opcache.save_comments=1/" /etc/php/8.1/fpm/php.ini

# 8. PHP-FPM Konfigurationsparameter anpassen
sed -i "s|;emergency_restart_threshold.*|emergency_restart_threshold = 10|g" /etc/php/8.1/fpm/php-fpm.conf
sed -i "s|;emergency_restart_interval.*|emergency_restart_interval = 1m|g" /etc/php/8.1/fpm/php-fpm.conf
sed -i "s|;process_control_timeout.*|process_control_timeout = 10|g" /etc/php/8.1/fpm/php-fpm.conf

# 9. APCU Konfiguration anpassen
sed -i '$aapc.enable_cli=1' /etc/php/8.1/mods-available/apcu.ini

# 10. ImageMagick Policy anpassen
sed -i "s/rights=\"none\" pattern=\"PS\"/rights=\"read|write\" pattern=\"PS\"/" /etc/ImageMagick-6/policy.xml
sed -i "s/rights=\"none\" pattern=\"EPS\"/rights=\"read|write\" pattern=\"EPS\"/" /etc/ImageMagick-6/policy.xml
sed -i "s/rights=\"none\" pattern=\"PDF\"/rights=\"read|write\" pattern=\"PDF\"/" /etc/ImageMagick-6/policy.xml
sed -i "s/rights=\"none\" pattern=\"XPS\"/rights=\"read|write\" pattern=\"XPS\"/" /etc/ImageMagick-6/policy.xml

# 11. PHP 8.x installieren und optimieren abgeschlossen, Dienst starten
systemctl start php8.1-fpm.service

				
			
				
					find /var/www/html/ -type f -name "*.Identifier" -delete
				
			

Apache Virtual Host Konfiguration -> Subdomains per Proxy Wordpress

				
					################################################################################################
# webtest.conf
#
# Apache Virtual Host Konfiguration für webtest.de (inkl. Subdomains frankfurt, berlin, hamburg)
# mit dynamischer CORS-Freigabe, spezifischen Sicherheitsheadern und gezieltem Caching.
#
# WordPress Admin & Login nur auf Hauptdomain zugänglich.
#
# Version: 1.0 (optimiert mit Partial Caching)
# Datum:   23.04.2024
################################################################################################

# Globale Sicherheitsheader und Optimierungen
<IfModule headers_module>
    # Schutz vor Cross-Site Scripting (XSS)
    Header set X-XSS-Protection "1; mode=block"

    # Verhindert, dass die Seite in Frames von anderen Domains eingebettet wird
    Header set X-Frame-Options "SAMEORIGIN"

    # Verhindert MIME-Typ Sniffing
    Header set X-Content-Type-Options "nosniff"

    # Entfernt den X-Powered-By-Header, um Informationen über die verwendete Technologie zu verbergen
    Header always unset X-Powered-By

    # Beschränkt Cross-Domain-Policies
    Header set X-Permitted-Cross-Domain-Policies "none"

    # Strenge Referrer-Policy
    Header set Referrer-Policy "strict-origin-when-cross-origin"

    # Erzwingt die Nutzung von HTTPS für alle zukünftigen Anfragen
    Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

    # Optimiert das Caching-Verhalten (Vary-Header)
    Header append Vary "Accept-Encoding, User-Agent, Referer"

    # Berechtigungs-Politik (Permissions-Policy)
    Header set Permissions-Policy "camera=(), fullscreen=(self), geolocation=(*), microphone=(self), autoplay=(self), payment=(), usb=(), vibrate=(), notifications=(self), accelerometer=(self), gyroscope=(self)"

    # Content-Security-Policy (CSP) für die gesamte Website
    Header set Content-Security-Policy "default-src 'self' https://*.google.com; \
    img-src 'self' data: https://*.google.com https://s.w.org https://images.dmca.com https://secure.gravatar.com https://*.youtube.com https://maps.google.com https://translate.google.com https://fonts.gstatic.com https://www.gstatic.com https://translate.googleapis.com; \
    font-src 'self' data: https://fonts.gstatic.com https://webtest.de https://*.webtest.de; \
    connect-src 'self' https://t.clarity.ms https://wid.cert-bund.de https://*.google.com https://maps.google.com https://translate.google.com https://translate.googleapis.com https://translate-pa.googleapis.com; \
    style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com https://*.google.com https://www.gstatic.com https://translate.google.com https://maps.google.com; \
    script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://*.google.com https://www.google-analytics.com https://www.clarity.ms https://www.googletagmanager.com https://www.gstatic.com https://images.dmca.com https://maps.google.com https://translate.google.com https://translate.googleapis.com https://translate-pa.googleapis.com; \
    script-src-elem 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://*.google.com https://www.google-analytics.com https://www.clarity.ms https://www.googletagmanager.com https://www.gstatic.com https://images.dmca.com https://maps.google.com https://translate.google.com https://translate.googleapis.com https://translate-pa.googleapis.com https://*.webtest.de https://webtest.de; \
    object-src 'none'; \
    worker-src 'self' blob:; \
    frame-src 'self' https://*.youtube.com https://maps.google.com https://translate.google.com https://www.google.com; \
    frame-ancestors 'self'; \
    form-action 'self'; \
    base-uri 'self';"

    # Entfernen bestimmter Sicherheitsheader für /wp-admin/
    Header unset Content-Security-Policy env=WP_ADMIN
    Header unset X-Frame-Options env=WP_ADMIN
    Header unset X-Content-Type-Options env=WP_ADMIN
    Header unset Referrer-Policy env=WP_ADMIN
    Header unset Strict-Transport-Security env=WP_ADMIN
    Header unset Permissions-Policy env=WP_ADMIN
</IfModule>

<LocationMatch "^/sicher/">
    # Entfernen der Sicherheitsheader für alle Anfragen an "/sicher/"
    Header  unset Content-Security-Policy
    Header  unset X-Frame-Options
    Header  unset X-Content-Type-Options
    Header  unset Referrer-Policy
    Header  unset Strict-Transport-Security
    Header  unset Permissions-Policy
</LocationMatch>

################################################################################################
# Umgebungsvariablen
################################################################################################

# Kennzeichnet diese Installation als lokale Entwicklungsumgebung
SetEnv LOCAL true

# Setzt Environment-Variables für /wp-admin/
SetEnvIf Request_URI "^/wp-admin/" WP_ADMIN=true

################################################################################################
# 1) HTTP (Port 80) - Alle Domains => Weiterleitung auf HTTPS
################################################################################################
<VirtualHost *:80>
    ServerName webtest.de
    ServerAlias www.webtest.de \
                frankfurt.webtest.de \
                berlin.webtest.de \
                hamburg.webtest.de

    DocumentRoot "/var/www/html/webtest.de/dist"

    # Logging
    ErrorLog  /var/www/html/webtest.de/logs/apache/error.log
    CustomLog /var/www/html/webtest.de/logs/apache/access.log combined

    ########################################################################
    # HTTP -> HTTPS Weiterleitung (für ALLE Domains)
    ########################################################################
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
</VirtualHost>

# Gemeinsame HTTPS Einstellungen für alle VirtualHosts
<IfModule mod_ssl.c>
    SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
    SSLCipherSuite HIGH:!aNULL:!MD5:!3DES
    SSLHonorCipherOrder on
    SSLCompression off
</IfModule>

################################################################################################
# 2) HTTPS (Port 443) - Hauptdomain (webtest.de / www.webtest.de)
################################################################################################
<VirtualHost *:443>
    ServerName webtest.de
    ServerAlias www.webtest.de

    DocumentRoot "/var/www/html/webtest.de/dist"

    # SSL-Konfiguration
    SSLEngine on
    SSLCertificateFile    "/var/www/html/webtest.de/ssl/webtest.de+1.pem"
    SSLCertificateKeyFile "/var/www/html/webtest.de/ssl/webtest.de+1-key.pem"

    # Logging
    ErrorLog  /var/www/html/webtest.de/logs/apache/error.log
    CustomLog /var/www/html/webtest.de/logs/apache/access.log combined

    ########################################################################
    # (Global) - Caching deaktiviert
    ########################################################################
    <IfModule mod_headers.c>
        Header always set Cache-Control "no-store, no-cache, must-revalidate, max-age=0"
        Header always set Pragma "no-cache"
        Header always set Expires "0"
    </IfModule>

    # Sicherheitsheader global gesetzt

    # Verzeichniskonfiguration - ges. "dist"
    <Directory "/var/www/html/webtest.de/dist">
        Options FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    ########################################################################
    # CORS-Header und (gezieltes) Caching in /wp-includes und /wp-content
    ########################################################################

    # => /wp-includes/
    <Directory "/var/www/html/webtest.de/dist/wp-includes">
        Require all granted

        # Erlaubt Subdomains von webtest.de
        <IfModule mod_headers.c>
            SetEnvIfNoCase Origin "^https?://([a-z0-9-]+\.)?webtest\.de(:[0-9]+)?$" AccessControlAllow=$0
            Header always set Access-Control-Allow-Methods "GET, POST, OPTIONS"
            Header always set Access-Control-Allow-Headers "Origin, Content-Type, Accept, Authorization"
            Header always set Access-Control-Allow-Credentials "true"
            Header always set Access-Control-Allow-Origin "%{AccessControlAllow}e" env=AccessControlAllow
        </IfModule>

        # Optionen: force OPTIONS to return 200
        RewriteEngine On
        RewriteCond %{REQUEST_METHOD} OPTIONS
        RewriteRule ^(.*)$ $1 [R=200,L]

        # (Optional) Override kein Cache => Aktiviert Browser-Caching NUR für statische Assets
        <IfModule mod_expires.c>
            ExpiresActive On
            # Standard: 7 Tage
            ExpiresDefault "access plus 7 days"
            # Beispiele für Typen
            ExpiresByType text/css "access plus 7 days"
            ExpiresByType text/javascript "access plus 7 days"
            ExpiresByType application/javascript "access plus 7 days"
            ExpiresByType image/svg+xml "access plus 7 days"
            ExpiresByType image/png "access plus 7 days"
            ExpiresByType image/jpeg "access plus 7 days"
            ExpiresByType image/jpg "access plus 7 days"
            ExpiresByType image/gif "access plus 7 days"
        </IfModule>

        <IfModule mod_headers.c>
            # Vorher global: no-cache => hier überschreiben wir's
            Header unset Cache-Control
            Header unset Pragma
            Header unset Expires
            Header set Cache-Control "max-age=604800, public"
        </IfModule>
    </Directory>

    # => /wp-content/
    <Directory "/var/www/html/webtest.de/dist/wp-content">
        Require all granted

        <IfModule mod_headers.c>
            SetEnvIfNoCase Origin "^https?://([a-z0-9-]+\.)?webtest\.de(:[0-9]+)?$" AccessControlAllow=$0
            Header always set Access-Control-Allow-Methods "GET, POST, OPTIONS"
            Header always set Access-Control-Allow-Headers "Origin, Content-Type, Accept, Authorization"
            Header always set Access-Control-Allow-Credentials "true"
            Header always set Access-Control-Allow-Origin "%{AccessControlAllow}e" env=AccessControlAllow
        </IfModule>

        RewriteEngine On
        RewriteCond %{REQUEST_METHOD} OPTIONS
        RewriteRule ^(.*)$ $1 [R=200,L]

        # (Optional) Override kein Cache => Aktiviert Browser-Caching NUR für statische Assets
        <IfModule mod_expires.c>
            ExpiresActive On
            ExpiresDefault "access plus 7 days"
            ExpiresByType text/css "access plus 7 days"
            ExpiresByType text/javascript "access plus 7 days"
            ExpiresByType application/javascript "access plus 7 days"
            ExpiresByType image/svg+xml "access plus 7 days"
            ExpiresByType image/png "access plus 7 days"
            ExpiresByType image/jpeg "access plus 7 days"
            ExpiresByType image/jpg "access plus 7 days"
            ExpiresByType image/gif "access plus 7 days"
        </IfModule>

        <IfModule mod_headers.c>
            Header unset Cache-Control
            Header unset Pragma
            Header unset Expires
            Header set Cache-Control "max-age=604800, public"
        </IfModule>
    </Directory>

    ########################################################################
    # PHP-FPM (über Unix-Socket)
    ########################################################################
    <FilesMatch \.php$>
        SetHandler "proxy:unix:/run/php/php8.4-fpm.sock|fcgi://localhost"
    </FilesMatch>

    # HTTP/2 Optimierungen
    Protocols h2 http/1.1
    H2Direct on
    H2ModernTLSOnly on
    H2Push on
    H2PushPriority * after
    H2MaxSessionStreams 100

    # Umgebungsvariablen
    SetEnv LOCAL true
    SetEnvIf Request_URI "^/wp-admin/" WP_ADMIN=true

    # Mailhog Proxy-Konfiguration (Beispiel)
    ProxyPass        /mail/ http://127.0.0.1:8080/
    ProxyPassReverse /mail/ http://127.0.0.1:8080/

    <Location /mail/>
        Require all granted
    </Location>

    <Proxy "http://127.0.0.1:8080/*">
        Require all granted
    </Proxy>
</VirtualHost>

################################################################################################
# 3) HTTPS (Port 443) - Frankfurt Subdomain
################################################################################################
<VirtualHost *:443>
    ServerName frankfurt.webtest.de
    DocumentRoot "/var/www/html/webtest.de/dist"

    # SSL-Konfiguration
    SSLEngine on
    SSLCertificateFile    "/var/www/html/webtest.de/ssl/webtest.de+1.pem"
    SSLCertificateKeyFile "/var/www/html/webtest.de/ssl/webtest.de+1-key.pem"

    # Logging
    ErrorLog  /var/www/html/webtest.de/logs/apache/error.log
    CustomLog /var/www/html/webtest.de/logs/apache/access.log combined
    LogLevel debug

    # Proxy- und SSL-Konfiguration
    ProxyRequests Off
    ProxyPreserveHost Off
    SSLProxyEngine On
    SSLProxyVerify none
    SSLProxyCheckPeerCN off
    SSLProxyCheckPeerName off

    # Globales No-Cache
    <IfModule mod_headers.c>
        Header always set Cache-Control "no-store, no-cache, must-revalidate, max-age=0"
        Header always set Pragma "no-cache"
        Header always set Expires "0"
    </IfModule>

    # Sicherheitsheader bereits global gesetzt

    # Host-Header => WP "denkt", Anfragen kommen von www.webtest.de
    RequestHeader set Host "www.webtest.de"
    RequestHeader unset Accept-Encoding

    ########################################################################
    # Wichtige WordPress-Pfade => Proxy an Hauptdomain
    ########################################################################

    # 1) WP-Standardverzeichnisse
    ProxyPass        "/wp-includes/" "https://www.webtest.de/wp-includes/"
    ProxyPassReverse "/wp-includes/" "https://www.webtest.de/wp-includes/"

    ProxyPass        "/wp-content/"  "https://www.webtest.de/wp-content/"
    ProxyPassReverse "/wp-content/"  "https://www.webtest.de/wp-content/"

    # 2) KEIN WP-Admin & Login hier => auskommentiert bzw. entfernt
    #ProxyPass        "/wp-admin/"  "https://www.webtest.de/wp-admin/"
    #ProxyPassReverse "/wp-admin/"  "https://www.webtest.de/wp-admin/"
    #ProxyPass        "/wp-login.php" "https://www.webtest.de/wp-login.php"
    #ProxyPassReverse "/wp-login.php" "https://www.webtest.de/wp-login.php"

    # 3) WordPress REST-API
    ProxyPass        "/wp-json/"   "https://www.webtest.de/wp-json/"
    ProxyPassReverse "/wp-json/"   "https://www.webtest.de/wp-json/"

    # 4) Favicon
    ProxyPass        "/favicon.ico" "https://www.webtest.de/favicon.ico"
    ProxyPassReverse "/favicon.ico" "https://www.webtest.de/favicon.ico"

    ########################################################################
    # Haupt-Content => /frankfurt/
    ########################################################################
    ProxyPass        "/" "https://www.webtest.de/frankfurt/"
    ProxyPassReverse "/" "https://www.webtest.de/frankfurt/"

    # Substitute (URL-Ersetzungen)
    SetOutputFilter SUBSTITUTE;DEFLATE
    Substitute "s|https://www\.webtest\.de/frankfurt/|/|i"

    # Cookie-Handling => Domain/Pfad umschreiben
    ProxyPassReverseCookieDomain www.webtest.de frankfurt.webtest.de
    ProxyPassReverseCookiePath   /frankfurt/ /

    # Header-Korrekturen => Weiterleitungen "umschreiben"
    Header edit* Location "^https://www\.webtest\.de/frankfurt/(.*)" "/$1"
    Header edit* Location "^https://www\.webtest\.de/(.*)"            "/$1"

    # OPTIONS für CORS-Preflight-Requests (200 OK)
    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} OPTIONS
    RewriteRule ^(.*)$ $1 [R=200,L]
</VirtualHost>

################################################################################################
# 4) HTTPS (Port 443) - Berlin Subdomain
################################################################################################
<VirtualHost *:443>
    ServerName berlin.webtest.de
    DocumentRoot "/var/www/html/webtest.de/dist"

    SSLEngine on
    SSLCertificateFile    "/var/www/html/webtest.de/ssl/webtest.de+1.pem"
    SSLCertificateKeyFile "/var/www/html/webtest.de/ssl/webtest.de+1-key.pem"

    ErrorLog  /var/www/html/webtest.de/logs/apache/error.log
    CustomLog /var/www/html/webtest.de/logs/apache/access.log combined
    LogLevel debug

    ProxyRequests Off
    ProxyPreserveHost Off
    SSLProxyEngine On
    SSLProxyVerify none
    SSLProxyCheckPeerCN off
    SSLProxyCheckPeerName off

    <IfModule mod_headers.c>
        Header always set Cache-Control "no-store, no-cache, must-revalidate, max-age=0"
        Header always set Pragma "no-cache"
        Header always set Expires "0"
    </IfModule>

    # Sicherheitsheader bereits global gesetzt
    RequestHeader set Host "www.webtest.de"
    RequestHeader unset Accept-Encoding

    # WP-Standardverzeichnisse
    ProxyPass        "/wp-includes/" "https://www.webtest.de/wp-includes/"
    ProxyPassReverse "/wp-includes/" "https://www.webtest.de/wp-includes/"
    ProxyPass        "/wp-content/"  "https://www.webtest.de/wp-content/"
    ProxyPassReverse "/wp-content/"  "https://www.webtest.de/wp-content/"

    # KEIN WP-Admin & Login
    #ProxyPass        "/wp-admin/"  "https://www.webtest.de/wp-admin/"
    #ProxyPassReverse "/wp-admin/"  "https://www.webtest.de/wp-admin/"
    #ProxyPass        "/wp-login.php" "https://www.webtest.de/wp-login.php"
    #ProxyPassReverse "/wp-login.php" "https://www.webtest.de/wp-login.php"

    # WordPress REST-API
    ProxyPass        "/wp-json/"   "https://www.webtest.de/wp-json/"
    ProxyPassReverse "/wp-json/"   "https://www.webtest.de/wp-json/"

    # Favicon-Fix
    ProxyPass        "/favicon.ico" "https://www.webtest.de/favicon.ico"
    ProxyPassReverse "/favicon.ico" "https://www.webtest.de/favicon.ico"

    # Haupt-Content => /berlin/
    ProxyPass        "/" "https://www.webtest.de/berlin/"
    ProxyPassReverse "/" "https://www.webtest.de/berlin/"

    SetOutputFilter SUBSTITUTE;DEFLATE
    Substitute "s|https://www\.webtest\.de/berlin/|/|i"

    ProxyPassReverseCookieDomain www.webtest.de berlin.webtest.de
    ProxyPassReverseCookiePath   /berlin/ /

    Header edit* Location "^https://www\.webtest\.de/berlin/(.*)" "/$1"
    Header edit* Location "^https://www\.webtest\.de/(.*)"         "/$1"

    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} OPTIONS
    RewriteRule ^(.*)$ $1 [R=200,L]
</VirtualHost>

################################################################################################
# 5) HTTPS (Port 443) - Hamburg Subdomain
################################################################################################
<VirtualHost *:443>
    ServerName hamburg.webtest.de
    DocumentRoot "/var/www/html/webtest.de/dist"

    SSLEngine on
    SSLCertificateFile    "/var/www/html/webtest.de/ssl/webtest.de+1.pem"
    SSLCertificateKeyFile "/var/www/html/webtest.de/ssl/webtest.de+1-key.pem"

    ErrorLog  /var/www/html/webtest.de/logs/apache/error.log
    CustomLog /var/www/html/webtest.de/logs/apache/access.log combined
    LogLevel debug

    ProxyRequests Off
    ProxyPreserveHost Off
    SSLProxyEngine On
    SSLProxyVerify none
    SSLProxyCheckPeerCN off
    SSLProxyCheckPeerName off

    <IfModule mod_headers.c>
        Header always set Cache-Control "no-store, no-cache, must-revalidate, max-age=0"
        Header always set Pragma "no-cache"
        Header always set Expires "0"
    </IfModule>

    # Sicherheitsheader bereits global gesetzt
    RequestHeader set Host "www.webtest.de"
    RequestHeader unset Accept-Encoding

    # WP-Standardverzeichnisse
    ProxyPass        "/wp-includes/" "https://www.webtest.de/wp-includes/"
    ProxyPassReverse "/wp-includes/" "https://www.webtest.de/wp-includes/"
    ProxyPass        "/wp-content/"  "https://www.webtest.de/wp-content/"
    ProxyPassReverse "/wp-content/"  "https://www.webtest.de/wp-content/"

    # KEIN WP-Admin & Login
    #ProxyPass        "/wp-admin/"  "https://www.webtest.de/wp-admin/"
    #ProxyPassReverse "/wp-admin/"  "https://www.webtest.de/wp-admin/"
    #ProxyPass        "/wp-login.php" "https://www.webtest.de/wp-login.php"
    #ProxyPassReverse "/wp-login.php" "https://www.webtest.de/wp-login.php"

    # WordPress REST-API
    ProxyPass        "/wp-json/"   "https://www.webtest.de/wp-json/"
    ProxyPassReverse "/wp-json/"   "https://www.webtest.de/wp-json/"

    # Favicon-Fix
    ProxyPass        "/favicon.ico" "https://www.webtest.de/favicon.ico"
    ProxyPassReverse "/favicon.ico" "https://www.webtest.de/favicon.ico"

    # Haupt-Content => /hamburg/
    ProxyPass        "/" "https://www.webtest.de/hamburg/"
    ProxyPassReverse "/" "https://www.webtest.de/hamburg/"

    SetOutputFilter SUBSTITUTE;DEFLATE
    Substitute "s|https://www\.webtest\.de/hamburg/|/|i"

    ProxyPassReverseCookieDomain www.webtest.de hamburg.webtest.de
    ProxyPassReverseCookiePath   /hamburg/ /

    Header edit* Location "^https://www\.webtest\.de/hamburg/(.*)" "/$1"
    Header edit* Location "^https://www\.webtest\.de/(.*)"          "/$1"

    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} OPTIONS
    RewriteRule ^(.*)$ $1 [R=200,L]
</VirtualHost>

################################################################################################
# PHP-FPM Konfiguration via FastCGI (Global, falls benötigt)
################################################################################################
<IfModule proxy_fcgi_module>
    # Setzt PHP-Admin-Umgebungsvariablen für FastCGI
    ProxyFCGISetEnvIf "true" PHP_ADMIN_VALUE "sendmail_path=/usr/local/bin/mhsendmail\nupload_max_filesize=1G\npost_max_size=500M\nmemory_limit=1G\nmax_input_time=300"
</IfModule>

				
			
				
					<?php
/**
 * @file torip.php
 * @brief Verwaltung und Überprüfung mehrerer Tor-Instanzen.
 *
 * Dieses Skript dient zur Verwaltung mehrerer Tor-Instanzen durch Überprüfung der
 * Konfigurationsdateien, Starten und Stoppen der Instanzen sowie Erneuern der
 * Tor-Verbindungen. Zusätzlich wird die aktuelle Tor-IP-Adresse überprüft.
 *
 * ## Schritt-für-Schritt Anleitung: Hinzufügen von www-data zur sudoers-Datei
 *
 * 1. **Öffnen der sudoers-Datei mit visudo**
 *
 *    Es ist wichtig, die `sudoers`-Datei mit dem Befehl `visudo` zu bearbeiten, da
 *    dieser Editor Syntaxfehler überprüft und verhindert, dass Sie die Datei mit
 *    fehlerhaften Einstellungen verlassen. Dies schützt Ihr System vor potenziellen
 *    Sicherheitslücken.
 *
 *    ```bash
 *    sudo visudo
 *    ```
 *
 * 2. **Hinzufügen der Berechtigungszeile**
 *
 *    Navigieren Sie zum Ende der geöffneten `sudoers`-Datei und fügen Sie die folgende Zeile hinzu:
 *
 *    ```plaintext
 *    www-data ALL=NOPASSWD: /usr/bin/tor
 *    ```
 *
 * 3. **Speichern und Beenden**
 *
 *    - **In `visudo` (standardmäßig verwendet `nano` oder `vi`):**
 *
 *      - **Für `nano`:**
 *        - Drücken Sie `CTRL + O`, um die Datei zu speichern.
 *        - Drücken Sie `ENTER`, um die Dateibenennung zu bestätigen.
 *        - Drücken Sie `CTRL + X`, um den Editor zu verlassen.
 *
 *      - **Für `vi`:**
 *        - Drücken Sie `ESC`, um den Befehlsmodus zu aktivieren.
 *        - Geben Sie `:wq` ein und drücken Sie `ENTER`, um zu speichern und zu beenden.
 *
 * ## Verwendung
 *
 * Führen Sie das Skript mit dem PHP-Interpreter aus:
 *
 * ```bash
 * php -q /var/www/html/web.test/dist/torip.php
 * ```
 *
 * ## Anforderungen
 * - PHP mit den folgenden Erweiterungen: `posix`, `curl`
 * - Tor installiert und konfiguriert auf den angegebenen Ports und Datenverzeichnissen
 *
 * ## Konfigurationsdateien
 * - `/etc/tor/torrc.1`
 * - `/etc/tor/torrc.2`
 * - `/etc/tor/torrc.3`
 *
 * ## Ports
 * - SocksPorts: 9050, 9060, 9070
 *
 */

// Array mit Pfaden zu Tor-Konfigurationsdateien.
$torConfigs = ['/etc/tor/torrc.1', '/etc/tor/torrc.2', '/etc/tor/torrc.3'];
// Definiert SocksPorts für die Verwendung in allen Funktionen.
$SocksPort = [9050, 9060, 9070];

// Globale Zugriffstoken (falls benötigt)
$globalAccessToken = '';

// Debugging-Flag
$debug = true; // Setze auf false, um Debugging-Ausgaben zu deaktivieren

/**
 * Debugging-Funktion
 *
 * Gibt eine Debug-Nachricht aus, wenn das Debugging aktiviert ist.
 *
 * @param string $message Die Nachricht, die ausgegeben werden soll.
 */
function debug($message) {
    global $debug;
    if ($debug) {
        echo "DEBUG: $message\n";
    }
}

/**
 * Fehler-Ausgabe und Skript beenden
 *
 * Gibt eine Fehlermeldung aus und beendet das Skript mit dem angegebenen Fehlercode.
 *
 * @param string $message Die Fehlermeldung.
 */
function error_exit($message) {
    echo "FEHLER: $message\n";
    exit(1);
}

// Debug: Skriptstart
debug("Skript gestartet.");

// Überprüfe, ob das Skript als Root oder als ein anderer spezifischer Benutzer ausgeführt wird
$currentUser = posix_getpwuid(posix_geteuid())['name'];
debug("Aktueller Benutzer: $currentUser");

// Optional: Stelle sicher, dass das Skript nicht als Root ausgeführt wird
if ($currentUser === 'root') {
    debug("Das Skript sollte nicht als Root ausgeführt werden ;o)");
}

// Überprüfe die Berechtigungen der Tor-Konfigurationsdateien und DataDirectories
foreach ($torConfigs as $index => $config) {
    // Überprüfe, ob die Konfigurationsdatei existiert und lesbar ist
    if (!file_exists($config)) {
        error_exit("Konfigurationsdatei '$config' existiert nicht.");
    }
    if (!is_readable($config)) {
        error_exit("Konfigurationsdatei '$config' ist nicht lesbar. Überprüfe die Berechtigungen.");
    }
    debug("Konfigurationsdatei '$config' existiert und ist lesbar.");

    // Bestimme das entsprechende DataDirectory
    $dataDir = "/var/lib/tor" . ($index + 1);
    
    // Überprüfe, ob das DataDirectory existiert
    if (!is_dir($dataDir)) {
        error_exit("DataDirectory '$dataDir' existiert nicht.");
    }
    
    // Überprüfe, ob das DataDirectory lesbar und beschreibbar ist
    if (!is_readable($dataDir)) {
        error_exit("DataDirectory '$dataDir' ist nicht lesbar. Überprüfe die Berechtigungen.");
    }
    if (!is_writable($dataDir)) {
        error_exit("DataDirectory '$dataDir' ist nicht beschreibbar. Überprüfe die Berechtigungen.");
    }
    debug("DataDirectory '$dataDir' existiert und hat die richtigen Berechtigungen.");
}

// Lösche alle laufenden Tor-PID-Prozesse
deleteTorPID();

// Starte Tor-Instanzen basierend auf den Konfigurationsdateien und Ports
startTorInstances($torConfigs, $SocksPort);

// URL zur Überprüfung der Tor-IP
$url = 'https://check.torproject.org/api/ip';

echo "\n\n";

// Iteriere über alle definierten SocksPorts
foreach ($SocksPort as $key => $Port) {
    debug("Überprüfe Tor-Instanz auf Port $Port.");

    // Hole die aktuelle Tor-IP
    $oldIP = getURL($url, $Port, $globalAccessToken, 5, 20);
    debug("Alte IP auf Port $Port: " . ($oldIP ?: 'Fehler beim Abrufen'));

    // Erneuere die Tor-Verbindung über den ControlPort (Port +1)
    renewTorConnection($Port + 1, '');
    debug("Tor-Verbindung auf ControlPort " . ($Port + 1) . " erneuert.");

    // Hole die neue Tor-IP
    $newIP = getURL($url, $Port, $globalAccessToken, 5, 20);
    debug("Neue IP auf Port $Port: " . ($newIP ?: 'Fehler beim Abrufen'));
}

// Debug: Skriptende
debug("Skript beendet.");

/**
 * Löscht alle laufenden Tor-PID-Prozesse.
 *
 * Diese Funktion sucht nach laufenden Tor-Prozessen und beendet sie.
 * Sie wiederholt den Vorgang, bis keine Tor-Prozesse mehr gefunden werden
 * oder eine maximale Anzahl von Versuchen erreicht ist.
 */
function deleteTorPID() {
    debug("Überprüfe laufende Tor-Prozesse zum Löschen.");
    $i = 0;
    while (true) {
        // Abrufen der Tor PID(s).
        $temp_pid = shell_exec('pidof tor');
        $temp_pid = trim($temp_pid ?? '');

        // Debug: Aktuelle PID(s)
        debug("Aktuelle Tor-PIDs: " . ($temp_pid ?: 'Keine gefunden'));

        // Beendet die Funktion, wenn keine PIDs mehr gefunden werden.
        if (empty($temp_pid)) {
            if ($i > 0) {
                debug("Alle Tor-PIDs wurden gelöscht!");
            }
            return;
        } else if ($i == 0) {
            // Nur ausgeben, wenn das erste Mal PIDs gefunden wurden.
            debug("Lösche Tor-PIDs...");
        }

        // Beendet alle gefundenen Tor-Prozesse.
        exec('pidof tor | xargs kill > /dev/null 2>&1', $output, $return_var);

        usleep(5000); // Wartet 5 Millisekunden vor dem nächsten Versuch.

        // Beendet die Schleife nach 250 Versuchen.
        if ($i >= 250) {
            error_exit("Maximale Wartezeit erreicht beim Löschen der Tor-PIDs.");
        }
        $i++;
    }
}

/**
 * Startet einen Befehl im Hintergrund als debian-tor Benutzer.
 *
 * Diese Funktion startet eine Tor-Instanz im Hintergrund unter Verwendung
 * einer spezifischen Konfigurationsdatei.
 *
 * @param string $config Der Pfad zur Tor-Konfigurationsdatei.
 */
function go_hintergrund($config) {
    debug("Starte Befehl im Hintergrund: tor -f $config");

    // Verwende sudo, um den Befehl als debian-tor auszuführen und Tor im Hintergrund zu starten
    $fullCommand = "sudo -u debian-tor tor -f $config > /dev/null 2>&1 &";

    debug("Vollständiger Befehl: $fullCommand");

    // Führe den Befehl aus
    exec($fullCommand, $output, $return_var);

    if ($return_var == 0) {
        debug("Befehl erfolgreich im Hintergrund gestartet: $fullCommand");
    } else {
        echo "FEHLER: Befehl mit Rückgabewert $return_var nicht erfolgreich gestartet: $fullCommand\n";
    }
}

/**
 * Startet Tor-Instanzen basierend auf den angegebenen Konfigurationsdateien,
 * falls sie auf den definierten SocksPorts noch nicht laufen.
 *
 * @param array $torConfigs Ein Array mit Pfaden zu Tor-Konfigurationsdateien.
 * @param array $SocksPort Ein Array mit den SocksPorts, die von den Tor-Instanzen verwendet werden sollen.
 * @return bool Gibt true zurück, wenn alle Instanzen erfolgreich gestartet wurden.
 */
function startTorInstances($torConfigs, $SocksPort) {
    debug("Überprüfung und Start von Tor-Instanzen...");

    foreach ($torConfigs as $index => $config) {
        $port = $SocksPort[$index];
        debug("Überprüfe Port $port für Konfigurationsdatei $config.");

        // Überprüfe, ob auf dem Port bereits eine Tor-Instanz läuft.
        $checkPortCommand = "netstat -tuln | grep ':$port '";
        exec($checkPortCommand, $output, $returnCode);

        if ($returnCode === 0) {
            // Wenn der Befehl erfolgreich war (exit code 0), 
            // bedeutet dies, dass der Port bereits verwendet wird.
            debug("Port $port wird bereits verwendet. Überspringe Start der Instanz mit $config.");
            continue;
        } else {
            // Wenn der Port nicht verwendet wird, starte Tor mit der Konfigurationsdatei im Hintergrund.
            go_hintergrund($config);
            debug("Tor-Instanz mit Konfigurationsdatei $config auf Port $port gestartet.");
        }
    }

    // Überprüfung, ob die erforderliche Anzahl von Tor-Instanzen läuft.
    $i = 0;
    do {
        usleep(80000); // Warte 80 Millisekunden vor dem nächsten Versuch.
        $temp_pid_array = explode(' ', trim(shell_exec('pidof tor')));
        debug("Aktuelle Anzahl laufender Tor-Instanzen: " . count($temp_pid_array));

        if (count($temp_pid_array) >= count($SocksPort)) {
            debug("Alle Tor-Instanzen wurden erfolgreich gestartet!");
            return true;
        }
        $i++;
    } while ($i < 250);

    error_exit("Tor-Instanzen konnten nicht innerhalb der erwarteten Zeit gestartet werden.");
}

/**
 * Erneuert die Tor-Verbindung über den ControlPort.
 *
 * Diese Funktion sendet das SIGNAL NEWNYM-Kommando an den Tor-ControlPort, um
 * die Tor-Verbindung zu erneuern und eine neue IP-Adresse zu erhalten.
 *
 * @param int $controlPort Der ControlPort (SocksPort + 1).
 * @param string $controlPassword Das Passwort für die Authentifizierung (optional).
 */
function renewTorConnection($controlPort, $controlPassword = '') {
    debug("Versuche, die Tor-Verbindung auf ControlPort $controlPort zu erneuern.");

    $controlSocket = fsockopen('127.0.0.1', $controlPort, $errno, $errstr, 30);
    if (!$controlSocket) {
        echo "FEHLER: Konnte keine Verbindung zum ControlPort $controlPort herstellen: $errno - $errstr \n";
    } else {
        fwrite($controlSocket, "AUTHENTICATE \"" . $controlPassword . "\"\r\n");
        $response = fread($controlSocket, 1024);
        debug("Authentifizierungsantwort vom ControlPort $controlPort: $response");

        // Prüfe die Antwort auf Authentifizierung
        $response = trim($response);
        if (strpos($response, '250') === 0) {
            fwrite($controlSocket, "SIGNAL NEWNYM\r\n");
            debug("SIGNAL NEWNYM an ControlPort $controlPort gesendet.");
        } else {
            echo "FEHLER: Authentifizierung fehlgeschlagen auf ControlPort $controlPort: $response\n";
        }
        fclose($controlSocket);
    }
}

/**
 * Holt den Inhalt einer URL über einen bestimmten SocksPort.
 *
 * Diese Funktion verwendet cURL, um eine HTTP-Anfrage über einen angegebenen
 * SocksPort zu senden und den Inhalt der URL abzurufen.
 *
 * @param string $url Die zu ladende URL.
 * @param int $SocksPort Der SocksPort, der verwendet werden soll.
 * @param string $globalAccessToken Ein globaler Zugriffstoken (optional).
 * @param int $connectTimeout Timeout für die Verbindung in Sekunden.
 * @param int $totalTimeout Gesamt-Timeout für die Anfrage in Sekunden.
 * @return mixed Der Inhalt der URL oder false bei Fehlern.
 */
function getURL($url, $SocksPort, $globalAccessToken, $connectTimeout = 5, $totalTimeout = 20) {
    //$token = ""; // Auth-Token, initial leer
    $userAgent = TorUserAgent(); // Generiert einen User-Agent für alle Anfragen
    //$selectedSocksPort = '127.0.0.1:' . $SocksPort[array_rand($SocksPort)]; // Wählt einen zufälligen SocksPort
    $selectedSocksPort = '127.0.0.1:' . $SocksPort; // Wählt einen spezifischen SocksPort

    // Debug: URL und Port, die verwendet werden
    debug("Abrufe URL $url über SocksPort $selectedSocksPort mit User-Agent '$userAgent'.");

    // Initialisiere cURL
    $ch = curl_init();

    // Konfiguriere cURL-Optionen
    curl_setopt_array($ch, [
        CURLOPT_URL => $url,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0,
        CURLOPT_RETURNTRANSFER => 1,
        CURLOPT_PROXYTYPE => CURLPROXY_SOCKS5_HOSTNAME,
        CURLOPT_PROXY => $selectedSocksPort,
        CURLOPT_USERAGENT => $userAgent,
        /*
        CURLOPT_HTTPHEADER => [
            "Authorization: $globalAccessToken",
            "Connection: keep-alive",
            "Accept: application/json" // Erwartet explizit JSON-Antworten
        ],
        */
        CURLOPT_ENCODING => '', // Akzeptiert alle unterstützten Encodings
        CURLOPT_CONNECTTIMEOUT => $connectTimeout,
        CURLOPT_TIMEOUT => $totalTimeout,
    ]);

    // Führe die Anfrage aus
    $content = curl_exec($ch);
    $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $error = curl_error($ch);

    // Debug: cURL-Antwort und Statuscode
    debug("cURL-Antwort Statuscode: $statusCode.");
    if ($error) {
        debug("cURL-Fehler: $error.");
    }

    // Überprüfe auf Fehler
    if (curl_errno($ch)) {
        echo "FEHLER: cURL-Fehler beim Abrufen der URL: $error\n";
        curl_close($ch);
        return false;
    } else {
        // Überprüfe den HTTP-Statuscode
        if ($statusCode === 200) {
            // Debug: Erfolgreiche Antwort
            debug("Erfolgreich abgerufen: $content");
            curl_close($ch);
            return $content;
        } else {
            echo "FEHLER: HTTP-Fehler: $statusCode\n";
            usleep(500000); // Warte 0,5 Sekunden vor dem nächsten Versuch (optional)
            curl_close($ch);
            return false;
        }
    }
}

/**
 * Generiert einen zufälligen User-Agent-String.
 *
 * Diese Funktion erstellt einen realistisch wirkenden User-Agent-String durch
 * zufällige Auswahl eines modernen Browsers und Betriebssystems.
 *
 * @return string Der generierte User-Agent.
 */
function TorUserAgent() {
    // Aktualisierte Liste moderner Browser
    $browsers = [
        'Firefox/78.0', // Beispiel für moderne Versionsangabe
        'Chrome/88.0',
        'Safari/14.0.3',
        'Opera/74.0',
        'Edge/88.0',
        'Brave/1.19' // Brave Browser hinzugefügt
    ];

    // Aktualisierte Liste moderner Betriebssysteme
    $os = [
        'Windows 10', // Spezifischere Versionsangaben
        'Windows 11',
        'macOS Big Sur', // Verwendung des Namens anstatt der Versionsnummer
        'macOS Monterey',
        'Ubuntu 20.04', // Aktuelle Versionen von Linux-Distributionen
        'Fedora 33',
        'Debian 10',
        'Android 11', // Hinzufügen von mobilen Betriebssystemen
        'iOS 14'
    ];

    // Zufällige Auswahl von Browser und Betriebssystem
    $browser = $browsers[array_rand($browsers)];
    $selectedOS = $os[array_rand($os)];

    // Generierung einer Zufallszahl als Kennung
    $randomId = rand(100000, 999999);

    // Generierung des User-Agent Strings mit Zufallszahl als Kennung
    return "$browser ($selectedOS; de-DE) Ver/$randomId"; // Hinzufügen der Kennung am Ende
}
?>

				
			
				
					<?php
function scrapeTitle($url) {
    $apiUrl = 'http://localhost:3000/scrape';
    $data = ['url' => $url];

    $options = [
        'http' => [
            'header'  => "Content-Type: application/json\r\n",
            'method'  => 'POST',
            'content' => json_encode($data),
            'timeout' => 30
        ],
    ];

    $context  = stream_context_create($options);
    $result = file_get_contents($apiUrl, false, $context);

    if ($result === FALSE) {
        return ['error' => 'Fehler bei der Anfrage an den Scrape-Service'];
    }

    return json_decode($result, true);
}

// Beispielnutzung
$url = 'https://example.com';
$response = scrapeTitle($url);

if (isset($response['title'])) {
    echo "Der Titel der Seite ist: " . htmlspecialchars($response['title']);
} else {
    echo "Fehler: " . htmlspecialchars($response['error']);
}
?>
				
			

Playwright-Docker Web-Scraping Service

Die Playwright-Docker-Anwendung ist ein effizienter Web-Scraping-Service, der über eine REST-API zugänglich ist. Entwickelt mit Express.js, nutzt sie Playwright für das Extrahieren von Webseitendaten in einer Docker-Umgebung. Ideal für Entwickler, die automatisierte Datenextraktion benötigen.

Kernfunktionen

1. Express.js REST-API auf Port 3000

Die REST-API ermöglicht das Senden von URLs und das Empfangen extrahierter Daten.


const express = require('express');
const app = express();
const { scrapeTitle } = require('./scraper');

app.use(express.json());

app.post('/scrape', async (req, res) => {
    const { url } = req.body;
    const title = await scrapeTitle(url);
    res.json({ title });
});

app.listen(3000, () => console.log('API läuft auf Port 3000'));

2. Headless Browser mit Playwright

Playwright lädt Webseiten ohne GUI und extrahiert Daten.


const { chromium } = require('playwright');

async function scrapeTitle(url) {
    const browser = await chromium.launch();
    const page = await browser.newPage();
    await page.goto(url);
    const title = await page.title();
    await browser.close();
    return title;
}

module.exports = { scrapeTitle };

3. Automatisches Neuladen mit Nodemon

Nodemon überwacht Änderungen und startet den Server automatisch neu.

Dockerfile-Auszug:


RUN npm install -g nodemon
CMD ["nodemon", "index.js"]

4. Samba-Integration für lokale Entwicklung

Ermöglicht das Teilen und Synchronisieren von Dateien über das Netzwerk, ideal für Teamarbeit.

Erweiterungen mit Playwright

Screenshots erstellen

Erfasse visuelle Zustände der Webseite.


await page.screenshot({ path: 'screenshot.png' });

PDF generieren

Speichere die Webseite als PDF.


await page.pdf({ path: 'page.pdf' });

Element-Interaktionen

Automatisiere Eingaben und Klicks.


await page.fill('input[name="search"]', 'Suchbegriff');
await page.click('button[type="submit"]');

Daten extrahieren

Sammle spezifische Informationen von der Webseite.


const data = await page.evaluate(() => {
    return {
        title: document.title,
        description: document.querySelector('meta[name="description"]')?.content,
        links: Array.from(document.links).map(link => link.href)
    };
});

Integration in die API:


app.post('/extract', async (req, res) => {
    const { url } = req.body;
    const data = await extractData(url);
    res.json(data);
});

Weitere Playwright-Codebeispiele

5. Umgang mit Authentifizierung

Automatisiere den Login-Prozess auf einer Webseite.


const { chromium } = require('playwright');

async function loginAndScrape(url, username, password) {
    const browser = await chromium.launch();
    const page = await browser.newPage();
    await page.goto(url);
    
    // Eingabe der Anmeldedaten
    await page.fill('input[name="username"]', username);
    await page.fill('input[name="password"]', password);
    await page.click('button[type="submit"]');
    
    // Warten bis die Navigation abgeschlossen ist
    await page.waitForNavigation();
    
    // Extrahiere nach dem Login Daten
    const data = await page.evaluate(() => {
        return {
            title: document.title
            // Weitere Datenfelder hier
        };
    });
    
    await browser.close();
    return data;
}

module.exports = { loginAndScrape };

6. Pagination Handling

Durchsuche mehrere Seiten einer Website mit Pagination.


const { chromium } = require('playwright');

async function scrapeWithPagination(baseUrl, totalPages) {
    const browser = await chromium.launch();
    const page = await browser.newPage();
    let allData = [];
    
    for (let i = 1; i <= totalPages; i++) { const url = `${baseUrl}?page=${i}`; await page.goto(url); const data = await page.evaluate(() => {
            // Beispiel: Sammle alle Titel auf der Seite
            return Array.from(document.querySelectorAll('h2.title')).map(el => el.textContent);
        });
        
        allData = allData.concat(data);
    }
    
    await browser.close();
    return allData;
}

module.exports = { scrapeWithPagination };

7. Scraping von Tabellen

Extrahiere Daten aus HTML-Tabellen.


const { chromium } = require('playwright');

async function scrapeTable(url) {
    const browser = await chromium.launch();
    const page = await browser.newPage();
    await page.goto(url);
    
    const tableData = await page.evaluate(() => {
        const rows = Array.from(document.querySelectorAll('table tr'));
        return rows.map(row => {
            const cells = Array.from(row.querySelectorAll('td, th'));
            return cells.map(cell => cell.textContent.trim());
        });
    });
    
    await browser.close();
    return tableData;
}

module.exports = { scrapeTable };

8. Umgang mit dynamischen Inhalten

Warte auf dynamisch geladene Inhalte, bevor du Daten extrahierst.


const { chromium } = require('playwright');

async function scrapeDynamicContent(url) {
    const browser = await chromium.launch();
    const page = await browser.newPage();
    await page.goto(url);
    
    // Warte auf ein spezifisches Element, das dynamisch geladen wird
    await page.waitForSelector('#dynamic-element');
    
    const data = await page.evaluate(() => {
        return document.querySelector('#dynamic-element').textContent;
    });
    
    await browser.close();
    return data;
}

module.exports = { scrapeDynamicContent };

9. Fehlerbehandlung und Wiederholungen

Implementiere Fehlerbehandlung und Wiederholungslogik für zuverlässiges Scraping.


const { chromium } = require('playwright');

async function robustScrape(url, retries = 3) {
    for (let attempt = 1; attempt <= retries; attempt++) { try { const browser = await chromium.launch(); const page = await browser.newPage(); await page.goto(url, { timeout: 60000 }); // 60 Sekunden Timeout const data = await page.evaluate(() => document.title);
            
            await browser.close();
            return data;
        } catch (error) {
            console.error(`Versuch ${attempt} fehlgeschlagen:`, error);
            if (attempt === retries) throw error;
            // Optional: Warte vor dem nächsten Versuch
            await new Promise(res => setTimeout(res, 2000));
        }
    }
}

module.exports = { robustScrape };

10. Vollseitige Screenshots erstellen

Nimm einen Screenshot der gesamten Seite auf.


await page.screenshot({ path: 'fullpage.png', fullPage: true });

11. Verwendung verschiedener Browser

Nutze verschiedene Browser-Engines für das Scraping.


const { chromium, firefox, webkit } = require('playwright');

async function scrapeWithDifferentBrowsers(url) {
    const browsers = [chromium, firefox, webkit];
    const results = {};

    for (const browserType of browsers) {
        const browser = await browserType.launch();
        const page = await browser.newPage();
        await page.goto(url);
        results[browserType.name()] = await page.title();
        await browser.close();
    }

    return results;
}

module.exports = { scrapeWithDifferentBrowsers };

12. Täuschung von Browser-Fingerprints

Vermeide die Erkennung durch Websites, die Browser-Fingerprinting verwenden, indem du Fingerprint-Daten manipulierst.


const { chromium } = require('playwright');

async function scrapeWithFingerprintSpoofing(url) {
    const browser = await chromium.launch({
        headless: true,
        args: [
            '--disable-blink-features=AutomationControlled' // Verstecke automatisierte Steuerung
        ]
    });
    const context = await browser.newContext({
        viewport: { width: 1280, height: 720 },
        userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)' +
                   ' Chrome/89.0.4389.82 Safari/537.36', // Setze einen gängigen User-Agent
        locale: 'en-US',
        geolocation: { longitude: 12.4924, latitude: 41.8902 },
        permissions: ['geolocation']
    });

    const page = await context.newPage();

    // Manipuliere Navigator-Objekt
    await page.addInitScript(() => {
        Object.defineProperty(navigator, 'webdriver', {
            get: () => false,
        });
        Object.defineProperty(navigator, 'plugins', {
            get: () => [1, 2, 3],
        });
        Object.defineProperty(navigator, 'languages', {
            get: () => ['en-US', 'en'],
        });
    });

    await page.goto(url);

    // Optional: Blockiere bestimmte Skripte oder Ressourcen, die Fingerprinting durchführen
    await page.route('**/fingerprint.js', route => route.abort());

    const title = await page.title();
    await browser.close();
    return title;
}

module.exports = { scrapeWithFingerprintSpoofing };

Dieses Beispiel zeigt, wie du die Erkennung durch Browser-Fingerprinting-Techniken umgehen kannst. Indem du das navigator.webdriver-Attribut auf false setzt und andere Navigator-Eigenschaften manipulierst, kannst du die Wahrscheinlichkeit verringern, dass deine Scraping-Aktivitäten erkannt werden.

Integration in die API:


app.post('/scrape-fingerprint', async (req, res) => {
    const { url } = req.body;
    try {
        const title = await scrapeWithFingerprintSpoofing(url);
        res.json({ title });
    } catch (error) {
        res.status(500).json({ error: 'Fehler beim Scraping' });
    }
});