
موجه SSH مدرك للشبكة - يوجه الاتصالات إلى عناوين IP/منافذ/مفاتيح/مضيفات قفز مختلفة بناءً على VPN أو الشبكة النشطة.
موجه SSH مدرك للشبكة. يكتشف شبكتك النشطة أو VPN ويختار تلقائيًا المضيف والمنفذ وملف الهوية ومضيف القفز الصحيح لكل اتصال SSH — دون لمس ~/.ssh/config.
حدد كل مضيف منطقي مرة واحدة باستخدام ملف تعريف default وإعدادات اختيارية لكل شبكة. في كل اتصال، يكتشف sshroute الشبكة التي تتصل منها (VPN، شبكة المكتب المحلية، نظير WireGuard، إلخ) ويحل معاملات SSH الصحيحة قبل تسليم الاتصال إلى /usr/bin/ssh الحقيقي.
ssh myserver
→ يكتشف sshroute: corp-vpn نشط
→ يحل: 10.100.0.50:2222 عبر bastion.corp.internal
→ ينفذ /usr/bin/ssh -p 2222 -i ~/.ssh/corp_key -J bastion.corp.internal 10.100.0.50
مختبرك المنزلي على الأرجح له واقعان على الأقل: إما أنك جالس في المنزل على الشبكة المحلية، أو أنك بعيد وتتصل عبر WireGuard أو VPN آخر. المشكلة أن ~/.ssh/config لا يعرف أي واقع أنت فيه — لذا ينتهي بك الأمر بأسماء مستعارة منفصلة (server-lan، server-vpn)، أو مضيف قفز يعمل نصف الوقت فقط، أو تحفظ عناوين IP عن ظهر قلب.
يحل sshroute هذه المشكلة باكتشاف شبكتك الحالية قبل كل اتصال. عندما يكون واجهة WireGuard نشطة ومسار النظير موجودًا، يتصل مباشرة بعنوان IP النفق. عندما تكون على الشبكة المحلية، يستخدم العنوان المحلي. عندما لا يكون أي منهما قابلًا للوصول، يعود إلى اسم المضيف العام. اسم مستعار واحد، ثلاثة أوضاع، بدون تبديل يدوي.
كما أنه يعترض SSH بشفافية — git push و rsync و scp كلها تمر عبره تلقائيًا بمجرد إعداد وضع الظل (shadow mode). بدون محولات أو دوال شيل أو تفكير.
الشبكات المؤسسية أسوأ. لديك الإنترنت العام، ربما VPN من موقع إلى موقع، ربما VPN شخصي بتقسيم النفق، وداخل ذلك لديك مضيفات قفز مختلفة اعتمادًا على البيئة التي تستهدفها — التطوير، التجهيز، الإنتاج، كل منها له حصن ومفتاح خاص به. الحفاظ على هذا في ~/.ssh/config يعني إما ملف إعداد ضخم ينكسر عندما تتغير البنية التحتية، أو كتابة سكربت يصونه كل شخص في الفريق بشكل مختلف.
يتيح لك sshroute تعريف منطق التوجيه بشكل تصريحي، والاحتفاظ به في ملف YAML مُرقم الإصدار، ومشاركته عبر الفريق. يعمل نفس الإعداد للجميع — يتم اكتشاف الشبكة الصحيحة تلقائيًا بناءً على الواجهات أو المسارات النشطة على كل جهاز. يتم حل المفاتيح والمنافذ والمستخدمين ومضيفات القفز دون أن يضطر المستخدم إلى التفكير في الأمر.
Teleport و Boundary هما فئة مختلفة — يضيفان التحكم في الوصول وسجلات التدقيق والمصادقة القائمة على الشهادات فوق التوجيه. إذا كان هذا ما تحتاجه، استخدمهما. sshroute لمن يريدون ذكاء التوجيه دون العبء التشغيلي لتشغيل خادم مصادقة مركزي.
قم بتحميل أحدث إصدار من GitHub Releases. الثنائيات متوفرة لأنظمة Linux و macOS و Android على AMD64 و ARM64.
go install github.com/thereisnotime/sshroute@latest
قم بتحميل حزمة android_arm64 المضغوطة من GitHub Releases، واستخرجها، وضع الثنائي في ~/.local/bin:
mkdir -p ~/.local/bin
curl -Lo "$TMPDIR/sshroute.tar.gz" \
https://github.com/thereisnotime/sshroute/releases/latest/download/sshroute_android_arm64.tar.gz
tar -xzf "$TMPDIR/sshroute.tar.gz" -C ~/.local/bin sshroute
chmod +x ~/.local/bin/sshroute
أضف ~/.local/bin إلى PATH في ~/.bashrc أو ~/.profile إذا لم يكن موجودًا بالفعل:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
بدلاً من ذلك، قم بتجميع من المصدر باستخدام Go الخاص بـ Termux. نظرًا لأن سلسلة أدوات Go الرسمية لا تنشر ثنائيات android/arm64، قم بتعيين GOTOOLCHAIN=local لاستخدام ما يوفره Termux:
GOTOOLCHAIN=local go install github.com/thereisnotime/sshroute@latest
بعد التثبيت، قم بتعيين مسار ثنائي SSH لأن Termux لا يحتوي على /usr/bin/ssh:
# ~/.config/sshroute/config.yaml
ssh_binary: /data/data/com.termux/files/usr/bin/ssh
أو عبر متغير بيئة: export SSHROUTE_SSH=$(which ssh)
docker run --rm -v ~/.config/sshroute:/root/.config/sshroute \
ghcr.io/thereisnotime/sshroute network
podman run --rm -v ~/.config/sshroute:/root/.config/sshroute \
ghcr.io/thereisnotime/sshroute network
على الأنظمة التي تعمل بـ SELinux (Fedora، RHEL، إلخ) أضف :Z إلى علامة الحجم:
podman run --rm -v ~/.config/sshroute:/root/.config/sshroute:Z \
ghcr.io/thereisnotime/sshroute network
قم بتثبيت sshroute كـ ssh في وقت مبكر من $PATH. جميع استدعاءات SSH — من الطرفية، git، rsync، scp — يتم اعتراضها تلقائيًا. المضيفون غير الموجودين في إعداداتك يتم تمريرهم إلى /usr/bin/ssh دون تغيير.
mkdir -p ~/.local/bin
ln -s $(which sshroute) ~/.local/bin/ssh
# أضف إلى ~/.bashrc أو ~/.zshrc إذا لم يكن موجودًا بالفعل:
export PATH="$HOME/.local/bin:$PATH"
# إضافة مضيف بملف تعريف افتراضي
sshroute add myserver --host myserver.example.com --user alice --key ~/.ssh/id_ed25519
# إضافة إعداد خاص بشبكة VPN
sshroute add myserver --network vpn --host 10.8.0.50 --port 2222 --jump bastion.vpn
# الاتصال — يتم اكتشاف الشبكة تلقائيًا
sshroute connect myserver
# معاينة الأمر المحلول دون تشغيله
sshroute connect myserver --dry-run
# رؤية الشبكة النشطة حاليًا
sshroute network
تنطبق هذه العلامات على كل أمر:
initإنشاء ملف إعدادات ابتدائي مع أمثلة معلقة. يفشل إذا كان الملف موجودًا بالفعل.
| العلم | الافتراضي | الوصف |
|---|---|---|
--force | false | استبدال ملف الإعدادات الموجود |
connect <alias>اكتشاف الشبكة النشطة، حل معاملات SSH لـ alias، وتنفيذ ثنائي SSH الحقيقي. أي وسائط إضافية بعد الاسم المستعار يتم تمريرها إلى SSH دون تغيير.
مع --reconnect، يحافظ sshroute على اتصال ssh عبر الاتصالات المنقطعة (نوم الكمبيوتر المحمول، انتقال WiFi، التجوال بين الشبكات). نظرًا لأنه يعيد اكتشاف الشبكة في كل إعادة اتصال، فإنه يتبعك إلى مسار مختلف: على سبيل المثال، النوم على الشبكة المحلية والاستيقاظ على نقطة ساخنة يعيد الاتصال عبر المسار العام بدلاً من إعادة محاولة عنوان LAN الذي لم يعد قابلاً للوصول. إنهاء جلسة نظيف (رمز الخروج 0) أو فشل المصادقة/الأمر البعيد يوقف الحلقة؛ فقط انقطاعات الاتصال الحقيقية تعيد الاتصال. تعمل إعادة الاتصال بتشغيل ssh كعملية فرعية (مثل --fallback)، لذلك يظل sshroute مقيمًا للجلسة؛ SIGINT/SIGTERM يوقفها. حالة الجلسة عبر الانقطاع هي مهمة أداة تعدد الإرسال الخاصة بك (tmux/zellij)؛ اجمع --reconnect مع -- tmux attach أو -- zellij attach -c <name> لتعود مباشرة إلى جلستك:
sshroute connect myserver --reconnect --fallback -- zellij attach -c work
listعرض جميع المضيفين المُعدين ومعاملات SSH التي سيتم استخدامها على الشبكة الحالية. يدعم -o table|json|yaml.
add <alias>إضافة مضيف أو تحديث موجود. العلامات المحذوفة تحتفظ بقيمتها الحالية. قم بتشغيله عدة مرات بقيم --network مختلفة لبناء إعدادات خاصة بكل شبكة.
remove <alias>إزالة جميع ملفات تعريف alias من الإعدادات.
networkطباعة اسم الشبكة المكتشفة حاليًا (أو default إذا لم يتطابق أي شيء).
network listعرض جميع الشبكات المُعدة مع أولويتها وقواعد الفحص والحالة النشطة الحالية. يدعم -o table|json|yaml.
network test <name>تشغيل كل فحص لشبكة name وطباعة نجاح/فشل لكل قاعدة. مفيد لتصحيح منطق الكشف.
configطباعة المسار المحلول لملف الإعدادات.
config editفتح ملف الإعدادات في $EDITOR (يعود إلى nano). يقوم بإنشاء الملف والدليل الأصلي إذا لم يكونا موجودين.
resolve <alias>طباعة معاملات SSH التي سيتم استخدامها لـ alias على الشبكة الحالية. مفيد للتصحيح والبرمجة النصية. استخدم --network <name> لتجاوز الشبكة المكتشفة. يدعم -o table|json|yaml.
| العلم | الافتراضي | الوصف |
|---|---|---|
--network | كشف تلقائي | ملف تعريف الشبكة للحل بناءً عليه |
copy <alias> <src> <dst>نسخ الملفات إلى أو من مضيف مُعد باستخدام scp مع نفس المعاملات المحلولة (المفتاح، المنفذ، القفز) مثل connect. استخدم بناء الجملة <alias>:<path> للمسارات البعيدة:
sshroute copy myserver ./local.txt myserver:/remote/path/
sshroute copy myserver myserver:/remote/file.txt ./local/
يتجاوز متغير البيئة SSHROUTE_SCP ثنائي scp المستخدم.
versionطباعة الإصدار و commit git وتاريخ البناء ومعلومات وقت تشغيل Go.
updateتحديث sshroute في نفس المكان إلى أحدث إصدار GitHub. يقوم بتنزيل الأرشيف لنظامك الأساسي، والتحقق من sha256 الخاص به مقابل checksums.txt، وإذا كان cosign مثبتًا — يقوم بالتحقق من توقيع cosign للإصدار، قبل استبدال الثنائي قيد التشغيل بشكل ذري.
sshroute update # تحميل والتحقق وتثبيت أحدث إصدار
sshroute update --check # فقط الإبلاغ عما إذا كان إصدار أحدث متاحًا
sshroute update --force # إعادة تثبيت الأحدث حتى لو كان حاليًا بالفعل
إذا فشل التحقق من sha256 (أو cosign، إذا كان موجودًا)، يتم إحباط التحديث ويترك الثنائي دون تغيير. هذا يستهدف تثبيتات الثنائي الصادر؛ إذا قمت بالتثبيت عبر go install أو مدير حزم، فتحديث باستخدام ذلك بدلاً من ذلك.
الموقع الافتراضي: ~/.config/sshroute/config.yaml
networks:
corp-vpn:
priority: 10 # الأقل = يُفحص أولاً
checks:
- type: interface
match: wg0
- type: route
match: 10.100.0.0
office:
priority: 20
checks:
- type: ping
host: 192.168.1.1
timeout: 500ms
hosts:
myserver:
default: # مطلوب — يُستخدم عندما لا تتطابق أي شبكة
host: myserver.example.com
port: 22
user: alice
key: ~/.ssh/id_ed25519
options: # اختياري — يُمرر كعلامات SSH -o Key=Value
ConnectTimeout: "10"
ServerAliveInterval: "30"
corp-vpn:
host: 10.100.0.50
port: 2222
key: ~/.ssh/corp_key
jump: bastion.corp.internal
options:
ConnectTimeout: "5" # يتجاوز الافتراضي لهذه الشبكة فقط
office:
host: 192.168.1.50
كل مضيف يجب أن يكون له ملف تعريف default. ملفات تعريف الشبكة تحتاج فقط إلى تحديد الحقول التي تختلف عن الافتراضي — الحقول غير المعينة ترث من default.
يتم دمج مفاتيح options من default إلى ملفات تعريف الشبكة — قيم الشبكة تتجاوز المفاتيح المتطابقة، المفاتيح غير المتداخلة موروثة.
يتم تقييم الشبكات بترتيب priority (أقل قيمة أولاً). الترتيب الأبجدي يفصل التعادل. يتم استخدام أول شبكة تمر جميع فحوصاتها؛ إذا لم يتطابق شيء، يتم تطبيق default.
الفحوصات المتعددة داخل تعريف شبكة واحد تستخدم منطق AND — يجب أن تمر جميعها.
ملفات إعدادات جاهزة للاستخدام موجودة في examples/:
أدلة متعمقة في docs/:
جميع أوامر القائمة تدعم تنسيقات إخراج متعددة:
sshroute list # جدول (الافتراضي)
sshroute list -o json # JSON — للبرمجة النصية
sshroute list -o yaml # YAML
sshroute network list -o json
احصل على البرنامج — قم بتحميل ثنائي مُجمّع مسبقًا من Releases أو ثبته باستخدام go install github.com/thereisnotime/sshroute@latest أو ابنِ من المصدر.
الملاحظات وتقارير الأخطاء — افتح مشكلة على GitHub Issues. استخدم قالب تقرير الأخطاء للسلوك غير المتوقع وقالب طلب الميزة للأفكار.
المساهمة — راجع CONTRIBUTING.md لكيفية إعداد المشروع وتشغيل الاختبارات وفتح طلب سحب. يجب الإبلاغ عن الثغرات الأمنية بشكل خاص عبر GitHub Security Advisories.
git clone [email protected]:thereisnotime/sshroute.git
cd sshroute
just build # ينتج bin/sshroute
just build-all # ترجمة متقاطعة linux/darwin × amd64/arm64
just test # تشغيل الاختبارات مع كاشف السباق
just install # go install مع حقن إصدار ldflags
|
|
| الميزة | ~/.ssh/config | WireGuard فقط | Teleport / Boundary | sshroute |
|---|
| يكتشف شبكتك الحالية | ❌ | ❌ | ❌ | ✅ |
| يختار أفضل مسار تلقائيًا | ❌ | ❌ | ❌ | ✅ |
| يعود للخلف عند فشل الاتصال | ❌ | ❌ | ✅ | ✅ |
| إعادة الاتصال تلقائيًا + إعادة التوجيه عند الانقطاع | ❌ | ⚠️ النفق يتنقل | ⚠️ عبر وكيل ثابت | ✅ |
| أمر واحد لكل مضيف، أي موقع | ❌ | ⚠️ يجب أن يكون VPN نشطًا | ✅ | ✅ |
| حجم الإعداد لـ 10 مضيفين × 4 مسارات | 📄 ~600 سطر | 📄 ~600 سطر + إعداد VPN | 📄 إعداد من جانب الخادم | 📄 ~60 سطر |
| الأجهزة المحمولة المتجولة | ⚠️ أسماء مستعارة يدوية | ⚠️ VPN مطلوب | ✅ | ✅ |
| سلسلة مضيف القفز تلقائيًا | ⚠️ -J يدوي | ➖ غير قابل للتطبيق | ✅ | ✅ |
| يعمل مع scp / rsync / git / Ansible | ✅ | ✅ | ⚠️ جزئي | ✅ |
| لا حاجة لتثبيت من جانب الخادم على الأهداف | ✅ | ❌ | ❌ | ✅ |
| لا حاجة لخادم مصادقة أو خدمة لتشغيلها | ✅ | ❌ | ❌ | ✅ |
| لا حاجة لعميل وكيل | ✅ | ❌ | ❌ | ✅ |
| مفتوح المصدر، مستضاف بالكامل ذاتيًا | ✅ | ✅ | ⚠️ نواة مفتوحة | ✅ |
| العلم | متغير البيئة | الافتراضي | الوصف |
|---|
--config | SSHROUTE_CONFIG | ~/.config/sshroute/config.yaml | مسار ملف الإعدادات |
-o, --output | table | تنسيق الإخراج: table، json، yaml | |
-v, --verbose | SSHROUTE_VERBOSE=1 | false | تسجيل التصحيح إلى stderr |
--dry-run | false | طباعة أمر SSH المحلول دون تنفيذه |
| العلم | الافتراضي | الوصف |
|---|
--fallback | false | تجربة كل ملف تعريف بترتيب الأولوية، وإعادة المحاولة التالية فقط عند فشل الاتصال (رمز الخروج 255) |
--reconnect | false | الإشراف على الاتصال وإعادة الاتصال تلقائيًا عند انقطاعه، مع إعادة اكتشاف الشبكة النشطة وإعادة حل المسار في كل مرة |
--reconnect-delay | 2s | الانتظار بين محاولات إعادة الاتصال عند تعيين --reconnect |
| العلم | الافتراضي | الوصف |
|---|
--host | اسم المضيف أو عنوان IP | |
--port | 22 | منفذ SSH |
--user | اسم مستخدم SSH | |
--key | مسار ملف الهوية (يدعم ~) | |
--jump | مضيف القفز — يُمرر كـ -J إلى SSH | |
--network | default | ملف تعريف الشبكة لكتابة المعاملات فيه |
| الحقل | النوع | الوصف |
|---|
host | string | اسم المضيف أو عنوان IP |
port | int | منفذ SSH (الافتراضي: 22) |
user | string | مستخدم SSH |
key | string | مسار ملف الهوية (يتم توسيع ~) |
jump | string | اسم مستعار لمضيف القفز أو user@host |
options | map | علامات SSH -o Key=Value عشوائية (مثل ConnectTimeout، StrictHostKeyChecking) |
comment | string | وصف يظهر في sshroute list |
tags | list | علامات للتصفية مع sshroute list --tag |
| نوع الفحص | يمر عندما | الحقول المطلوبة |
|---|
route | يظهر الشبكة الفرعية/عنوان IP في جدول توجيه النواة | match |
interface | تكون الواجهة المسماة موجودة ونشطة تشغيليًا | match |
ping | يستجيب المضيف لـ ICMP echo خلال المهلة | host، timeout (اختياري، الافتراضي 2s) |
exec | يخرج أمر الصدفة برمز 0 | command |
| الملف | حالة الاستخدام |
|---|
basic.yaml | مضيف واحد، VPN مقابل العودة للعام |
multi-network.yaml | شبكة المكتب المحلية، VPN الشركة، VPN البعيد، العام |
wireguard-backconnect.yaml | نظير WireGuard يعاود الاتصال بك |
jump-hosts.yaml | حصون مختلفة لكل شبكة |
multi-zone-roaming.yaml | معمل منزلي متعدد المناطق مع بوابة WireGuard وأجهزة محمولة متجولة |
| الدليل | الوصف |
|---|
| إعداد المعمل المنزلي | معمل منزلي متعدد المناطق مع WireGuard ومضيفات القفز و NAS وعقد k3s |
| التجوال متعدد المناطق | شبكات LAN متعددة، بوابة WireGuard، أجهزة محمولة تتجول بين الشبكات |
| البيئة المؤسسية / متعددة البيئات | تطوير/تجهيز/إنتاج مع حصون خاصة بكل بيئة وكشف VPN |
| وضع الظل | استبدال SSH الشفاف — git، rsync، scp، Ansible |
| إكمال الصدفة | إكمال الاسم المستعار ديناميكيًا لـ bash، zsh، fish |
| البرمجة النصية والأتمتة | استخدام resolve و copy في السكربتات وخطوط أنابيب CI |