mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: release QA hardening across processing, media, security, and CI gates (#649)
A release-readiness QA pass over the whole product. The commits split into defects a user would hit and gates that were reporting green while measuring nothing. ## Fixes that change behaviour Rate limiting was bypassable on every install: TRUST_PROXY defaulted to true, so request.ip came from a client-set header and a forged X-Forwarded-For got past the login limiter. The default is now a private-network trust list. A transient Postgres outage stranded in-flight jobs, leaving finished output on disk with no row pointing at it. A reconciler now resolves those rows and adopts the bytes rather than dropping the work. A Redis connection that moved to a new address wedged every read-blocked consumer, so completions stopped signalling while health still answered 200. Socket timeouts plus subscriber pings recover it. Installing more than one AI bundle left the shared venv multi-versioned and silently broke three tools. The installer now reconciles distributions to one version each. Converting an image to JXL at quality 1 through 4 returned a 500, because libjxl 0.7 rejects the distance those values compute. The quality is floored at what the encoder honours. A missing ffmpeg was also reported to the user as a corrupt upload; it now says the engine is unavailable. RAW uploads reached an unpatched LibRaw on arm64, so it is built from source at 0.22.2, and the release scan was split so it can fail on an unfixed critical instead of hiding it behind ignore-unfixed. ## Gates that could not fail Two mutation lanes ran zero mutants because Stryker crawled the gitignored docs build; coverage discarded its whole report on any failing test; the lint gate skipped root tests, scripts, and two workspaces; and several generated matrices counted a host missing ffmpeg as a passing tool. Each now measures what it claims. Full evidence and the outstanding release items are tracked locally and are not part of this branch.
This commit is contained in:
+88
-159
@@ -1,8 +1,9 @@
|
||||
---
|
||||
description: "Przewodnik po wzmacnianiu bezpieczeństwa SnapOtter. Bezpieczeństwo kontenerów, izolacja sieci, sekrety Docker, wdrożenie Kubernetes i artefakty zgodności."
|
||||
i18n_source_hash: 986f7658430c
|
||||
i18n_provenance: human
|
||||
i18n_output_hash: c616d661a5d1
|
||||
i18n_source_hash: 9ff337fa0417
|
||||
i18n_provenance: machine
|
||||
i18n_output_hash: 20c36e0b6bb9
|
||||
i18n_hash_version: 2
|
||||
---
|
||||
|
||||
# Bezpieczeństwo i wzmacnianie {#security-hardening}
|
||||
@@ -11,133 +12,43 @@ SnapOtter przetwarza pliki w całości na twojej infrastrukturze. Domyślnie wys
|
||||
|
||||
Kontener działa jako dedykowany użytkownik nie-root (`snapotter`) z odrzuconymi wszystkimi uprawnieniami Linuksa poza minimalnym wymaganym zestawem. Po pełną politykę ujawniania podatności i architekturę bezpieczeństwa zobacz [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md) na GitHub.
|
||||
|
||||
## Wzmacnianie kontenera {#container-hardening}
|
||||
## Hartowanie kontenera {#container-hardening}
|
||||
|
||||
[Domyślny docker-compose.yml](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose.yml) zawiera produkcyjne wzmacnianie bezpieczeństwa. Oto rozbicie każdej opcji i wyjaśnienie, dlaczego ma znaczenie:
|
||||
Źródłem prawdy są kanoniczne pliki [CPU](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose.yml) i [GPU](https://github.com/snapotter-hq/SnapOtter/blob/main/docker/docker-compose-gpu.yml). Nie kopiuj skróconego przykładu do produkcji; wdróż plik ze zweryfikowanego tagu wydania.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
SnapOtter:
|
||||
image: snapotter/snapotter:latest
|
||||
ports:
|
||||
# Bind to localhost only for internet-facing deployments:
|
||||
- "127.0.0.1:1349:1349"
|
||||
volumes:
|
||||
- SnapOtter-data:/data
|
||||
- SnapOtter-workspace:/tmp/workspace
|
||||
environment:
|
||||
- AUTH_ENABLED=true
|
||||
- DEFAULT_PASSWORD=change-me-immediately
|
||||
- RATE_LIMIT_PER_MIN=1000
|
||||
- DATABASE_URL=postgres://snapotter:snapotter@postgres:5432/snapotter
|
||||
- REDIS_URL=redis://redis:6379
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
Obydwa stosy stosują następujące elementy sterujące:
|
||||
|
||||
# --- Resource limits ---
|
||||
mem_limit: 6g # Prevents runaway memory from crashing the host
|
||||
memswap_limit: 6g # No swap - fail fast instead of degrading the host
|
||||
cpus: 4 # Cap CPU usage to 4 cores
|
||||
pids_limit: 512 # Prevents fork bombs
|
||||
- Limity pamięci, wymiany, procesora i PID powodują niekontrolowane przetwarzanie natywne.
|
||||
- Każda usługa powoduje utratę wszystkich możliwości Linuksa. Aplikacja dodaje tylko `CHOWN, SETUID, SETGID, DAC_OVERRIDE, FOWNER, KILL` dla własności wolumenu, jednokierunkową utratę tożsamości `gosu` i płynne przekazywanie sygnału. PostgreSQL i Redis otrzymują tylko podzbiór potrzebny ich oficjalnym punktom wejścia.
|
||||
- `security_opt: [no-new-privileges:true]` uniemożliwia procesom w aplikacji, kontenerach PostgreSQL i Redis uzyskanie dodatkowych uprawnień. Pozostaje to zgodne z `gosu`: punkt wejścia zaczyna się jako root, przygotowuje woluminy i przechodzi tylko do dedykowanego użytkownika `snapotter`.
|
||||
- Wejścia obrazów PostgreSQL i Redis są przypinane za pomocą skrótu. Aplikację należy również przypiąć do zweryfikowanego tagu wydania lub podsumowania, a nie `latest`.
|
||||
|
||||
# --- Capability restrictions ---
|
||||
cap_drop:
|
||||
- ALL # Drop ALL Linux capabilities first
|
||||
cap_add:
|
||||
- CHOWN # Needed for volume permission setup
|
||||
- SETUID # Needed for gosu privilege drop (root -> snapotter)
|
||||
- SETGID # Needed for gosu privilege drop
|
||||
- DAC_OVERRIDE # Needed for volume permission setup
|
||||
- FOWNER # Needed for volume permission setup
|
||||
— Kontrole stanu, ograniczona rotacja dzienników JSON, trwała funkcja Redis AOF i zasady ponownego uruchamiania są definiowane centralnie w plikach kanonicznych.
|
||||
|
||||
# --- Logging ---
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "50m" # Rotate logs at 50 MB
|
||||
max-file: "5" # Keep 5 rotated log files
|
||||
|
||||
# --- Health check ---
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-sf", "--max-time", "5", "http://localhost:1349/api/v1/health"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
start_period: 60s
|
||||
retries: 3
|
||||
|
||||
shm_size: "2gb" # Required for Python ML shared memory
|
||||
restart: unless-stopped
|
||||
|
||||
postgres:
|
||||
image: postgres:17-alpine
|
||||
environment:
|
||||
POSTGRES_USER: snapotter
|
||||
POSTGRES_PASSWORD: snapotter
|
||||
POSTGRES_DB: snapotter
|
||||
volumes:
|
||||
- SnapOtter-pgdata:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U snapotter"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 15s
|
||||
|
||||
redis:
|
||||
image: redis:8-alpine
|
||||
command: ["redis-server", "--maxmemory-policy", "noeviction", "--appendonly", "yes"]
|
||||
volumes:
|
||||
- SnapOtter-redisdata:/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
SnapOtter-data:
|
||||
SnapOtter-workspace:
|
||||
SnapOtter-pgdata:
|
||||
SnapOtter-redisdata:
|
||||
```
|
||||
|
||||
### Dlaczego `no-new-privileges` nie jest ustawione {#why-no-new-privileges-is-not-set}
|
||||
|
||||
`security_opt: [no-new-privileges:true]` jest celowo pominięte. Punkt wejścia startuje jako root, aby naprawić własność wolumenów, a następnie schodzi do użytkownika `snapotter` przez [gosu](https://github.com/tianon/gosu), które wymaga setuid. Po ukończeniu obniżenia uprawnień proces działa jako `snapotter` z usuniętymi wszystkimi uprawnieniami poza pięcioma wymienionymi powyżej.
|
||||
|
||||
Jeśli używasz Kubernetes lub flagi `--user` Dockera, aby uruchomić bezpośrednio jako nie-root (omijając gosu), `no-new-privileges` można bezpiecznie włączyć.
|
||||
W przypadku wdrożenia z dostępem do Internetu powiąż port 1349 z pętlą zwrotną i zakończ protokół TLS na utrzymywanym zwrotnym serwerze proxy. Wygeneruj unikalne dane uwierzytelniające PostgreSQL i Redis, przechowuj sekrety w chronionych plikach lub menedżerze sekretów i natychmiast zmień początkowe hasło administratora.
|
||||
|
||||
### Dlaczego `read_only` nie jest ustawione {#why-read-only-is-not-set}
|
||||
|
||||
`read_only: true` nie jest ustawione, ponieważ remapowanie PUID/PGID zapisuje do `/etc/passwd` i `/etc/group` przy uruchamianiu. Jeśli używasz flagi `--user` Dockera lub `runAsUser` Kubernetes zamiast PUID/PGID, możesz bezpiecznie włączyć system plików root tylko do odczytu.
|
||||
`read_only: true` nie jest ustawiony, ponieważ ponowne mapowanie PUID/PGID zapisuje podczas uruchamiania `/etc/passwd` i `/etc/group`. Jeśli zamiast PUID/PGID użyjesz flagi `--user` Dockera lub Kubernetes `runAsUser`, możesz bezpiecznie włączyć główny system plików tylko do odczytu.
|
||||
|
||||
## Izolacja sieci {#network-isolation}
|
||||
|
||||
Podczas normalnej pracy kontener nawiązuje **zero wychodzących połączeń sieciowych**. Całe przetwarzanie plików odbywa się lokalnie z użyciem dołączonych bibliotek.
|
||||
Przetwarzanie plików odbywa się lokalnie, ale instalacja domyślna **nie jest systemem bez ruchu wychodzącego**. Anonimowe analizy produktów korzystają z PostHog, a raportowanie o awariach korzysta z Sentry, gdy włączona jest telemetria. Ustaw `SNAPOTTER_TELEMETRY=0` (lub wyłącz analizę w obszarze Ustawienia > System > Prywatność), aby wyłączyć oba. SnapOtter nigdy nie uwzględnia w tych zdarzeniach przesłanych plików, nazw plików, danych wyjściowych OCR, tekstu dokumentu ani innej zawartości plików.
|
||||
|
||||
```
|
||||
Browser --> Reverse Proxy (TLS) --> SnapOtter container --> (nothing)
|
||||
```
|
||||
|
||||
Jedynym wyjątkiem są **pobrania modeli AI**: gdy użytkownik instaluje pakiet funkcji AI przez interfejs, kontener pobiera gotowe archiwum pakietu z Hugging Face plus kilka pojedynczych plików modeli z GitHub Releases, Google Storage i PyPI. Te pobrania odbywają się raz na pakiet i są przechowywane w wolumenie `/data`.
|
||||
Pozostały ruch wychodzący jest oparty na funkcjach: instalacja pakietu/modelu AI powoduje pobranie podpisanych danych wejściowych wersji; Import adresu URL powoduje pobranie publicznego adresu URL żądanego przez użytkownika; i jawnie skonfigurowane OIDC, SAML, OpenTelemetry, webhooki, pamięć zgodna z S3 lub podobne integracje łączą się z miejscami docelowymi wybranymi przez administratora. Pobieranie modeli w czasie wykonywania jest domyślnie wyłączone. Ustaw `SNAPOTTER_ALLOW_MODEL_DOWNLOAD=1` tylko po to, aby jawnie włączyć automatyczne pobieranie zastępcze. [Import pakietu offline](/pl/guide/deployment) może zapewnić funkcje AI bez konieczności wychodzenia z modelu środowiska wykonawczego.
|
||||
|
||||
**Zalecenia dotyczące zapory sieciowej:**
|
||||
|
||||
| Scenariusz | Reguła wychodząca |
|
||||
|Scenariusz|Reguła wychodząca|
|
||||
|---|---|
|
||||
| Odizolowany od sieci (bez AI) | Zablokuj cały ruch wychodzący z kontenera |
|
||||
| Potrzebne pakiety AI | Zezwól na HTTPS do `huggingface.co`, `*.xethub.hf.co`, `cdn-lfs.huggingface.co`, `github.com`, `objects.githubusercontent.com`, `storage.googleapis.com`, `pypi.org`, `files.pythonhosted.org` podczas instalacji, następnie zablokuj |
|
||||
| Po instalacji AI | Zablokuj cały ruch wychodzący, modele są buforowane lokalnie |
|
||||
|Szczelina powietrzna|Ustaw `SNAPOTTER_TELEMETRY=0` i `SNAPOTTER_ALLOW_MODEL_DOWNLOAD=0`, użyj importu pakietów AI offline, wyłącz import adresów URL i integracje zewnętrzne, a następnie zablokuj wyjście|
|
||||
|Domyślna telemetria|Zezwól na punkty końcowe PostHog i Sentry wymienione w dziennikach przeglądarki/sieci; wyłącz telemetrię, jeśli zasady na to nie pozwalają|
|
||||
|Potrzebne pakiety AI|Podczas instalacji zezwól HTTPS na `huggingface.co, *.xethub.hf.co, cdn-lfs.huggingface.co, github.com, objects.githubusercontent.com, storage.googleapis.com, pypi.org, files.pythonhosted.org`; następnie zablokuj te hosty|
|
||||
|Integracje zewnętrzne|Zezwalaj tylko na dokładnie skonfigurowane przez administratora miejsca docelowe OIDC/SAML/OTLP/webhook/object-storage|
|
||||
|
||||
Archiwa pakietów są serwowane z pamięci masowej Xet Hugging Face, która przesyła przez punkty końcowe `*.xethub.hf.co` równolegle i to właśnie sprawia, że pobrania wielogigabajtowych pakietów są szybkie. Jeśli twoja zapora zezwala na `huggingface.co`, ale blokuje `*.xethub.hf.co`, instalacje nadal się powodzą, ale przechodzą na wolniejsze pobieranie jednostrumieniowe, więc dodaj hosty Xet do listy dozwolonych, aby pozostać na szybkiej ścieżce. W pełni offline instalacje mogą pominąć to wszystko i zamiast tego użyć [Importu pakietów offline](/pl/guide/deployment).
|
||||
Archiwa pakietów są obsługiwane z pamięci Xet firmy Hugging Face, która jest przesyłana równolegle przez punkty końcowe `*.xethub.hf.co` i dzięki temu pobieranie pakietów o wielkości wielu GB jest szybkie. Jeśli twoja zapora sieciowa pozwala na `huggingface.co`, ale blokuje `*.xethub.hf.co`, instalacje nadal się powiodą, ale powrócą do wolniejszego pobierania w jednym strumieniu, więc umieść hosty Xet na liście dozwolonych, aby pozostały na szybkiej ścieżce. Instalacje w pełni offline mogą to wszystko pominąć i zamiast tego użyć [Import pakietu offline](/pl/guide/deployment).
|
||||
|
||||
Po konfigurację reverse proxy (Nginx, Traefik, Caddy, Tunele Cloudflare) zobacz [Przewodnik wdrożenia](/pl/guide/deployment#reverse-proxy).
|
||||
Informacje na temat konfiguracji odwrotnego proxy (Nginx, Traefik, Caddy, Cloudflare Tunnels) można znaleźć w [Przewodniku wdrażania](/pl/guide/deployment#reverse-proxy).
|
||||
|
||||
## Sekrety Docker {#docker-secrets}
|
||||
|
||||
@@ -255,85 +166,103 @@ Ponieważ `runAsUser: 999` jest ustawione na poziomie poda, punkt wejścia całk
|
||||
|
||||
Po dobór rozmiaru zasobów zobacz [Wymagania sprzętowe](/pl/guide/deployment#hardware-requirements).
|
||||
|
||||
## Kopie zapasowe i odzyskiwanie {#backup-and-recovery}
|
||||
## Kopia zapasowa i odzyskiwanie {#backup-and-recovery}
|
||||
|
||||
Trwały stan jest podzielony na dwa wolumeny:
|
||||
Produkcyjny stos Compose definiuje cztery woluminy. Zatrzymaj ruch wejściowy i poczekaj na zakończenie aktywnych zadań przed wykonaniem skoordynowanej kopii zapasowej, tak aby PostgreSQL, Redis i stan pliku opisywały ten sam punkt w czasie.
|
||||
|
||||
| Wolumen | Zawartość | Krytyczny? |
|
||||
|Tom|Zawartość|Leczenie regeneracyjne|
|
||||
|---|---|---|
|
||||
| `SnapOtter-pgdata` | Baza danych PostgreSQL (użytkownicy, ustawienia, potoki, zadania, dziennik audytu) | Tak |
|
||||
| `/data` (wolumen aplikacji) | Pliki przesłane przez użytkownika, modele AI, venv Pythona | Częściowo (patrz niżej) |
|
||||
|`SnapOtter-pgdata`|Użytkownicy PostgreSQL, ustawienia, potoki, zadania, metadane plików i dziennik audytu|Krytyczny; użyj niezawodnego zrzutu logicznego do odzyskiwania przenośnego|
|
||||
|`SnapOtter-data`|Zapisane obiekty biblioteki, dzienniki i stan AI (`/data/files, /data/logs, /data/ai, /data/ai/venv`)|Utwórz kopię zapasową całego woluminu; aby zaoszczędzić miejsce, celowo pomiń cały stan AI i zainstaluj ponownie jego pakiety|
|
||||
|`SnapOtter-redisdata`|Redis AOF dla trwałego stanu kolejki BullMQ|Utwórz kopię zapasową po wstrzymaniu aplikacji i wymuszeniu `SAVE`; wymagane do dokładnego wznowienia pracy w kolejce|
|
||||
|`SnapOtter-workspace`|Tymczasowe klucze do przechowywania obiektów (`/tmp/workspace/uploads, /tmp/workspace/outputs`)|Nie twórz kopii zapasowych po wyczerpaniu lub anulowaniu wszystkich zadań; nigdy go nie wyrzucaj, gdy zadania są aktywne|
|
||||
|
||||
W obrębie wolumenu `/data`:
|
||||
|
||||
| Ścieżka | Zawartość | Krytyczna? |
|
||||
|---|---|---|
|
||||
| `/data/uploads/`, `/data/outputs/` | Pliki użytkownika i wyniki przetwarzania | Tak |
|
||||
| `/data/ai/` | Pobrane pliki modeli AI | Nie (do ponownego pobrania) |
|
||||
| `/data/venv/` | Wirtualne środowisko Pythona | Nie (odbudowywane przy starcie) |
|
||||
Funkcja Compose zwykle poprzedza nazwy woluminów nazwą projektu. Rozwiąż rzeczywisty wolumin źródłowy z zamontowanego kontenera, zamiast zakładać, że nazwa wyświetlana, taka jak `SnapOtter-data`, jest nazwą woluminu Docker.
|
||||
|
||||
### Kopia zapasowa bazy danych {#database-backup}
|
||||
|
||||
Użyj `pg_dump`, aby wykonać kopię zapasową bazy danych podczas działania stosu:
|
||||
Użyj niestandardowego formatu archiwum PostgreSQL i zweryfikuj archiwum, zanim potraktujesz kopię zapasową jako kompletną:
|
||||
|
||||
```bash
|
||||
# Dump the database
|
||||
docker exec SnapOtter-postgres pg_dump -U snapotter snapotter > backup.sql
|
||||
docker exec SnapOtter-postgres \
|
||||
pg_dump --format=custom --no-owner -U snapotter snapotter > snapotter.dump
|
||||
test -s snapotter.dump
|
||||
docker exec -i SnapOtter-postgres pg_restore --list < snapotter.dump >/dev/null
|
||||
|
||||
# Restore into a fresh database
|
||||
cat backup.sql | docker exec -i SnapOtter-postgres psql -U snapotter snapotter
|
||||
# Restore only into a fresh/disposable target first; any SQL error fails the command.
|
||||
docker exec -i SnapOtter-postgres \
|
||||
pg_restore --exit-on-error --clean --if-exists --no-owner \
|
||||
-U snapotter -d snapotter < snapotter.dump
|
||||
```
|
||||
|
||||
Alternatywnie zatrzymaj stos i wykonaj migawkę wolumenu `SnapOtter-pgdata`:
|
||||
Przetestuj każdą kopię zapasową, przywracając ją do izolowanego stosu, sprawdzając rekordy bazy danych i sumy kontrolne plików oraz uruchamiając aplikację. `tests/qa/backup-restore-drill.sh` repozytorium automatyzuje tę bramkę zwolnienia w stosunku do jawnego `QA_IMAGE`.
|
||||
|
||||
Jeśli zamiast tego Twoja platforma wykonuje migawki woluminów spójne w czasie awarii, najpierw zatrzymaj cały stos i wykonaj migawkę wszystkich krytycznych woluminów jako jeden zestaw. Surowa kopia katalogu danych PostgreSQL z działającego kontenera nie jest obsługiwaną logiczną kopią zapasową.
|
||||
|
||||
### Kopia zapasowa plików i kolejek {#file-and-queue-backup}
|
||||
|
||||
Wstrzymaj aplikację przed przechwyceniem woluminów plików i kolejek. Użyj `docker inspect`, aby rozwiązać rzeczywistą nazwę woluminu, wymuś na Redis zachowanie bieżącego stanu i zarchiwizuj z zachowaniem własności i uprawnień:
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
docker run --rm -v SnapOtter-pgdata:/data -v $(pwd)/backup:/backup \
|
||||
alpine tar czf /backup/snapotter-pgdata.tar.gz -C /data .
|
||||
docker stop SnapOtter
|
||||
docker exec SnapOtter-redis redis-cli -a "$REDIS_PASSWORD" --no-auth-warning SAVE
|
||||
docker stop SnapOtter-redis
|
||||
|
||||
DATA_VOLUME="$(docker inspect SnapOtter --format '{{range .Mounts}}{{if eq .Destination "/data"}}{{.Name}}{{end}}{{end}}')"
|
||||
REDIS_VOLUME="$(docker inspect SnapOtter-redis --format '{{range .Mounts}}{{if eq .Destination "/data"}}{{.Name}}{{end}}{{end}}')"
|
||||
|
||||
install -d -m 700 backup
|
||||
docker run --rm -v "$DATA_VOLUME:/source:ro" -v "$PWD/backup:/backup" \
|
||||
alpine:3.22@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce tar czf /backup/snapotter-data.tar.gz -C /source .
|
||||
docker run --rm -v "$REDIS_VOLUME:/source:ro" -v "$PWD/backup:/backup" \
|
||||
alpine:3.22@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce tar czf /backup/snapotter-redis.tar.gz -C /source .
|
||||
sha256sum backup/snapotter-*.tar.gz > backup/SHA256SUMS
|
||||
```
|
||||
|
||||
### Kopia zapasowa plików użytkownika {#user-files-backup}
|
||||
|
||||
```bash
|
||||
# Snapshot the app data volume (excluding re-downloadable AI models)
|
||||
docker run --rm -v SnapOtter-data:/data -v $(pwd)/backup:/backup \
|
||||
alpine tar czf /backup/snapotter-files.tar.gz \
|
||||
--exclude='ai' --exclude='venv' -C /data .
|
||||
```
|
||||
|
||||
Modele AI wynoszą łącznie do około 24 GB dla wszystkich pakietów. Ponieważ można je ponownie pobrać, wyłącz `/data/ai/` i `/data/venv/` z kopii zapasowych, aby zaoszczędzić miejsce. Tylko baza danych i pliki użytkownika są krytyczne.
|
||||
Uruchom ponownie Redis przed aplikacją. Jeśli celowo wykluczysz `/data/ai`, usuń całe poddrzewo AI, zamiast zachowywać rekord `installed.json` bez jego modeli i środowiska wirtualnego. Przechowuj pliki kopii zapasowych w sposób szyfrowany, z kontrolą dostępu i oddzielnie od hosta, na którym działa SnapOtter.
|
||||
|
||||
## Artefakty zgodności {#compliance-artifacts}
|
||||
|
||||
Każde wydanie SnapOtter zawiera następujące artefakty bezpieczeństwa:
|
||||
Każde wydanie SnapOtter zawiera następujące artefakty zabezpieczeń:
|
||||
|
||||
| Artefakt | Format | Gdzie go znaleźć |
|
||||
| Artefakt | Format | Gdzie to znaleźć |
|
||||
|---|---|---|
|
||||
| SBOM (CycloneDX) | JSON | Zasób [Wydania GitHub](https://github.com/snapotter-hq/SnapOtter/releases): `snapotter-v{version}-sbom.cdx.json` |
|
||||
| SBOM (SPDX) | JSON | Zasób [Wydania GitHub](https://github.com/snapotter-hq/SnapOtter/releases): `snapotter-v{version}-sbom.spdx.json` |
|
||||
| Skan podatności | Trivy JSON | Zasób [Wydania GitHub](https://github.com/snapotter-hq/SnapOtter/releases): `snapotter-v{version}-trivy.json` |
|
||||
| Skan podatności | SARIF | Zakładka [GitHub Security](https://github.com/snapotter-hq/SnapOtter/security) |
|
||||
| Analiza statyczna | CodeQL (JS/TS + Python) | Zakładka [GitHub Security](https://github.com/snapotter-hq/SnapOtter/security), uruchamiana co tydzień + na każdy PR |
|
||||
| Przegląd zależności | Natywny GitHub | Kontrola na PR, zawodzi przy dodaniach o wysokiej wadze |
|
||||
| Audyt zależności Pythona | pip-audit | Log uruchomienia CI przy każdym push |
|
||||
| Zwolnij powiązanie tematu | Atest kanoniczny JSON + GitHub | [Wydanie GitHub](https://github.com/snapotter-hq/SnapOtter/releases) zasób: `snapotter-v{version}-release-subjects.json` |
|
||||
| Archiwum SBOM | CycloneDX i SPDX JSON | Wydanie zasobów: `snapotter-v{version}-archive-linux-{arch}-sbom.{cdx,spdx}.json` |
|
||||
| Obraz SBOM | CycloneDX i SPDX JSON | Wydanie zasobów: `snapotter-v{version}-image-linux-{arch}-sbom.{cdx,spdx}.json` |
|
||||
| Skanowanie podatności | Trivy JSON | Zwolnij zasoby z pasującymi prefiksami `archive-linux-{arch}` lub `image-linux-{arch}` |
|
||||
| Skanowanie podatności | SARIF | Zakładka [Zabezpieczenia GitHub](https://github.com/snapotter-hq/SnapOtter/security). |
|
||||
| Analiza statyczna | CodeQL (JS/TS + Python) | Karta [GitHub Security](https://github.com/snapotter-hq/SnapOtter/security), uruchamiana co tydzień + za PR |
|
||||
| Przegląd zależności | Natywny GitHub | Kontrola na PR kończy się niepowodzeniem w przypadku dodatków o dużej ważności |
|
||||
| Audyt zależności Python | pip-audit | Dziennik przebiegu CI przy każdym naciśnięciu |
|
||||
| Polityka bezpieczeństwa | Markdown | [SECURITY.md](https://github.com/snapotter-hq/SnapOtter/blob/main/SECURITY.md) w repozytorium |
|
||||
| Aktualizacje zależności | Dependabot | Zautomatyzowane cotygodniowe PR dla npm, pip, Docker, Actions |
|
||||
|
||||
**Uruchamianie własnego skanu:**
|
||||
**Uruchamianie własnego skanowania:**
|
||||
|
||||
Pobierz SBOM z wydania i przeskanuj go preferowanym narzędziem:
|
||||
Pobierz manifest tematu wydania i sprawdź, czy został on potwierdzony w przepływie pracy wydania:
|
||||
|
||||
```bash
|
||||
gh attestation verify snapotter-v2.1.0-release-subjects.json \
|
||||
--repo snapotter-hq/SnapOtter \
|
||||
--signer-workflow snapotter-hq/SnapOtter/.github/workflows/release.yml
|
||||
```
|
||||
|
||||
Manifest rejestruje oddzielnie `releaseTag`, `releaseCommit` i `workflowTriggerCommit`. Sprawdź, czy `releaseCommit` jest zatwierdzeniem usuniętym z niezmiennego znacznika, a następnie sprawdź skrót SHA-256 archiwum, obrazu, SBOM lub skanu, który wykorzystujesz, względem jego wpisu w `subjects`. To rozróżnienie jest zamierzone: sprawdzenie nowo utworzonego zatwierdzenia wydania nie zmienia tożsamości zatwierdzenia w poświadczeniu OIDC przepływu pracy.
|
||||
|
||||
Możesz także zeskanować pobrany plik SBOM lub obraz bezpośrednio:
|
||||
|
||||
```bash
|
||||
# Scan with Grype using the CycloneDX SBOM
|
||||
grype sbom:snapotter-v1.17.2-sbom.cdx.json
|
||||
grype sbom:snapotter-v2.1.0-image-linux-amd64-sbom.cdx.json
|
||||
|
||||
# Scan with Trivy using the SPDX SBOM
|
||||
trivy sbom snapotter-v1.17.2-sbom.spdx.json
|
||||
trivy sbom snapotter-v2.1.0-image-linux-amd64-sbom.spdx.json
|
||||
|
||||
# Scan the Docker image directly
|
||||
trivy image snapotter/snapotter:1.17.2
|
||||
trivy image snapotter/snapotter:2.1.0
|
||||
```
|
||||
|
||||
::: info
|
||||
SBOM i skan podatności odzwierciedlają dokładny obraz opublikowany dla tego wydania. Pakiety modeli AI zainstalowane po wdrożeniu nie są uwzględnione w SBOM, ponieważ są pobierane w czasie działania.
|
||||
::: info
|
||||
Obraz SBOMs i skany odzwierciedlają dokładnie obraz specyficzny dla architektury opublikowany dla tej wersji. Archiwum SBOMs i skany opisują wstępnie zbudowane archiwum osobno. Pakiety modelu AI zainstalowane po wdrożeniu nie są uwzględnione w tych pakietach SBOMs, ponieważ są pobierane w czasie wykonywania.
|
||||
:::
|
||||
|
||||
Reference in New Issue
Block a user