#!/bin/bash # ============================================================================= # Skrypt instalacji samodzielnie utrzymywanego NetBird z Zitadel # Utworzony dla OS: Debian 13 (Trixie) # ============================================================================= # Skrypt wykonuje pełną instalację serwera NetBird od zera: # 1. Konfiguracja systemu (hostname, pakiety) # 2. Konfiguracja firewalla (UFW) # 3. Instalacja Docker # 4. Instalacja NetBird (oficjalny skrypt getting-started-with-zitadel.sh) # 5. Instalacja CrowdSec (opcjonalnie) # 6. Konfiguracja CrowdSec # 7. Weryfikacja końcowa # ============================================================================= set -euo pipefail # !!!!!! Konfiguracja !!!!!! # # Skonfiguruj parametry poniżej przed uruchomieniem skryptu: HOSTNAME="netbird" # Wpisz nazwę hosta NETBIRD_DOMAIN="vpn.ariscard.eu" # Domena DNS, pod którą ma działać NetBird NETBIRD_DIR="/netbird" # Katalog roboczy — tu trafią docker-compose.yml i pliki konfiguracyjne INSTALL_CROWDSEC=false # true = zainstaluj CrowdSec, false = pomiń CROWDSEC_PORT=8081 # Port CrowdSec API (domyślny 8080 koliduje z NetBird) CROWDSEC_ENROLL_KEY="1234567890" # Klucz rejestracji z panelu web CrowdSec TIMESTAMP=$(date +%Y%m%d_%H%M%S) LOG_FILE="/var/log/netbird-install_${TIMESTAMP}.log" # Porty do odblokowania w zaporze ogniowej UFW UFW_TCP_PORTS="22 80 443 33073 10000 33080" UFW_UDP_PORTS="3478 49152:65535" # Pakiety systemowe BASE_PACKAGES=(mc sudo jq curl htop cron ca-certificates gnupg) # --- Kolory --- RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' # --- Funkcje pomocnicze --- log() { echo -e "${GREEN}[$(date '+%H:%M:%S')] [INFO]${NC} $*" | tee -a "$LOG_FILE"; } warn() { echo -e "${YELLOW}[$(date '+%H:%M:%S')] [WARN]${NC} $*" | tee -a "$LOG_FILE"; } err() { echo -e "${RED}[$(date '+%H:%M:%S')] [ERROR]${NC} $*" | tee -a "$LOG_FILE" >&2; } header() { echo -e "\n${CYAN}${BOLD}=== $* ===${NC}" | tee -a "$LOG_FILE"; } abort() { err "$1" err "Skrypt przerwany. Log: ${LOG_FILE}" exit 1 } confirm() { local msg="${1:-Kontynuować?}" read -r -p "$(echo -e "${YELLOW}${msg} [t/N]: ${NC}")" answer /dev/null | tail -1) || true if [[ -n "$resolved_ip" ]]; then log "DNS ${NETBIRD_DOMAIN} -> ${resolved_ip}" else warn "Nie udało się rozwiązać domeny ${NETBIRD_DOMAIN}." warn "Upewnij się, że rekordy DNS (A/AAAA) wskazują na adres IP tego serwera." if ! confirm "Kontynuować mimo braku rozwiązania DNS?"; then abort "Przerwano — domena nie jest skonfigurowana." fi fi # Sprawdzenie połączenia z internetem if ! wget -q --spider --timeout=10 https://deb.debian.org 2>/dev/null && \ ! bash -c 'echo >/dev/tcp/deb.debian.org/443' 2>/dev/null; then abort "Brak połączenia z internetem. Wymagane do instalacji pakietów." fi log "Połączenie z internetem: OK" # Sprawdzenie wolnego miejsca (min. 5GB) local available_kb available_kb=$(df / --output=avail | tail -1 | tr -d ' ') if [[ "$available_kb" -lt 5242880 ]]; then warn "Mało wolnego miejsca na dysku (< 5GB)." if ! confirm "Kontynuować mimo mało miejsca?"; then abort "Przerwano — za mało miejsca na dysku." fi fi log "Wolne miejsce na dysku: $(df -h / --output=avail | tail -1 | tr -d ' ')" # Sprawdzenie czy Docker/NetBird nie są już zainstalowane if command -v docker &>/dev/null && docker compose ps 2>/dev/null | grep -qi netbird; then warn "Wykryto działające kontenery NetBird. Możliwe, że NetBird jest już zainstalowany." if ! confirm "Kontynuować mimo to?"; then abort "Przerwano — NetBird może być już zainstalowany." fi fi log "Walidacja wstępna zakończona pomyślnie." } # ============================================================================= # ETAP 1: Konfiguracja systemu # ============================================================================= configure_system() { header "ETAP 1: Konfiguracja systemu" CURRENT_STAGE="konfiguracja systemu" # Hostname local current_hostname current_hostname=$(hostname) if [[ "$current_hostname" != "$HOSTNAME" ]]; then log "Ustawianie hostname: ${HOSTNAME}" hostnamectl set-hostname "$HOSTNAME" log "Hostname zmieniony: ${current_hostname} -> ${HOSTNAME}" else log "Hostname już ustawiony: ${HOSTNAME}" fi # Aktualizacja systemu log "Aktualizacja systemu..." export DEBIAN_FRONTEND=noninteractive if ! retry 3 10 apt-get update -qq; then abort "Nie udało się zaktualizować listy pakietów." fi if ! apt-get upgrade -y -qq 2>&1 | tee -a "$LOG_FILE"; then warn "Aktualizacja pakietów zakończyła się z ostrzeżeniami." fi log "System zaktualizowany." # Instalacja pakietów bazowych log "Instalacja pakietów bazowych: ${BASE_PACKAGES[*]}" if ! apt-get install -y -qq "${BASE_PACKAGES[@]}" 2>&1 | tee -a "$LOG_FILE"; then abort "Nie udało się zainstalować pakietów bazowych." fi log "Pakiety bazowe zainstalowane." } # ============================================================================= # ETAP 2: Firewall (UFW) # ============================================================================= configure_ufw() { header "ETAP 2: Konfiguracja firewalla (UFW)" CURRENT_STAGE="konfiguracja UFW" if ! command -v ufw &>/dev/null; then log "Instalacja UFW..." apt-get install -y -qq ufw 2>&1 | tee -a "$LOG_FILE" fi # Reset reguł (czysta konfiguracja) log "Konfiguracja reguł UFW..." ufw --force reset >> "$LOG_FILE" 2>&1 # Domyślna polityka ufw default deny incoming >> "$LOG_FILE" 2>&1 ufw default allow outgoing >> "$LOG_FILE" 2>&1 # Reguły TCP for port in $UFW_TCP_PORTS; do log " Otwieranie TCP: ${port}" ufw allow "$port"/tcp >> "$LOG_FILE" 2>&1 || ufw allow "$port" >> "$LOG_FILE" 2>&1 done # Reguły UDP for port in $UFW_UDP_PORTS; do log " Otwieranie UDP: ${port}" ufw allow "$port"/udp >> "$LOG_FILE" 2>&1 done # Włączenie UFW ufw --force enable >> "$LOG_FILE" 2>&1 log "UFW włączony." # Status log "Status UFW:" ufw status verbose 2>&1 | tee -a "$LOG_FILE" } # ============================================================================= # ETAP 3: Instalacja Docker # ============================================================================= install_docker() { header "ETAP 3: Instalacja Docker" CURRENT_STAGE="instalacja Docker" # Sprawdzenie czy Docker już zainstalowany if command -v docker &>/dev/null && docker info &>/dev/null 2>&1; then local docker_version docker_version=$(docker --version) log "Docker już zainstalowany: ${docker_version}" if docker compose version &>/dev/null 2>&1; then log "Docker Compose plugin: $(docker compose version)" return 0 else warn "Docker Compose plugin nie jest zainstalowany. Kontynuuję instalację..." fi fi # Dodanie klucza GPG Docker log "Dodawanie klucza GPG Docker..." install -m 0755 -d /etc/apt/keyrings if ! retry 3 5 curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc; then abort "Nie udało się pobrać klucza GPG Docker." fi chmod a+r /etc/apt/keyrings/docker.asc # Dodanie repozytorium Docker log "Dodawanie repozytorium Docker..." source /etc/os-release tee /etc/apt/sources.list.d/docker.sources > /dev/null <&1 | tee -a "$LOG_FILE"; then abort "Nie udało się zainstalować Docker." fi # Weryfikacja if ! docker info &>/dev/null 2>&1; then log "Uruchamianie Docker daemon..." systemctl enable --now docker sleep 3 fi if ! docker info &>/dev/null 2>&1; then abort "Docker daemon nie działa po instalacji." fi if ! docker compose version &>/dev/null 2>&1; then abort "Docker Compose plugin nie jest dostępny po instalacji." fi log "Docker zainstalowany: $(docker --version)" log "Docker Compose: $(docker compose version)" } # ============================================================================= # ETAP 4: Instalacja NetBird # ============================================================================= install_netbird() { header "ETAP 4: Instalacja NetBird" CURRENT_STAGE="instalacja NetBird" # Utworzenie katalogu roboczego if [[ ! -d "$NETBIRD_DIR" ]]; then log "Tworzenie katalogu roboczego: ${NETBIRD_DIR}" mkdir -p "$NETBIRD_DIR" fi log "Katalog roboczy: ${NETBIRD_DIR}" log "Domena: ${NETBIRD_DOMAIN}" log "Pobieranie i uruchamianie oficjalnego skryptu instalacyjnego NetBird..." log "Źródło: https://github.com/netbirdio/netbird/releases/latest/download/getting-started-with-zitadel.sh" export NETBIRD_DOMAIN="$NETBIRD_DOMAIN" if ! curl -fsSL https://github.com/netbirdio/netbird/releases/latest/download/getting-started-with-zitadel.sh -o /tmp/netbird-install.sh; then abort "Nie udało się pobrać skryptu instalacyjnego NetBird." fi chmod +x /tmp/netbird-install.sh log "Skrypt instalacyjny pobrany. Uruchamianie z katalogu ${NETBIRD_DIR}..." # Uruchomienie z katalogu NETBIRD_DIR — oficjalny skrypt generuje pliki # (docker-compose.yml, Caddyfile, *.env, management.json, itp.) # w bieżącym katalogu roboczym cd "$NETBIRD_DIR" if ! bash /tmp/netbird-install.sh 2>&1 | tee -a "$LOG_FILE"; then abort "Skrypt instalacyjny NetBird zakończył się błędem." fi rm -f /tmp/netbird-install.sh # Odczytanie danych logowania z wyniku instalacji NETBIRD_DASHBOARD_URL=$(grep 'dashboard at' "$LOG_FILE" | tail -1 | sed 's/.*dashboard at //' | tr -d ' ') || true NETBIRD_ADMIN_USER=$(grep 'Username:' "$LOG_FILE" | tail -1 | awk '{print $NF}') || true NETBIRD_ADMIN_PASS=$(grep 'Password:' "$LOG_FILE" | tail -1 | awk '{print $NF}') || true log "Instalacja NetBird zakończona. Pliki w: ${NETBIRD_DIR}" } # ============================================================================= # ETAP 5: Instalacja CrowdSec # ============================================================================= install_crowdsec() { header "ETAP 5: Instalacja CrowdSec" CURRENT_STAGE="instalacja CrowdSec" # Instalacja repozytorium CrowdSec log "Dodawanie repozytorium CrowdSec..." if ! retry 3 5 bash -c 'curl -s https://install.crowdsec.net | sh' 2>&1 | tee -a "$LOG_FILE"; then warn "Nie udało się dodać repozytorium CrowdSec." if ! confirm "Pominąć instalację CrowdSec?"; then abort "Przerwano — nie udało się zainstalować CrowdSec." fi return 0 fi # Instalacja CrowdSec log "Instalacja CrowdSec..." if ! apt-get install -y -qq crowdsec 2>&1 | tee -a "$LOG_FILE"; then warn "Nie udało się zainstalować CrowdSec." return 1 fi # Instalacja firewall bouncer log "Instalacja CrowdSec Firewall Bouncer..." if ! apt-get install -y -qq crowdsec-firewall-bouncer-iptables 2>&1 | tee -a "$LOG_FILE"; then warn "Nie udało się zainstalować firewall bouncer." fi log "CrowdSec zainstalowany." } # ============================================================================= # ETAP 6: Konfiguracja CrowdSec (zmiana portu z 8080 na CROWDSEC_PORT) # ============================================================================= configure_crowdsec() { header "ETAP 6: Konfiguracja CrowdSec (port ${CROWDSEC_PORT})" CURRENT_STAGE="konfiguracja CrowdSec" local config_file="/etc/crowdsec/config.yaml" local creds_file="/etc/crowdsec/local_api_credentials.yaml" local bouncer_file="/etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml" # Sprawdzenie czy port 8080 jest zajęty (przez NetBird) if ss -tlnp | grep -q ':8080 '; then log "Port 8080 zajęty (NetBird). Zmiana portu CrowdSec na ${CROWDSEC_PORT}." else log "Port 8080 wolny, ale zmieniam na ${CROWDSEC_PORT} prewencyjnie." fi # Zmiana portu w config.yaml if [[ -f "$config_file" ]]; then log "Modyfikacja ${config_file}..." if grep -q 'listen_uri' "$config_file"; then sed -i "s|listen_uri:.*|listen_uri: 127.0.0.1:${CROWDSEC_PORT}|" "$config_file" else warn "Nie znaleziono 'listen_uri' w ${config_file}. Sprawdź ręcznie." fi else warn "Brak pliku ${config_file}" fi # Zmiana URL w local_api_credentials.yaml if [[ -f "$creds_file" ]]; then log "Modyfikacja ${creds_file}..." sed -i "s|url:.*|url: http://127.0.0.1:${CROWDSEC_PORT}/|" "$creds_file" else warn "Brak pliku ${creds_file}" fi # Zmiana URL w bouncer config if [[ -f "$bouncer_file" ]]; then log "Modyfikacja ${bouncer_file}..." sed -i "s|api_url:.*|api_url: http://127.0.0.1:${CROWDSEC_PORT}/|" "$bouncer_file" else warn "Brak pliku ${bouncer_file}" fi # Restart serwisów CrowdSec log "Restartowanie CrowdSec..." systemctl restart crowdsec 2>&1 | tee -a "$LOG_FILE" || warn "Nie udało się zrestartować crowdsec." sleep 3 if systemctl is-active --quiet crowdsec; then log "CrowdSec działa na porcie ${CROWDSEC_PORT}." else warn "CrowdSec nie uruchomił się poprawnie. Sprawdź: journalctl -u crowdsec" fi # Restart firewall bouncer log "Restartowanie firewall bouncer..." systemctl enable --now crowdsec-firewall-bouncer 2>&1 | tee -a "$LOG_FILE" || \ warn "Nie udało się uruchomić firewall bouncer." if systemctl is-active --quiet crowdsec-firewall-bouncer; then log "CrowdSec Firewall Bouncer działa." else warn "Firewall Bouncer nie uruchomił się. Sprawdź: journalctl -u crowdsec-firewall-bouncer" fi # Enroll do konsoli CrowdSec if [[ -n "$CROWDSEC_ENROLL_KEY" ]]; then log "Rejestracja w konsoli CrowdSec..." if cscli console enroll -e context "$CROWDSEC_ENROLL_KEY" 2>&1 | tee -a "$LOG_FILE"; then log "Rejestracja w konsoli CrowdSec zakończona." else warn "Nie udało się zarejestrować w konsoli CrowdSec." fi fi log "Konfiguracja CrowdSec zakończona." } # ============================================================================= # ETAP 7: Weryfikacja końcowa # ============================================================================= final_verification() { header "ETAP 7: Weryfikacja końcowa" CURRENT_STAGE="weryfikacja końcowa" local errors=0 # Docker log "Sprawdzanie Docker..." if docker info &>/dev/null 2>&1; then log " Docker: OK" else err " Docker: NIE DZIAŁA" errors=$((errors + 1)) fi # Kontenery NetBird log "Sprawdzanie kontenerów NetBird..." if [[ -f "${NETBIRD_DIR}/docker-compose.yml" ]]; then cd "$NETBIRD_DIR" local services mapfile -t services < <(docker compose config --services 2>/dev/null) local running=0 local total=${#services[@]} for svc in "${services[@]}"; do local state state=$(docker compose ps --format '{{.State}}' "$svc" 2>/dev/null || echo "missing") if [[ "$state" == "running" ]]; then log " ${svc}: OK" running=$((running + 1)) else err " ${svc}: ${state}" errors=$((errors + 1)) fi done log " Kontenery: ${running}/${total} działają." else warn " Nie znaleziono ${NETBIRD_DIR}/docker-compose.yml — pomijam sprawdzanie kontenerów." fi # UFW log "Sprawdzanie UFW..." if ufw status | grep -q "Status: active"; then log " UFW: aktywny" else warn " UFW: nieaktywny" errors=$((errors + 1)) fi # CrowdSec if [[ "$INSTALL_CROWDSEC" == true ]]; then log "Sprawdzanie CrowdSec..." if systemctl is-active --quiet crowdsec 2>/dev/null; then log " CrowdSec: OK (port ${CROWDSEC_PORT})" else warn " CrowdSec: nie działa" fi if systemctl is-active --quiet crowdsec-firewall-bouncer 2>/dev/null; then log " Firewall Bouncer: OK" else warn " Firewall Bouncer: nie działa" fi else log " CrowdSec: pominięty (INSTALL_CROWDSEC=false)" fi # Sprawdzenie portów log "Sprawdzanie nasłuchujących portów..." ss -tlnp | grep -E ':(80|443|8080|33073|10000|33080|3478|8081) ' 2>&1 | tee -a "$LOG_FILE" || true # Podsumowanie header "PODSUMOWANIE INSTALACJI" log "Hostname: $(hostname)" log "System: $(grep PRETTY_NAME /etc/os-release | cut -d= -f2 | tr -d '"')" log "Docker: $(docker --version 2>/dev/null || echo 'N/A')" log "Compose: $(docker compose version 2>/dev/null || echo 'N/A')" log "Domena: ${NETBIRD_DOMAIN}" log "UFW: $(ufw status | head -1)" if [[ "$INSTALL_CROWDSEC" == true ]]; then log "CrowdSec: $(systemctl is-active crowdsec 2>/dev/null || echo 'N/A')" else log "CrowdSec: pominięty" fi if [[ -f "${NETBIRD_DIR}/docker-compose.yml" ]]; then log "Kontenery: ${running:-0}/${total:-0} działają" log "Katalog: ${NETBIRD_DIR}" fi log "Log: ${LOG_FILE}" if [[ $errors -gt 0 ]]; then warn "Instalacja zakończona z ${errors} problemami. Sprawdź log." else log "Instalacja zakończona pomyślnie!" fi # Dane logowania do panelu NetBird if [[ -n "$NETBIRD_DASHBOARD_URL" || -n "$NETBIRD_ADMIN_USER" || -n "$NETBIRD_ADMIN_PASS" ]]; then header "DANE LOGOWANIA DO PANELU NETBIRD" log "Panel dostępny pod adresem: ${NETBIRD_DASHBOARD_URL:-N/A}" log "Nazwa użytkownika: ${NETBIRD_ADMIN_USER:-N/A}" log "Hasło: ${NETBIRD_ADMIN_PASS:-N/A}" else warn "Nie udało się odczytać danych logowania z wyniku instalacji." warn "Sprawdź log: ${LOG_FILE}" fi } # ============================================================================= # MAIN # ============================================================================= main() { header "NetBird Installation — Start" log "Data: $(date)" log "Log: ${LOG_FILE}" echo "" echo -e "${BOLD}Ten skrypt wykona następujące operacje:${NC}" echo " 1. Walidacja wstępna (system, sieć, DNS)" echo " 2. Konfiguracja systemu (hostname: ${HOSTNAME}, pakiety)" echo " 3. Konfiguracja firewalla (UFW)" echo " 4. Instalacja Docker" echo " 5. Instalacja NetBird (domena: ${NETBIRD_DOMAIN}, katalog: ${NETBIRD_DIR})" if [[ "$INSTALL_CROWDSEC" == true ]]; then echo " 6. Instalacja i konfiguracja CrowdSec" else echo " 6. Instalacja i konfiguracja CrowdSec — POMINIĘTA (INSTALL_CROWDSEC=false)" fi echo " 7. Weryfikacja końcowa" echo "" if ! confirm "Rozpocząć instalację?"; then log "Przerwano przez użytkownika." exit 0 fi preflight_checks configure_system configure_ufw install_docker install_netbird if [[ "$INSTALL_CROWDSEC" == true ]]; then install_crowdsec configure_crowdsec else log "Instalacja CrowdSec pominięta (INSTALL_CROWDSEC=false)." fi