Files

331 lines
16 KiB
Markdown

# Портал администрирования: HAProxy + Django + AD → Backend admin panels
## Установка на виртуальную машину (без Docker)
Единая точка входа для администраторов: логин через Active Directory,
персональный список доступных ресурсов по AD-группам, переход в нужную
админку по адресу `https://<FQDN>/<adminusergroup>/<resource>/...`.
Все компоненты устанавливаются как обычные пакеты/systemd-сервисы
непосредственно на одну ВМ (Ubuntu 22.04/24.04 или Debian 12).
## 1. Состав решения
```
admin-portal-vm/
├── README.md — этот файл
├── .env.example — шаблон переменных окружения
├── haproxy/
│ ├── haproxy.cfg — полный конфиг HAProxy (-> /etc/haproxy/haproxy.cfg)
│ └── group_map.cfg — карта "путь -> backend" (-> /etc/haproxy/group_map.cfg)
├── systemd/
│ └── admin-portal.service — юнит Gunicorn (Django-приложение)
├── django_app/ — код портала (Django)
│ ├── manage.py
│ ├── requirements.txt
│ ├── config/ — settings.py, urls.py, wsgi.py
│ ├── portal/ — модели, вью, шаблоны, LDAP-логика
│ └── resources.example.yaml — пример декларативного каталога ресурсов
├── backend_demo/ — 2 демо-админки (Nginx) для проверки маршрутизации
│ ├── resource1/{index.html,nginx.conf}
│ └── resource2/{index.html,nginx.conf}
├── scripts/
│ ├── install.sh — автоматизация шагов 3–9 (см. ниже)
│ └── generate_secrets.sh — генерация DJANGO_SECRET_KEY / RESOURCE_TOKEN_SECRET
└── docs/
└── architecture.md — полное описание архитектуры и решений
```
## 2. Архитектура (кратко)
```
Браузер админа
│ HTTPS (443)
▼
HAProxy (systemd-сервис haproxy) ── TLS termination, ACL-роутинг по пути
│
├── путь портала / нет валидного resource_token ──► Gunicorn (127.0.0.1:8000)
│ └─ Django frontend
│ ├─ django-auth-ldap -> AD
│ ├─ PostgreSQL (localhost)
│ └─ Redis (localhost, сессии)
│
└── путь /{adminusergroup}/{resource}/... + resource_token
│
▼
backend_<группа> — Nginx на 127.0.0.1:808x (демо) или
реальная админка на другом хосте сети
```
Полное описание потоков, модели данных и вопросов безопасности — в
`docs/architecture.md`.
## 3. Требования к ВМ
- Ubuntu 22.04/24.04 LTS или Debian 12, root/sudo-доступ
- 2 vCPU / 2 GB RAM достаточно для старта (PostgreSQL, Redis, Gunicorn,
HAProxy, Nginx — все на одной машине)
- Сетевой доступ до контроллера домена AD (LDAP/LDAPS, порт 389/636)
- Технический пользователь AD с правом bind + поиска (service account),
без прав записи
- Валидный TLS-сертификат на FQDN портала (или self-signed для теста)
- DNS-запись FQDN, указывающая на эту ВМ
- Порты 80/443 открыты снаружи; всё остальное (5432, 6379, 8000, 808x) —
только на loopback, наружу не публикуется
## 4. Установка — автоматически (рекомендуемый способ)
```bash
# 1. Скопировать архив решения на ВМ и распаковать
unzip admin-portal-vm.zip
cd admin-portal-vm
# 2. Подготовить .env
cp .env.example .env
bash scripts/generate_secrets.sh # подставить значения ключей в .env вручную
nano .env # заполнить AD_*, POSTGRES_PASSWORD и т.д.
# 3. Запустить установку (пакеты, БД, Redis, venv, миграции, systemd, HAProxy, Nginx)
sudo bash scripts/install.sh ./.env
```
Скрипт последовательно:
1. ставит `haproxy`, `nginx`, `postgresql`, `redis-server`, Python и dev-библиотеки
для сборки `python-ldap`;
2. создаёт системного пользователя `adminportal` без права логина;
3. копирует `django_app/` в `/opt/admin-portal/django_app`;
4. создаёт venv и ставит зависимости из `requirements.txt`;
5. кладёт `.env` в `/etc/admin-portal/admin-portal.env` (права 640, владелец root:adminportal);
6. создаёт роль и базу PostgreSQL по значениям из `.env`, включает PostgreSQL и Redis;
7. прогоняет `migrate` и `collectstatic` от имени `adminportal`;
8. устанавливает и включает `systemd`-юнит `admin-portal.service` (Gunicorn на 127.0.0.1:8000);
9. раскладывает `haproxy.cfg`/`group_map.cfg`, поднимает 2 демо-бэкенда через Nginx
на 127.0.0.1:8081/8082, открывает 80/443 в `ufw`.
После скрипта останутся 3 ручных шага (секреты и сертификат сознательно не
автоматизированы):
```bash
# TLS-сертификат
cat fullchain.pem privkey.pem > /etc/haproxy/certs/fqdn.pem
haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy
# Суперпользователь Django
sudo -u adminportal bash -c \
'set -a; source /etc/admin-portal/admin-portal.env; set +a; \
/opt/admin-portal/venv/bin/python /opt/admin-portal/django_app/manage.py createsuperuser'
# Наполнение каталога ресурсов
sudo -u adminportal bash -c \
'set -a; source /etc/admin-portal/admin-portal.env; set +a; \
/opt/admin-portal/venv/bin/python /opt/admin-portal/django_app/manage.py \
sync_resources /opt/admin-portal/django_app/resources.example.yaml'
```
Дальше — к разделу 6 (проверка).
## 5. Установка — вручную, шаг за шагом
Если нужно понимать/контролировать каждое действие (или адаптировать под
другой дистрибутив), либо не доверяете единому скрипту — то же самое руками.
### Шаг 1. Системные пакеты
```bash
sudo apt-get update
sudo apt-get install -y haproxy nginx postgresql redis-server \
python3 python3-venv python3-dev \
libldap2-dev libsasl2-dev libssl-dev build-essential ufw
```
### Шаг 2. Системный пользователь приложения
```bash
sudo useradd --system --home /opt/admin-portal --shell /usr/sbin/nologin adminportal
```
### Шаг 3. Код приложения и виртуальное окружение
```bash
sudo mkdir -p /opt/admin-portal
sudo cp -r django_app /opt/admin-portal/django_app
sudo chown -R adminportal:adminportal /opt/admin-portal
sudo python3 -m venv /opt/admin-portal/venv
sudo /opt/admin-portal/venv/bin/pip install --upgrade pip
sudo /opt/admin-portal/venv/bin/pip install -r /opt/admin-portal/django_app/requirements.txt
sudo chown -R adminportal:adminportal /opt/admin-portal/venv
```
### Шаг 4. Переменные окружения
```bash
cp .env.example .env
bash scripts/generate_secrets.sh # вставить значения в .env
nano .env
```
Заполнить как минимум: `DJANGO_SECRET_KEY`, `DJANGO_ALLOWED_HOSTS`,
`POSTGRES_PASSWORD`, `AD_SERVER_URI`, `AD_BIND_DN`, `AD_BIND_PASSWORD`,
`AD_USER_SEARCH_BASE`, `AD_GROUP_SEARCH_BASE`, `RESOURCE_TOKEN_SECRET`.
```bash
sudo mkdir -p /etc/admin-portal
sudo install -o root -g adminportal -m 640 .env /etc/admin-portal/admin-portal.env
```
### Шаг 5. PostgreSQL
```bash
source .env # только для этой команды, читаем значения POSTGRES_*
sudo -u postgres psql -c "CREATE ROLE ${POSTGRES_USER} LOGIN PASSWORD '${POSTGRES_PASSWORD}';"
sudo -u postgres psql -c "CREATE DATABASE ${POSTGRES_DB} OWNER ${POSTGRES_USER};"
sudo systemctl enable --now postgresql
```
### Шаг 6. Redis
```bash
sudo systemctl enable --now redis-server
```
По умолчанию Redis слушает только `127.0.0.1` — это то, что нужно, менять
`bind` в `/etc/redis/redis.conf` не требуется.
### Шаг 7. Миграции и статика
```bash
sudo -u adminportal bash -c \
'set -a; source /etc/admin-portal/admin-portal.env; set +a; \
/opt/admin-portal/venv/bin/python /opt/admin-portal/django_app/manage.py migrate --noinput && \
/opt/admin-portal/venv/bin/python /opt/admin-portal/django_app/manage.py collectstatic --noinput'
```
### Шаг 8. Gunicorn как systemd-сервис
```bash
sudo install -m 644 systemd/admin-portal.service /etc/systemd/system/admin-portal.service
sudo systemctl daemon-reload
sudo systemctl enable --now admin-portal
sudo systemctl status admin-portal # должен быть active (running)
```
### Шаг 9. HAProxy
```bash
sudo install -m 644 haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg
sudo install -m 644 haproxy/group_map.cfg /etc/haproxy/group_map.cfg
sudo mkdir -p /etc/haproxy/certs && sudo chmod 700 /etc/haproxy/certs
```
Положить сертификат:
```bash
cat fullchain.pem privkey.pem | sudo tee /etc/haproxy/certs/fqdn.pem >/dev/null
sudo chmod 600 /etc/haproxy/certs/fqdn.pem
```
Для теста — self-signed:
```bash
openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
-keyout /tmp/key.pem -out /tmp/cert.pem -subj "/CN=portal.example.com"
cat /tmp/cert.pem /tmp/key.pem | sudo tee /etc/haproxy/certs/fqdn.pem >/dev/null
```
Проверить и включить:
```bash
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl enable --now haproxy
```
### Шаг 10. Демо-бэкенды (Nginx) — опционально, для сквозной проверки
```bash
for res in resource1 resource2; do
sudo mkdir -p /var/www/admin-demo-${res}
sudo cp backend_demo/${res}/index.html /var/www/admin-demo-${res}/index.html
sudo install -m 644 backend_demo/${res}/nginx.conf /etc/nginx/sites-available/admin-demo-${res}
sudo ln -sf /etc/nginx/sites-available/admin-demo-${res} /etc/nginx/sites-enabled/admin-demo-${res}
done
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl enable --now nginx && sudo systemctl reload nginx
```
### Шаг 11. Фаервол
```bash
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable # если ещё не включён — проверить, что не потеряете SSH (22/tcp)
```
### Шаг 12. Суперпользователь и каталог ресурсов
```bash
sudo -u adminportal bash -c \
'set -a; source /etc/admin-portal/admin-portal.env; set +a; \
/opt/admin-portal/venv/bin/python /opt/admin-portal/django_app/manage.py createsuperuser'
```
Дальше зайти на `https://<FQDN>/django-admin/` и создать **ResourceGroup**
(привязка к реальной AD-группе) и **Resource** (конкретный ресурс), либо
воспользоваться декларативным файлом:
```bash
sudo -u adminportal bash -c \
'set -a; source /etc/admin-portal/admin-portal.env; set +a; \
/opt/admin-portal/venv/bin/python /opt/admin-portal/django_app/manage.py \
sync_resources /opt/admin-portal/django_app/resources.example.yaml'
```
## 6. Проверка
```bash
systemctl status admin-portal haproxy nginx postgresql redis-server
curl -kI https://portal.example.com/ # ожидаем редирект на /login/
```
Залогиниться доменной учёткой, убедиться, что на портале появились ресурсы,
доступные группам этого пользователя, и что клик по ресурсу открывает
демо-страницу (`Demo Resource 1/2`) — это подтверждает, что вся цепочка
HAProxy → Django → AD → выдача токена → маршрутизация на backend работает.
## 7. Добавление нового ресурса/backend'а в дальнейшем
1. Поднять реальную админку ресурса — на этой же ВМ (новый порт на
`127.0.0.1`) либо на отдельном хосте сети, доступном от HAProxy, но не
доступном напрямую из интернета.
2. Добавить в `/etc/haproxy/haproxy.cfg` новую `backend be_<name>` секцию со
`server` этой админки (по образцу `be_admin_resource1`).
3. Добавить строку в `/etc/haproxy/group_map.cfg`:
`/adgroup/resource-slug/ be_<name>`.
4. Проверить и перечитать конфиг без разрыва соединений:
`sudo haproxy -c -f /etc/haproxy/haproxy.cfg && sudo systemctl reload haproxy`.
5. Создать запись `Resource` в Django admin или через `sync_resources`.
## 8. Эксплуатация
- **Логи**: `journalctl -u admin-portal -f` (Django/Gunicorn),
`journalctl -u haproxy -f`, `/var/log/nginx/*.log` (демо-бэкенды).
- **Обновление кода портала**:
```bash
sudo -u adminportal rsync -a --delete django_app/ /opt/admin-portal/django_app/
sudo -u adminportal bash -c \
'set -a; source /etc/admin-portal/admin-portal.env; set +a; \
/opt/admin-portal/venv/bin/python /opt/admin-portal/django_app/manage.py migrate --noinput && \
/opt/admin-portal/venv/bin/python /opt/admin-portal/django_app/manage.py collectstatic --noinput'
sudo systemctl restart admin-portal
```
- **Резервное копирование**: штатный `pg_dump ${POSTGRES_DB}` по расписанию
(cron/systemd timer). Redis бэкапить не нужно — там только сессии/кэш.
- **Обновление TLS-сертификата**: заменить `/etc/haproxy/certs/fqdn.pem` и
выполнить `systemctl reload haproxy` (без разрыва активных соединений).
- **Масштабирование**: вынести PostgreSQL/Redis на отдельные ВМ (поменять
`POSTGRES_HOST`/`REDIS_URL` в `.env`), поднять второй Django+Gunicorn на
другой ВМ и добавить вторую строку `server` в `backend be_django`;
HAProxy балансирует между ними без изменения остальной конфигурации.
Полное описание архитектуры, потоков данных, вариантов передачи identity в
backend и вопросов безопасности — см. `docs/architecture.md`.