Я шукав рішення цієї проблеми ввімкнення та вимкнення протягом останніх 16 місяців. Але кожен раз, коли я дивлюся, здається, що це неможливо зробити за допомогою протоколу SSH, як зазначено у відповідних RFC та реалізованих основними реалізаціями.
Однак якщо ви готові використовувати трохи модифікований клієнт SSH і ви готові використовувати протоколи таким чином, який не був призначений саме тоді, коли вони були розроблені, то цього можна досягти. Детальніше про це нижче.
Чому це неможливо
Клієнт не надсилає ім'я хоста в рамках протоколу SSH.
Він може надіслати ім'я хоста як частину пошуку DNS, але це може бути кешоване, і шлях від клієнта через резолютори до авторитетних серверів може не перетинати проксі, і навіть якщо у нього немає надійного способу асоціації конкретних пошукових записів DNS з конкретні клієнти SSH.
З цим протоколом SSH немає нічого фантазійного. Ви повинні вибрати сервер, навіть не побачивши банер версії SSH від клієнта. Ви повинні надіслати банер клієнту, перш ніж він надішле щось проксі. Банери з серверів можуть бути різними, і ви не маєте шансів здогадатися, який з них є правильним.
Хоча цей банер надсилається незашифрованим, ви не можете його змінити. Кожен шматочок цього банера буде підтверджений під час налаштування з'єднання, тож ви будете спричиняти зрив зв'язку трохи нижче лінії.
Висновок для мене досить чіткий, треба щось змінити на стороні клієнта, щоб цей зв’язок працював.
Більшість обхідних шляхів є інкапсуляцією трафіку SSH всередині іншого протоколу. Можна також уявити додаток до самого протоколу SSH, в якому банер версій, що надсилається клієнтом, включає ім'я хоста. Це може залишатися сумісним з існуючими серверами, оскільки частина банера в даний час вказана як поле ідентифікації вільної форми, і хоча клієнти, як правило, чекають банера версії з сервера, перш ніж надсилати свій власний, протокол дозволяє клієнту надсилати свій банер спочатку. Деякі останні версії ssh-клієнта (наприклад, версія Ubuntu 14.04) дійсно надсилають банер, не чекаючи банера сервера.
Я не знаю жодного клієнта, який вжив заходів для включення імені хоста сервера в цей банер. Я надсилаю виправлення до списку розсилки OpenSSH, щоб додати таку функцію. Але його було відхилено на основі бажання нікому не розкривати ім'я хоста, котра переслідує трафік SSH. Оскільки таємне ім’я хоста принципово не сумісне з функцією проксі-сервера, не очікуйте, що незабаром з’явиться офіційне розширення SNI для протоколу SSH.
Реальне рішення
Найкраще для мене вирішило використання IPv6.
За допомогою IPv6 у мене може бути окрема IP-адреса, призначена кожному серверу, тому шлюз може використовувати IP-адресу призначення, щоб дізнатися, на який сервер надіслати пакет. Клієнти SSH іноді можуть працювати в мережах, де єдиним способом отримати адресу IPv6 було б за допомогою Teredo. Відомо, що Teredo є ненадійним, але лише тоді, коли в корінному з'єднанні IPv6 використовується загальнодоступне реле Teredo. Можна просто поставити реле Тередо на шлюз, де ви запустили б проксі. Miredo можна встановити і налаштувати як реле менш ніж за п’ять хвилин.
Обхідний шлях
Можна використовувати хост стрибків / бастіон. Цей підхід призначений для тих випадків, коли ви не хочете виставляти SSH-порт окремих серверів безпосередньо у загальнодоступному Інтернеті. Це має додаткову перевагу щодо зменшення кількості IP-адреси, яка стоїть зовні, необхідної для SSH, і тому вона може бути використана в цьому сценарії. Той факт, що це рішення, призначене для додання іншого рівня захисту з міркувань безпеки, не заважає вам використовувати його, коли вам не потрібна додаткова безпека.
ssh -o ProxyCommand='ssh -W %h:%p user1@bastion' user2@target
Брудне зламати, щоб змусити його працювати, якщо справжнє рішення (IPv6) знаходиться поза вами
Хак, який я збираюся описати, повинен використовуватися лише в крайньому випадку. Перш ніж навіть подумати про використання цього хаку, я настійно рекомендую отримати IPv6 адресу для кожного із серверів, до яких ви хочете бути доступними зовні через SSH. Використовуйте IPv6 як свій основний метод доступу до ваших серверів SSH, і цей хак використовуйте лише тоді, коли вам потрібно запустити SSH-клієнт із мережі, що використовується лише для IPv4, де ви не маєте ніякого впливу на розгортання IPv6.
Ідея полягає в тому, що трафік між клієнтом і сервером повинен бути ідеально правильним трафіком SSH. Але проксі потрібно лише достатньо зрозуміти про потік пакетів, щоб ідентифікувати ім'я хоста. Оскільки SSH не визначає спосіб надсилання імені хоста, ви можете замість цього розглянути інші протоколи, які надають таку можливість.
І HTTP, і HTTPS дозволяють клієнту надіслати ім'я хоста до того, як сервер надішле будь-які дані. Тепер питання полягає в тому, чи можна побудувати потік байтів, який одночасно дійсний як трафік SSH і як HTTP, так і HTTPS. HTTP - це майже нестартер, але HTTP можливий (для досить ліберальних визначень HTTP).
SSH-2.0-OpenSSH_6.6.1 / HTTP/1.1
$:
Host: example.com
Це вам схоже на SSH чи HTTP? Це SSH і повністю сумісний з RFC (за винятком того, що деякі бінарні символи трохи заплуталися SF-рендерінгом).
Рядок версії SSH включає поле коментаря, яке у вищезазначеному має значення / HTTP/1.1
. Після нового рядка SSH має деякі двійкові пакетні дані. Перший пакет - це MSG_SSH_IGNORE
повідомлення, надіслане клієнтом і ігнорування сервера. Корисне навантаження, яке слід ігнорувати:
:
Host: example.com
Якщо проксі-сервер HTTP є достатньо ліберальним у тому, що він приймає, то та сама послідовність байтів буде інтерпретуватися як метод HTTP, який називається SSH-2.0-OpenSSH_6.6.1
і двійкові дані на початку повідомлення про ігнорування будуть інтерпретуватися як ім'я заголовка HTTP.
Проксі-сервер не розумів ні методу HTTP, ні першого заголовка. Але це міг зрозумітиHost
заголовок, що все, що йому потрібно для того, щоб знайти підсилку.
Для того, щоб це працювало, проксі повинен бути розроблений за принципом, що йому потрібно лише зрозуміти достатню кількість HTTP, щоб знайти бекенд, і після того, як бекенд знайдений, проксі просто передасть необмежений потік байтів і залишить справжнє завершення HTTP-з'єднання, яке повинно бути виконано бекендером.
Це може здатися трохи розтягнутим, щоб зробити так багато припущень щодо HTTP-проксі. Але якщо ви були готові встановити нову частину програмного забезпечення, розробленого з метою підтримки SSH, то вимоги до HTTP-проксі не здаються занадто поганими.
У власному випадку я виявив, що цей метод працює над вже встановленим проксі, не змінюючи код, конфігурацію чи інше. І це було для проксі-сервера, написаного лише HTTP і не маючи на увазі SSH.
Доказ концепції клієнта та проксі . (Відмовтеся від того, що проксі - це послуга, якою я керую. Не соромтеся замінювати посилання після підтвердження будь-якого іншого проксі для підтримки цього використання.)
Печери цього хака
- Не використовуйте його. Краще скористатися реальним рішенням, яким є IPv6.
- Якщо проксі намагається зрозуміти трафік HTTP, він, безумовно, перерветься.
- Покладатися на модифікований клієнт SSH - це не приємно.