Files
SnapOtter/apps/docs/ja/guide/security.md
T
SnapOtterandGitHub 5f21588f6c chore: prepare the 2.2.0 release (#660)
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.
2026-07-27 22:09:31 +08:00

20 KiB
Raw Blame History

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 6082010b0b69 2

セキュリティと強化

SnapOtter はファイルを完全にあなたのインフラ上で処理します。プロジェクトの改善に役立てるため、デフォルトで匿名かつ内容を含まないプロダクトアナリティクスとクラッシュレポートを送信します。あなたのファイル、ファイル名、ファイルの内容、OCR の出力、画像のメタデータ、ドキュメントのテキストを送信することは決してありません。任意のフィードバックは、ユーザーが送信したときにのみ、かつアナリティクスが有効な場合にのみ送信され、連絡先フィールドは明示的な連絡先の同意がある場合にのみ含まれます。管理者は Settings > System > Privacy でアナリティクスとフィードバック収集をワンクリックでオフにでき、リビルドは不要です。ファイル処理は常にコンテナ内に留まります。

コンテナは専用の非 root ユーザー(snapotter)として実行され、必要最小限のセットを除くすべての Linux capability が削除されています。完全な脆弱性開示ポリシーとセキュリティアーキテクチャについては、GitHub の SECURITY.md を参照してください。

コンテナの強化

正規の CPU および GPU Compose ファイルが信頼できる情報源です。短縮された例を運用環境にコピーしないでください。確認したリリース タグからファイルをデプロイします。

どちらのスタックも次の制御を適用します。

  • メモリ、スワップ、CPU、および PID の制限には、暴走したネイティブ処理が含まれています。
  • すべてのサービスで、すべての Linux 機能が削除されます。アプリケーションは、ボリューム所有権、一方向 gosu ID ドロップ、および正常な信号転送のために CHOWN, SETUID, SETGID, DAC_OVERRIDE, FOWNER, KILL のみを追加し直します。 PostgreSQL と Redis は、公式エントリポイントに必要なサブセットのみを受け取ります。
  • security_opt: [no-new-privileges:true] は、アプリケーション、PostgreSQL、および Redis コンテナー内のプロセスが追加の権限を取得できないようにします。これは gosu との互換性を維持します。エントリポイントは root として開始され、ボリュームを準備し、専用の snapotter ユーザーにのみドロップされます。
  • PostgreSQL および Redis のイメージ入力はダイジェストによって固定されます。同様に、アプリケーションは latest ではなく、検証済みのリリース タグまたはダイジェストに固定する必要があります。
  • ヘルスチェック、制限された JSON ログローテーション、永続的な Redis AOF、および再起動ポリシーは、正規ファイルで一元的に定義されます。

インターネットに接続する展開の場合は、ポート 1349 をループバックにバインドし、維持されているリバース プロキシで TLS を終了します。一意の PostgreSQL および Redis 認証情報を生成し、保護されたファイルまたはシークレット マネージャーにシークレットを保存し、初期管理者パスワードをすぐに変更します。

read_only が {#why-read-only-is-not-set} に設定されない理由

PUID/PGID の再マッピングにより起動時に /etc/passwd および /etc/group に書き込まれるため、read_only: true は設定されません。 PUID/PGID の代わりに Docker の --user フラグまたは Kubernetes runAsUser を使用すると、読み取り専用のルート ファイルシステムを安全に有効にすることができます。

ネットワーク分離

ファイル処理はローカルですが、デフォルトのインストールは出力のないシステムではありません。テレメトリが有効な場合、匿名の製品分析では PostHog が使用され、クラッシュ レポートでは Sentry が使用されます。 SNAPOTTER_TELEMETRY=0 を設定 (または [設定] > [システム] > [プライバシー] で分析を無効に) して両方をオフにします。 SnapOtter は、アップロードされたファイル、ファイル名、OCR 出力、ドキュメント テキスト、またはその他のファイル コンテンツをこれらのイベントに含めることはありません。

その他のアウトバウンド トラフィックは機能主導型です。AI バンドル/モデルのインストールは、署名されたリリース入力をダウンロードします。 URL インポートでは、ユーザーが要求したパブリック URL を取得します。明示的に構成された OIDC、SAML、OpenTelemetry、Webhook、S3 互換ストレージ、または同様の統合は、管理者が選択した宛先に接続します。ランタイムでのモデルのダウンロードはデフォルトで無効です。自動フォールバック ダウンロードを明示的に有効にする場合にのみ、SNAPOTTER_ALLOW_MODEL_DOWNLOAD=1 を設定してください。オフライン バンドル インポート は、ランタイム モデルの出力なしで AI 機能をプロビジョニングできます。

ファイアウォールの推奨事項:

シナリオ アウトバウンドルール
エアギャップ SNAPOTTER_TELEMETRY=0SNAPOTTER_ALLOW_MODEL_DOWNLOAD=0 を設定し、オフライン AI バンドル インポートを使用し、URL インポートと外部統合を無効にして、下りをブロックします
デフォルトのテレメトリ ブラウザ/ネットワーク ログにリストされている PostHog および Sentry エンドポイントを許可します。ポリシーで許可されていない場合はテレメトリを無効にする
AI バンドルが必要 インストール中に、huggingface.co, *.xethub.hf.co, cdn-lfs.huggingface.co, github.com, objects.githubusercontent.com, storage.googleapis.com, pypi.org, files.pythonhosted.org への HTTPS を許可します。次にそれらのホストをブロックします
外部統合 管理者が正確に構成した OIDC/SAML/OTLP/webhook/object-storage 宛先のみを許可します

バンドル アーカイブは、Hugging Face の Xet ストレージから提供されます。これにより、*.xethub.hf.co エンドポイント経由で並行して転送され、複数 GB のバンドルのダウンロードが高速になります。ファイアウォールで huggingface.co は許可されるが *.xethub.hf.co はブロックされる場合でも、インストールは成功しますが、より遅い単一ストリーム ダウンロードにフォールバックするため、高速パス上に留まるように Xet ホストをホワイトリストに登録します。完全なオフライン インストールでは、これをすべてスキップし、代わりに オフライン バンドル インポート を使用できます。

リバース プロキシ構成 (Nginx、Traefik、Caddy、Cloudflare トンネル) については、導入ガイド を参照してください。

Docker シークレット

本番デプロイでは、シークレットを平文の環境変数として渡すことは避けてください。エントリーポイントは Docker の _FILE 規約をサポートしています。シークレットをファイルとしてマウントし、対応する _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 として実行されている(たとえば Kubernetes の runAsUser 経由)ことを検出し、gosu の権限降格を自動的にスキップします。その場合、自らマウントされたボリュームを chown できないため、書き込み可能かどうかを検証し、書き込めない場合は実行可能なガイダンスを出して早期に終了します。fsGroup や外来 UID の構成(TrueNAS、OpenShift)については ストレージの権限 を参照してください。

推奨される Pod の 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 が Pod レベルで設定されているため、エントリーポイントは gosu を完全にスキップします。これにより、allowPrivilegeEscalation: falsedrop: [ALL] の capability を競合なしに使えます。

リソースのサイジングについては、ハードウェア要件 を参照してください。

バックアップとリカバリ

実稼働 Compose スタックは 4 つのボリュームを定義します。 PostgreSQL、Redis、およびファイルの状態が同じ時点を表すように、調整されたバックアップを取得する前にイングレスを停止し、アクティブなジョブを終了させます。

音量 コンテンツ 回復治療
SnapOtter-pgdata PostgreSQL ユーザー、設定、パイプライン、ジョブ、ファイル メタデータ、監査ログ 致命的;ポータブルリカバリにフェイルファスト論理ダンプを使用する
SnapOtter-data 保存されたライブラリ オブジェクト、ログ、および AI 状態 (/data/files, /data/logs, /data/ai, /data/ai/venv) ボリューム全体をバックアップします。スペースを節約するために、すべての AI 状態を意図的に省略し、そのバンドルを再インストールします
SnapOtter-redisdata 耐久性のある BullMQ キュー状態のための Redis AOF アプリを一時停止して SAVE を強制した後、バックアップします。キューに入れられた作業を正確に再開するために必要
SnapOtter-workspace 一時オブジェクトストレージキー (/tmp/workspace/uploads, /tmp/workspace/outputs) すべてのジョブがドレインまたはキャンセルされた後はバックアップしないでください。ジョブがアクティブな間は決して破棄しないでください

Compose は通常、ボリューム名の前にプロジェクト名を付けます。 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 に対するゲートの解放を自動化します。

代わりにプラットフォームがクラッシュ整合性ボリューム スナップショットを取得する場合は、最初にスタック全体を停止し、すべての重要なボリュームを 1 つのセットとしてスナップショットします。実行中のコンテナからの生の 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 を意図的に除外する場合は、モデルや仮想環境なしで installed.json レコードを保存するのではなく、AI サブツリー全体を削除します。バックアップ ファイルは暗号化され、アクセスが制御され、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 セキュリティ タブ、毎週 + PR ごとに実行
依存関係のレビュー GitHub ネイティブ PR ごとのチェック、重大度の高い追加で失敗する
Python 依存関係の監査 pip-audit プッシュごとの CI 実行ログ
セキュリティポリシー Markdown リポジトリ内の SECURITY.md
依存関係の更新 Dependabot npm、pip、Docker、アクションの自動化された毎週の PR

独自のスキャンを実行する:

リリース対象のマニフェストをダウンロードし、それがリリース ワークフローによって証明されていることを確認します。

gh attestation verify snapotter-v2.2.0-release-subjects.json \
  --repo snapotter-hq/SnapOtter \
  --signer-workflow snapotter-hq/SnapOtter/.github/workflows/release.yml

マニフェストには、releaseTagreleaseCommit、および workflowTriggerCommit が個別に記録されます。 releaseCommit が不変タグから剥がされたコミットであることを確認してから、使用したアーカイブ、イメージ、SBOM、またはスキャンの SHA-256 ダイジェストを subjects のエントリと照合して確認します。この区別は意図的なものです。新しく作成されたリリース コミットをチェックアウトしても、ワークフローの OIDC 資格情報のコミット ID は変更されません。

ダウンロードした 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 には含まれません。 :::