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.
24 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 |
|---|---|---|---|---|
| Посібник із посилення безпеки для SnapOtter. Безпека контейнерів, мережева ізоляція, секрети Docker, розгортання в Kubernetes та артефакти відповідності. | 9ff337fa0417 | machine | 09a77c679598 | 2 |
Безпека та посилення
SnapOtter обробляє файли повністю на вашій інфраструктурі. Він за замовчуванням надсилає анонімну продуктову аналітику й звіти про збої без вмісту, щоб допомогти покращити проєкт. Він ніколи не надсилає ваші файли, імена файлів, вміст файлів, вивід OCR, метадані зображень чи текст документів. Необов'язковий відгук надсилається лише після того, як користувач його подасть, лише коли аналітика увімкнена, а контактні поля включаються лише за явної згоди на контакт. Адміністратор може вимкнути збір аналітики й відгуків одним кліком у Settings > System > Privacy, без потреби у повторному збиранні. Обробка файлів завжди залишається всередині вашого контейнера.
Контейнер працює від імені виділеного не-root користувача (snapotter) з усіма скиненими можливостями Linux, окрім мінімального необхідного набору. Щодо повної політики розкриття вразливостей і архітектури безпеки див. SECURITY.md на GitHub.
Зміцнення контейнера
Канонічні файли Compose CPU і GPU є джерелом правди. Не копіюйте скорочений приклад у виробництво; розгорнути файл із тегу випуску, який ви перевірили.
Обидва стеки застосовують такі елементи керування:
- Обмеження пам'яті, підкачки, процесора та PID містять нестандартну власну обробку.
- Кожна служба втрачає всі можливості Linux. Додаток додає лише
CHOWN, SETUID, SETGID, DAC_OVERRIDE, FOWNER, KILLдля володіння томом, одностороннє видалення ідентифікаційної інформаціїgosuі витончене пересилання сигналу. PostgreSQL і Redis отримують лише ту підмножину, яку потребують їхні офіційні точки входу.
— security_opt: [no-new-privileges:true] не дозволяє процесам у контейнерах програми, PostgreSQL і Redis отримати додаткові привілеї. Це залишається сумісним із gosu: точка входу починається від імені root, готує томи та передається лише виділеному користувачеві snapotter.
— Вхідні дані зображень PostgreSQL і Redis закріплені дайджестом. Програму також слід прикріпити до тегу перевіреного випуску або дайджесту, а не до latest.
— Перевірки працездатності, обмежена ротація журналів JSON, надійний Redis AOF і політика перезапуску визначаються централізовано в канонічних файлах.
Для розгортання з підключенням до Інтернету прив’яжіть порт 1349 до loopback і завершіть TLS на підтримуваному зворотному проксі-сервері. Створюйте унікальні облікові дані PostgreSQL і Redis, зберігайте секрети в захищених файлах або в менеджері секретів і негайно змінюйте початковий пароль адміністратора.
Чому read_only не встановлено
read_only: true не встановлено, оскільки перевідповідання PUID/PGID записує в /etc/passwd і /etc/group під час запуску. Якщо ви використовуєте прапор Docker --user або Kubernetes runAsUser замість PUID/PGID, ви можете безпечно ввімкнути кореневу файлову систему лише для читання.
Ізоляція мережі
Обробка файлів є локальною, але інсталяція за замовчуванням не є системою без виходу. Анонімна аналітика продуктів використовує PostHog, а звіти про збої використовують Sentry, якщо ввімкнено телеметрію. Встановіть SNAPOTTER_TELEMETRY=0 (або вимкніть аналітику в меню «Налаштування» > «Система» > «Конфіденційність»), щоб вимкнути обидва параметри. SnapOtter ніколи не включає в ці події завантажені файли, імена файлів, вихід OCR, текст документа чи інший вміст файлу.
Інший вихідний трафік керується функціями: інсталяція комплекту/моделі штучного інтелекту завантажує підписані вхідні дані випуску; Імпорт URL-адреси отримує загальнодоступну URL-адресу, яку запитує користувач; і явно налаштовані OIDC, SAML, OpenTelemetry, веб-хуки, S3-сумісне сховище або подібні інтеграції зв’язуються з пунктами призначення, вибраними адміністратором. Завантаження моделей під час виконання за замовчуванням вимкнено. Установіть SNAPOTTER_ALLOW_MODEL_DOWNLOAD=1 лише для явного ввімкнення автоматичних резервних завантажень. Офлайн-пакет імпорту може надавати функції AI без виходу моделі середовища виконання.
Рекомендації щодо брандмауера:
| Сценарій | Вихідне правило |
|---|---|
| З повітряним проміжком | Встановіть SNAPOTTER_TELEMETRY=0 і SNAPOTTER_ALLOW_MODEL_DOWNLOAD=0, використовуйте офлайн-імпорт пакетів AI, вимкніть імпорт URL-адрес і зовнішню інтеграцію, а потім заблокуйте вихід |
| Телеметрія за замовчуванням | Дозволити кінцеві точки PostHog і Sentry, указані в журналах вашого браузера/мережі; вимкнути телеметрію, якщо політика не дозволяє їх |
| Потрібні пакети AI | Під час встановлення дозвольте HTTPS до huggingface.co, *.xethub.hf.co, cdn-lfs.huggingface.co, github.com, objects.githubusercontent.com, storage.googleapis.com, pypi.org, files.pythonhosted.org; потім заблокуйте ці хости |
| Зовнішні інтеграції | Дозволити лише точні призначення OIDC/SAML/OTLP/webhook/object-storage, налаштовані адміністратором |
Архіви пакетів обслуговуються зі сховища Xet Hugging Face, яке паралельно передається через кінцеві точки *.xethub.hf.co і завдяки чому швидко завантажуються пакети на кілька ГБ. Якщо ваш брандмауер дозволяє huggingface.co, але блокує *.xethub.hf.co, встановлення все одно вдасться, але повернеться до повільнішого однопотокового завантаження, тому внесіть хости Xet у білий список, щоб залишатися на швидкому шляху. Повністю автономна інсталяція може пропустити все це та використовувати Offline Bundle Import.
Для конфігурації зворотного проксі (Nginx, Traefik, Caddy, Cloudflare Tunnels) див. Посібник із розгортання.
Секрети Docker
Для продакшн-розгортань уникайте передавання секретів як звичайних текстових змінних середовища. Точка входу підтримує конвенцію _FILE Docker: змонтуйте секрет як файл і встановіть відповідну змінну _FILE на її шлях.
Підтримувані секрети:
| Змінна | Еквівалент _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 |
Приклад із секретами 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 Секрети Docker Compose (без Swarm) потребують Compose v2.23 або новіше. :::
Розгортання в Kubernetes
Точка входу виявляє, коли контейнер уже працює від імені не-root (наприклад, через runAsUser Kubernetes), і автоматично пропускає скидання привілеїв gosu. У цьому разі вона не може сама змінити власника змонтованих томів, тож перевіряє, чи вони доступні для запису, і достроково виходить з дієвими вказівками, якщо ні — див. Права доступу до сховища щодо fsGroup і налаштувань із чужим UID (TrueNAS, OpenShift).
Рекомендований SecurityContext поду:
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
Оскільки runAsUser: 999 встановлено на рівні поду, точка входу повністю пропускає gosu. Це дозволяє можливості allowPrivilegeEscalation: false і drop: [ALL] без конфлікту.
Щодо підбору ресурсів див. Вимоги до апаратного забезпечення.
Резервне копіювання та відновлення
Виробничий стек Compose визначає чотири томи. Зупиніть вхід і дайте активним завданням завершитися, перш ніж виконувати координоване резервне копіювання, щоб PostgreSQL, Redis і стан файлу описували той самий момент часу.
| Обсяг | Зміст | Відновлювальне лікування |
|---|---|---|
SnapOtter-pgdata |
Користувачі PostgreSQL, налаштування, конвеєри, завдання, метадані файлів і журнал аудиту | Критичний; використовуйте швидкий логічний дамп для портативного відновлення |
SnapOtter-data |
Збережені бібліотечні об’єкти, журнали та стан AI (/data/files, /data/logs, /data/ai, /data/ai/venv) |
Створіть резервну копію всього тому; щоб заощадити місце, навмисно пропустіть усі стани ШІ та перевстановіть його комплекти |
SnapOtter-redisdata |
Redis AOF для тривалого стану черги BullMQ | Резервне копіювання після призупинення програми та примусового запуску SAVE; необхідні для точного відновлення роботи в черзі |
SnapOtter-workspace |
Тимчасові ключі зберігання об’єктів (/tmp/workspace/uploads, /tmp/workspace/outputs) |
Не створюйте резервну копію після того, як усі завдання вичерпано або скасовано; ніколи не викидайте його, поки завдання активні |
Компонувати зазвичай префікси імен томів із назвою проекту. Розділіть реальний вихідний том із підключеного контейнера замість того, щоб припускати, що відображуване ім’я, наприклад SnapOtter-data, є ім’ям тому Docker.
Резервне копіювання бази даних
Використовуйте спеціальний формат архіву PostgreSQL і перевірте архів, перш ніж розглядати резервну копію як завершену:
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
Перевірте кожну резервну копію, відновивши її в ізольований стек, перевіривши записи бази даних і контрольні суми файлів і запустивши програму. tests/qa/backup-restore-drill.sh репозиторію автоматизує цей шлюз випуску проти явного QA_IMAGE.
Якщо натомість ваша платформа робить миттєві знімки томів, що відповідають збоям, спочатку зупиніть увесь стек і зробіть миттєві знімки всіх критичних томів як один набір. Необроблена копія каталогу даних PostgreSQL із запущеного контейнера не є підтримуваною логічною резервною копією.
Резервне копіювання файлів і черги
Призупиніть програму перед захопленням томів файлів і черги. Використовуйте docker inspect, щоб розпізнати фактичну назву тому, змусити Redis зберегти поточний стан і архівувати зі збереженням права власності та дозволів:
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
Перезапустіть Redis перед програмою. Якщо ви навмисно виключаєте /data/ai, видаліть усе піддерево AI, а не зберігайте запис installed.json без його моделей або віртуального середовища. Зберігайте файли резервних копій у зашифрованому вигляді, з контрольованим доступом і окремо від хоста, на якому запущено SnapOtter.
Артефакти відповідності
Кожен випуск SnapOtter містить такі артефакти безпеки:
| Артефакт | Формат | Де його знайти |
|---|---|---|
| Звільнити предметну прив'язку | Канонічна атестація JSON + GitHub | Випуск GitHub актив: snapotter-v{version}-release-subjects.json |
| Архів SBOM | CycloneDX і SPDX JSON | Випустити активи: snapotter-v{version}-archive-linux-{arch}-sbom.{cdx,spdx}.json |
| Зображення SBOM | CycloneDX і SPDX JSON | Випустити активи: snapotter-v{version}-image-linux-{arch}-sbom.{cdx,spdx}.json |
| Сканування вразливостей | Trivy JSON | Випустіть активи з відповідними префіксами archive-linux-{arch} або image-linux-{arch} |
| Сканування вразливостей | SARIF | Вкладка Безпека GitHub. |
| Статичний аналіз | CodeQL (JS/TS + Python) | Вкладка GitHub Security, запускається щотижня + за PR |
| Огляд залежності | GitHub рідний | Перевірка за PR, не вдається виконати додавання високої серйозності |
| Аудит залежностей Python | pip-audit | Журнал запуску CI під час кожного натискання |
| Політика безпеки | Markdown | SECURITY.md у сховищі |
| Оновлення залежностей | Dependabot | Автоматизовані щотижневі PR для npm, pip, Docker, Actions |
Запуск власного сканування:
Завантажте маніфест теми випуску та переконайтеся, що він підтверджений робочим процесом випуску:
gh attestation verify snapotter-v2.2.0-release-subjects.json \
--repo snapotter-hq/SnapOtter \
--signer-workflow snapotter-hq/SnapOtter/.github/workflows/release.yml
У маніфесті окремо записуються releaseTag, releaseCommit і workflowTriggerCommit. Переконайтеся, що releaseCommit є комітом, видаленим із незмінного тегу, а потім перевірте дайджест SHA-256 архіву, зображення, SBOM або сканування, який ви використовуєте, на його запис у subjects. Ця відмінність є навмисною: перевірка щойно створеного коміту випуску не змінює ідентифікатор коміту в облікових даних OIDC робочого циклу.
Ви також можете сканувати завантажений SBOM або безпосередньо зображення:
# 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 Зображення SBOMs і скановані зображення відображають точне зображення конкретної архітектури, опубліковане для цього випуску. Архів SBOMs і скани описують попередньо зібраний архів окремо. Комплекти моделей AI, встановлені після розгортання, не включені в ці SBOMs, оскільки вони завантажуються під час виконання. :::