العودة إلى التحديثات
New releaseJul 29, 2026

swift-nio-ssh v0.15.0

SwiftNIO SSH هو تنفيذ برمجي لـ SSH باستخدام SwiftNIO

مشاركة

SwiftNIO SSH

يحتوي هذا المشروع على دعم SSH باستخدام SwiftNIO.

ما هو SwiftNIO SSH؟

SwiftNIO SSH هو تطبيق برمجي لـ SSH: أي أنه مجموعة من واجهات برمجة التطبيقات (APIs) التي تسمح للمبرمجين بتنفيذ نقاط نهاية تتحدث بروتوكول SSH. والأهم من ذلك، أن هذا يعني أنه يشبه libssh2 أكثر من openssh. لا يوفر SwiftNIO SSH عملاء وخوادم SSH جاهزة للإنتاج، ولكنه يوفر اللبنات الأساسية لبناء هذا النوع من العملاء والخوادم.

هناك عدة أسباب لتوفير تطبيق برمجي لـ SSH. أحدها هو أن SSH له علاقة فريدة بالتفاعل مع المستخدم. المستخدمون التقنيون معتادون بشدة على التفاعل مع SSH بشكل تفاعلي، إما لتشغيل الأوامر على أجهزة بعيدة أو لتشغيل واجهات شل تفاعلية. إن القدرة على الاستجابة برمجيًا لهذه الطلبات تتيح أنماطًا بديلة مثيرة للاهتمام من التفاعل. كأمثلة سابقة، يمكننا الإشارة إلى Twisted's Manhole، والذي يستخدم تطبيق SSH برمجي يسمى conch لتوفير مترجم بايثون تفاعلي داخل خادم بايثون قيد التشغيل، أو ssh-chat، وهو خادم SSH يوفر غرفة دردشة بدلاً من وظيفة شل SSH العادية. يمكن أيضًا تخيل استخدامات مبتكرة لإعادة توجيه TCP.

سبب جيد آخر لتوفير SSH برمجي هو أنه ليس من غير المألوف أن تحتاج الخدمات إلى التفاعل مع خدمات أخرى بطريقة تتضمن تشغيل الأوامر. بينما يحل Process هذه المشكلة للحالة المحلية، إلا أن الأوامر التي يجب استدعاؤها قد تكون بعيدة. على الرغم من أن Process يمكنه تشغيل عميل ssh كعملية فرعية لتنفيذ هذا الاستدعاء، إلا أنه يمكن أن يكون أبسط بكثير مجرد استدعاء SSH مباشرة. هذا هو حالة الاستخدام المستهدفة لـ libssh2. يوفر SwiftNIO SSH ما يعادل طبقة الشبكات والتشفير في libssh2، مما يسمح للمستخدمين المتحمسين بقيادة جلسات SSH مباشرة من داخل خدمات Swift.

تدعم أحدث إصدارات SwiftNIO SSH إصدار Swift 5.9 والإصدارات الأحدث. الحد الأدنى من إصدار Swift الذي تدعمه إصدارات SwiftNIO SSH موضح أدناه:

SwiftNIO SSHالحد الأدنى من إصدار Swift
0.0.0 ..< 0.3.05.1
0.3.0 ..< 0.4.05.2
0.4.0 ..< 0.5.05.4
0.5.0 ..< 0.6.25.5.2
0.6.2 ..< 0.9.05.6
0.9.0 ..< 0.9.25.8
0.9.2 ..< 0.10.05.9
0.10.0 ... 0.12.05.10
0.12.0 ..< 0.13.06.0
0.13.0 ..<6.1

ما الذي يدعمه SwiftNIO SSH؟

يدعم SwiftNIO SSH الإصدار SSHv2 مع مجموعة الميزات التالية:

  • جميع ميزات القناة الخاصة بالجلسة، بما في ذلك طلبات shell و exec للقناة
  • إعادة توجيه منفذ TCP المباشر والعكسي
  • البدائل التشفيرية الحديثة فقط: Ed25519 و ECDSA على المنحنيات NIST الرئيسية (P256 و P384 و P521) للتشفير غير المتماثل، و AES-GCM للتشفير المتماثل، و x25519 لتبادل المفاتيح
  • مصادقة المستخدم بكلمة مرور ومفتاح عام
  • يدعم جميع المنصات التي يدعمها SwiftNIO و Swift Crypto

كيف أستخدم SwiftNIO SSH؟

يوفر SwiftNIO SSH معالج قناة (ChannelHandler) من SwiftNIO هو NIOSSHHandler. يقوم هذا المعالج بتنفيذ الجزء الأكبر من بروتوكول SSH مباشرة. لا يُتوقع من المستخدمين إنشاء رسائل SSH مباشرة: بدلاً من ذلك، يتفاعلون مع NIOSSHHandler من خلال قنوات فرعية ومفوّضين.

