أحكِم إغلاق الخادم، وضَعْ WAF أمام تطبيقك

منذ أول نشر، لا يسمح erpc worker بدخول SSH إلا من عنوانك، ويُسقط كل اتصال وارد آخر. ويضع جدول [waf] بروكسي تصفية أمام الـ API الخاص بك.
تصف هذه الصفحة ERPC CLI v0.1.0-rc.4، وهو إصدار تمهيدي.

جدار الحماية

يضبط النشر جدار حماية الخادم قبل أول تشغيل للتطبيق:
  • SSH فقط من firewall.ssh_allow ومن العنوان الذي يأتي منه النشر؛
  • يُسقَط كل اتصال وارد آخر، باستثناء run.port من firewall.app_allow؛ وفي تطبيق خلف WAF يحلّ منفذ WAF محله، ولا يكون ذلك إلا حيث يشغّل WAF على الخادم بالفعل القواعد التي يضعها [waf] الآن (أدناه).
يعمل كل تغيير تحت مؤقِّت تراجع مدته 120 ثانية، ولا يُحتفَظ به إلا بعد أن ينجح اتصال جديد في الدخول، فلا يمكن لتغيير خاطئ أن يمنعك من الدخول.
غير أن قائمة السماح تبقى بعد زوال العنوان الذي أُنشئت منه: فإذا تغيّر عنوان منزلك، فطريق العودة هو دعم ERPC لخادم ERPC، أو وحدة تحكم المزوّد لخادم خاص بك. احتفظ في firewall.ssh_allow بعنوان ثانٍ يمكنك الوصول منه إلى الخادم (مكتب، أو مخرج VPN) قبل أن تعتمد على عنوان منزلي.
إذا تغيّر عنوانك، فأضف كل عنوان ثابت يستخدمه المسؤولون لديك:
bash
erpc worker firewall allow --to ssh 203.0.113.10 198.51.100.7
الأمرما يفعله
erpc worker firewall listيعرض من يمكنه الوصول إلى الخادم: erpc.toml بجانب جدول الخادم
erpc worker firewall allow --to ssh|app <addresses>يسمح بدخول العناوين، ويطبّق ذلك. يُسمح بـSSH من عناوين مفردة؛ ولا تُقبل الشبكة إلا مع --to app
erpc worker firewall deny --to ssh|app <addresses>يوقف السماح بدخولها
erpc worker firewall syncيعيد جدار حماية الخادم إلى ما يقوله erpc.toml

ضع WAF أمام تطبيقك

أضف جدولًا واحدًا إلى erpc.toml:
toml
[waf]
port = 8080
بعد ذلك يشغّل erpc worker deploy بروكسيًا عكسيًا لـHTTP على الخادم، في وحدة systemd خاصة به (erpc-waf-<app>-<env>): يعمل باسم مستخدم ديناميكي بلا امتيازات، بلا مفتاح ولا سر ولا أي شيء من الـ worker، ومحدودًا بـ1 GiB من الذاكرة. يستمع على waf.port، ويقرأ كل طلب قبل أن يقرأه التطبيق، ويرفض ما تسمّيه قواعده، ويمرّر الباقي إلى التطبيق على 127.0.0.1:<run.port>.
  • يبقى التطبيق على واجهة loopback. لا يُفتح منفذه الخاص من الخارج أبدًا. يصل erpc worker api وفحص الصحة إلى التطبيق على واجهة loopback، متجاوزَين WAF.
  • من يمكنه الاتصال لا تزال تحدده قائمة جدار الحماية، ومنفذ WAF هو المكان الوحيد الذي يجوز فيه لتلك القائمة أن تقول «الجميع»:
    sh
    erpc worker firewall allow --to app 0.0.0.0/0     # and ::/0
    إذا لم يُدرَج أحد، يستمع WAF ولا يُسمح لأحد بالدخول، ويذكر النشر ذلك.
  • يُفتح المنفذ بعد أن يستمع WAF عليه، وليس قبل ذلك أبدًا. النشر الذي لا يعمل فيه WAF يفشل ويذكر السبب، مع بقاء التطبيق قيد التشغيل.
  • عندما تغيّر [waf]، لا يُفتح المنفذ لأي شخص جديد حتى يضع نشرٌ القواعدَ الجديدة موضع التنفيذ.
  • إزالة [waf] من erpc.toml ثم النشر يزيلان WAF من الخادم.

