Nginx Proxy Manager — установка и настройка на Alma Linux 9

Сложная иллюстрация стек технологии Nginx Proxy Manager, Docker, CrowdSec, SSL, SELinux и AlmaLinux в высокотехнологичном окружении.

Инструкция по уставновке и настройке стека компонентов: Docker · MariaDB · CrowdSec · SSL · SELinux · c автозапуском всех сервисов

Обзор

Nginx Proxy Manager (NPM) — веб-приложение с графическим интерфейсом для управления обратными прокси на базе Nginx. В связке с CrowdSec обеспечивает комплексную защиту от сканеров, брутфорса и известных CVE-атак.

Nginx Proxy Manager (NPM) — веб-приложение с графическим интерфейсом для управления обратными прокси на базе Nginx. В связке с CrowdSec обеспечивает комплексную защиту от сканеров, брутфорса и известных CVE-атак.

Стек компонентов:

  • Nginx Proxy Manager — маршрутизация трафика, SSL, управление хостами
  • MariaDB — база данных NPM (изолирована во внутренней Docker-сети)
  • CrowdSec — анализ логов, обнаружение атак (45+ сценариев)
  • CrowdSec Firewall Bouncer — блокировка атакующих через iptables

Схема сети

ПараметрЗначение
Публичный трафикИнтернет → 80/443 → NPM → внутренние сервисы
Admin-панель (81)Только из подсетей 10.5.5.0/24 и 10.3.3.0/24
MariaDBТолько внутри Docker npm-internal (не публикуется)
CrowdSecЧитает логи NPM, блокирует через iptables
IPv6Отключён (DISABLE_IPV6: true)

Требования

  • AlmaLinux 9 (минимальная установка)
  • Пользователь с правами sudo
  • Минимум 1 ГБ RAM, 10 ГБ диска
  • Порты 80/443 открыты публично, порт 81 — только внутренние подсети
  • Домен направленный на публичный IP (для SSL)
  • Подсети администраторов: 10.5.5.0/24 и 10.3.3.0/24

Подготовка системы

Обновление пакетов

sudo dnf update -y
sudo dnf install -y curl wget nano policycoreutils-python-utils mc

policycoreutils-python-utils нужен для управления SELinux контекстами (semanage).

Установка VMware Tools

Если сервер работает на VMware:

sudo dnf install -y open-vm-tools
sudo systemctl enable --now vmtoolsd
sudo systemctl status vmtoolsd   # → active (running)

Установка Docker

Добавить репозиторий:

sudo dnf install -y dnf-plugins-core
sudo dnf config-manager \
  --add-repo https://download.docker.com/linux/centos/docker-ce.repo

Установить Docker Engine:

sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

Включить автозапуск и запустить:

sudo systemctl enable --now docker
sudo systemctl is-enabled docker   # → enabled
sudo docker --version              # → Docker version 29.x.x
sudo docker compose version        # → Docker Compose version v5.x.x

Настройка фаервола

Открываем только публичные порты. Порт 81 через firewalld НЕ открываем — его защитим через iptables DOCKER-USER.

sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports   # → 80/tcp 443/tcp

НЕ открывайте порт 81 через firewalld. Docker обходит firewalld напрямую через iptables. Для защиты Docker-портов используется цепочка DOCKER-USER.

Установка Nginx Proxy Manager

Создание структуры проекта

Размещаем проект в /opt — стандартное место для сервисов в Linux. Это также решает проблему SELinux контекстов (файлы из /root недоступны для CrowdSec).

mkdir /opt/nginx-proxy-manager
cd /opt/nginx-proxy-manager
mkdir .secrets
chmod 700 .secrets

Структура проекта:

/opt/nginx-proxy-manager/
├── docker-compose.yml      # конфигурация контейнеров
├── .secrets/               # файлы паролей (chmod 700)
│   ├── mysql_pwd.txt       # пароль пользователя БД
│   └── db_root_pwd.txt     # root-пароль MariaDB
├── .gitignore
├── data/                   # данные NPM (создаётся автоматически)
│   └── logs/               # логи nginx (читает CrowdSec)
├── letsencrypt/            # SSL-сертификаты
└── mysql/                  # данные MariaDB

Создание Docker Secrets

Docker Secrets хранят пароли в файлах и передают их в контейнер через защищённый механизм. Пароли не видны в docker inspect, не попадают в логи и историю shell.

# Генерация надёжных паролей
echo "$(openssl rand -base64 32)" > .secrets/mysql_pwd.txt
echo "$(openssl rand -base64 32)" > .secrets/db_root_pwd.txt
 
