انتقل إلى المحتوى

أمان الخادم ​

يرسل خادم Signal Lab حركة مرور حقيقية من الجهاز الذي يعمل عليه: OSC وUDP وHTTP وMQTT والعواصف وعمليات المسح والبث. ومن يستطيع استخدامه يستطيع فعل ذلك كله من ذلك الجهاز، لذا يأتي الخادم مغلقًا افتراضيًا ولا ينفتح إلا برمز مميز.

WARNING

يُعامل رمز الوصول معاملة كلمة المرور إلى شبكة الجهاز. فمن يملكه يستطيع إرسال حركة مرور من الخادم إلى أي شيء يصل إليه الخادم.

لمحة سريعة ​

  • بلا رمز مميز، هذا الجهاز فقط. من دون رمز مميز يستمع الخادم على عنوان الاسترجاع ولا يجيب إلا على أسماء مضيف الاسترجاع. وعلى أي عنوان آخر يرفض البدء.
  • رمز مميز لكل من عداه. تسجّل المتصفحات الدخول مرة واحدة وتحصل على ملف تعريف ارتباط للجلسة؛ وترسل السكربتات الرمز المميز مع كل طلب.
  • صفحاته وحدها. الطلبات التي تغيّر شيئًا، وWebSocket الأحداث، يجب أن تأتي من أصل الخادم نفسه؛ والأوامر تقبل JSON فقط.
  • أسماؤه وحدها. اسم المضيف الذي لا يجيب عليه الخادم يُرفض، وهذا يمنع إعادة ربط DNS.
  • الأسرار تبقى في الداخل. للقراءة فقط، من البيئة أو الملفات، ولا تُعاد أبدًا، وتُحجب حيثما كانت ستظهر.
  • لا شيء يتجاوز مهمته. لا يغيّر جدار حماية المضيف أبدًا، ولا يقدّم ملفًا من خارج مجلد بياناته، ولا يحتاج إلى صلاحيات.

بلا رمز مميز: هذا الجهاز فقط ​

حين يبدأ الخادم بلا رمز مميز، يستمع على 127.0.0.1:1430 ولا يحتاج إلى تسجيل دخول: فهو أداة للشخص الجالس أمام هذا الجهاز. ولمنع صفحة ويب في أي متصفح على هذا الجهاز من الوصول إليه عبر اسم يُحلّ إلى 127.0.0.1 (إعادة ربط DNS)، لا يجيب إلا على الطلبات التي يكون Host فيها اسم استرجاع — localhost، أو اسمًا ينتهي بـ .localhost، أو 127.x.x.x، أو [::1] — أو اسمًا يسمح به --allowed-host.

وإذا طُلب منه الاستماع على أي عنوان آخر بلا رمز مميز، فلا يبدأ: يذكر السبب ويخرج بالرمز 2.

الرمز المميز ​

يتألف الرمز المميز من 24 حرفًا على الأقل، بلا مسافات ولا فواصل أسطر. يطبع signal-lab-server token رمزًا عشوائيًا من 64 حرفًا سداسيًا عشريًا، وينشئ --generate-token (المفعّل في الصورة) رمزًا عند أول بدء، ويحفظه في مجلد البيانات بحيث لا يقرؤه إلا مستخدم الخادم نفسه، ويطبعه مرة واحدة. وكل طرق إعطاء رمز مذكورة في رمز الوصول.

مقارنة الرمز المميز تستغرق الزمن نفسه أينما اختلف، والرمز الخاطئ يكلّف انتظار ثانية وتحذيرًا في السجل — فالتخمين بطيء ويترك أثرًا.

المتصفحات: الجلسات ​

المتصفح الذي لم يسجّل الدخول يُرسَل إلى صفحة تسجيل الدخول. ويُستبدل رمزه المميز مرة واحدة بجلسة، تُحفظ في ملف تعريف ارتباط:

  • HttpOnly — فلا يستطيع أي سكربت في صفحة قراءته؛
  • SameSite=Strict — فلا تستطيع صفحة أي موقع آخر جعل المتصفح يرسله؛
  • صالح 7 أيام؛
  • Secure مع --secure-cookie، فلا ينتقل إلا عبر HTTPS (يُعيَّن خلف وكيل HTTPS).

تعيش الجلسات في ذاكرة الخادم: إعادة التشغيل تُخرج الجميع، ويُنهي تسجيل الخروج واحدة فورًا. ويُحتفظ بـ 1024 جلسة على الأكثر؛ والأقدم يخرج أولًا.