إعدادات WAF

المطلوب هو port فقط. كل مفتاح مع قيمته الافتراضية:
toml
[waf]
port = 8080          # the public port, 1024 or above; the app keeps [run] port
methods = ["GET", "POST", "PUT", "DELETE"]
allow_paths = []     # every path
deny_paths = []
max_body_bytes = 1048576
max_header_bytes = 16384
rate = { per_second = 50, burst = 100 }
max_connections_per_address = 32
ban = { after = 100, window_secs = 60, for_secs = 600 }
block_probes = true
header_timeout_secs = 10
body_timeout_secs = 30
upstream_timeout_secs = 30
new_connections = { per_second = 50, burst = 100 }
المفتاحما يفعلهما يتلقاه المرفوض
methodsالطرق التي تُمرَّر، بالأحرف الكبيرة (16 على الأكثر؛ ولا CONNECT أبدًا)405، مع Allow
allow_pathsالمسارات التي تُمرَّر؛ وعدم إدراج أي مسار يعني كل المسارات. النمط مسار واحد، أو كل ما تحت بادئة عندما ينتهي بـ*: /api/* تعني /api وكل ما تحته. يُقارَن بالمسار بعد فك ترميز النسبة المئوية مرة واحدة، مع التمييز بين الأحرف الكبيرة والصغيرة404
deny_pathsمسارات تُرفض، تُكتب بالطريقة نفسها وتُفحص أولًا403
max_body_bytesأكبر حجم لجسم الطلب، حتى 64 MiB413
max_header_bytesأكبر حجم لسطر الطلب والترويسات معًا، من 1024 إلى 65536431
rateعدد الطلبات في الثانية من عنوان واحد، وكم طلبًا يُقبل دفعة واحدة قبل أن ينطبق المعدل429، مع Retry-After
max_connections_per_addressالاتصالات التي يحتفظ بها عنوان واحد في وقت واحديُغلق الاتصال دون رد
banبعد after من الطلبات المحجوبة خلال window_secs، يُرفض العنوان مدة for_secs (حتى 30 يومًا)تُغلق اتصالاته دون رد
block_probesيرفض ما تطلبه أدوات المسح (/.env و/.git و/wp-login.php وما شابهها)403
header_timeout_secsالمدة المتاحة للعميل لإرسال رأس الطلب، من 1 إلى 120. وهي أيضًا مدة انتظار عميل توقف في منتصف الجسم، أو توقف عن تلقي ردّه408 للرأس؛ و400 لجسم توقف؛ ويُغلق الاتصال لرد لم يُتلقَّ
body_timeout_secsالمدة الإجمالية التي يجوز أن يستغرقها جسم الطلب، من 1 إلى 600، دون احتساب الوقت الذي ينتظر فيه WAF التطبيق408
upstream_timeout_secsالمدة المتاحة للتطبيق لقبول الاتصال وبدء الرد، من 1 إلى 600504؛ و502 عندما لا يستمع التطبيق
new_connectionsحد جدار الحماية نفسه لاتصالات TCP الجديدة في الثانية من عنوان واحد إلى منفذ WAF، ويُسقطها nftables قبل أن يراها البروكسي. وما دامت مجموعة العناوين في nftables ممتلئة، لا يُحتسب العنوان غير الموجود فيها، فلا ينطبق عليه هذا الحد حتى تتوفر في المجموعة مساحة من جديد؛ أما حدود البروكسي نفسه (rate وmax_connections_per_address وban) فلا تزال ساريةتُسقَط الحزمة
تُعطَّل rate وmax_connections_per_address وban وnew_connections بالقيمة false (ban = false)، والمفتاح المحذوف يأخذ قيمته الافتراضية. ويذكر erpc worker check ما سيفعله جدول [waf] وما فيه من أخطاء، قبل أي نشر.

ما يفعله WAF دون حاجة إلى أي إعداد

  • الطلب الذي لا يُقرأ كطلب HTTP/1 واحد لا لبس فيه يتلقى 400، وكذلك المسار الذي ليس بصيغته القياسية.
  • طلب تغيير البروتوكول (Upgrade) يتلقى 501.
  • كل رفض يُحتسب ضمن الحظر، وكل رفض يُنهي اتصاله.
  • العنوان هو عنوان IPv4، أو /64 من IPv6.
  • لا يمرّر WAF ترويسات اتصال العميل بالبروكسي (Connection والترويسات التي تسمّيها، وKeep-Alive وProxy-Authenticate وProxy-Authorization وProxy-Connection وTE وTrailer)، ولا Expect، ولا الترويسات التي يحتوي اسمها على شرطة سفلية، ولا ترويسات Forwarded وX-Real-IP وX-Forwarded-Proto الخاصة بالعميل. ويضبط X-Real-IP على العنوان الذي جاء منه الاتصال، ويضيف ذلك العنوان إلى نهاية X-Forwarded-For؛ والمُدخل الأخير وحده في X-Forwarded-For هو ما يشهد به البروكسي.

اطّلع على ما يفعله WAF

الأمرما يعرضه
erpc worker statusمنفذ WAF، والجهة التي يمرّر إليها، ومنذ متى، والأعداد منذ ذلك الحين
erpc worker firewall listمن يمكنه الوصول إلى منفذ WAF، وما حُجب، حسب القاعدة
erpc worker logs --wafكل طلب محجوب: القاعدة، والحالة، وعنوان العميل، والطريقة، وأول 200 بايت من الهدف
الطلب المُمرَّر لا يترك سطرًا في السجل، ولا تُسجَّل أي ترويسة أو جسم أبدًا؛ أما استعلام الطلب المحجوب فيُسجَّل، في حدود ما تبلغه الـ 200 بايت. ولا يمكن لما يرسله العميل أن يكتب على طرفيتك: فـWAF يكتب سجله بأحرف ASCII قابلة للطباعة.

ما لا يفعله WAF

  • لا TLS. يعمل بـHTTP/1.1 العادي على منفذه.
  • لا HTTP/2: يرد عليه بـ400.
  • لا WebSocket، ولا أي Upgrade آخر: 501.
  • لا فحص لمحتوى الأجسام. يُقاس الجسم ولا يُقرأ أبدًا. قائمة طلبات المسح قصيرة، وهي مرشّح للضجيج لا ضمان: لا يزال التطبيق هو من يتحقق من مدخلاته.
  • لا مصادقة. من يحق له استدعاء التطبيق أمرٌ يقرره التطبيق، وقائمة جدار الحماية.
  • العنوان الواحد عميل واحد. العملاء خلف NAT أو بروكسي أو CDN واحد يتشاركون معدلًا واحدًا وحظرًا واحدًا؛ والعميل الذي له عناوين كثيرة له معدلات بعددها.
  • يُنسى الحظر والأعداد عند إعادة تشغيل WAF.
  • يتذكر 8192 عنوانًا على الأكثر ويحتفظ بـ1024 اتصالًا على الأكثر في وقت واحد.
  • يستمع على IPv4 فقط، لذا فإن مُدخل IPv6 في app_allow لا يصل إلى شيء حتى الآن.
  • يُقارَن المسار بالبايتات التي يُفك ترميزه إليها. التطبيق الذي يقرأ المسارات بتساهل أكبر (متجاهلًا حالة الأحرف أو معاملات ;، أو فاكًّا الترميز مرتين) يمكن الوصول إليه باسم لا يغطيه deny_paths. أما allow_paths فليس فيه هذه الثغرة: ما لم يُدرَج لا يُمرَّر.
  • القارئ البطيء يُخدَم حتى النهاية. العميل الذي يواصل تلقي الرد، مهما كان بطيئًا، يُخدَم حتى النهاية.
  • يمكن للإغراق أن يقطع رفعًا كبيرًا. يعمل WAF على خيط واحد، لذا يمكن للإغراق أن يقطع رفعًا كبيرًا بـ408. والتطبيق الذي يقبل أجسامًا كبيرة يرفع body_timeout_secs مع max_body_bytes.
دليل الـ worker يتضمن كل قاعدة، بما في ذلك متى يُرجأ تغيير app_allow حتى النشر التالي.