Files
SnapOtter/apps/docs/ar/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

21 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 dd2ace57860c 2

الأمان والتعزيز

يعالج SnapOtter الملفات بالكامل على بنيتك التحتية. يرسل تحليلات منتج مجهولة وخالية من المحتوى وتقارير أعطال افتراضيًا للمساعدة في تحسين المشروع. لا يرسل أبدًا ملفاتك، ولا أسماء ملفاتك، ولا محتويات ملفاتك، ولا مخرجات OCR، ولا بيانات الصور الوصفية، ولا نص المستندات. تُرسَل الملاحظات الاختيارية فقط بعد أن يقدّمها المستخدم، وفقط عند تفعيل التحليلات، وتُضمَّن حقول جهة الاتصال فقط مع موافقة صريحة على الاتصال. يمكن للمسؤول إيقاف التحليلات والتقاط الملاحظات بنقرة واحدة ضمن Settings > System > Privacy، دون الحاجة لإعادة بناء. تبقى معالجة الملفات دائمًا داخل حاويتك.

تعمل الحاوية كمستخدم مخصص غير root (snapotter) مع إسقاط جميع قدرات Linux باستثناء المجموعة الدنيا المطلوبة. للاطلاع على سياسة الإفصاح الكاملة عن الثغرات ومعمارية الأمان، راجع SECURITY.md على GitHub.

تصلب الحاويات

تعد ملفات الإنشاء الأساسية CPU وGPU مصدر الحقيقة. لا تنسخ مثالًا مختصرًا إلى الإنتاج؛ انشر الملف من علامة الإصدار التي قمت بالتحقق منها.

تطبق كلا المجموعتين عناصر التحكم التالية:

  • تحتوي حدود الذاكرة والمبادلة ووحدة المعالجة المركزية وPID على معالجة أصلية هاربة.
  • كل خدمة تسقط جميع إمكانيات Linux. يضيف التطبيق CHOWN, SETUID, SETGID, DAC_OVERRIDE, FOWNER, KILL فقط لملكية الحجم، وإسقاط هوية gosu أحادية الاتجاه، وإعادة توجيه الإشارة بسلاسة. يتلقى PostgreSQL وRedis فقط المجموعة الفرعية التي تحتاجها نقاط الدخول الرسمية الخاصة بهم.
  • يمنع security_opt: [no-new-privileges:true] العمليات في حاويات التطبيق وPostgreSQL وRedis من الحصول على امتيازات إضافية. يظل هذا متوافقًا مع gosu: تبدأ نقطة الإدخال كجذر، وتقوم بإعداد وحدات التخزين، ولا تصل إلا إلى مستخدم snapotter المخصص.
  • يتم تثبيت مدخلات صور PostgreSQL وRedis بواسطة الملخص. يجب أيضًا تثبيت التطبيق على علامة إصدار أو ملخص تم التحقق منه بدلاً من latest.
  • يتم تحديد عمليات التحقق من الصحة وتدوير سجل JSON المحدود وRedis AOF الدائم وسياسة إعادة التشغيل مركزيًا في الملفات الأساسية.

بالنسبة للنشر الذي يواجه الإنترنت، قم بربط المنفذ 1349 للاسترجاع وإنهاء TLS في وكيل عكسي يتم الحفاظ عليه. قم بإنشاء بيانات اعتماد PostgreSQL وRedis فريدة، وقم بتخزين الأسرار في ملفات محمية أو مدير سري، وقم بتغيير كلمة مرور المسؤول الأولية على الفور.

لماذا لم يتم تعيين read_only على

لم يتم تعيين read_only: true لأن إعادة تعيين PUID/PGID تكتب إلى /etc/passwd و/etc/group عند بدء التشغيل. إذا كنت تستخدم علامة Docker's --user أو Kubernetes runAsUser بدلاً من PUID/PGID، فيمكنك تمكين نظام ملفات جذر للقراءة فقط بأمان.

عزل الشبكة

