This page was machine-translated. Spotted a mistake?Help improve it.
Skip to content

Bezpieczeństwo i wzmacnianie

SnapOtter przetwarza pliki w całości na twojej infrastrukturze. Domyślnie wysyła anonimową, pozbawioną treści analitykę produktu i raporty o awariach, aby pomóc ulepszać projekt. Nigdy nie wysyła twoich plików, nazw plików, zawartości plików, wyniku OCR, metadanych obrazów ani tekstu dokumentów. Opcjonalna informacja zwrotna jest wysyłana dopiero po jej przesłaniu przez użytkownika, tylko gdy analityka jest włączona, a pola kontaktowe są dołączane wyłącznie za wyraźną zgodą na kontakt. Administrator może wyłączyć analitykę i zbieranie informacji zwrotnej jednym kliknięciem w Ustawienia > System > Prywatność, bez konieczności przebudowy. Przetwarzanie plików zawsze pozostaje wewnątrz twojego kontenera.

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 na GitHub.

Wzmacnianie kontenera

Domyślny docker-compose.yml zawiera produkcyjne wzmacnianie bezpieczeństwa. Oto rozbicie każdej opcji i wyjaśnienie, dlaczego ma znaczenie:

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

    # --- 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

    # --- 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

    # --- 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

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, 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ć.

Dlaczego read_only nie jest ustawione

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.

Izolacja sieci

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.

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.

Zalecenia dotyczące zapory sieciowej:

ScenariuszReguła wychodząca
Odizolowany od sieci (bez AI)Zablokuj cały ruch wychodzący z kontenera
Potrzebne pakiety AIZezwó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 AIZablokuj cały ruch wychodzący, modele są buforowane lokalnie

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.

Po konfigurację reverse proxy (Nginx, Traefik, Caddy, Tunele Cloudflare) zobacz Przewodnik wdrożenia.

Sekrety Docker

Dla wdrożeń produkcyjnych unikaj przekazywania sekretów jako zmiennych środowiskowych w postaci zwykłego tekstu. Punkt wejścia obsługuje konwencję _FILE Dockera: zamontuj sekret jako plik i ustaw odpowiednią zmienną _FILE na jego ścieżkę.

Obsługiwane sekrety:

ZmiennaOdpowiednik _FILE
DEFAULT_PASSWORDDEFAULT_PASSWORD_FILE
COOKIE_SECRETCOOKIE_SECRET_FILE
OIDC_CLIENT_SECRETOIDC_CLIENT_SECRET_FILE
S3_ACCESS_KEY_IDS3_ACCESS_KEY_ID_FILE
S3_SECRET_ACCESS_KEYS3_SECRET_ACCESS_KEY_FILE
SNAPOTTER_LICENSE_KEYSNAPOTTER_LICENSE_KEY_FILE

Przykład z sekretami Docker Compose:

yaml
services:
  SnapOtter:
    image: snapotter/snapotter:latest
    environment:
      - AUTH_ENABLED=true
      - DEFAULT_USERNAME=admin
      - DEFAULT_PASSWORD_FILE=/run/secrets/snapotter_password
      - COOKIE_SECRET_FILE=/run/secrets/cookie_secret
    secrets:
      - snapotter_password
      - cookie_secret

secrets:
  snapotter_password:
    file: ./secrets/snapotter_password.txt
  cookie_secret:
    file: ./secrets/cookie_secret.txt

TIP

Sekrety Docker Compose (bez Swarm) wymagają Compose v2.23 lub nowszego.

Wdrożenie Kubernetes

Punkt wejścia wykrywa, kiedy kontener już działa jako nie-root (np. przez runAsUser Kubernetes) i automatycznie pomija obniżenie uprawnień gosu. W takim przypadku nie może sam zmienić własności zamontowanych wolumenów przez chown, więc weryfikuje, czy są zapisywalne, i wcześnie kończy z praktycznymi wskazówkami, jeśli nie są, zobacz Uprawnienia pamięci masowej po fsGroup i konfiguracje z obcym UID (TrueNAS, OpenShift).

