Моя шпаргалка :)

Мануалы и настройки => Настройки *nix (почта, web, система etc) => Тема начата: George от Май 21, 2026, 03:22

Название: 📘 HAProxy Dynamic Provisioning (Maps + Certbot + Multi-backend)
Отправлено: George от Май 21, 2026, 03:22
HAProxy Dynamic Provisioning System

Назначение:
Система автоматического управления HAProxy:
- создание backend'ов
- работа с maps (hosts / passth / acme)
- выпуск SSL сертификатов (Let's Encrypt)
- поддержка multi-node backend
- zero-downtime reload

---

📦 Архитектура

Client
  ↓
HAProxy (:80 / :443)
  ↓
Maps routing:
   ├── hosts.map (HTTP backend)
   ├── passth.map (TLS SNI backend)
   └── acme_backends.map (ACME HTTP backend)
  ↓
Backends (auto-generated)

---

⚙️ CLI инструмент

Основной скрипт:

/usr/local/bin/haproxy-map-update.sh

---

📌 Общий синтаксис

haproxy-map-update.sh set DOMAIN BACKEND MODE IP_LIST PORT [-y]
haproxy-map-update.sh del DOMAIN

---

🧪 Примеры использования

---

1. HTTP сайт (один сервер)

haproxy-map-update.sh set ftp.cloud-life.site ftp http 10.10.1.37 80 -y

Результат:
- backend ftp:80
- certbot через HAProxy (8888)
- hosts.map обновлён

---

2. HTTPS backend (multi-node passthrough)

haproxy-map-update.sh set games.cloud-life.site games passth "10.10.1.46,10.10.1.47,10.10.1.48" 443 -y

Результат:
- acme_games (HTTP/80)
- games backend (HTTPS/443)
- roundrobin между нодами
- SSL verify none

---

3. Удаление домена

haproxy-map-update.sh del games.cloud-life.site

---

🔐 SSL схема (Let's Encrypt)

HTTP challenge flow:

Internet → HAProxy :80
         → /.well-known/acme-challenge/
         → 127.0.0.1:8888 (certbot standalone)

---

⚙️ Backend types

HTTP backend:
- один сервер или несколько
- порт 80
- certbot active

PASSTH backend:
- TLS SNI routing
- порт 443
- multi-node support
- acme backend auto-created

---

📂 Maps

/etc/haproxy/maps/hosts.map
/etc/haproxy/maps/passth.map
/etc/haproxy/maps/acme_backends.map

---

🧠 Особенности системы


---

🚀 Multi-server пример

IP_LIST="10.10.1.46,10.10.1.47,10.10.1.48"
PORT=443

Результат:
server games1 10.10.1.46:443 ssl verify none check
server games2 10.10.1.47:443 ssl verify none check
server games3 10.10.1.48:443 ssl verify none check

---

💡 Режим -y

Автоматическое создание backend без вопросов:

-y

---

🏁 Итог

Система заменяет:
- ручное редактирование haproxy.cfg
- ручное управление backend'ами
- ручное создание SSL
- ручной reload

И превращает HAProxy в:
динамический ingress controller уровня mini-Kubernetes
Название: От: 📘 HAProxy Dynamic Provisioning (Maps + Certbot + Multi-backend)
Отправлено: George от Май 21, 2026, 03:24
📌 Core Scripts (HAProxy Dynamic System)

1. MAIN CLI:
/usr/local/bin/haproxy-map-update.sh

---

2. CERT ENGINE:
/usr/local/bin/haproxy-cert.sh

---

⚙️ 1. haproxy-cert.sh

#!/bin/bash
set -euo pipefail

DOMAIN=${1:-}

if [ -z "$DOMAIN" ]; then
    echo "[CERT] no domain!"
    exit 1
fi

echo "[CERT] Issuing certificate for $DOMAIN"

certbot certonly --standalone \
  --http-01-port=8888 \
  -d "$DOMAIN" \
  --non-interactive \
  --agree-tos \
  --email george@cloud-life.site \
  --expand

echo "[CERT] Building PEM"

cat /etc/letsencrypt/live/$DOMAIN/fullchain.pem \
    /etc/letsencrypt/live/$DOMAIN/privkey.pem \
    > /etc/ssl/haproxy/$DOMAIN.pem

echo "[CERT] Done"

---

⚙️ 2. haproxy-map-update.sh

#!/bin/bash

GREEN="\033[0;32m"
NC="\033[0m"
RED="\033[0;31m"

CFG="/etc/haproxy/haproxy.cfg"

HOSTS_MAP="/etc/haproxy/maps/hosts.map"
PASSTH_MAP="/etc/haproxy/maps/passth.map"
ACME_MAP="/etc/haproxy/maps/acme_backends.map"

usage() {
    echo -e "${GREEN}"
    echo "Usage:"
    echo "  $0 set DOMAIN BACKEND MODE IP_LIST PORT [-y]"
    echo "  $0 del DOMAIN"
    echo -e "${NC}"
    exit 1
}

if [ $# -lt 2 ]; then
    usage
fi

ACTION=$1
DOMAIN=$2
BACKEND=${3:-}
MODE=${4:-}
IP_LIST=${5:-}
PORT=${6:-}
AUTO_YES=${7:-}

set -euo pipefail

backend_exists() {
    grep -q "^backend $BACKEND" "$CFG"
}

build_servers() {
    IFS=',' read -ra IPS <<< "$IP_LIST"
    i=1
    SERVERS=""

    for ip in "${IPS[@]}"; do
        name="${BACKEND}${i}"

        if [ "$PORT" == "443" ]; then
            SERVERS+="    server ${name} ${ip}:${PORT} ssl verify none check\n"
        else
            SERVERS+="    server ${name} ${ip}:${PORT} check\n"
        fi

        i=$((i+1))
    done
}

create_backend() {

    echo "[BACKEND] Creating: $BACKEND"

    if [ -z "${IP_LIST:-}" ]; then
        read -p "IP(s): " IP_LIST
    fi

    if [ -z "${PORT:-}" ]; then
        read -p "PORT: " PORT
    fi

    build_servers

    TMP=$(mktemp)

    awk -v block="$(cat <<EOF
backend $BACKEND
    mode http
    balance roundrobin
    option forwardfor header X-Real-IP
    http-request set-header X-Forwarded-For %[src]
    http-request set-header X-Forwarded-Port %[dst_port]

$SERVERS
EOF
)" '
    /# --- AUTO_BACKENDS_END ---/ {
        print block
    }
    { print }
    ' "$CFG" > "$TMP"

    mv "$TMP" "$CFG"
}

issue_cert() {
    /usr/local/bin/haproxy-cert.sh "$DOMAIN"
}

update_map() {
    local MAP=$1
    local KEY=$2
    local VALUE=$3

    TMP=$(mktemp)
    [ -f "$MAP" ] && cp "$MAP" "$TMP"

    grep -v "^$KEY " "$TMP" > "$TMP.new" || true
    echo "$KEY $VALUE" >> "$TMP.new"

    mv "$TMP.new" "$MAP"
    rm -f "$TMP"
}

delete_maps() {
    for m in "$HOSTS_MAP" "$PASSTH_MAP" "$ACME_MAP"; do
        [ -f "$m" ] && grep -v "^$DOMAIN " "$m" > "$m.tmp" && mv "$m.tmp" "$m"
    done
}

engine() {
    for MAP in "$HOSTS_MAP" "$PASSTH_MAP" "$ACME_MAP"; do
        [ -f "$MAP" ] || continue
        awk '{ print length($1), $0 }' "$MAP" | sort -nr | cut -d" " -f2- > "$MAP.tmp"
        mv "$MAP.tmp" "$MAP"
    done
}

echo "[MAP] $DOMAIN"

if [ "$ACTION" == "set" ]; then

    if ! backend_exists; then
        if [[ "$AUTO_YES" == "-y" ]]; then
            create_backend
        else
            read -p "Create backend $BACKEND? [Y/n]: " c
            [[ "$c" =~ ^[Nn]$ ]] && exit 1
            create_backend
        fi
    fi

    if [ "$MODE" == "http" ]; then
        issue_cert
        update_map "$HOSTS_MAP" "$DOMAIN" "$BACKEND"

    elif [ "$MODE" == "passth" ]; then
        update_map "$PASSTH_MAP" "$DOMAIN" "$BACKEND"
        update_map "$ACME_MAP" "$DOMAIN" "acme_${BACKEND}"
    fi

    delete_maps
    engine
    systemctl reload haproxy

elif [ "$ACTION" == "del" ]; then
    delete_maps
    engine
    systemctl reload haproxy
fi

echo -e "${GREEN}[DONE]${NC}"

---

📌 Требуемая структура HAProxy:

# --- AUTO_BACKENDS_END ---

---

🏁 Результат