Bumps every version surface to 2.2.0, fixes a latent version-coupling bug in the OCR runtime tests, and stops an absent GPU runner from silently stalling a release. Version surfaces: scripts/sync-version.sh covers the 11 workspaces, APP_VERSION, and the docs release commands across all locales. Root package.json plus the three surfaces the script never reaches are done by hand: the DOCKERHUB.md banner and tag table, the docker-tags.md pinning table in 21 locales, and the example runtimeVersion in tools/image/ocr.md in 21 locales. The release-notes archive step is deliberately not pre-run, so the notes text stays editable until the release. Latent bug: runtime-state rejects any runtime whose compatibility.snapotterVersion is not exactly APP_VERSION, and five fixtures pinned the literal 2.1.0. Since semantic-release rewrites APP_VERSION on every release, the first PR after any bump would have gone red for a reason nobody would trace to the release. The fixtures now derive from APP_VERSION. GPU runner: sign-ocr-index needs verify-ocr-nvidia on self-hosted hardware, and the gated manifest job needs ai-bundles, so a missing runner queued instead of failing and produced no image tags. preflight-gpu-runner claims the same labels with no dependencies, so it is scheduled first and validates the GPU before the 90-minute build. An API preflight is impossible because listing self-hosted runners needs Administration:read, which GITHUB_TOKEN cannot hold, so RELEASE.md carries the maintainer-side check.
17 KiB
description, i18n_source_hash, i18n_provenance, i18n_output_hash, i18n_hash_version
| description | i18n_source_hash | i18n_provenance | i18n_output_hash | i18n_hash_version |
|---|---|---|---|---|
| Guia de hardening de segurança para o SnapOtter. Segurança de contêineres, isolamento de rede, Docker secrets, implantação em Kubernetes e artefatos de conformidade. | 9ff337fa0417 | machine | 1639c7483bb1 | 2 |
Segurança e Hardening
O SnapOtter processa arquivos inteiramente na sua infraestrutura. Ele envia analytics de produto e relatórios de falha anônimos e sem conteúdo por padrão, para ajudar a melhorar o projeto. Ele nunca envia seus arquivos, nomes de arquivos, conteúdos de arquivos, saída de OCR, metadados de imagem ou texto de documento. O feedback opcional é enviado apenas depois que um usuário o submete, apenas quando os analytics estão habilitados, e os campos de contato são incluídos somente com consentimento de contato explícito. Um administrador pode desativar a captura de analytics e feedback em um clique em Configurações > Sistema > Privacidade, sem necessidade de rebuild. O processamento de arquivos sempre permanece dentro do seu contêiner.
O contêiner roda como um usuário dedicado não-root (snapotter) com todas as capabilities do Linux removidas, exceto o conjunto mínimo necessário. Para a política completa de divulgação de vulnerabilidades e a arquitetura de segurança, veja SECURITY.md no GitHub.
Endurecimento de contêineres
Os arquivos canônicos CPU e GPU Compose são a fonte da verdade. Não copie um exemplo abreviado para produção; implante o arquivo da tag de lançamento que você verificou.
Ambas as pilhas aplicam os seguintes controles:
- Os limites de memória, swap, CPU e PID contêm processamento nativo descontrolado.
- Cada serviço elimina todos os recursos do Linux. O aplicativo adiciona de volta apenas
CHOWN, SETUID, SETGID, DAC_OVERRIDE, FOWNER, KILLpara propriedade de volume, queda de identidadegosuunidirecional e encaminhamento de sinal elegante. PostgreSQL e Redis recebem apenas o subconjunto necessário para seus pontos de entrada oficiais. security_opt: [no-new-privileges:true]evita que processos no aplicativo, PostgreSQL e contêineres Redis obtenham privilégios adicionais. Isso permanece compatível comgosu: o ponto de entrada começa como root, prepara os volumes e passa apenas para o usuáriosnapotterdedicado.- As entradas de imagem PostgreSQL e Redis são fixadas pelo resumo. O aplicativo também deve ser fixado em uma tag de lançamento verificada ou resumo, em vez de
latest. - Verificações de integridade, rotação de log JSON limitada, Redis AOF durável e política de reinicialização são definidas centralmente nos arquivos canônicos.
Para uma implantação voltada para a Internet, vincule a porta 1349 ao loopback e encerre o TLS em um proxy reverso mantido. Gere credenciais exclusivas do PostgreSQL e Redis, armazene segredos em arquivos protegidos ou em um gerenciador de segredos e altere a senha inicial do administrador imediatamente.
Por que read_only não está definido como
read_only: true não está definido porque o remapeamento PUID/PGID grava em /etc/passwd e /etc/group na inicialização. Se você usar o sinalizador --user do Docker ou Kubernetes runAsUser em vez de PUID/PGID, poderá ativar com segurança um sistema de arquivos raiz somente leitura.
Isolamento de rede
O processamento de arquivos é local, mas uma instalação padrão não é um sistema livre de saída. A análise anônima de produtos usa PostHog e os relatórios de falhas usam Sentry quando a telemetria está habilitada. Defina SNAPOTTER_TELEMETRY=0 (ou desative a análise em Configurações > Sistema > Privacidade) para desligar ambos. SnapOtter nunca inclui arquivos carregados, nomes de arquivos, saída de OCR, texto de documento ou outro conteúdo de arquivo nesses eventos.
Outro tráfego de saída é orientado por recursos: instalação de pacote/modelo de IA baixa entradas de liberação assinadas; A importação de URL busca um URL público solicitado pelo usuário; e OIDC, SAML, OpenTelemetry, webhooks, armazenamento compatível com S3 ou integrações semelhantes configurados explicitamente entram em contato com os destinos escolhidos pelo administrador. Os downloads de modelos em tempo de execução são desativados por padrão. Defina SNAPOTTER_ALLOW_MODEL_DOWNLOAD=1 somente para ativar explicitamente os downloads automáticos de fallback. Uma importação de pacote off-line pode provisionar recursos de IA sem saída do modelo de tempo de execução.
Recomendações de firewall:
| Cenário | Regra de saída |
|---|---|
| Sem ar | Defina SNAPOTTER_TELEMETRY=0 e SNAPOTTER_ALLOW_MODEL_DOWNLOAD=0, use a importação de pacote de IA off-line, desative a importação de URL e integrações externas e, em seguida, bloqueie a saída |
| Telemetria padrão | Permita os endpoints PostHog e Sentry listados pelos registros do seu navegador/rede; desativar a telemetria se a política não permitir |
| Pacotes de IA necessários | Durante a instalação, permita HTTPS para huggingface.co, *.xethub.hf.co, cdn-lfs.huggingface.co, github.com, objects.githubusercontent.com, storage.googleapis.com, pypi.org, files.pythonhosted.org; então bloqueie esses hosts |
| Integrações externas | Permitir apenas os destinos OIDC/SAML/OTLP/webhook/armazenamento de objetos exatos configurados pelo administrador |
Os arquivos de pacotes são servidos a partir do armazenamento Xet do Hugging Face, que é transferido pelos endpoints *.xethub.hf.co em paralelo e é o que torna rápidos os downloads de pacotes de vários GB. Se o seu firewall permitir huggingface.co, mas bloquear *.xethub.hf.co, as instalações ainda serão bem-sucedidas, mas voltarão para um download de fluxo único mais lento, portanto, coloque os hosts Xet na lista de permissões para permanecer no caminho rápido. Instalações totalmente off-line podem ignorar tudo isso e usar Importação de pacote off-line.
Para configuração de proxy reverso (Nginx, Traefik, Caddy, Cloudflare Tunnels), consulte o Guia de implantação.
Docker Secrets
Para implantações em produção, evite passar segredos como variáveis de ambiente em texto plano. O entrypoint oferece suporte à convenção _FILE do Docker: monte um segredo como um arquivo e defina a variável _FILE correspondente para o caminho dele.
Segredos suportados:
| Variável | 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 |
Exemplo com secrets do 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 Os secrets do Docker Compose (sem Swarm) exigem o Compose v2.23 ou posterior. :::
Implantação em Kubernetes
O entrypoint detecta quando o contêiner já está rodando como não-root (por exemplo, via runAsUser do Kubernetes) e pula a mudança de privilégio do gosu automaticamente. Nesse caso, ele não consegue fazer chown dos volumes montados por conta própria, então verifica se eles são graváveis e sai cedo com orientação acionável se não forem — veja Permissões de armazenamento para fsGroup e configurações de UID estrangeiro (TrueNAS, OpenShift).
SecurityContext de Pod recomendado:
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
Como runAsUser: 999 é definido no nível do pod, o entrypoint pula o gosu por completo. Isso permite as capabilities allowPrivilegeEscalation: false e drop: [ALL] sem conflito.
Para o dimensionamento de recursos, veja Requisitos de Hardware.
Backup e recuperação
A pilha de produção do Compose define quatro volumes. Interrompa a entrada e deixe os trabalhos ativos terminarem antes de fazer um backup coordenado para que o PostgreSQL, o Redis e o estado do arquivo descrevam o mesmo momento.
| Volume | Conteúdo | Tratamento de recuperação |
|---|---|---|
SnapOtter-pgdata |
Usuários, configurações, pipelines, trabalhos, metadados de arquivos e log de auditoria do PostgreSQL | Crítico; use um dump lógico rápido para recuperação portátil |
SnapOtter-data |
Objetos de biblioteca salvos, logs e estado de IA (/data/files, /data/logs, /data/ai, /data/ai/venv) |
Faça backup de todo o volume; para economizar espaço, omitir deliberadamente todo o estado da IA e reinstalar seus pacotes |
SnapOtter-redisdata |
Redis AOF para estado de fila BullMQ durável | Faça backup após pausar o aplicativo e forçar SAVE; necessário para retomar o trabalho na fila exatamente |
SnapOtter-workspace |
Chaves temporárias de armazenamento de objetos (/tmp/workspace/uploads, /tmp/workspace/outputs) |
Não faça backup depois que todos os trabalhos forem esgotados ou cancelados; nunca descarte-o enquanto os trabalhos estiverem ativos |
O Compose normalmente prefixa os nomes dos volumes com o nome do projeto. Resolva o volume de origem real do contêiner montado em vez de assumir que um nome de exibição como SnapOtter-data é o nome do volume do Docker.
Backup do banco de dados
Use o formato de arquivo personalizado do PostgreSQL e verifique o arquivo antes de considerar o backup completo:
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 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
Teste cada backup restaurando-o em uma pilha isolada, verificando os registros do banco de dados e as somas de verificação dos arquivos e iniciando o aplicativo. O tests/qa/backup-restore-drill.sh do repositório automatiza esse portão de liberação em relação a um QA_IMAGE explícito.
Se, em vez disso, sua plataforma tirar snapshots de volumes consistentes com falhas, interrompa a pilha inteira primeiro e faça snapshots de todos os volumes críticos como um conjunto. Uma cópia bruta do diretório de dados PostgreSQL de um contêiner em execução não é um backup lógico compatível.
Backup de arquivos e filas
Pause o aplicativo antes de capturar volumes de arquivos e filas. Use docker inspect para resolver o nome real do volume, forçar o Redis a persistir em seu estado atual e arquivar com propriedade e permissões preservadas:
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
Reinicie o Redis antes do aplicativo. Se você excluir /data/ai intencionalmente, remova toda a subárvore AI em vez de preservar um registro installed.json sem seus modelos ou ambiente virtual. Mantenha os arquivos de backup criptografados, com acesso controlado e separados do host que executa o SnapOtter.
Artefatos de conformidade
Cada versão SnapOtter inclui os seguintes artefatos de segurança:
| Artefato | Formatar | Onde encontrar |
|---|---|---|
| Liberar vinculação de assunto | Atestado canônico JSON + GitHub | Lançamento GitHub ativo: snapotter-v{version}-release-subjects.json |
| Arquivo SBOM | CycloneDX e SPDX JSON | Liberar ativos: snapotter-v{version}-archive-linux-{arch}-sbom.{cdx,spdx}.json |
| Imagem SBOM | CycloneDX e SPDX JSON | Liberar ativos: snapotter-v{version}-image-linux-{arch}-sbom.{cdx,spdx}.json |
| Verificações de vulnerabilidade | Trivy JSON | Liberar ativos com prefixos archive-linux-{arch} ou image-linux-{arch} correspondentes |
| Verificação de vulnerabilidade | SARIF | Guia Segurança GitHub |
| Análise estática | CodeQL (JS/TS + Python) | Guia Segurança GitHub, executada semanalmente + por PR |
| Revisão de dependência | GitHub nativo | Verificação por PR, falha em adições de alta gravidade |
| Auditoria de dependência Python | pip-audit | Log de execução do CI em cada push |
| Política de segurança | Markdown | SECURITY.md no repositório |
| Atualizações de dependência | Dependabot | PRs semanais automatizados para npm, pip, Docker, Actions |
Executando sua própria verificação:
Baixe o manifesto do assunto do lançamento e verifique se ele foi atestado pelo fluxo de trabalho do lançamento:
gh attestation verify snapotter-v2.2.0-release-subjects.json \
--repo snapotter-hq/SnapOtter \
--signer-workflow snapotter-hq/SnapOtter/.github/workflows/release.yml
O manifesto registra releaseTag, releaseCommit e workflowTriggerCommit separadamente. Verifique se releaseCommit é o commit retirado da tag imutável e, em seguida, verifique o resumo SHA-256 do arquivo, imagem, SBOM ou varredura que você consome em relação à sua entrada em subjects. Essa distinção é intencional: o check-out de um commit de versão recém-criado não altera a identidade do commit na credencial OIDC do fluxo de trabalho.
Você também pode digitalizar um SBOM baixado ou a imagem diretamente:
# Scan with Grype using the CycloneDX SBOM
grype sbom:snapotter-v2.2.0-image-linux-amd64-sbom.cdx.json
# Scan with Trivy using the SPDX SBOM
trivy sbom snapotter-v2.2.0-image-linux-amd64-sbom.spdx.json
# Scan the Docker image directly
trivy image snapotter/snapotter:2.2.0
::: info A imagem SBOMs e as varreduras refletem a imagem exata específica da arquitetura publicada para essa versão. O arquivo SBOMs e as varreduras descrevem o arquivo pré-construído separadamente. Os pacotes configuráveis do modelo AI instalados após a implementação não são incluídos nestes SBOMs porque são baixados no tempo de execução. :::