SSH هو بروتوكول متعدد الإرسال: كل اتصال SSH ينقسم إلى قنوات اتصال ثنائية الاتجاه متعددة تسمى، بشكل مناسب، قنوات. يعكس SwiftNIO SSH هذا البناء باستخدام تجريد "القناة الفرعية". عندما ينشئ نظير قناة SSH جديدة، سيقوم SwiftNIO SSH بإنشاء قناة NIO جديدة (Channel) تستخدم لتمثيل كل حركة المرور على قناة SSH تلك. داخل هذه القناة الفرعية (Channel)، يتم ترتيب جميع الأحداث بدقة بالنسبة لبعضها البعض: ومع ذلك، قد يتم تداخل الأحداث في قنوات مختلفة (Channels) بحرية بواسطة التطبيق.

لذلك فإن اتصال SSH نشط يبدو كما يلي:

┌ ─ NIO Channel ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐

│     ┌────────────────────────────────┐    │
      │                                │
│     │                                │    │
      │                                │
│     │                                │    │
      │                                │
│     │                                │    │
      │         NIOSSHHandler          │───────────────────────┐
│     │                                │    │                  │
      │                                │                       │
│     │                                │    │                  │
      │                                │                       │
│     └────────────────────────────────┘    │                  │
                                                               │
└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘                  │
                                                               │
                                                               │
                                                               │
                                                               │
                                                               ▼
                     ┌── SSH Child Channel ─────────────────────────────────────────────────────────────┐
                     │                                                                                  │
                     │   ┌────────────────────────────────┐      ┌────────────────────────────────┐     ├───┐
                     │   │                                │      │                                │     │   │
                     │   │                                │      │                                │     │   ├───┐
                     │   │                                │      │                                │     │   │   │
                     │   │                                │      │                                │     │   │   │
                     │   │          User Handler          │      │          User Handler          │     │   │   │
                     │   │                                │      │                                │     │   │   │
                     │   │                                │      │                                │     │   │   │
                     │   │                                │      │                                │     │   │   │
                     │   │                                │      │                                │     │   │   │
                     │   └────────────────────────────────┘      └────────────────────────────────┘     │   │   │
                     │                                                                                  │   │   │
                     └───┬──────────────────────────────────────────────────────────────────────────────┘   │   │
                         │                                                                                  │   │
                         └───┬──────────────────────────────────────────────────────────────────────────────┘   │
                             │                                                                                  │
                             └──────────────────────────────────────────────────────────────────────────────────┘

يتم استدعاء قناة SSH بنوع القناة. يدعم NIOSSH ثلاثة أنواع: session و directTCPIP و forwardedTCPIP. النوع الأكثر شيوعًا هو session: يُستخدم session لتمثيل استدعاء برنامج، سواء كان برنامجًا محددًا باسم أو شل. النوعان الآخران للقناة مرتبطان بإعادة توجيه منفذ TCP، وسيتم مناقشتهما لاحقًا.

تعمل قناة SSH على نوع بيانات واحد: SSHChannelData. يغلف هذا الهيكل حقيقة أن SSH يدعم بيانات القناة العادية و "الموسعة". يتم استخدام بيانات القناة العادية (SSHChannelData.DataType.channel) للغالبية العظمى من البيانات الأساسية. في قنوات session، يتم استخدام نوع البيانات .channel للإدخال القياسي والإخراج القياسي: يتم استخدام نوع البيانات .stdErr للخطأ القياسي (بطبيعة الحال). في قنوات إعادة توجيه TCP، يكون نوع البيانات .channel هو النوع الوحيد المستخدم، ويمثل البيانات المُعاد توجيهها.

أحداث القناة

تمثل قناة session استدعاء أمر. يتم توصيل كيفية عمل القناة بالضبط في عدد من أحداث المستخدم الواردة. الأحداث التالية مهمة:

  • SSHChannelRequestEvent.PseudoTerminalRequest: يطلب تخصيص محطة طرفية زائفة.
  • SSHChannelRequestEvent.EnvironmentRequest: يطلب متغير بيئة واحد لاستدعاء الأمر. يُرسل دائمًا قبل الأمر نفسه.
  • SSHChannelRequestEvent.ShellRequest: يطلب أن يكون الأمر المطلوب استدعاؤه هو شل المستخدم المُصادق عليه.
  • SSHChannelRequestEvent.ExecRequest: يطلب استدعاء أمر معين.
  • SSHChannelRequestEvent.ExitStatus: يُستخدم للإشارة إلى أن الأمر البعيد قد خرج، ويقوم بتوصيل رمز الخروج.
  • SSHChannelRequestEvent.ExitSignal: يُستخدم للإشارة إلى أن الأمر البعيد قد تم إنهاؤه استجابة لإشارة، وما هي تلك الإشارة.
  • SSHChannelRequestEvent.SignalRequest: يُستخدم لإرسال إشارة إلى الأمر البعيد.
  • SSHChannelRequestEvent.LocalFlowControlRequest: يُستخدم للإشارة إلى ما إذا كان العميل قادرًا على إجراء التحكم في التدفق Ctrl-Q/Ctrl-S بنفسه.
  • SSHChannelRequestEvent.WindowChangeRequest: يُستخدم للتواصل مع تغيير في حجم نافذة الطرفية على العميل إلى المحطة الطرفية الزائفة المخصصة.
  • SSHChannelRequestEvent.SubsystemRequest: يُستخدم لطلب استدعاء نظام فرعي معين. معنى هذا خاص بحالات الاستخدام الفردية.