السكربتات: الرمز المميز Bearer ​

يرسل السكربت وsignallab --server وCI الرمز المميز مع كل طلب:

http
Authorization: Bearer <token>

كل نقطة نهاية تحتاج إلى الرمز المميز أو جلسة، عدا GET /api/health (هل يجيب الخادم، وإصداره، وهل يطلب رمزًا مميزًا) وصفحة تسجيل الدخول. والطلب إلى واجهة API دونهما يتلقى 401 مع الخطأ auth.required؛ والصفحة تتلقى صفحة تسجيل الدخول.

أسماء المضيف ​

بدء الخادمأسماء المضيف التي يجيب عليها
بلا رمز مميزأسماء الاسترجاع، والأسماء في --allowed-host
برمز مميز، بلا --allowed-hostأي اسم
برمز مميز ومع --allowed-hostأسماء الاسترجاع، والأسماء في --allowed-host

يأخذ --allowed-host (SIGNALLAB_ALLOWED_HOSTS) أسماء تفصل بينها فواصل، وتُقارن بلا المنفذ وبلا اعتبار لحالة الأحرف:

bash
signal-lab-server --listen 0.0.0.0:1430 --token-file token.txt --allowed-host lab-pc.example.com,192.0.2.10

أي Host آخر يتلقى 403 مع الخطأ auth.host. يُعيَّن على خادم يمكن الوصول إليه بأسماء معروفة، حتى لا تستطيع صفحة في موقع آخر الوصول إليه عبر اسم من عندها.

الأصل ونوع المحتوى ​

  • كل طلب يغيّر شيئًا (أي شيء عدا GET وHEAD)، وWebSocket الأحداث، يجب ألا يحمل Origin، أو أن يحمل أصل الخادم نفسه — المضيف والمنفذ نفسهما اللذان في Host. والصفحة في موقع آخر، أو التي ترسل Origin: null، تتلقى 403 مع auth.origin. أما السكربتات وcurl فلا ترسل Origin ولا تتأثر.
  • تقبل الأوامر Content-Type: application/json فقط (وإلا 415 مع command.json_required)، فلا يستطيع نموذج في موقع آخر إرسال أمر.
  • ولا يجيب الخادم على طلبات عبر الأصول (CORS).

ترويسات الاستجابة ​

تحمل كل استجابة:

الترويسةالقيمة
Content-Security-Policydefault-src 'self'; connect-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'self'
X-Content-Type-Optionsnosniff
X-Frame-OptionsDENY
Referrer-Policysame-origin
Cache-Controlno-store لواجهة API وصفحة تسجيل الدخول

لا تحمّل الواجهة إلا ما يقدّمه الخادم، ولا تتخاطب إلا معه، ولا يمكن لصفحة أخرى تضمينها في إطار.

الملفات والأحجام ​

  • لا يُقدَّم التنزيل (GET /api/files?path=…) إلا من داخل مجلد البيانات، وبحد أقصى 256 MiB؛ وأي شيء آخر 404.
  • متن الطلب 24 MiB على الأكثر.

الأسرار ​

قيمة السر لا تغادر المحرك أبدًا:

  • على الخادم، القيم للقراءة فقط: متغير البيئة SIGNALLAB_SECRET_<NAME>، أو الملف <NAME> في مجلد الأسرار (/run/secrets/signallab افتراضيًا). وتعيين قيمة أو إزالتها من المتصفح مرفوضان (secret.read_only)، فلا تنتهي قيمة كُتبت في صفحة مخزنةً في مكان أضعف. انظر الأسرار.
  • لا يعيد أي أمر قيمة؛ ولا تعرف الواجهة إلا هل الاسم معيَّن.
  • تسمّي التجارب الأسرار بالصيغة {{secret.NAME}}. وبينما يستخدمها تشغيل أو إرسال، يعرض كل نص يبلغ عنه — الخطوات والأخطاء وتقرير التشغيل — •••• مكانها، وتُحجب الإطارات في الفاحص بايتًا ببايت.
  • تتحول بيانات اعتماد عقدة HTTP إلى ترويسة Authorization لحظة إرسال الطلب فقط؛ وتحمل الخطوات والإطارات والتقارير الاستجابة، لا تلك الترويسة أبدًا.

ما يُسجَّل ​