تتم معالجة الملفات محليًا، ولكن التثبيت الافتراضي ليس نظامًا خاليًا من الخروج. تستخدم تحليلات المنتج المجهولة PostHog وتستخدم تقارير الأعطال Sentry عند تمكين القياس عن بعد. قم بتعيين SNAPOTTER_TELEMETRY=0 (أو قم بتعطيل التحليلات ضمن الإعدادات > النظام > الخصوصية) لإيقاف تشغيل كليهما. لا يتضمن SnapOtter أبدًا الملفات التي تم تحميلها أو أسماء الملفات أو مخرجات التعرف الضوئي على الحروف أو نص المستند أو محتويات الملفات الأخرى في تلك الأحداث.

تعتمد حركة المرور الصادرة الأخرى على الميزات: تنزيلات تثبيت حزمة/نموذج الذكاء الاصطناعي، ومدخلات الإصدار الموقعة؛ يؤدي استيراد عنوان URL إلى جلب عنوان URL عام يطلبه المستخدم؛ وOIDC، أو SAML، أو OpenTelemetry، أو webhooks، أو التخزين المتوافق مع S3، أو عمليات التكامل المماثلة التي تم تكوينها بشكل صريح، اتصل بالوجهات التي اختارها المسؤول. يتم تعطيل تنزيلات النماذج في وقت التشغيل افتراضيًا. اضبط SNAPOTTER_ALLOW_MODEL_DOWNLOAD=1 فقط للاشتراك صراحةً في التنزيلات الاحتياطية التلقائية. يمكن لـ استيراد الحزمة دون اتصال توفير ميزات الذكاء الاصطناعي دون الخروج من نموذج وقت التشغيل.

توصيات جدار الحماية:

سيناريو القاعدة الصادرة
فجوة الهواء قم بتعيين SNAPOTTER_TELEMETRY=0 وSNAPOTTER_ALLOW_MODEL_DOWNLOAD=0، واستخدم استيراد حزمة AI دون اتصال، وقم بتعطيل استيراد عنوان URL وعمليات التكامل الخارجية، ثم قم بحظر الخروج
القياس عن بعد الافتراضي السماح بنقطتي نهاية PostHog وSentry المدرجة في سجلات المتصفح/الشبكة لديك؛ قم بتعطيل القياس عن بعد إذا كانت السياسة لا تسمح بذلك
هناك حاجة إلى حزم الذكاء الاصطناعي أثناء التثبيت، اسمح بـ 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) الإصدار v2.23 من Compose أو أحدث. :::

نشر Kubernetes

تكتشف نقطة الدخول متى تعمل الحاوية بالفعل كـ non-root (مثلًا عبر Kubernetes runAsUser) وتتخطى إسقاط امتياز gosu تلقائيًا. في تلك الحالة لا يمكنها تغيير ملكية وحدات التخزين المركّبة بنفسها، لذا تتحقق من كونها قابلة للكتابة وتخرج مبكرًا مع إرشادات قابلة للتنفيذ إن لم تكن كذلك — راجع أذونات التخزين لإعدادات 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: false و drop: [ALL] دون تعارض.

لتحجيم الموارد، راجع متطلبات العتاد.

النسخ الاحتياطي والاسترداد

يحدد مكدس Compose الإنتاج أربعة مجلدات. أوقف الدخول واترك المهام النشطة تنتهي قبل أخذ نسخة احتياطية منسقة، بحيث تصف PostgreSQL وRedis وحالة الملف نفس النقطة الزمنية.

مقدار محتويات علاج الانتعاش
SnapOtter-pgdata مستخدمو PostgreSQL والإعدادات وخطوط الأنابيب والوظائف وبيانات تعريف الملف وسجل التدقيق شديد الأهمية؛ استخدم تفريغًا منطقيًا سريع الفشل للاسترداد المحمول
SnapOtter-data كائنات المكتبة المحفوظة والسجلات وحالة الذكاء الاصطناعي (/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، تعمل أسبوعيًا + لكل العلاقات العامة
مراجعة التبعية GitHub مواطن يفشل فحص كل PR في الإضافات عالية الخطورة
تدقيق التبعية Python pip-audit سجل تشغيل CI في كل دفعة
السياسة الأمنية Markdown SECURITY.md في المستودع
تحديثات التبعية Dependabot تقارير العلاقات العامة الأسبوعية الآلية لـ 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 لأنه يتم تنزيلها في وقت التشغيل. :::