Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,8 @@ All services (except `backup-runner` and `rclone` — **two** deliberate excepti

**OIDC / Keycloak account binding** — `DJANGO_BPP_OIDC_GRACE_BIND` + `DJANGO_BPP_OIDC_TRUSTED_EMAIL_DOMAINS` (CSV) + `DJANGO_BPP_OIDC_GRACE_BIND_PRIVILEGED`, all off by default, all with a `DJANGO_BPP_OIDC_<SKROT>_…` variant that wins over the bare one. **Symptom that identifies this situation:** Keycloak login completes end-to-end (302 → 302 → 200, **no 403 — the WAF is not involved**) but the user stays logged out, and `appserver` logs `OIDC: konto z tym adresem już istnieje — połącz je z SSO przez profil` plus an AXES failure on `/oidc/callback/`. Cause: BPP matches identities by `(issuer, sub)`, never by e-mail (matching by e-mail = account takeover), so pre-SSO accounts have no binding yet. The built-in remedy (profile → „Połącz konto z SSO") requires a **local password**, so it is a dead end on pure-SSO installs — that's what these flags are for. Trust comes from the **domain list**, not `email_verified`: LDAP realms put the institutional address in `mail` and a private one in `email`, and `email_verified` describes the latter. **Anti-fixes — do NOT:** clear the account's e-mail to dodge the collision (creates a second empty account, orphans the real one with its permissions/PBN token/linked `Autor`); make the backend match users by e-mail again; or set `GRACE_BIND_PRIVILEGED` without a domain list (it's a deliberate no-op — the list is the only gate protecting admin accounts). No Compose change needed: all three reach Django via the wholesale `env_file`. Needs a BPP image with `iplweb/bpp#753`; older images ignore them. Operator doc: `docs/konfiguracja/architektura.md`.

**Operator-variable registry** — `scripts/config-registry.tsv` (lib: `scripts/lib-config-registry.sh`, driver: `scripts/configure.sh`) is the single list of `.env` vars only the operator can decide: admin e-mail, SSL mode/LE e-mail, Slack, Rollbar, Google, backup dir/retention/rclone remote, Freshdesk widgets (`DJANGO_BPP_FRESHDESK_WIDGET_ID`/`_ID_ANON`/`_JWT_SECRET`, iplweb/bpp#868). It feeds `init-configs` (`--init`), `make up`/`up-quick`/`refresh` (`ask-missing-config` = `--missing`), `make configure` (group menu), `make config-check` + `make doctor` item 7 (`--check`) and the table in `docs/konfiguracja/zmienne.md` (`make docs-config-table`; staleness fails `test_configure_registry`). **A new ask-the-operator var = one TSV row + `make docs-config-table`** — no `ensure_env_var`, no migration; it still needs a default where it's read (Compose/Django), because autoupdate/CI never answer. Vars with their own logic (hostnames, DB, PG versions, `ADMINS`) stay in `init-configs.sh`. **The decision is the var's PRESENCE in `.env`, not its value:** an empty answer writes `VAR=` (= disabled) and is never asked again. **Anti-fixes — do NOT:** (1) change the check to "non-empty" — every `make up` would nag operators who deliberately said no; (2) write `--missing` answers one by one — EOF/timeout must leave `.env` untouched ("no answer" is not a decision); (3) let it prompt when `BPP_SKIP_HEALTH_GATE`/`BPP_NO_CONFIG_PROMPT` is set — screen gives autoupdate a pseudo-TTY, a prompt would hang the cycle while it holds the lock (a new non-interactive `make up` caller must set one); (4) read registry rows with `IFS=$'\t'` — tab is whitespace-IFS, two tabs collapse and an empty DOMYSLNA shifts every column (rows come out `\037`-separated); (5) read rows from stdin inside the prompt loop — stdin carries the answers (rows go through fd 3); (6) relax the `$`/`#`/quote ban — `.env` is also `-include`d by the Makefile. Operator doc: `docs/konfiguracja/zmienne.md`.

**html2docx fallback** — optional **HTTP sidecar** (`docker-compose.application.yml`, `profiles: ['html2docx']`), off by default. HTML→DOCX export normally uses pandoc from the appserver image; the sidecar is for hosts where pandoc core-dumps (VMWare ESX). Enabling is **two** opt-ins in **two different** `.env` files: `COMPOSE_PROFILES=html2docx` in the **repo-local** `.env` (starts the container) + `DJANGO_BPP_HTML2DOCX_URL=http://html2docx:3030/convert` in `$BPP_CONFIGS_DIR/.env` (points Django at it). Missing URL = soft degradation, not a crash. No published port, **no `docker.sock`** — removing the socket from `appserver` (which used to `docker run` the converter) was the whole point of the change. Image pinned by `HTML2DOCX_VERSION`, independent of `DOCKER_VERSION`. The old flag `DJANGO_BPP_ENABLE_HTML2DOCX_IMAGE` is **dead** — nothing reads it; `init-configs` deliberately neither writes nor strips it (harmless in old `.env`). Operator doc: `docs/konfiguracja/architektura.md`.

## Backwards Compatibility and `.env` Migrations — CRITICAL
Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ help:
@echo " init-configs - Re-initialize config directory structure"
@echo " configure-resources - Tune Docker memory/CPU limits for this host"
@echo " configure-rate-limits - Interaktywnie: limity ruchu nginx (globalne + spowalnianie per IP)"
@echo " configure - Interaktywnie: zmienne operatora (admin, SSL, powiadomienia, backup, Freshdesk...)"
@echo " config-check - Sprawdz poprawnosc zmiennych operatora w .env"
@echo " create-monitoring-user - Create read-only bpp_monitor PG role (Grafana/Netdata)"
@echo " grant-pg-monitor - (deprecated alias) -> create-monitoring-user"
@echo " pg-monitoring-setup - slow-query log + pg_stat_statements + bpp_monitor"
Expand Down
5 changes: 4 additions & 1 deletion docs/eksploatacja/komendy.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,15 @@ make update-ssl-certs # Przeładuj nginx po zmianie certyfikatów
make init-configs # Uzupełnij brakujące pliki/zmienne (idempotentne)
make configure-resources # Dostrój limity RAM/CPU
make configure-rate-limits # Interaktywnie: limity ruchu nginx (globalne + spowalnianie per IP)
make configure # Interaktywnie: zmienne operatora (admin, SSL, powiadomienia, backup, Freshdesk)
make config-check # Sprawdź poprawność zmiennych operatora w .env (też w make doctor)
make docs-config-table # (dla deweloperów) przegeneruj tabelę w docs/konfiguracja/zmienne.md
make generate-snakeoil-certs # Wygeneruj samopodpisane certyfikaty SSL
make ssl-letsencrypt-issue # Wystaw cert Let's Encrypt (PROD=1 dla prawdziwego)
make ssl-letsencrypt-renew # Manualny renew certów LE
```

Szczegóły: [SSL](../konfiguracja/ssl.md), [Limity zasobów](../konfiguracja/limity-zasobow.md), [kreator limitów ruchu](../architektura/rate-limiting.md#kreator).
Szczegóły: [SSL](../konfiguracja/ssl.md), [Limity zasobów](../konfiguracja/limity-zasobow.md), [kreator limitów ruchu](../architektura/rate-limiting.md#kreator), [zmienne operatora](../konfiguracja/zmienne.md).

## Backup

Expand Down
35 changes: 4 additions & 31 deletions docs/konfiguracja/architektura.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,41 +294,14 @@ DJANGO_BPP_FRESHDESK_WIDGET_JWT_SECRET=wklej-tu-sekret-z-freshdeska
Ustawiaj go tylko na serwerach, nad którymi masz pełną kontrolę (dostęp do `.env`).
Nigdy nie commituj go do repozytorium.

### Kreator: `make configure-optional` i pytanie przy `make up` {#kreator-opcjonalnych}

Tych wartości nie da się wygenerować ani zgadnąć, więc kreator po prostu o nie pyta:

```bash
make configure-optional # pyta o wszystkie trzy; Enter = bez zmian, "-" = pusta
```

**`make up` (a także `make run`, `up-quick` i `refresh`) pyta sam**, jeśli którejś
z tych zmiennych **nie ma wcale** w `${BPP_CONFIGS_DIR}/.env`. Zwykle zdarza się to
raz, po `git pull` wydania, które je wprowadziło. **Pusta odpowiedź (Enter) też jest
odpowiedzią.** Zapisuje się wtedy `ZMIENNA=` (pusta wartość = funkcja wyłączona)
i przy następnym `make up` kreator już o nią nie pyta. Jeśli zmienisz zdanie, uruchom
`make configure-optional`. Wyjątek: przy ID widgetu dla zalogowanych Enter zapisuje
domyślne `205000000433`, czyli dotychczasowe działanie.

Kreator **nie pyta**, gdy nie ma kogo pytać. Wtedy niczego nie zapisuje i `make up`
idzie dalej:

- `make up` bez terminala (cron, CI, `| tee`): wypisuje tylko jedną linię z przypomnieniem,
- automatyczne aktualizacje i przerwa techniczna (`make autoupdate`,
`make run-with-warning`), a także `restore` i `upgrade-postgres`. Wszystkie te ścieżki
ustawiają `BPP_SKIP_HEALTH_GATE=1`. Pytania można też wyłączyć ręcznie przez
`BPP_NO_CONFIG_PROMPT=1 make up`,
- nikt nie odpowiada przez 120 s (`BPP_CONFIG_PROMPT_TIMEOUT`), np. przy `ssh -t`
bez człowieka po drugiej stronie.

Sekret nie pojawia się na ekranie ani przy wpisywaniu, ani w podsumowaniu (kreator
pokazuje tylko jego długość). Kreator przyjmuje tylko litery, cyfry i `. _ ~ + / = -`.
`.env` czytają też Makefile i Compose, a dla nich `$` i `#` są składnią.

Przy włączonym JWT widget dla zalogowanych nie otwiera się bez tokenu. Zalogowany bez
tokenu dostaje widget anonimowy, a gdy `..._ANON` jest pusty, zakładka kieruje do
support.iplweb.pl.

Wartości najwygodniej ustawić kreatorem: `make up` sam zapyta o nie raz, a zmienić je można
przez `make configure` (grupa *Freshdesk*). Opis kreatora:
[Zmienne operatora](zmienne.md).

Szczegóły działania (endpoint `/freshdesk/widget-token/`, odnawianie tokenu, użytkownicy
bez e-maila) opisuje dokumentacja BPP: *Administrator → Widget wsparcia Freshdesk*.
Zmienne działają od wydania BPP zawierającego osobne widgety (PR iplweb/bpp#868). Na
Expand Down
137 changes: 137 additions & 0 deletions docs/konfiguracja/zmienne.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Zmienne operatora — `make configure`

Większość zmiennych w `${BPP_CONFIGS_DIR}/.env` jest generowana albo ma rozsądną wartość
domyślną. Kilkanaście zależy jednak wyłącznie od Ciebie: e-mail administratora, tryb SSL,
powiadomienia, katalog backupów, widgety Freshdesk. Zebrane są w jednym rejestrze
(`scripts/config-registry.tsv`), z którego korzystają:

- **`make init-configs`**: pyta o nie przy instalacji,
- **`make up`** (także `make run`, `up-quick`, `refresh`): pyta **raz** o te, których
w `.env` jeszcze nie ma, np. po aktualizacji, która dodała nową zmienną,
- **`make configure`**: menu grup do przejrzenia i zmiany wartości,
- **`make config-check`** i **`make doctor`** (pozycja *config*): sprawdzają, czy wartości są
poprawne (e-mail wygląda jak e-mail, ID widgetu jest liczbą itd.),
- tabela niżej na tej stronie, generowana z rejestru.

## Kreator: `make configure`

```bash
make configure
```

```
=== Konfiguracja BPP — /home/bpp/publikacje-uczelnia/.env ===
1) Administrator (1)
2) SSL (2)
3) Powiadomienia (2)
4) Google (2)
5) Backup (3)
6) Freshdesk (3)
z) zapisz i wyjdz q) wyjdz bez zapisu
Wybierz:
```

Po wyborze grupy kreator pyta po kolei o jej zmienne. **Enter** zostawia wartość
z nawiasu, **`-`** ustawia pustą (= funkcja wyłączona). Zmiany trafiają do `.env`
dopiero po **`z`**, a `q` je porzuca. Nowe wartości działają po `make up`.

Sekrety (np. sekret JWT Freshdeska, token Rollbara) nie pojawiają się na ekranie ani
przy wpisywaniu, ani w podsumowaniu. Kreator pokazuje tylko ich długość.

## Pytanie przy `make up` — „raz i już” {#pytanie-przy-make-up}

Jeśli nowa wersja bpp-deploy wprowadza zmienną, o której musisz zdecydować (np. ID
widgetów Freshdesk), `make up` zatrzyma się przed startem kontenerów i zapyta o nią.

**Pusta odpowiedź (Enter) też jest odpowiedzią.** Zapisuje się wtedy `ZMIENNA=` (pusta
wartość = funkcja wyłączona) i przy następnym `make up` kreator już o nią nie pyta.
O tym, że decyzja zapadła, świadczy **sama obecność zmiennej** w `.env`. Jeśli zmienisz
zdanie, uruchom `make configure`.

Kreator **nie pyta**, gdy nie ma kogo pytać. Wtedy niczego nie zapisuje i `make up`
idzie dalej:

- `make up` bez terminala (cron, CI, `| tee`): wypisuje tylko jedną linię z przypomnieniem,
- automatyczne aktualizacje i przerwa techniczna (`make autoupdate`,
`make run-with-warning`), a także `restore` i `upgrade-postgres`. Wszystkie te ścieżki
ustawiają `BPP_SKIP_HEALTH_GATE=1`. Pytania można też wyłączyć ręcznie przez
`BPP_NO_CONFIG_PROMPT=1 make up`,
- nikt nie odpowiada przez 120 s (`BPP_CONFIG_PROMPT_TIMEOUT`), np. przy `ssh -t`
bez człowieka po drugiej stronie.

Przerwanie w połowie (Ctrl-D, timeout) **nie zapisuje niczego**, także odpowiedzi już
udzielonych, bo brak odpowiedzi nie jest decyzją. Kreator zapyta znowu przy następnym
`make up`.

Zmienne z kolumną *Pytanie* = `nie` (np. tryb SSL, retencja backupów) mają wartość
domyślną, którą `init-configs` wpisuje bez pytania. `make up` o nie nie pyta, a zmienić je
można tylko w `make configure`.

## Dozwolone znaki

`.env` czytają trzy programy: Docker Compose (interpoluje `$`), `make` (dla niego `#`
to komentarz, a `$` to składnia) i skrypty. Dlatego kreator nie przyjmuje w wartościach
`$`, `#`, cudzysłowów, `\` ani `` ` ``. Sekrety mogą zawierać tylko litery, cyfry
i `. _ ~ + / = -`.

## Lista zmiennych

<!-- rejestr:start (generowane: make docs-config-table, nie edytuj recznie) -->

### Administrator

| Zmienna | Typ | Domyślnie | Pytanie przy instalacji / `make up` | Może być pusta | Opis |
|---|---|---|---|---|---|
| `DJANGO_BPP_ADMIN_EMAIL` | email | `admin@example.com` | tak | nie | E-mail administratora BPP (konto admina, maile o bledach) |

### SSL

| Zmienna | Typ | Domyślnie | Pytanie przy instalacji / `make up` | Może być pusta | Opis |
|---|---|---|---|---|---|
| `DJANGO_BPP_SSL_MODE` | jedno z: manual/letsencrypt | `manual` | nie | nie | Skad nginx bierze certyfikat: manual = ssl/, letsencrypt = letsencrypt/live/&lt;host&gt;/ |
| `DJANGO_BPP_LETSENCRYPT_EMAIL` | email | = `DJANGO_BPP_ADMIN_EMAIL` | nie | nie | E-mail dla Let's Encrypt (powiadomienia o wygasajacych certyfikatach) |

### Powiadomienia

| Zmienna | Typ | Domyślnie | Pytanie przy instalacji / `make up` | Może być pusta | Opis |
|---|---|---|---|---|---|
| `DJANGO_BPP_SLACK_WEBHOOK` | url | *(pusta)* | tak | tak | Slack webhook URL (pusta = bez powiadomien na Slacka) |
| `ROLLBAR_ACCESS_TOKEN` | sekret | *(pusta)* | tak | tak | Rollbar access token typu post_server_item (pusta = backup-cycle bez powiadomien) |

### Google

| Zmienna | Typ | Domyślnie | Pytanie przy instalacji / `make up` | Może być pusta | Opis |
|---|---|---|---|---|---|
| `DJANGO_BPP_GOOGLE_ANALYTICS_PROPERTY_ID` | tekst | *(pusta)* | tak | tak | Google Analytics Property ID (pusta = bez GA) |
| `DJANGO_BPP_GOOGLE_VERIFICATION_CODE` | tekst | *(pusta)* | tak | tak | Google Search Console verification code (pusta = brak) |

### Backup

| Zmienna | Typ | Domyślnie | Pytanie przy instalacji / `make up` | Może być pusta | Opis |
|---|---|---|---|---|---|
| `DJANGO_BPP_HOST_BACKUP_DIR` | sciezka | `backups/` obok katalogu konfiguracyjnego | tak | nie | Katalog backupow na hoscie (sciezka bezwzgledna) |
| `DJANGO_BPP_BACKUP_KEEP_LAST` | liczba | `7` | nie | nie | Ile ostatnich kopii kazdego typu trzymac lokalnie |
| `DJANGO_BPP_RCLONE_REMOTE` | tekst | `backup_enc:` | nie | nie | Remote rclone dla kopii zdalnych (nazwa z rclone.conf zakonczona dwukropkiem) |

### Freshdesk

| Zmienna | Typ | Domyślnie | Pytanie przy instalacji / `make up` | Może być pusta | Opis |
|---|---|---|---|---|---|
| `DJANGO_BPP_FRESHDESK_WIDGET_ID` | id | `205000000433` | tak | tak | ID widgetu Freshdesk dla ZALOGOWANYCH (pusta = brak zakladki Pomoc dla zalogowanych) |
| `DJANGO_BPP_FRESHDESK_WIDGET_ID_ANON` | id | *(pusta)* | tak | tak | ID widgetu Freshdesk dla ANONIMOW (pusta = anonimowi nie widza zakladki Pomoc) |
| `DJANGO_BPP_FRESHDESK_WIDGET_JWT_SECRET` | sekret | *(pusta)* | tak | tak | Sekret JWT widgetu (Freshdesk: Admin &gt; Account &gt; Security &gt; Widget settings; pusta = bez JWT) |

<!-- rejestr:end -->

Szczegóły poszczególnych funkcji: [SSL](ssl.md),
[backup i rclone](../eksploatacja/backup-i-rclone.md),
[widget Freshdesk](architektura.md#widget-wsparcia-freshdesk-django_bpp_freshdesk_widget_).

## Dla deweloperów: dodanie zmiennej

Nowa zmienna, o którą operator ma być zapytany, to **jeden wiersz** w
`scripts/config-registry.tsv` (format opisany w nagłówku pliku), a potem
`make docs-config-table`. Nie trzeba dopisywać `ensure_env_var` w `init-configs.sh`
ani migracji `.env`, bo brak zmiennej po prostu wywoła pytanie przy `make up`. Zmienna musi
mieć wartość domyślną w miejscu, które ją czyta (Compose `${VAR:-…}` albo ustawienia
Django), bo autoupdate i CI o nią nie zapytają.
24 changes: 23 additions & 1 deletion mk/configs.mk
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: update-ssl-certs generate-grafana-datasources update-configs configure-resources configure-rate-limits ensure-config-files zaspawaj-wersje
.PHONY: update-ssl-certs generate-grafana-datasources update-configs configure-resources configure-rate-limits configure ask-missing-config config-check docs-config-table ensure-config-files zaspawaj-wersje

# Non-interactive guard przed `make up`: dokopiowuje brakujace pliki z defaults/
# do $BPP_CONFIGS_DIR (np. gdy nowy release dodaje kolejny bind-mount, a user
Expand Down Expand Up @@ -31,6 +31,28 @@ configure-resources:
configure-rate-limits:
@bash scripts/configure-rate-limits.sh

# Zmienne operatora z rejestru scripts/config-registry.tsv (e-mail admina, SSL,
# Slack/Rollbar, Google, backupy, Freshdesk). Menu grup, zapis na koncu.
configure:
@bash scripts/configure.sh

# Prerequisit make up/up-quick/refresh: pyta TYLKO o zmienne z rejestru
# (PYTAJ=tak), ktorych w .env nie ma wcale. Pusta odpowiedz zapisuje `VAR=`
# = decyzja "nie mamy", wiec pytanie pada raz. Bez terminala / pod
# BPP_SKIP_HEALTH_GATE / BPP_NO_CONFIG_PROMPT nie pyta (autoupdate pod
# screenem ma pseudo-TTY).
ask-missing-config:
@bash scripts/configure.sh --missing

# Walidacja wartosci z rejestru w .env (tez z make doctor). Exit 1 = bledy.
config-check:
@bash scripts/configure.sh --check

# Przegenerowuje tabele zmiennych w docs/konfiguracja/zmienne.md z rejestru
# (miedzy znacznikami rejestr:start / rejestr:end). Test pilnuje aktualnosci.
docs-config-table:
@bash scripts/render-config-docs.sh

# Przypina DOCKER_VERSION w $(BPP_CONFIGS_DIR)/.env do wersji CalVer, na
# ktorej faktycznie chodzi appserver (lub jawnej: TAG=202606.1386).
# Szczegoly i kontrakt: docs/eksploatacja/komendy.md, CLAUDE.md.
Expand Down
6 changes: 3 additions & 3 deletions mk/deployment.mk
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ restart: validate-env-quotes update-configs
docker compose down
docker compose up -d

refresh: validate-env-quotes prune-orphan-volumes ensure-config-files update-configs
refresh: validate-env-quotes prune-orphan-volumes ask-missing-config ensure-config-files update-configs
docker system prune -f
docker compose pull
docker compose stop
Expand All @@ -32,7 +32,7 @@ pull: validate-env-quotes
build:
docker compose build

up: validate-env-quotes ensure-config-files update-configs
up: validate-env-quotes ask-missing-config ensure-config-files update-configs
docker compose up -d --wait --force-recreate --remove-orphans
@bash scripts/create-monitoring-user.sh --soft
@# Stack jest UP i healthy (--wait powyzej; przy bledzie make juz by stanal).
Expand Down Expand Up @@ -63,7 +63,7 @@ rmrf:
@read -p "Are you sure? [y/N] " confirm && [ "$$confirm" = "y" ] || (echo "Aborted."; exit 1)
docker compose rm -f

up-quick: validate-env-quotes ensure-config-files pull
up-quick: validate-env-quotes ask-missing-config ensure-config-files pull
docker compose up -d --wait
$(MAKE) invalidate

Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ nav:
- Pierwsze uruchomienie: instalacja/pierwsze-uruchomienie.md
- Konfiguracja:
- Architektura konfiguracji: konfiguracja/architektura.md
- Zmienne operatora (make configure): konfiguracja/zmienne.md
- Struktura katalogów: konfiguracja/struktura-katalogow.md
- SSL (manual / Let's Encrypt): konfiguracja/ssl.md
- Multi-host: konfiguracja/multi-host.md
Expand Down
Loading
Loading