يسجّل السجل، مع عنوان العميل: كل مهمة بدأت — عواصف وعمليات مسح وبث ومراقبات ومولّدات وتشغيلات ومحاكيات — وكل تسجيل دخول، وكل تسجيل دخول برمز مميز خاطئ. انظر السجلات.

ما لا يفعله الخادم أبدًا ​

  • تغيير جدار حماية المضيف. يستطيع التطبيق المكتبي إضافة قاعدة لجدار الحماية حين يُطلب منه ذلك؛ وعلى الخادم يُرفض هذا الأمر (firewall.server). فجدار حماية المضيف شأن من يشغّل المضيف. (ويعرض سكربت التثبيت بأمر واحد فتح منفذ الخادم في ufw أو firewalld، ويسأل أولًا — انظر جدار حماية المضيف.)
  • البدء قابلًا للوصول بلا رمز مميز، على أي عنوان غير عنوان الاسترجاع.
  • تقديم ملف من خارج مجلد بياناته.
  • تخزين سر كُتب في متصفح.
  • التخاطب بـ TLS بنفسه: يوضع وكيل HTTPS أمامه (انظر خلف وكيل HTTPS).

وكل حد في المحرك — 1024 مضيفًا على الأكثر في التمشيط، و50,000 حزمة في الثانية على الأكثر من منارة — يسري على الخادم كما في التطبيق. إنها حواجز أمان، لا إذن: لا تُرسل حركة مرور إلا إلى أنظمة تملكها أو يحق لك اختبارها.

الحاوية ​

تعمل الصورة بمستخدم بلا امتيازات (uid وgid بالقيمة 10001) ولا تكتب إلا في /data. وتعمل دون تغيير مع نظام ملفات جذر للقراءة فقط، وبلا قدرات، ومع no-new-privileges — كما يشغّلها سكربت التثبيت وdeploy/compose.yaml. وتُنشر كل صورة مع SBOM وإثبات للبناء وشهادة GitHub موقّعة:

bash
gh attestation verify oci://ghcr.io/proanima/signallab:1.0.0 -R ProAnima/SignalLab

ما يرسله Signal Lab إلى العالم الخارجي ​

إلى جانب حركة المرور التي ترسلها، يتخاطب Signal Lab مع جهتين، كلتاهما للاستوديو.

التحقق من التحديثات ​

التطبيق المكتبي وحده يبحث عن التحديثات؛ أما الخادم والمتصفح فلا يفعلان ذلك أبدًا. فحين يكون الخيار التحقق مرة يوميًا مفعّلًا (في حول Signal Lab)، مرة في اليوم، وكلما ضُغط التحقق من وجود تحديثات، يسأل التطبيق مركز الاستوديو (hub.proanima.net) — ولا يلجأ إلى أحدث إصدار على GitHub إلا إذا تعذّر الوصول إلى المركز. ويحمل السؤال:

  • إصدار التطبيق؛
  • نظام التشغيل ومعمارية المعالج؛
  • رقمًا عشوائيًا لهذا التثبيت (X-Install-Id)، يُنشأ مرة واحدة ويُحفظ مع إعدادات التطبيق، حتى يصل الإصدار الجديد إلى نسبة من التثبيتات أولًا. وهو لا يدل على المستخدم ولا على الكمبيوتر.

ولا تُعرض إلا الإصدارات المنشورة. والتنزيل الذي لا يطابق توقيعه المفتاح المدمج في التطبيق لا يُثبَّت، ولا يُثبَّت شيء قبل الضغط على التثبيت وإعادة التشغيل.

الملاحظات ​

يرسل الزر مراسلة المطوّرين (علامة ✉ في الشريط العلوي، وهي أيضًا في حول Signal Lab) رسالة إلى المطوّرين عبر مركز الاستوديو، الذي يرسلها بالبريد الإلكتروني؛ ولا يحمل التطبيق أي كلمة مرور لذلك. ولا يرسل إلا ما يعرضه النموذج: رسالتك، وبريدك الإلكتروني إن أعطيته، ولقطات الشاشة التي تضيفها، وفي قسم تُرفق تلقائيًا — سجل وحدة التحكم والإصدار والنظام، ويمكن فتح كلٍّ منهما قبل الإرسال وإلغاء تحديده. ويُستبعد منهما اسم هذا الكمبيوتر وعنوانه ومجلدات المستخدم. ومن المتصفح، يرسل الخادم النموذج.