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.DS_Store/
sudo systemctl restart smbd.service nmbd.service
                
            

PHP-FPM tuning

                
                    mcedit /etc/php/8.COMEND_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.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
#     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/htmlpm.max_children = '$FPMS'/' /etc/php/8.1/fpm/pool.d/www.conf
sed -i 's/pm.start_servers =.COMEND_pm.start_servers = '$PStartS'/' /etc/php/8.1/fpm/pool.d/www.conf
sed -i 's/pm.min_spare_servers =.COMEND_pm.min_spare_servers = '$PMinSS'/' /etc/php/8.1/fpm/pool.d/www.conf
sed -i 's/pm.max_spare_servers =.COMEND_pm.max_spare_servers = '$PMaxSS'/' /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/;pm.max_requests =.COMEND_pm.max_requests = 1000/" /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/allow_url_fopen =.COMEND_allow_url_fopen = 1/" /etc/php/8.1/fpm/php.ini
# 6. PHP CLI Konfiguration optimieren
sed -i "s/output_buffering =.COMEND_output_buffering = 'Off'/" /etc/php/8.1/cli/php.ini
sed -i "s/max_execution_time =.COMEND_max_execution_time = 3600/" /etc/php/8.1/cli/php.ini
sed -i "s/max_input_time =.COMEND_max_input_time = 3600/" /etc/php/8.1/cli/php.ini
sed -i "s/post_max_size =.COMEND_post_max_size = 10240M/" /etc/php/8.1/cli/php.ini
sed -i "s/upload_max_filesize =.COMEND_upload_max_filesize = 10240M/" /etc/php/8.1/cli/php.ini
sed -i "s/;date.timezone.COMEND_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 =.COMEND_output_buffering = 'Off'/" /etc/php/8.1/fpm/php.ini
sed -i "s/max_execution_time =.COMEND_max_execution_time = 3600/" /etc/php/8.1/fpm/php.ini
sed -i "s/max_input_time =.COMEND_max_input_time = 3600/" /etc/php/8.1/fpm/php.ini
sed -i "s/post_max_size =.COMEND_post_max_size = 10240M/" /etc/php/8.1/fpm/php.ini
sed -i "s/upload_max_filesize =.COMEND_upload_max_filesize = 10240M/" /etc/php/8.1/fpm/php.ini
sed -i "s/;date.timezone.COMEND_date.timezone = Europe\/Berlin/" /etc/php/8.1/fpm/php.ini
sed -i "s/;session.cookie_secure.COMEND_session.cookie_secure = True/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.enable=.COMEND_opcache.enable=1/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.enable_cli=.COMEND_opcache.enable_cli=1/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.memory_consumption=.COMEND_opcache.memory_consumption=128/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.interned_strings_buffer=.COMEND_opcache.interned_strings_buffer=16/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.max_accelerated_files=.COMEND_opcache.max_accelerated_files=10000/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.revalidate_freq=.COMEND_opcache.revalidate_freq=1/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.save_comments=.COMEND_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:/
// 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
function debug($message) {
    global $debug;
    if ($debug) {
        echo "DEBUG: $message\n";
    }
}
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.");
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++;
    }
}
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";
    }
}
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.");
}
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);
    }
}
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_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;
        }
    }
}
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('*COMEND_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' });
    }
});