Загрузить файлы в «/»
This commit is contained in:
1 parent
f2055f312c
commit
2b1132c097
2 files changed
+351
-3
No files matched your search
@@ -0,0 +1,29 @@
|
||||
# Копируется в /etc/admin-portal/admin-portal.env (см. README.md, шаг 5)
|
||||
# Владелец файла — root, права 640, группа adminportal (см. install.sh)
|
||||
|
||||
# Django
|
||||
DJANGO_SECRET_KEY=change-me
|
||||
DJANGO_DEBUG=false
|
||||
DJANGO_ALLOWED_HOSTS=portal.example.com,127.0.0.1
|
||||
|
||||
# PostgreSQL (локальный на этой же ВМ)
|
||||
POSTGRES_DB=admin_portal
|
||||
POSTGRES_USER=admin_portal
|
||||
POSTGRES_PASSWORD=change-me
|
||||
POSTGRES_HOST=127.0.0.1
|
||||
POSTGRES_PORT=5432
|
||||
|
||||
# Redis (локальный на этой же ВМ, сессии + кэш групп AD)
|
||||
REDIS_URL=redis://127.0.0.1:6379/1
|
||||
|
||||
# Active Directory / LDAP
|
||||
AD_SERVER_URI=ldaps://dc01.example.com:636
|
||||
AD_BIND_DN=CN=svc-portal,OU=ServiceAccounts,DC=example,DC=com
|
||||
AD_BIND_PASSWORD=change-me
|
||||
AD_USER_SEARCH_BASE=OU=Users,DC=example,DC=com
|
||||
AD_GROUP_SEARCH_BASE=OU=Groups,DC=example,DC=com
|
||||
AD_GROUP_CACHE_TTL_SECONDS=600
|
||||
|
||||
# Токен для backend admin panels
|
||||
RESOURCE_TOKEN_SECRET=change-me-separate-secret
|
||||
RESOURCE_TOKEN_TTL_SECONDS=60
|
||||
@@ -1,3 +1,322 @@
|
||||
# Test1
|
||||
|
||||
Что-то тестируем
|
||||
# Портал администрирования: HAProxy + Django + AD → Backend admin panels
|
||||
## Общее описание реализованного проекта
|
||||
|
||||
Единая точка входа для администраторов: логин через Active Directory,
|
||||
персональный список доступных ресурсов по AD-группам, переход в нужную
|
||||
админку по адресу `https://<FQDN>/<adminusergroup>/<resource>/...`.
|
||||
|
||||
Все компоненты устанавливаются как обычные пакеты/systemd-сервисы
|
||||
непосредственно на одну ВМ.
|
||||
|
||||
## 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. Требования к ВМ
|
||||
|
||||
- РедОС либо АстраЛинукс
|
||||
- 2 vCPU / 4 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
|
||||
```
|
||||
|
||||
Проверить и включить:
|
||||
|
||||
```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`.
|
||||
Reference in new issue
Block a user