# Ограничить доступ
chmod 600 .secrets/*.txt
 
# Проверить
ls -la .secrets/
# -rw-------. mysql_pwd.txt
# -rw-------. db_root_pwd.txt
# Добавить в .gitignore
cat > .gitignore << 'EOF'
.secrets/
data/
letsencrypt/
mysql/
EOF

Никогда не вставляйте пароли напрямую в docker-compose.yml — они видны в docker inspect и системных логах.

Создание docker-compose.yml

nano docker-compose.yml
secrets:
  MYSQL_PWD:
    file: .secrets/mysql_pwd.txt
  DB_ROOT_PWD:
    file: .secrets/db_root_pwd.txt
 
services:
  app:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: npm-app
    restart: unless-stopped
    ports:
      - '80:80'      # HTTP публичный
      - '443:443'    # HTTPS публичный
      - '81:81'      # Admin (защищён iptables DOCKER-USER)
    environment:
      DB_MYSQL_HOST: "db"
      DB_MYSQL_PORT: 3306
      DB_MYSQL_USER: "npm"
      DB_MYSQL_PASSWORD__FILE: /run/secrets/MYSQL_PWD
      DB_MYSQL_NAME: "npm"
      DISABLE_IPV6: 'true'
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    secrets:
      - MYSQL_PWD
    depends_on:
      - db
    networks:
      - npm-internal
      - npm-public
 
  db:
    image: 'jc21/mariadb-aria:latest'
    container_name: npm-db
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD__FILE: /run/secrets/DB_ROOT_PWD
      MYSQL_DATABASE: 'npm'
      MYSQL_USER: 'npm'
      MYSQL_PASSWORD__FILE: /run/secrets/MYSQL_PWD
      MARIADB_AUTO_UPGRADE: '1'
    volumes:
      - ./mysql:/var/lib/mysql
    secrets:
      - DB_ROOT_PWD
      - MYSQL_PWD
    networks:
      - npm-internal
 
networks:
  npm-internal:
    driver: bridge
    internal: true   # БД недоступна снаружи Docker-сети
  npm-public:
    driver: bridge

Сохраните: Ctrl+X → Y → Enter

ПараметрЗначение
DB_MYSQL_PASSWORD__FILEДвойное подчёркивание (__FILE) — стандарт Docker Secrets
DISABLE_IPV6: trueIPv6 отключён на сервере — без этого NPM не запустится
npm-internal: internalMariaDB изолирована — только NPM может к ней обратиться
restart: unless-stoppedПерезапуск при сбое, но не при ручной остановке

Запуск контейнеров

docker compose up -d
 
# Проверить статус
docker compose ps
# npm-app  Up  0.0.0.0:80->80, 0.0.0.0:443->443, 0.0.0.0:81->81
# npm-db   Up  3306/tcp  ← порт НЕ опубликован наружу

Настройка безопасности

Ограничение доступа к Admin (iptables)

Docker напрямую управляет iptables и обходит firewalld. Для ограничения доступа к портам контейнеров используется цепочка DOCKER-USER — Docker её не перезаписывает.

Добавить правила whitelist:

# Разрешить подсеть администраторов 1
sudo iptables -I DOCKER-USER -p tcp --dport 81 -s 10.5.5.0/24 -j ACCEPT
 
# Разрешить подсеть администраторов 2
sudo iptables -I DOCKER-USER -p tcp --dport 81 -s 10.3.3.0/24 -j ACCEPT
 
# Блокировать всех остальных на порт 81
sudo iptables -A DOCKER-USER -p tcp --dport 81 -j DROP

Проверить правила:

sudo iptables -L DOCKER-USER -n --line-numbers
# 1  ACCEPT  tcp  10.3.3.0/24  anywhere  tcp dpt:81
# 2  ACCEPT  tcp  10.5.5.0/24  anywhere  tcp dpt:81
# 3  DROP    tcp  anywhere     anywhere  tcp dpt:81

Порядок правил важен: ACCEPT должны стоять выше DROP. Флаг -I вставляет в начало, -A добавляет в конец.

Сохранить правила (автовосстановление после перезагрузки):

sudo dnf install -y iptables-services
sudo iptables-save | sudo tee /etc/sysconfig/iptables
sudo systemctl enable iptables

Настройка SELinux для логов NPM

CrowdSec читает логи NPM с хоста. SELinux по умолчанию блокирует доступ к файлам в нестандартных директориях. Необходимо установить постоянный контекст var_log_t для директории логов.

Без правильного SELinux контекста CrowdSec не сможет читать логи NPM — атаки не будут обнаруживаться.

Установить постоянное правило SELinux:

# Добавить постоянное правило для директории логов
sudo semanage fcontext -a -t var_log_t "/opt/nginx-proxy-manager/data/logs(/.*)?"
 
# Применить правило к существующим файлам
sudo restorecon -Rv /opt/nginx-proxy-manager/data/logs/
 
# Проверить контекст
ls -laZ /opt/nginx-proxy-manager/data/logs/ | head -3
# system_u:object_r:var_log_t:s0  ← правильно

Правило semanage постоянное — новые файлы proxy-host-N_access.log автоматически получат контекст var_log_t при создании.

Установка CrowdSec

Что такое CrowdSec

CrowdSec — система обнаружения и предотвращения вторжений. Анализирует логи в реальном времени, использует глобальную базу репутации IP-адресов и блокирует атакующих через firewall bouncer.

ПараметрЗначение
CrowdSec agentЧитает логи, анализирует поведение, принимает решения о блокировке
Firewall BouncerПрименяет решения через iptables — блокирует IP на уровне сети
Время бана4 часа по умолчанию
Сценариев защиты45+ (SQLi, XSS, CVE, сканеры, брутфорс, бэкдоры)
Глобальная база IPОбновляется в реальном времени от сообщества CrowdSec

Установка CrowdSec

На AlmaLinux 9 CDN packagecloud.io может быть недоступен. Устанавливаем из архива:

Скачать архив (на Windows-машине и передать через SCP)

# Скачать crowdsec-release.tgz со страницы:
# https://github.com/crowdsecurity/crowdsec/releases/latest
 
# Передать на сервер через SCP (с Windows):
# scp crowdsec-release.tgz root@<IP>:/tmp/
 
# Распаковать на сервере
cd /tmp
tar -xzf crowdsec-release.tgz
cd crowdsec-v*
 
# Установить
sudo bash wizard.sh --unattended

Проверить установку

sudo systemctl status crowdsec   # → active (running)
sudo systemctl is-enabled crowdsec  # → enabled

Установка Firewall Bouncer

Архив crowdsec-firewall-bouncer-linux-amd64.tgz уже включён в crowdsec-release.tgz:

cd /tmp
tar -xzf crowdsec-firewall-bouncer-linux-amd64.tgz
cd crowdsec-firewall-bouncer-v*
 
# Установить (выбрать iptables когда спросит)
sudo bash install.sh
# Found nftables and iptables, which firewall? → iptables
 
# Проверить
sudo systemctl status crowdsec-firewall-bouncer  # → active (running)

Подключение логов NPM к CrowdSec

Создать конфиг источника логов

nano /etc/crowdsec/acquis.d/npm.yaml
filenames:
  - /opt/nginx-proxy-manager/data/logs/*.log
labels:
  type: nginx
---
filenames:
  - /opt/nginx-proxy-manager/data/logs/proxy-host-*_access.log
labels:
  type: nginx

Установить коллекцию правил для Nginx

sudo cscli collections install crowdsecurity/nginx

Перезапустить CrowdSec

sudo systemctl restart crowdsec
sleep 60
 
# Проверить что логи NPM читаются
sudo cscli metrics | grep -A 15 'Acquisition'
# file:/opt/nginx-proxy-manager/data/logs/proxy-host-1_access.log | 95 | 94 ...

Логи появятся в метриках только после первого реального трафика через NPM. Создайте хотя бы один прокси-хост.

Проверка работы CrowdSec

# Просмотр активных блокировок
sudo cscli decisions list
 
# Метрики и статистика
sudo cscli metrics
 
# Список установленных сценариев
sudo cscli scenarios list
 
# Ручная блокировка IP
sudo cscli decisions add --ip 1.2.3.4 --duration 4h --reason 'manual ban'
 
# Снять блокировку
sudo cscli decisions delete --ip 1.2.3.4

Первичная настройка NPM

Вход в веб-интерфейс

Подключитесь с хоста в подсети 10.5.5.0/24 или 10.3.3.0/24:

http://<IP-сервера>:81

ПараметрЗначение
Emailadmin@example.com
Парольchangeme

Немедленно смените email и пароль после первого входа. Дефолтные данные публично известны.

Добавление прокси-хоста с SSL

  1. Hosts → Proxy Hosts → Add Proxy Host
  2. Details: Domain Names = ваш домен (например app.example.com)
  3. Forward Hostname/IP = IP сервиса, Forward Port = порт сервиса
  4. Включить: Block Common Exploits
  5. SSL → Request a new SSL Certificate
  6. Включить: Force SSL, HTTP/2 Support
  7. Ввести email, принять условия Let’s Encrypt → Save

Если домен через Cloudflare — НЕ включайте Force SSL. В Cloudflare установите SSL Mode = Full.

Для SSL необходимо чтобы домен указывал на публичный IP сервера и порт 80 был доступен из интернета.

Настройка Access Lists

Для ограничения доступа к конкретным сервисам по IP:

  1. Access Lists → Add Access List
  2. Имя: например internal-only
  3. Вкладка Access: Allow → добавить подсети 10.5.5.0/24, 10.3.3.0/24
  4. Save → назначить нужному Proxy Host в поле Access List

Автозапуск при загрузке системы

Автозапуск обеспечивается тремя независимыми механизмами:

МеханизмЧто делает
systemctl enable dockerDocker запускается при старте системы
restart: unless-stoppedКонтейнеры NPM и MariaDB поднимаются вместе с Docker
systemctl enable iptablesПравила DOCKER-USER восстанавливаются после перезагрузки
systemctl enable crowdsecCrowdSec agent запускается автоматически
systemctl enable crowdsec-firewall-bouncerBouncer запускается автоматически

Все пять механизмов настраиваются автоматически в ходе установки по данной инструкции.

Чеклист безопасности

#Мера безопасностиСтатус
1Docker установлен из официального репозитория✅ Шаг 2.3
2Порты 80/443 открыты, порт 81 НЕ открыт через firewalld✅ Шаг 2.4
3Проект размещён в /opt (не в /root — SELinux)✅ Шаг 3.1
4Пароли хранятся через Docker Secrets (не в env)✅ Шаг 3.2
5Файлы .secrets/ chmod 600, добавлены в .gitignore✅ Шаг 3.2
6MariaDB не публикует порты, изолирована в npm-internal✅ Шаг 3.3
7DISABLE_IPV6: true (IPv6 отключён на сервере)✅ Шаг 3.3
8restart: unless-stopped на обоих контейнерах✅ Шаг 3.3
9iptables DOCKER-USER: :81 только из 10.5.5.0/24, 10.3.3.0/24✅ Шаг 4.1
10Правила iptables сохранены через iptables-services✅ Шаг 4.1
11SELinux: var_log_t постоянно на /opt/…/data/logs✅ Шаг 4.2
12CrowdSec установлен и читает логи NPM✅ Шаг 5
13Firewall Bouncer активен (блокировка через iptables)✅ Шаг 5.3
14Коллекция crowdsecurity/nginx (45+ сценариев)✅ Шаг 5.4
15Сменить дефолтные креды admin при первом входе⚠️ Вручную
16Зарегистрировать аккаунт на app.crowdsec.net для мониторинга⚠️ Рекомендуется
17Ежемесячно обновлять образы: docker compose pull⚠️ Регулярно

Полезные команды

Управление NPM

cd /opt/nginx-proxy-manager
 
# Статус контейнеров
docker compose ps
 
# Логи в реальном времени
docker compose logs -f
docker compose logs -f app   # только NPM
 
# Остановить / перезапустить
docker compose down
docker compose restart
 
# Обновить образы
docker compose pull && docker compose up -d
docker image prune -f

Управление CrowdSec

# Активные блокировки
sudo cscli decisions list
 
# Метрики
sudo cscli metrics
 
# Заблокировать IP вручную
sudo cscli decisions add --ip 1.2.3.4 --duration 4h
 
# Снять блокировку
sudo cscli decisions delete --ip 1.2.3.4
 
# Обновить сценарии
sudo cscli hub update && sudo cscli hub upgrade

Управление iptables

# Просмотр правил DOCKER-USER
sudo iptables -L DOCKER-USER -n --line-numbers -v
 
# Сохранить текущие правила
sudo iptables-save | sudo tee /etc/sysconfig/iptables
 
# Восстановить правила вручную
sudo iptables-restore < /etc/sysconfig/iptables

Решение проблем

ПараметрЗначение
Admin :81 недоступен из сетиПроверьте iptables -L DOCKER-USER -n. Убедитесь что хост в разрешённой подсети
CrowdSec не видит логи NPMПроверьте SELinux: ls -laZ …/data/logs/ → должно быть var_log_t
SELinux блокирует доступsudo semanage fcontext -a -t var_log_t «/opt/…/logs(/.*)?» && restorecon -Rv
Контейнер не стартуетdocker compose logs app. Проверьте наличие .secrets/*.txt
Ошибка MYSQL_PASSWORDДвойное подчёркивание: DB_MYSQL_PASSWORD__FILE (не одинарное)
Ошибка подключения к БДПароли в .secrets/mysql_pwd.txt должны совпадать у app и db
SSL не выдаётсяДомен должен указывать на публичный IP. Порт 80 должен быть доступен
IPv6 ошибки в логахDISABLE_IPV6: ‘true’ в секции environment контейнера app
Правила iptables сброшеныsudo iptables-restore < /etc/sysconfig/iptables
CDN packagecloud.io недоступенСкачать crowdsec-release.tgz с GitHub и передать через SCP
Redirect loop на CloudflareВ Cloudflare: SSL Mode = Full. В NPM: отключить Force SSL
CrowdSec метрики пустые после restartПодождать 60 сек — читает только новые строки. Проверить трафик

Оцените статью
IT-Sierra