This commit is contained in:
Tower committed 2026-10-22 20:03:58 +02:00
1 parent 29b9f02d64
commit ca026c2369
14 files changed
+1046

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
MARIADB_ROOT_PASSWORD=V1IS8xj59fjQ08xxgB
MARIADB_WORKER_PASSWORD=G7aMe70Mmv7D0HVfnr
SEAFILE_DB_ROOT_PASSWORD=Q8q9YBGroBN7L27KaT
SEAFILE_DB_PASSWORD=cS6ow4WKAj5r85tQno
SEAFILE_REDIS_PASSWORD=pz1lh57X97ha15pAfi
SEAFILE_JWT_KEY=1i8ddVH5Tu7OxV4Qak
SEAFILE_ADMIN_EMAIL=bruno@mrhossa.de
SEAFILE_ADMIN_PASSWORD=d51dU0oa8srTmyJ983
+1
View File
@@ -0,0 +1 @@
./SEAFILE/data
+73
View File
@@ -0,0 +1,73 @@
global
log stdout format raw local0 info
maxconn 8192
defaults
log global
mode http
option httplog
option dontlognull
option forwardfor
option redispatch
retries 3
timeout connect 5000
timeout client 50000
timeout server 50000
frontend https
bind *:80
bind *:443 ssl crt /usr/local/etc/haproxy/mshossa.pem alpn h2,http/1.1 ssl-min-ver TLSv1.2
# Force HTTPS
http-request redirect scheme https code 301 unless { ssl_fc }
http-request set-header X-Forwarded-Proto https if { ssl_fc }
# Security headers
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains"
http-response set-header X-Content-Type-Options "nosniff"
http-response set-header X-Frame-Options "SAMEORIGIN"
acl valid_method method PUT DELETE GET POST HEAD PATCH
http-request deny if !valid_method
# ACLs zuerst deklarieren
acl is_trusted src 178.19.224.210 152.53.159.169
acl is_public hdr(host) -i mshossa.de
acl is_stats hdr(host) -i stats.mshossa.de
acl is_seafile hdr(host) -i ftp.mshossa.de
acl is_seafile_login path_beg /accounts/login /api2/auth-token
stick-table type ip size 100k expire 60s store http_req_rate(60s)
# Allgemeines Limit (nicht für Seafile, dort erzeugen Sync-Clients viele Requests)
http-request track-sc0 src
acl rate_too_high sc0_http_req_rate gt 240
http-request deny deny_status 429 if rate_too_high !is_trusted !is_seafile
# Strengeres Limit nur für Seafile-Logins
http-request track-sc1 src table st_seafile_login if is_seafile is_seafile_login
acl login_rate_too_high sc1_http_req_rate gt 10
http-request deny deny_status 429 if is_seafile is_seafile_login login_rate_too_high !is_trusted
use_backend stats if is_stats
use_backend seafile if is_seafile
default_backend web_public
backend st_seafile_login
stick-table type ip size 100k expire 5m store http_req_rate(1m)
backend seafile
timeout server 300s
server seafile 172.30.0.60:80 check
backend stats
stats enable
stats uri /
stats realm Haproxy\ Statistics
stats auth worker:bBHofurUJWKzPE8Z
acl is_trusted src 178.19.224.210
stats admin if TRUE is_trusted
# backend web_public: hier muss dein bestehendes Backend stehen
+89
View File
@@ -0,0 +1,89 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg7UV7nbTUPTKMza98
RXKECiFKmc6zBq5pLyz6dL4QdkihRANCAATqEjXI+cLpJYw8k9SGC+9EA5HcEgnV
HTZiQpS4J0zpTn+c8edGd/4yfYwwiRQJP8EbU2O7bLkibU5k9lyiTjYU
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIEPzCCA8WgAwIBAgISBiOdY6XpvmNrnR0bjiH5fSAYMAoGCCqGSM49BAMDMDMx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQDEwNZ
RTIwHhcNMjYxMDA1MTc0NzEwWhcNMjcwMTAzMTc0NzA5WjAVMRMwEQYDVQQDEwpt
cmhvc3NhLmRlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6hI1yPnC6SWMPJPU
hgvvRAOR3BIJ1R02YkKUuCdM6U5/nPHnRnf+Mn2MMIkUCT/BG1Nju2y5Im1OZPZc
ok42FKOCAtUwggLRMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcD
ATAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQhY3x6BwIrFq2LdMg7vh2yh612YDAf
BgNVHSMEGDAWgBS5WfKOzyLwhtM3SP92FBi6gthVhzAzBggrBgEFBQcBAQQnMCUw
IwYIKwYBBQUHMAKGF2h0dHA6Ly95ZTIuaS5sZW5jci5vcmcvMIHQBgNVHREEgcgw
gcWCEmJhY2tlbmQubXJob3NzYS5kZYIUZmxhdG5vdGVzLm1yaG9zc2EuZGWCEGdp
dGVhLm1yaG9zc2EuZGWCEWhvbWFyci5tcmhvc3NhLmRlghJqZW5raW5zLm1yaG9z
c2EuZGWCCm1yaG9zc2EuZGWCFHBvcnRhaW5lci5tcmhvc3NhLmRlghBzdGF0cy5t
cmhvc3NhLmRlghZ2YXVsdHdhcmRlbi5tcmhvc3NhLmRlghR3aXJlZ3VhcmQubXJo
b3NzYS5kZTATBgNVHSAEDDAKMAgGBmeBDAECATAuBgNVHR8EJzAlMCOgIaAfhh1o
dHRwOi8veWUyLmMubGVuY3Iub3JnLzI1LmNybDCCAQ0GCisGAQQB1nkCBAIEgf4E
gfsA+QB2AGBMmq96f3dfAdQG/JINyJnrCxx9+MlSG/r6F3c7l4vJAAABoQ1izPoA
AAQDAEcwRQIhAN20nTZhRCkj2Wg/MdK6Ue0L3+W92YDcEfAFXYa4qrCVAiANeJZk
V1OS94rSKOqQHHBrEzzUrCKnHf7abPxc0rEtTwB/AKKBABhzThduHUfglUDzgbpU
ZpfNY6hDUHFuuAlO2vENAAABoQ1izpcACAAABQAdEpG9BAMASDBGAiEAs5FjS+jS
ZQdCNZ0jqggaKk+qbxjYmKNBnTkA5X65T/QCIQD/ge0oKSuhk3MuVOKcRQrEODBn
O9Hp0pnAZXHoFpkc2jAKBggqhkjOPQQDAwNoADBlAjEA8mYRaKbcjiFHwxRJtSrs
PYVdHbPejW0/az5E6MgtWcOwaVu2AjB7LhADjXP5wlBAAjBVoHoU2AvSTPgCgpEk
BA9DkuzIKQPdBcvAMz2Duo4QaeEvXuT2rLr20itVV2QUMus=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICjDCCAhGgAwIBAgIQTfOxXdbAeExQfNN7WObxFTAKBggqhkjOPQQDAzAuMQsw
CQYDVQQGEwJVUzENMAsGA1UEChMESVNSRzEQMA4GA1UEAxMHUm9vdCBZRTAeFw0y
NTA5MDMwMDAwMDBaFw0yODA5MDIyMzU5NTlaMDMxCzAJBgNVBAYTAlVTMRYwFAYD
VQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQDEwNZRTIwdjAQBgcqhkjOPQIBBgUr
gQQAIgNiAARxmrQzkdbEEL3MqXt3dJQttYc47axkdDTHud5TPqM2z5uSD5cmk0Wr
HlWXvnlvqBLqiB34kluxIbmMyAiq3/YD6e80/vV259K8XQIdjFXloYOa0mIU71f7
HQ09PvYDlw+jge4wgeswDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUF
BwMBMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFLlZ8o7PIvCG0zdI/3YU
GLqC2FWHMB8GA1UdIwQYMBaAFKPIJlqOoUzQNWP8myPIOq5W809WMDIGCCsGAQUF
BwEBBCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3llLmkubGVuY3Iub3JnLzATBgNV
HSAEDDAKMAgGBmeBDAECATAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veWUuYy5s
ZW5jci5vcmcvMAoGCCqGSM49BAMDA2kAMGYCMQDIcnw5dcZLN9ffynXnnkLD/itS
JEycJPb3sRkzeqBowup7vOsAwaqoCnNn/jh9wycCMQCJM6CPlaOC4pQYYbJtVPYb
DKrIb2EKk5NpOpE6/XttQYZV/3gilB9l+Cc/DOVwmyg=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICpjCCAiugAwIBAgIRAIchZfw0tuX7qK3Vs3BftTowCgYIKoZIzj0EAwMwTzEL
MAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNo
IEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDIwHhcNMjYwNTEzMDAwMDAwWhcN
MzIwOTAyMjM1OTU5WjAuMQswCQYDVQQGEwJVUzENMAsGA1UEChMESVNSRzEQMA4G
A1UEAxMHUm9vdCBZRTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDwS/6vhrcVqcbBo
+wgdI3fwn9x7DNJJOY/lTOti0vkwuRN87RhEhTH17E7XyFjWsPYhIPt/wzOqxTd2
b+4ZJNy9ID04YywF9U5zasDVyGSNErVNtz8uSGh5izW87j77GaOB6zCB6DAOBgNV
HQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB
/zAdBgNVHQ4EFgQUo8gmWo6hTNA1Y/ybI8g6rlbzT1YwHwYDVR0jBBgwFoAUfEKW
rt5LSDv6kviejM9ti6lyN5UwMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzAChhZo
dHRwOi8veDIuaS5sZW5jci5vcmcvMBMGA1UdIAQMMAowCAYGZ4EMAQIBMCcGA1Ud
HwQgMB4wHKAaoBiGFmh0dHA6Ly94Mi5jLmxlbmNyLm9yZy8wCgYIKoZIzj0EAwMD
aQAwZgIxAMU19WCtmxVND8UHBZRoma49Z7jPs64Dma0eTu1OChVbB/2J7GV3nvYK
Ax54uk1G9QIxAO0miLVJu8PLNiXXXkiE/gsK3CTRTF/aeo4bMX42Zw40csRU6AC2
6hSW1/IWaas6dg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEcDCCAligAwIBAgIQbI8dxyfHEX97r4U6yYD5zTANBgkqhkiG9w0BAQsFADBP
MQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFy
Y2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMTAeFw0yNjA1MTMwMDAwMDBa
Fw0zMjA5MDIyMzU5NTlaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5l
dCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgy
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0H
ttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7
AlF9ItgKbppbd9/w+kHsOdx1ymgHDB/qo4H1MIHyMA4GA1UdDwEB/wQEAwIBBjAd
BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUwAwEB/zAd
BgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwHwYDVR0jBBgwFoAUebRZ5nu2
5eQBc4AIiMgaWPbpm24wMgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzAChhZodHRw
Oi8veDEuaS5sZW5jci5vcmcvMBMGA1UdIAQMMAowCAYGZ4EMAQIBMCcGA1UdHwQg
MB4wHKAaoBiGFmh0dHA6Ly94MS5jLmxlbmNyLm9yZy8wDQYJKoZIhvcNAQELBQAD
ggIBAD2/e9frmMxNpCV03qUHegg+MV2wz9644YoXdqtH8RyWYcBO7xfjjGEXdU1e
/o0OkEFiynUCOSIk/vLLo7ttz6CPAeNlWfC0XNkoGeWgK6jjXvozBaGuGH5n0Ufo
shMeWTuURqNN5G00sSXDTBrpp2+mgvdZQjb8K11TYMA25QA+YHNfbIEL0BniAhKS
2gsnJjSzrdZLI+EZ7SEyqdR2rkjd1KutLDU+n3TFyxjniZVGur4YlhMP3mY/dV95
IruAkkjOZier6hGBdEgZXXvaCz9u9iVEadsIE75pAGL8oHV5vxdARDiotRpul1IN
/UZwzAbrfUFcw1HkAcYD/mlZfnQ2ieCF2MS7j3Vhv7JPDKp45fmykmzYNSrumRW0
upFFKDBOoF7hsOb7oLyHS+Uft6jOUfOrogj8YUx38hKb2K20r42OgsSdDdxdeYWc
MS3Sb6mwJeSZEYxJ2gaXnDSPaKhhrNkYwljyVQyr4Nq+MEJytXNTnHqaAcrNwZlV
pcJL1KBnMrMjP7eanvUwL3FYj3cF17jtboLt7gLoi4+2rWZFvn+w54jmd/FIuhhZ
cEaU/wvU6BUNMtcVquVGHp7itQeDth5j+XL3j4WJ2SABwzUl6OeYdgpIt/ITZa+p
TT0mQ/r5XyA4MEAiabn7XJjvCERlF2dcn2wqJw+CreTkkQ2R
-----END CERTIFICATE-----
+15
View File
@@ -0,0 +1,15 @@
defaults
auth on
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile ~/.msmtp.log
account mrhossa
host mail.mrhossa.de
port 465
tls_starttls off
from noreply@mrhossa.de
user noreply@mrhossa.de
password qeYysJ5b0PPqtAA
account default : mrhossa
+109
View File
@@ -0,0 +1,109 @@
#!/bin/bash
# -------------------------------
# Server Status Report Script
# -------------------------------
# Datum/Zeit
DATE=$(date +"%Y-%m-%d %H:%M:%S")
# -------------------------------
# Systemauslastung
# -------------------------------
CPU_LOAD=$(uptime | awk -F 'load average:' '{ print $2 }' | sed 's/^ *//')
MEMORY_USAGE=$(free -h | awk '/Mem:/ {print $3 " used / " $2 " total"}')
DISK_USAGE=$(df -h /)
# -------------------------------
# Docker Status
# -------------------------------
docker_list() {
local name image status ports
local short_image short_status short_ports host_ports
{
printf '%s\x1f%s\x1f%s\x1f%s\n' "NAME" "IMAGE" "STATUS" "PORTS"
docker ps --format '{{.Names}}'$'\x1f''{{.Image}}'$'\x1f''{{.Status}}'$'\x1f''{{.Ports}}' |
while IFS=$'\x1f' read -r name image status ports; do
short_image=$(sed -E 's#^.*/##' <<<"$image")
short_status=$(sed -E \
-e 's/^Up //' \
-e 's/^Exited \(([0-9]+)\)/exit \1,/' \
-e 's/\(healthy\)/ok/' \
-e 's/\(unhealthy\)/!!/' \
-e 's/\(health: starting\)/../' \
-e 's/ ago$//' <<<"$status")
host_ports=$( (grep -oE ':[0-9]+->' <<<"$ports" || true) | (grep -oE '[0-9]+' || true) | sort -un | paste -sd, - )
if [[ -n "$host_ports" ]]; then
short_ports="$host_ports"
elif [[ -n "$ports" ]]; then
short_ports="(intern)"
else
short_ports="-"
fi
printf '%s\x1f%s\x1f%s\x1f%s\n' "$name" "$short_image" "$short_status" "$short_ports"
done
} | awk -F'\x1f' '
{
for (i = 1; i <= NF; i++) {
cell[NR, i] = $i
if (length($i) > width[i]) width[i] = length($i)
}
cols = NF
rows = NR
}
END {
for (r = 1; r <= rows; r++) {
line = ""
for (i = 1; i <= cols; i++) {
line = line cell[r, i]
if (i < cols) {
pad = width[i] - length(cell[r, i]) + 2
for (p = 0; p < pad; p++) line = line " "
}
}
print line
}
}
'
}
if command -v speedtest-cli &> /dev/null
then
SPEEDTEST=$(speedtest-cli --simple 2>/dev/null)
else
SPEEDTEST="speedtest-cli nicht installiert (apt install speedtest-cli)"
fi
# -------------------------------
# Ausgabe auf Konsole
# -------------------------------
DOCKER_STATS=$(docker_list)
echo "Server Status Report"
echo =====================================================
echo
echo "Hostname : $(hostname)"
echo "Datum : $DATE"
echo "CPU Load : $CPU_LOAD"
echo "RAM : $MEMORY_USAGE"
echo
echo "Internet-Geschwindigkeit:"
echo "$SPEEDTEST"
echo
echo "System Festplatten:"
echo "$DISK_USAGE"
echo
echo "Docker Stats:"
echo "$DOCKER_STATS"
echo
echo "====================================================="
+69
View File
@@ -0,0 +1,69 @@
#!/bin/bash
DB_HOST="127.0.0.1"
DB_USER="worker"
DB_PASS="G7aMe70Mmv7D0HVfnr"
DB_NAME="LIVE"
CERT_NAME="mshossa"
EMAIL="bruno@mrhossa.de"
PEM_FILE="/data/HAPROXY/mshossa.pem"
BACKUP_DIR="/data/HAPROXY/backups"
mkdir -p "$BACKUP_DIR"
echo "Lade Domains aus MySQL..."
DOMAINS=$(mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" -D "$DB_NAME" -N -B -e "SELECT SUBDOMAIN FROM Domains;")
DOMAIN_ARGS=()
for domain in $DOMAINS
do
DOMAIN_ARGS+=("-d" "$domain")
done
echo "Domains:"
printf '%s\n' "${DOMAIN_ARGS[@]}"
# Backup vorhandenes Zertifikat
if [ -f "$PEM_FILE" ]; then
TIMESTAMP=$(date +"%Y-%m-%d_%H-%M-%S")
cp "$PEM_FILE" "$BACKUP_DIR/mrhossa_$TIMESTAMP.pem"
echo "Backup erstellt: $BACKUP_DIR/mrhossa_$TIMESTAMP.pem"
fi
echo "Bereinige alte Backups..."
ls -t $BACKUP_DIR/*.pem 2>/dev/null | tail -n +21 | xargs -r rm -f
echo "Stoppe HAProxy Container..."
sudo docker stop haproxy
echo "Hole/erneuere Zertifikat..."
sudo certbot certonly --standalone \
--cert-name "$CERT_NAME" \
"${DOMAIN_ARGS[@]}" \
--non-interactive \
--agree-tos \
-m "$EMAIL" \
--keep-until-expiring
echo "Finde aktuelles Zertifikat..."
LATEST_CERT=$(ls -dt /etc/letsencrypt/live/mrhossa* | head -n1)
echo "Benutze Zertifikat aus:"
echo "$LATEST_CERT"
echo "Baue HAProxy PEM..."
cat "$LATEST_CERT/privkey.pem" \
"$LATEST_CERT/fullchain.pem" \
> "$PEM_FILE"
echo "Starte HAProxy Container..."
sudo docker start haproxy
echo "Fertig."
+291
View File
@@ -0,0 +1,291 @@
#!/usr/bin/env bash
# ./deploy.sh public
# ./deploy.sh backend
# ./deploy.sh server
# ./deploy.sh all
# ./deploy.sh list-backups public
# ./deploy.sh rollback public
# ./deploy.sh rollback public PUBLIC-20260216-143015-server01
# ./deploy.sh rollback server
# ./deploy.sh rollback server SERVER-20260216-143015-server01.commit
set -Eeuo pipefail
########################################
# Farben
########################################
RED="\e[31m"
GREEN="\e[32m"
YELLOW="\e[33m"
BLUE="\e[34m"
BOLD="\e[1m"
RESET="\e[0m"
########################################
# Logging
########################################
log_info() { echo -e "${BLUE}[INFO]${RESET} $1"; }
log_success() { echo -e "${GREEN}[OK]${RESET} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${RESET} $1"; }
log_error() { echo -e "${RED}[ERROR]${RESET} $1"; }
trap 'log_error "Fehler in Zeile $LINENO"' ERR
########################################
# Konfiguration
########################################
BASE_DIR="/data/WEBSERVER"
BACKUP_DIR="$BASE_DIR/BACKUP"
SERVER_REPO="http://gitea.mrhossa.de/bruno/Server-Mshossa.git"
# Zielverzeichnisse
SERVER_TARGET="/data" # liegt direkt in /data, NICHT in BASE_DIR!
SERVER_BRANCH="main" # ggf. anpassen (z. B. "live")
SERVER_CONTAINER="dnsmasq"
TIMESTAMP=$(date +"%Y%m%d-%H%M%S")
HOSTNAME=$(hostname)
########################################
# Root-Check
########################################
if [[ "$EUID" -ne 0 ]]; then
log_error "Script muss als root laufen!"
exit 1
fi
########################################
# Lockfile
########################################
exec 9>/var/lock/webdeploy.lock
flock -n 9 || { log_error "Deploy läuft bereits!"; exit 1; }
########################################
# Helper
########################################
ensure_directory() {
[[ -d "$1" ]] || mkdir -p "$1"
}
restart_container() {
local container="$1"
if [[ -z "$container" ]]; then
log_info "Kein Container konfiguriert – überspringe Neustart."
return
fi
if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then
docker restart "$container" >/dev/null
log_success "Container $container neu gestartet."
else
log_warn "Container $container existiert nicht."
fi
}
deploy() {
local name="$1"
local repo="$2"
local container="$3"
local target="$4"
log_info "Deploying $name ..."
backup_current "$name" "$target"
ensure_directory "$(dirname "$target")"
git clone "$repo" "$target"
if [[ "$name" == "BACKEND" ]]; then
git -C "$target" checkout live
fi
restart_container "$container"
log_success "$name erfolgreich deployed."
}
list_backups() {
local name="$1"
log_info "Verfügbare Backups für $name:"
ls -ldt "$BACKUP_DIR/${name}-"* 2>/dev/null || log_warn "Keine Backups gefunden."
}
rollback() {
local name="$1"
local container="$2"
local target="$3"
local specific_backup="${4:-}"
ensure_directory "$BACKUP_DIR"
local current="$target"
local backup_to_restore
if [[ -n "$specific_backup" ]]; then
backup_to_restore="$BACKUP_DIR/$specific_backup"
else
backup_to_restore=$(ls -dt "$BACKUP_DIR/${name}-"* 2>/dev/null | head -n1)
fi
if [[ -z "${backup_to_restore:-}" || ! -d "$backup_to_restore" ]]; then
log_error "Kein gültiges Backup gefunden!"
exit 1
fi
log_warn "Rollback von $name → $(basename "$backup_to_restore")"
if [[ -n "$container" ]] && docker ps --format '{{.Names}}' | grep -q "^${container}$"; then
log_info "Stoppe Container $container ..."
docker stop "$container" >/dev/null
fi
if [[ -d "$current" ]]; then
log_info "Entferne aktuelles Verzeichnis ..."
rm -rf "$current"
fi
mv "$backup_to_restore" "$current"
if [[ -n "$container" ]]; then
docker start "$container" >/dev/null
fi
log_success "Rollback abgeschlossen."
}
########################################
# SERVER (Git-basiertes Deploy, liegt direkt in /data)
########################################
deploy_server() {
local target="$SERVER_TARGET"
if [[ -d "$target/.git" ]]; then
# Aktuellen Commit als "Backup" protokollieren
local current_commit
current_commit=$(git -C "$target" rev-parse HEAD)
ensure_directory "$BACKUP_DIR"
echo "$current_commit" > "$BACKUP_DIR/SERVER-${TIMESTAMP}-${HOSTNAME}.commit"
log_info "Aktueller Server-Commit gesichert: $current_commit"
log_info "Aktualisiere Server-Repo ..."
git -C "$target" fetch origin
git -C "$target" reset --hard "origin/$SERVER_BRANCH"
else
# Erstinstallation: /data ist nicht leer (WEBSERVER liegt dort), daher über Temp-Ordner
log_info "Server-Repo noch nicht vorhanden, initialisiere ..."
local tmp_clone
tmp_clone=$(mktemp -d)
git clone "$SERVER_REPO" "$tmp_clone"
shopt -s dotglob
for item in "$tmp_clone"/*; do
mv "$item" "$target/"
done
shopt -u dotglob
rmdir "$tmp_clone"
fi
restart_container "$SERVER_CONTAINER"
log_success "SERVER erfolgreich deployed."
}
rollback_server() {
local specific_backup="${1:-}"
local target="$SERVER_TARGET"
local commit_file
ensure_directory "$BACKUP_DIR"
if [[ -n "$specific_backup" ]]; then
commit_file="$BACKUP_DIR/$specific_backup"
else
commit_file=$(ls -t "$BACKUP_DIR/SERVER-"*.commit 2>/dev/null | head -n1)
fi
if [[ -z "${commit_file:-}" || ! -f "$commit_file" ]]; then
log_error "Kein gültiges Server-Backup (Commit) gefunden!"
exit 1
fi
local commit_hash
commit_hash=$(cat "$commit_file")
log_warn "Rollback von SERVER → Commit $commit_hash"
git -C "$target" fetch origin
git -C "$target" reset --hard "$commit_hash"
restart_container "$SERVER_CONTAINER"
log_success "Server-Rollback abgeschlossen."
}
list_backups_server() {
log_info "Verfügbare Server-Backups (Commits):"
ls -lt "$BACKUP_DIR/SERVER-"*.commit 2>/dev/null || log_warn "Keine Backups gefunden."
}
########################################
# Fetch
########################################
fetch_changes() {
local name="$1"
local target="$2"
if [[ -d "$target/.git" ]]; then
git -C "$target" fetch origin
git -C "$target" diff HEAD..origin/main --name-only
else
log_warn "$name ist nicht installiert."
fi
}
########################################
# CLI
########################################
ACTION="${1:-}"
TARGET_ARG="${2:-}"
OPTIONAL="${3:-}"
case "$ACTION" in
all)
deploy_server
;;
server)
deploy_server
;;
rollback)
case "$TARGET_ARG" in
server)
rollback_server "$OPTIONAL"
;;
*)
echo "Usage: $0 rollback {public|backend|server} [backup-name]"
exit 1
;;
esac
;;
list-backups)
case "$TARGET_ARG" in
server)
list_backups_server
;;
*)
echo "Usage: $0 list-backups {public|backend|server}"
exit 1
;;
esac
;;
fetch)
fetch_changes "SERVER" "$SERVER_TARGET"
;;
*)
echo "Usage:"
echo " $0 {all|server}"
echo " $0 rollback {server} [backup-name]"
echo " $0 list-backups {server}"
echo " $0 fetch"
exit 1
;;
esac
+233
View File
@@ -0,0 +1,233 @@
#!/usr/bin/env bash
set -Eeuo pipefail
RED="\e[31m"
GREEN="\e[32m"
YELLOW="\e[33m"
BLUE="\e[34m"
RESET="\e[0m"
log_info() { echo -e "${BLUE}[INFO]${RESET} $1"; }
log_success() { echo -e "${GREEN}[OK]${RESET} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${RESET} $1"; }
log_error() { echo -e "${RED}[ERROR]${RESET} $1"; }
trap 'log_error "Fehler in Zeile $LINENO"' ERR
if [[ "$EUID" -ne 0 ]]; then
log_error "Script muss als root laufen!"
exit 1
fi
exec 9>/var/lock/docker-control.lock
flock -n 9 || { log_error "Script läuft bereits!"; exit 1; }
docker_up() {
local CONTAINER="$1"
if [[ "$CONTAINER" == "all" ]]; then
log_info "Starte alle gestoppten Container"
docker start $(docker ps -aq)
else
log_info "Starte Container: $CONTAINER"
docker start "$CONTAINER"
fi
log_success "Start abgeschlossen"
}
docker_stop() {
local CONTAINER="$1"
if [[ "$CONTAINER" == "all" ]]; then
log_info "Stoppe alle Container"
docker stop $(docker ps -q)
else
log_info "Stoppe Container: $CONTAINER"
docker stop "$CONTAINER"
fi
log_success "Stop abgeschlossen"
}
docker_restart() {
local CONTAINER="$1"
if [[ "$CONTAINER" == "all" ]]; then
log_info "Restart aller Container"
docker restart $(docker ps -q)
else
log_info "Restart Container: $CONTAINER"
docker restart "$CONTAINER"
fi
log_success "Restart abgeschlossen"
}
docker_recreate() {
local CONTAINER="$1"
if [[ "$CONTAINER" == "all" ]]; then
log_info "Rebuild aller Container"
docker compose -f /data/docker-compose.yml up -d --force-recreate
else
log_info "Rebuild Container: $CONTAINER"
docker compose -f /data/docker-compose.yml up -d --force-recreate "$CONTAINER"
fi
log_success "Recreate abgeschlossen"
}
docker_build() {
local CONTAINER="$1"
if [[ "$CONTAINER" == "all" ]]; then
log_info "Baue alle Container"
docker compose -f /data/docker-compose.yml up -d --build
else
log_info "Baue Container: $CONTAINER"
docker compose -f /data/docker-compose.yml up -d --build "$CONTAINER"
fi
log_success "Build abgeschlossen"
}
docker_remove() {
local CONTAINER="$1"
if [[ "$CONTAINER" == "all" ]]; then
log_info "Loesche aller Container"
docker stop $(docker ps -q)
docker rm $(docker ps -q)
else
log_info "Loesche Container: $CONTAINER"
docker stop "$CONTAINER"
docker rm "$CONTAINER"
fi
log_success "Loeschen abgeschlossen"
}
docker_list() {
local name image status ports
local short_image short_status short_ports host_ports
{
printf '%s\x1f%s\x1f%s\x1f%s\n' "NAME" "IMAGE" "STATUS" "PORTS"
docker ps --format '{{.Names}}'$'\x1f''{{.Image}}'$'\x1f''{{.Status}}'$'\x1f''{{.Ports}}' |
while IFS=$'\x1f' read -r name image status ports; do
short_image=$(sed -E 's#^.*/##' <<<"$image")
short_status=$(sed -E \
-e 's/^Up //' \
-e 's/^Exited \(([0-9]+)\)/exit \1,/' \
-e 's/\(healthy\)/ok/' \
-e 's/\(unhealthy\)/!!/' \
-e 's/\(health: starting\)/../' \
-e 's/ ago$//' <<<"$status")
host_ports=$( (grep -oE ':[0-9]+->' <<<"$ports" || true) | (grep -oE '[0-9]+' || true) | sort -un | paste -sd, - )
if [[ -n "$host_ports" ]]; then
short_ports="$host_ports"
elif [[ -n "$ports" ]]; then
short_ports="(intern)"
else
short_ports="-"
fi
printf '%s\x1f%s\x1f%s\x1f%s\n' "$name" "$short_image" "$short_status" "$short_ports"
done
} | awk -F'\x1f' '
{
for (i = 1; i <= NF; i++) {
cell[NR, i] = $i
if (length($i) > width[i]) width[i] = length($i)
}
cols = NF
rows = NR
}
END {
for (r = 1; r <= rows; r++) {
line = ""
for (i = 1; i <= cols; i++) {
line = line cell[r, i]
if (i < cols) {
pad = width[i] - length(cell[r, i]) + 2
for (p = 0; p < pad; p++) line = line " "
}
}
print line
}
}
'
}
docker_stats() {
docker stats --no-stream
}
########################################
# CLI
########################################
ACTION="${1:-}"
TARGET="${2:-}"
case "$ACTION" in
up)
docker_up "$TARGET"
;;
stop)
docker_stop "$TARGET"
;;
restart)
docker_restart "$TARGET"
;;
remove)
docker_remove "$TARGET"
;;
build)
docker_build "$TARGET"
;;
recreate)
docker_recreate "$TARGET"
;;
list)
docker_list
;;
stats)
docker_stats
;;
*)
echo "Usage:"
echo " $0 up <container|all>"
echo " $0 stop <container|all>"
echo " $0 build <container|all>"
echo " $0 remove <container|all>"
echo " $0 restart <container|all>"
echo " $0 recreate <container|all>"
echo " $0 list"
echo " $0 stats"
exit 1
;;
esac
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
echo "Git Config"
USER="bruno"
MAIL="bruno.eckhardt07@gmail.com"
git config --global credential.helper store
git config --global user.name $USER
git config --global user.email $MAIL
+15
View File
@@ -0,0 +1,15 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"files.exclude": {
// "**/.git": true,
// "**/WEBSERVER": true,
},
"window.title": "Server-MSHOSSA",
"workbench.editor.wrapTabs": true,
}
}
+3
View File
@@ -0,0 +1,3 @@
MAILTO="bruno@mrhossa.de"
TERM=xterm
SHELL=/bin/bash
+102
View File
@@ -0,0 +1,102 @@
services:
haproxy:
image: haproxy:latest
container_name: haproxy
volumes:
- ./HAPROXY:/usr/local/etc/haproxy
ports:
- "80:80"
- "443:443"
networks:
docker-net:
ipv4_address: 172.30.0.2
restart: always
mariadb:
image: mariadb:latest
container_name: mariadb
volumes:
- ./MARIADB:/var/lib/mysql
environment:
MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
MARIADB_DATABASE: LIVE
MARIADB_USER: worker
MARIADB_PASSWORD: ${MARIADB_WORKER_PASSWORD}
ports:
- "127.0.0.1:3306:3306"
networks:
docker-net:
ipv4_address: 172.30.0.10
restart: always
seafile-db:
image: mariadb:11
container_name: seafile-db
volumes:
- ./SEAFILE/db:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: ${SEAFILE_DB_ROOT_PASSWORD}
MYSQL_LOG_CONSOLE: "true"
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 20s
timeout: 5s
retries: 10
networks:
seafile-internal:
ipv4_address: 172.31.0.10
restart: always
seafile-redis:
image: redis:7-alpine
container_name: seafile-redis
command: redis-server --requirepass ${SEAFILE_REDIS_PASSWORD}
networks:
seafile-internal:
ipv4_address: 172.31.0.11
restart: always
seafile:
image: seafileltd/seafile-mc:12.0-latest
container_name: seafile
depends_on:
seafile-db:
condition: service_healthy
seafile-redis:
condition: service_started
volumes:
- ./SEAFILE/data:/shared
environment:
SEAFILE_MYSQL_DB_HOST: 172.31.0.10
SEAFILE_MYSQL_DB_USER: seafile
SEAFILE_MYSQL_DB_PASSWORD: ${SEAFILE_DB_PASSWORD}
INIT_SEAFILE_MYSQL_ROOT_PASSWORD: ${SEAFILE_DB_ROOT_PASSWORD}
CACHE_PROVIDER: redis
REDIS_HOST: 172.31.0.11
REDIS_PORT: 6379
REDIS_PASSWORD: ${SEAFILE_REDIS_PASSWORD}
JWT_PRIVATE_KEY: ${SEAFILE_JWT_KEY}
TIME_ZONE: Europe/Berlin
SEAFILE_SERVER_HOSTNAME: ftp.mrhossa.de
SEAFILE_SERVER_PROTOCOL: https
INIT_SEAFILE_ADMIN_EMAIL: ${SEAFILE_ADMIN_EMAIL}
INIT_SEAFILE_ADMIN_PASSWORD: ${SEAFILE_ADMIN_PASSWORD}
networks:
docker-net:
ipv4_address: 172.30.0.12
seafile-internal:
ipv4_address: 172.31.0.2
restart: always
networks:
docker-net:
driver: bridge
ipam:
config:
- subnet: 172.30.0.0/16
seafile-internal:
driver: bridge
internal: true
ipam:
config:
- subnet: 172.31.0.0/24
+28
View File
@@ -0,0 +1,28 @@
#!/bin/bash
set -Eeuo pipefail
RED="\e[31m"
GREEN="\e[32m"
YELLOW="\e[33m"
BLUE="\e[34m"
RESET="\e[0m"
log_info() { echo -e "${BLUE}[INFO]${RESET} $1"; }
log_success() { echo -e "${GREEN}[OK]${RESET} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${RESET} $1"; }
log_error() { echo -e "${RED}[ERROR]${RESET} $1"; }
trap 'log_error "Fehler in Zeile $LINENO"' ERR
if [[ "$EUID" -ne 0 ]]; then
log_error "Script muss als root laufen!"
exit 1
fi
apt update
apt upgrade
apt install msmtp msmtp-mta speedtest-cli