Files
SnapOtter/apps/docs/it/guide/security.md
T
SnapOtterandGitHub fe85dd2b98 docs(guide): add a low-resource deployment guide in 21 languages (#548)
New guide/low-resource page: what runs well on 2 GB machines, a Raspberry Pi / old laptop Compose walkthrough with tuned caps, the env-var knobs that matter on small hardware, and what to skip. Linked from getting-started, the deployment hardware section, and the sidebar. Translated into all 20 non-English locales via the i18n batch pipeline; parity check and VitePress build pass.

Admin merge: docs-only PR, the path-filtered required integration contexts never report (#420 precedent).

Closes #497
2026-07-17 00:40:48 +08:00

14 KiB

description, i18n_source_hash, i18n_provenance, i18n_output_hash
description i18n_source_hash i18n_provenance i18n_output_hash
Guida al rafforzamento della sicurezza per SnapOtter. Sicurezza del container, isolamento di rete, Docker secrets, distribuzione Kubernetes e artefatti di conformità. 986f7658430c human 24c63a8f7f16

Sicurezza e rafforzamento

SnapOtter elabora i file interamente sulla tua infrastruttura. Invia analytics di prodotto anonime e prive di contenuti e report di crash per impostazione predefinita, per aiutare a migliorare il progetto. Non invia mai i tuoi file, i nomi dei file, il contenuto dei file, l'output OCR, i metadati delle immagini o il testo dei documenti. Il feedback facoltativo viene inviato solo dopo che un utente lo ha inviato, solo quando le analytics sono abilitate, e i campi di contatto sono inclusi solo con esplicito consenso al contatto. Un amministratore può disattivare la raccolta di analytics e feedback con un solo clic in Impostazioni > Sistema > Privacy, senza bisogno di ricostruzione. L'elaborazione dei file resta sempre all'interno del tuo container.

Il container gira come utente non-root dedicato (snapotter) con tutte le capacità Linux rimosse tranne il set minimo richiesto. Per la policy completa di divulgazione delle vulnerabilità e l'architettura di sicurezza, vedi SECURITY.md su GitHub.

Rafforzamento del container

Il docker-compose.yml predefinito include il rafforzamento della sicurezza per la produzione. Ecco una descrizione di ciascuna opzione e del perché è importante:

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:

Perché no-new-privileges non è impostato

security_opt: [no-new-privileges:true] è volutamente omesso. L'entrypoint parte come root per correggere la proprietà dei volumi, poi scende all'utente snapotter tramite gosu, che richiede setuid. Una volta completata la riduzione dei privilegi, il processo gira come snapotter con tutte le capacità rimosse tranne le cinque elencate sopra.

Se usi Kubernetes o il flag --user di Docker per eseguire direttamente come non-root (bypassando gosu), no-new-privileges può essere abilitato in sicurezza.

Perché read_only non è impostato

read_only: true non è impostato perché il rimappaggio PUID/PGID scrive su /etc/passwd e /etc/group all'avvio. Se usi il flag --user di Docker o runAsUser di Kubernetes invece di PUID/PGID, puoi abilitare in sicurezza un filesystem root di sola lettura.

Isolamento di rete

Durante il normale funzionamento, il container effettua zero connessioni di rete in uscita. Tutta l'elaborazione dei file avviene localmente usando librerie integrate.

Browser  -->  Reverse Proxy (TLS)  -->  SnapOtter container  -->  (nothing)

L'unica eccezione sono i download dei modelli AI: quando un utente installa un bundle di funzionalità AI tramite l'interfaccia, il container scarica l'archivio del bundle pre-costruito da Hugging Face, più alcuni singoli file di modelli da GitHub Releases, Google Storage e PyPI. Questi download avvengono una volta per bundle e sono memorizzati nel volume /data.

Raccomandazioni sul firewall:

Scenario Regola in uscita
Air-gapped (senza AI) Blocca tutto il traffico in uscita dal container
Bundle AI necessari Consenti HTTPS verso huggingface.co, *.xethub.hf.co, cdn-lfs.huggingface.co, github.com, objects.githubusercontent.com, storage.googleapis.com, pypi.org, files.pythonhosted.org durante l'installazione, poi blocca
Dopo l'installazione AI Blocca tutto il traffico in uscita, i modelli sono memorizzati nella cache locale

Gli archivi dei bundle vengono serviti dall'archiviazione Xet di Hugging Face, che trasferisce in parallelo attraverso gli endpoint *.xethub.hf.co ed è ciò che rende veloci i download di bundle da diversi GB. Se il tuo firewall consente huggingface.co ma blocca *.xethub.hf.co, le installazioni riescono comunque ma ripiegano su un download a flusso singolo più lento, quindi metti in allowlist gli host Xet per restare sul percorso veloce. Le installazioni completamente offline possono saltare tutto questo e usare invece l'Importazione di bundle offline.

Per la configurazione del reverse proxy (Nginx, Traefik, Caddy, Cloudflare Tunnels), vedi la guida alla distribuzione.

Docker Secrets

Per le distribuzioni in produzione, evita di passare i segreti come variabili d'ambiente in chiaro. L'entrypoint supporta la convenzione _FILE di Docker: monta un segreto come file e imposta la corrispondente variabile _FILE sul suo percorso.

Segreti supportati:

Variabile Equivalente _FILE
DEFAULT_PASSWORD DEFAULT_PASSWORD_FILE
COOKIE_SECRET COOKIE_SECRET_FILE
OIDC_CLIENT_SECRET OIDC_CLIENT_SECRET_FILE
S3_ACCESS_KEY_ID S3_ACCESS_KEY_ID_FILE
S3_SECRET_ACCESS_KEY S3_SECRET_ACCESS_KEY_FILE
SNAPOTTER_LICENSE_KEY SNAPOTTER_LICENSE_KEY_FILE

Esempio con i secrets di Docker Compose:

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 I secrets di Docker Compose (senza Swarm) richiedono Compose v2.23 o successivo. :::

Distribuzione Kubernetes

L'entrypoint rileva quando il container è già in esecuzione come non-root (ad es. tramite runAsUser di Kubernetes) e salta automaticamente la riduzione dei privilegi gosu. In quel caso non può fare il chown dei volumi montati da solo, quindi verifica che siano scrivibili ed esce subito con indicazioni pratiche se non lo sono, vedi Permessi di archiviazione per fsGroup e le configurazioni con UID estraneo (TrueNAS, OpenShift).

SecurityContext del pod consigliato:

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

Poiché runAsUser: 999 è impostato a livello di pod, l'entrypoint salta del tutto gosu. Questo consente le capacità allowPrivilegeEscalation: false e drop: [ALL] senza conflitti.

Per il dimensionamento delle risorse, vedi Requisiti hardware.

Backup e ripristino

Lo stato persistente è suddiviso su due volumi:

Volume Contenuti Critico?
SnapOtter-pgdata Database PostgreSQL (utenti, impostazioni, pipeline, job, log di audit)
/data (volume app) File caricati dagli utenti, modelli AI, venv Python Parzialmente (vedi sotto)

All'interno del volume /data:

Percorso Contenuti Critico?
/data/uploads/, /data/outputs/ File utente e risultati di elaborazione
/data/ai/ File di modelli AI scaricati No (riscaricabili)
/data/venv/ Ambiente virtuale Python No (ricostruito all'avvio)

Backup del database

Usa pg_dump per eseguire il backup del database mentre lo stack è in esecuzione:

# 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

In alternativa, ferma lo stack e crea uno snapshot del volume SnapOtter-pgdata:

docker compose down
docker run --rm -v SnapOtter-pgdata:/data -v $(pwd)/backup:/backup \
  alpine tar czf /backup/snapotter-pgdata.tar.gz -C /data .

Backup dei file utente

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

I modelli AI ammontano fino a circa 24 GB su tutti i bundle. Poiché sono riscaricabili, escludi /data/ai/ e /data/venv/ dai backup per risparmiare spazio. Solo il database e i file utente sono critici.

Artefatti di conformità

Ogni release di SnapOtter include i seguenti artefatti di sicurezza:

Artefatto Formato Dove trovarlo
SBOM (CycloneDX) JSON Asset della release GitHub: snapotter-v{version}-sbom.cdx.json
SBOM (SPDX) JSON Asset della release GitHub: snapotter-v{version}-sbom.spdx.json
Scansione delle vulnerabilità Trivy JSON Asset della release GitHub: snapotter-v{version}-trivy.json
Scansione delle vulnerabilità SARIF Scheda GitHub Security
Analisi statica CodeQL (JS/TS + Python) Scheda GitHub Security, eseguita settimanalmente + per PR
Revisione delle dipendenze Nativa di GitHub Controllo per PR, fallisce sulle aggiunte ad alta gravità
Audit delle dipendenze Python pip-audit Log di esecuzione CI a ogni push
Policy di sicurezza Markdown SECURITY.md nel repository
Aggiornamenti delle dipendenze Dependabot PR settimanali automatiche per npm, pip, Docker, Actions

Eseguire la tua scansione:

Scarica l'SBOM dalla release e scansionalo con lo strumento che preferisci:

# 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 L'SBOM e la scansione delle vulnerabilità riflettono l'immagine esatta pubblicata per quella release. I bundle di modelli AI installati dopo la distribuzione non sono inclusi nell'SBOM poiché vengono scaricati a runtime. :::