هذه الأحداث غير مستخدمة في رسائل إعادة توجيه المنفذ. تطبيقات SSH التي تدعم أنواع قنوات .session تحتاج إلى أن تكون مستعدة للتعامل مع معظم أو كل هذه الأحداث بطرق مختلفة.

كل من هذه الأحداث يحتوي أيضًا على حقل wantReply. يشير هذا إلى ما إذا كان الطلب يحتاج إلى رد للإشارة إلى النجاح أو الفشل. إذا كان الأمر كذلك، يتم استخدام الحدثين التاليين:

  • ChannelSuccessEvent، للتواصل بالنجاح.
  • ChannelFailureEvent، للتواصل بالفشل.

الإغلاق النصفي

يستخدم بروتوكول شبكة SSH بشكل واسع الإغلاق النصفي في القنوات الفرعية. عادةً ما يكون دعم الإغلاق النصفي معطلاً افتراضيًا في قنوات NIO، ويحترم SwiftNIO SSH هذا الإعداد الافتراضي في قنواته الفرعية أيضًا. ومع ذلك، إذا تركت هذا الإعداد على قيمته الافتراضية، فسوف تتصرف قنوات SSH الفرعية بشكل غير متوقع للغاية. لهذا السبب، يُوصى بشدة بتمكين دعم الإغلاق النصفي لجميع القنوات الفرعية:

channel.setOption(ChannelOptions.allowRemoteHalfClosure, true)

يستخدم هذا بعد ذلك دعم الإغلاق النصفي القياسي لـ NIO. سيتم التواصل مع النظير البعيد الذي يرسل EOF من خلال حدث مستخدم وارد، ChannelEvent.inputClosed. لإرسال EOF بنفسك، اتصل بـ close(mode: .output).

مصادقة المستخدم

مصادقة المستخدم جزء حيوي من SSH. لإدارتها، يستخدم SwiftNIO SSH زوجًا من بروتوكولات المفوّضين: NIOSSHClientUserAuthenticationDelegate و NIOSSHServerUserAuthenticationDelegate. يجب على العملاء والخوادم توفير تطبيقات لبروتوكولات المفوّضين هذه لإدارة مصادقة المستخدم.

بروتوكول العميل واضح ومباشر: سيقوم SwiftNIO SSH باستدعاء الطريقة nextAuthenticationType(availableMethods:nextChallengePromise:) على المفوّض. سيكون availableMethods مثيلاً من NIOSSHAvailableUserAuthenticationMethods الذي يوضح طرق المصادقة التي اقترحها الخادم بأنها مقبولة. يمكن للمفوّض بعد ذلك إكمال nextChallengePromise إما بطلب مصادقة جديد، أو بـ nil للإشارة إلى أن العميل قد نفد ما يمكنه تجربته.

بروتوكول الخادم أكثر تعقيدًا. يجب على المفوّض توفير خاصية supportedAuthenticationMethods التي توضح طرق المصادقة التي يدعمها المفوّض. بعد ذلك، في كل مرة يرسل العميل طلب مصادقة مستخدم، سيتم استدعاء الطريقة requestReceived(request:responsePromise:). قد يتم استدعاء هذه الطريقة عدة مرات بالتوازي، حيث يُسمح للعملاء بإصدار طلبات مصادقة بالتوازي. يجب أن يتم إكمال responsePromise بنتيجة المصادقة. هناك ثلاث نتائج: .success و .failure واضحتان، ولكن من حيث المبدأ يمكن للخادم أن يتطلب تحديات متعددة باستخدام .partialSuccess(remainingMethods:).

إعادة توجيه المنفذ المباشر

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

يمكن للعملاء فتح هذه القنوات مباشرة باستخدام نوع القناة .directTCPIP.

إعادة توجيه المنفذ عن بعد والطلبات العامة

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

يتم بدء الطلبات العامة باستخدام NIOSSHHandler.sendGlobalRequest، ويتم استلامها ومعالجتها عن طريق GlobalRequestDelegate. هناك طلبان عامان مدعومان اليوم:

  • GlobalRequest.TCPForwardingRequest.listen(host:port:): طلب من الخادم الاستماع على مضيف ومنفذ معينين.
  • GlobalRequest.TCPForwardingRequest.cancel(host:port:): طلب لإلغاء الاستماع على المضيف والمنفذ المعينين.

يمكن إخطار الخوادم بهذه الطلبات والاستجابة لها باستخدام GlobalRequestDelegate. الطريقة التي يجب تنفيذها هنا هي tcpForwardingRequest(_:handler:promise:). سيتم استدعاء هذه الطريقة في المفوّض في أي وقت يتم فيه استلام طلب عام. يتم تمرير الرد على الطلب إلى promise.

يتم بعد ذلك إرسال القنوات المعاد توجيهها من الخادم إلى العميل باستخدام نوع القناة .forwardedTCPIP.

الفئات