Files
upstream-twenty/packages/twenty-docs/l/ar/developers/self-host/capabilities/key-rotation.mdx
upstream-mirror e7da97194a
Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s
snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
2026-09-17 23:28:13 +05:00

61 lines
5.8 KiB
Plaintext

---
title: تدوير المفاتيح
icon: rotate
---
يمتلك Twenty عائلتين مستقلتين من المفاتيح:
* **مفاتيح توقيع JWT** — أزواج مفاتيح غير متماثلة ES256 (مع علامة `kid`) مُخزَّنة في `core."signingKey"`، تُستخدم لتوقيع والتحقق من رموز الوصول / التحديث.
* **مفتاح التشفير أثناء السكون (At-rest encryption key)** — `ENCRYPTION_KEY`، يُستخدم لتشفير رموز OAuth، ومتغيرات التطبيق، ومفاتيح التوقيع الخاصة، وقيم الإعدادات الحساسة، وأسرار TOTP داخل غلاف `enc:v2:`.
يُعد `APP_SECRET` سراً قديماً مُحتفَظاً به لأغراض التوافق مع الإصدارات السابقة: عندما لا يكون `ENCRYPTION_KEY` مضبوطاً، فإنه يعمل كحل احتياطي لمفتاح التشفير أثناء السكون / ملفات تعريف الارتباط للجلسة، ولا يزال يتحقق من رموز الوصول HS256 الموجودة مسبقاً. سيتم إهماله (إيقاف دعمه).
## مفاتيح توقيع JWT
يحمل كل مفتاح قيمة `publicKey` (يُحتفَظ بها إلى أجل غير مسمى حتى يمكنها التحقق من الرموز المصدرة مسبقاً)، و`privateKey` مُشفَّراً (يُستخدم فقط أثناء كون المفتاح حالياً)، وراية `isCurrent` (صف واحد فقط في كل وقت)، وحقل `revokedAt` اختياري.
### تدوير المفتاح الحالي
اضبط `SIGNING_KEY_ROTATION_DAYS` للتفعيل: عندها تصدر مهمة cron يومية مفتاحًا حاليًا جديدًا بمجرد أن يصبح المفتاح القائم أقدم من تلك العتبة. لا يتم إبطال المفاتيح السابقة، لذلك تستمر الرموز الموقعة تحتها في التحقق. اترك المتغير غير معيّن لتعطيل التدوير التلقائي.
<Note>ميزة التدوير التلقائي متوفّرة ابتداءً من الإصدار v2.6+.</Note>
### إبطال مفتاح (في حالات التسريب / الطوارئ فقط)
**Settings → Admin Panel → Signing keys → Revoke** على صف غير حالي. تعمل على مسح المادة الخاصة المشفرة، وتعيين `revokedAt`، ورفض كل رمز حالي موقَّع تحت هذا الـ `kid`.
## تدوير `ENCRYPTION_KEY`
<Note>أمر `secret-encryption:rotate` الموضَّح أدناه متوفر ابتداءً من الإصدار v2.6+.</Note>
يُغلَّف كل مقدار مُشفَّر بالشكل `enc:v2:\<keyId>:\<payload>`، حيث إن `\<keyId>` هو بادئة مكوَّنة من 8 أرقام ست عشرية مشتقة من المفتاح الخام. تتم عملية التدوير (الاستبدال) أثناء العمل، وقابلة للاستئناف.
1. **إنشاء مفتاح جديد**: `openssl rand -base64 32`.
2. **ضَبْط المفتاحين معاً جنباً إلى جنب** في ملف `.env`، ثم إعادة تشغيل الخدمة:
```ini
ENCRYPTION_KEY=NEW_VALUE
FALLBACK_ENCRYPTION_KEY=OLD_VALUE
```
تستخدم عمليات الكتابة الجديدة المفتاح الجديد، بينما لا تزال الصفوف الحالية تُفك تشفيرها عبر المفتاح الاحتياطي (fallback).
3. **إعادة تشفير الصفوف الحالية**:
```bash
docker exec -it {server_container} yarn command:prod secret-encryption:rotate
```
يتناول الأمر ستة مواقع (`connected-account-tokens`، `application-variable`، `application-registration-variable`، `signing-key-private-keys`، `sensitive-config-storage`، `totp-secrets`). يُهمِل عامل تصفية SQL الصفوف الموجودة بالفعل على `\<keyId>` الجديد، لذلك يكون الأمر عديم الأثر التكراري (idempotent): يمكنك مقاطعته وإعادة تشغيله حسب الحاجة. يخرج بقيمة مختلفة عن الصفر إذا فشل أي صف — أعد تشغيله لإعادة المحاولة.
| خيار | الوصف |
| ---------------------------------------- | ------------------------------------------------------------- |
| `-s, --site \<site>` | حصر التنفيذ على موقع واحد فقط. |
| `-b, --batch-size \<n>` | عدد الصفوف في كل دفعة (الافتراضي `200`، والحد الأقصى `5000`). |
| `-d, --dry-run` | فك التشفير + إعادة التشفير في الذاكرة، مع تخطي جملة `UPDATE`. |
4. **إزالة المفتاح الاحتياطي (fallback)** بمجرد أن يُظهِر الخيار `--dry-run` عدم وجود صفوف متبقية: أزِل `FALLBACK_ENCRYPTION_KEY` وأعد التشغيل.
## دعم `APP_SECRET` القديم
تستخدم النُسخ الأقدم التي لم تُعيِّن `ENCRYPTION_KEY` مطلقاً المتغيِّر `APP_SECRET` كمفتاح التشفير أثناء السكون (وكسرّ ملف تعريف الارتباط الخاص بالجلسة، المشتقّ منه). يُحفَظ هذا المسار لأغراض التوافق مع الإصدارات السابقة ولكنه **مهمل (موقوف الدعم)** — عيِّن متغير `ENCRYPTION_KEY` مخصَّصاً واتبع إجراء التدوير الموضَّح أعلاه للانتقال بعيداً عنه. يبقى `APP_SECRET` نفسه قيد الاستخدام للتحقق من رموز الوصول HS256 القديمة.