Zalecany SecurityContext poda:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: snapotter
spec:
  replicas: 1
  selector:
    matchLabels:
      app: snapotter
  template:
    metadata:
      labels:
        app: snapotter
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 999
        runAsGroup: 999
        fsGroup: 999
      containers:
        - name: snapotter
          image: snapotter/snapotter:latest
          ports:
            - containerPort: 1349
          securityContext:
            allowPrivilegeEscalation: false
            capabilities:
              drop: [ALL]
          resources:
            requests:
              cpu: "1"
              memory: 2Gi
            limits:
              cpu: "4"
              memory: 6Gi
          livenessProbe:
            httpGet:
              path: /api/v1/health
              port: 1349
            initialDelaySeconds: 60
            periodSeconds: 30
            timeoutSeconds: 5
          readinessProbe:
            httpGet:
              path: /api/v1/health
              port: 1349
            initialDelaySeconds: 10
            periodSeconds: 10
            timeoutSeconds: 5
          volumeMounts:
            - name: data
              mountPath: /data
            - name: workspace
              mountPath: /tmp/workspace
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: snapotter-data
        - name: workspace
          emptyDir:
            medium: Memory
            sizeLimit: 2Gi

Ponieważ runAsUser: 999 jest ustawione na poziomie poda, punkt wejścia całkowicie pomija gosu. Pozwala to na uprawnienia allowPrivilegeEscalation: false i drop: [ALL] bez konfliktu.

Po dobór rozmiaru zasobów zobacz Wymagania sprzętowe.

Kopie zapasowe i odzyskiwanie

Trwały stan jest podzielony na dwa wolumeny:

WolumenZawartośćKrytyczny?
SnapOtter-pgdataBaza danych PostgreSQL (użytkownicy, ustawienia, potoki, zadania, dziennik audytu)Tak
/data (wolumen aplikacji)Pliki przesłane przez użytkownika, modele AI, venv PythonaCzęściowo (patrz niżej)

W obrębie wolumenu /data:

ŚcieżkaZawartośćKrytyczna?
/data/uploads/, /data/outputs/Pliki użytkownika i wyniki przetwarzaniaTak
/data/ai/Pobrane pliki modeli AINie (do ponownego pobrania)
/data/venv/Wirtualne środowisko PythonaNie (odbudowywane przy starcie)

Kopia zapasowa bazy danych

Użyj pg_dump, aby wykonać kopię zapasową bazy danych podczas działania stosu:

bash
# Dump the database
docker exec SnapOtter-postgres pg_dump -U snapotter snapotter > backup.sql

# Restore into a fresh database
cat backup.sql | docker exec -i SnapOtter-postgres psql -U snapotter snapotter

Alternatywnie zatrzymaj stos i wykonaj migawkę wolumenu SnapOtter-pgdata:

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 .

Kopia zapasowa plików użytkownika

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.

Artefakty zgodności

Każde wydanie SnapOtter zawiera następujące artefakty bezpieczeństwa:

ArtefaktFormatGdzie go znaleźć
SBOM (CycloneDX)JSONZasób Wydania GitHub: snapotter-v{version}-sbom.cdx.json
SBOM (SPDX)JSONZasób Wydania GitHub: snapotter-v{version}-sbom.spdx.json
Skan podatnościTrivy JSONZasób Wydania GitHub: snapotter-v{version}-trivy.json
Skan podatnościSARIFZakładka GitHub Security
Analiza statycznaCodeQL (JS/TS + Python)Zakładka GitHub Security, uruchamiana co tydzień + na każdy PR
Przegląd zależnościNatywny GitHubKontrola na PR, zawodzi przy dodaniach o wysokiej wadze
Audyt zależności Pythonapip-auditLog uruchomienia CI przy każdym push
Polityka bezpieczeństwaMarkdownSECURITY.md w repozytorium
Aktualizacje zależnościDependabotZautomatyzowane cotygodniowe PR dla npm, pip, Docker, Actions

Uruchamianie własnego skanu:

Pobierz SBOM z wydania i przeskanuj go preferowanym narzędziem:

bash
# Scan with Grype using the CycloneDX SBOM
grype sbom:snapotter-v1.17.2-sbom.cdx.json

# Scan with Trivy using the SPDX SBOM
trivy sbom snapotter-v1.17.2-sbom.spdx.json

# Scan the Docker image directly
trivy image snapotter/snapotter:1.17.2

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.