Загрузить файлы в «/»
This commit is contained in:
1 parent
f2055f312c
commit
2b1132c097
2 files changed
+350
-2
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