Обробка запитів http та https за допомогою одного порту з nginx


16

Мені було цікаво, чи вміє nginx обробляти http та https-запити на одному порті . [*]

Це те, що я намагаюся зробити. Я запускаю веб-сервер (lighttpd), що обробляє http-запити, і програму C, яка обслуговує певний розділ дерева документів через https. Ці два процеси працюють на одному сервері.

На рівні брандмауера я можу мати лише один порт, який перенаправляє трафік на цей сервер . Отже, що я хотів би зробити, це встановити nginx на цьому сервері, щоб він слухав запити на одному порту, а потім:

A) переспрямовує всі запити http://myhost.com/ *, щоб вони перейшли до localhost: 8080 (де прослуховується lighttpd)

Б) якщо користувач запитує URL, починаючи, наприклад, з https: // myhost.com/app, він надсилає цей запит до localhost: 8008 (програма C). Зауважте, що в цьому випадку трафік між віддаленим браузером та nginx повинен бути зашифрований.

Як ви вважаєте, це могло бути можливим? Якщо це так, як це можна зробити?

Я знаю, як це зробити за допомогою двох різних портів. Проблема, з якою я стикаюсь, виконує це лише за допомогою одного порту (на жаль, у мене немає контролю над конфігурацією брандмауера в даному конкретному середовищі, тому це обмеження, яке я не можу уникнути). Використання таких методів, як зворотне просування порту через ssh для обходу брандмауера, також не працюватиме, оскільки це має працювати для віддалених користувачів, які не мають нічого іншого, ніж веб-браузер та Інтернет-посилання.

Якщо це більше можливостей nginx, чи знаєте ви про будь-який інший продукт, який міг би відповідати цим вимогам? (поки що я невдало налаштовував це з lighttpd та фунтом). Я також вважаю за краще уникати Apache (хоча я готовий використовувати його, якщо це єдиний можливий вибір).

Заздалегідь дякую, Алекс

[*] Щоб зрозуміти, я говорю про обробку зашифрованих та незашифрованих HTTP-з'єднань через один і той же порт. Не має значення, чи шифрування здійснюється через SSL або TLS.


HTTPS-запити переходять до порту 443 за замовчуванням, тож навіть якщо ви можете працювати так (і я думаю, що це можливо за допомогою трохи хакерів), вам потрібно буде використовувати yourhost.com і yourhost.com:80 в якості посилань (або yourhost.com:243 та yourhost.com ).
Zanchey

Гаразд, я новачок у серверній помилці, і я не знаю, чи можу я видалити власне питання. Оскільки здається, що проблема недостатньо чітко сформульована, я відкрию нове запитання замість цього. У будь-якому випадку, дякую всім, хто зробив корисні пропозиції щодо цього питання.
alemartini

Відповіді:


18

Відповідно до статті вікіпедії про коди статусу, у Nginx є спеціальний код помилки, коли трафік http надсилається на порт https (код помилки 497)

Відповідно до документів nginx на сторінці error_page , ви можете визначити URI, який буде показаний для конкретної помилки.
Таким чином, ми можемо створити ури, до яких будуть надсилатися клієнти, коли буде порушено код помилки 497.

nginx.conf

#lets assume your IP address is 89.89.89.89 and also that you want nginx to listen on port 7000 and your app is running on port 3000

server {
    listen 7000 ssl;

    ssl_certificate /path/to/ssl_certificate.cer;
    ssl_certificate_key /path/to/ssl_certificate_key.key;
    ssl_client_certificate /path/to/ssl_client_certificate.cer;

    error_page 497 301 =307 https://89.89.89.89:7000$request_uri;

    location / {
        proxy_pass http://89.89.89.89:3000/;

        proxy_pass_header Server;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Protocol $scheme;
    }
}

Однак якщо клієнт робить запит будь-яким іншим способом, окрім GET, цей запит буде перетворений на GET. Таким чином, зберегти метод запиту, через який клієнт зайшов; ми використовуємо переадресації обробки помилок, як показано в документах nginx на сторінці error_page

Ось чому ми використовуємо 301 =307переспрямування.

Використовуючи показаний тут файл nginx.conf, ми можемо прослуховувати http і https на одному порті


17

Для тих, хто може шукати:

Додайте ssl on;і error_page 497 $request_uri;до свого визначення сервера.


8
Мало поліпшення HoverHells відповісти: Додати ssl on;і error_page 497 =200 $request_uri;до вашим визначенням сервера. Це змінить код статусу на 200.
Mawi12345

Ця відповідь про помилку пояснює, чому це рішення працює.
SiliconMind

4

Якщо ви хочете бути дійсно розумними, ви можете використовувати проксі-з'єднання, щоб нюхати перші пару байтів вхідного потоку даних і передавати з'єднання на основі вмісту байту 0: якщо це 0x16 (SSL / TLS ' рукостискання 'байт'), передайте з'єднання стороні SSL, якщо це алфавітний символ, зробіть звичайний HTTP. Мій коментар щодо нумерації портів застосовується .


2

Так, це можливо, але потрібно виправлення вихідного коду nginx (HoverHell має рішення без виправлення). Nginx трактує це як неправильну конфігурацію, а не дійсну конфігурацію.

Змінна $ ssl_session_id може використовуватися для відмінності між звичайним та ssl-з'єднанням.

Виправлення проти nginx-0.7.65:

--- src/http/ngx_http_request.c-orig    2011-05-03 15:47:09.000000000 +0200
+++ src/http/ngx_http_request.c 2011-05-03 15:44:01.000000000 +0200
@@ -1545,12 +1545,14 @@

    c = r->connection;

+    /* disable plain http over https port warning
     if (r->plain_http) {
         ngx_log_error(NGX_LOG_INFO, c->log, 0,
                       "client sent plain HTTP request to HTTPS port");
         ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);
         return;
     }
+    */

#if (NGX_HTTP_SSL)

Конфігурація сервера:

server {
    listen 80;
    index index.html;

    location / {
        root html;
        if ($ssl_session_id) {
            root html_ssl;
        }
    }

    ssl on;
    ssl_certificate cert.crt;
    ssl_certificate_key cert.key;
}

1

Я не думаю, що є щось, що може обробляти два різних протоколи на одному порті ...

Мені цікаво, чому ви можете переслати лише один порт, але це вбік ... це не ідеально, але якби я був у вашому взутті, я б обслуговував все на https.


Привіт Віл, і дякую за вашу відповідь! Я думаю, що розміщення всього через https може бути варіантом, хоча я хотів би мати можливість налаштувати це так, як я описав. Можливо, це могло б бути зроблено, якби передній веб-сервер (виступаючи як зворотний проксі) міг встановити нормальний сеанс http, а потім оновити його до https, не змінюючи порти. Я думаю, що така поведінка описана в RFC2817 (оновлення до TLS за допомогою HTTP / 1.1), але я не впевнений, чи nginx чи інші веб-сервери знають, як боротися з цим стандартом.
alemartini

Я не встигаю прочитати цілий RFC (і не впевнений, що я досить розумний, щоб це зрозуміти!), Але ви говорите про стандартні переговори до встановлення безпечної сесії або повного виходу різних сесій? Я думаю, що я розумію трохи більше - сервер обслуговується на двох портах, і саме проксі посилається на запит - звучить круто, але я ніколи не бачив цього робити. Можливо, рішення може бути через створення одного захищеного сайту на одному порту та наявність цілого віртуального каталогу, який просто успадковує / імпортує інший веб-сайт? Це не вирішує усі питання, але може просто спрацювати: S
William Hilsum

1

Ви не можете підтримувати і HTTP, і HTTPS через один і той же порт, тому що обидва кінці з'єднання очікують спілкуватися певною мовою, і вони недостатньо розумні, щоб випрацювати, якщо інший кінець говорить щось інше.

Як ваш коментар до відповіді Віла запропонував, ви можете використовувати оновлення TLS (я вважаю, що новіші версії nginx підтримують це, хоча я не пробував), але це не запущені HTTP і HTTPS, це просто запуск HTTP з оновленням TLS. Проблема все ще є підтримкою браузера - більшість браузерів (досі) не підтримують її. Якщо у вас обмежений пул клієнтів, то це є можливість.


1
Зашифрований і незашифрований HTTP-трафік може оброблятися через один порт. Що я хотів би знати, якщо це можливо, використовуючи nginx або інші продукти (наприклад, lighttpd), що діють як зворотні проксі. Цілком імовірно, цим способом налаштування може займатися Apache, але я забув згадати в своєму первісному запитанні, що я вважаю за краще не використовувати Apache (хоча я би зробив це, якщо в Linux немає іншого вибору платформа для цього).
alemartini

Як я вже говорив у своїй відповіді, "я вважаю, що новіша підтримка випуску nginx [оновлення TLS], хоча я не пробував". Якщо вам потрібно, щоб я прочитав посібник для вас, то вам не пощастило.
жіноча

Вибачте, якщо мені склалося враження, що мені потрібен хтось, щоб прочитати мені посібник. Просто здається, що проблема (і питання щодо неї) були описані недостатньо точно (моя помилка), що призводило до різних інтерпретацій того, що я просив чи потребував. Тому я вирішив відкрити нове запитання щодо цього питання і спробувати уникнути будь-якої можливої ​​плутанини щодо проблеми чи конкретних питань щодо неї. У будь-якому випадку, дякую за ваш час та за те, що ви поділились своїми ідеями.
алемартіні

0

Я не впевнений, як це знімає, але CUPSD відповідає на http і https на порту 631. Якщо nginx не може цього зробити, можливо, вони можуть дізнатися, як команда CUPS знімає його, але CUPS знаходиться під GPL, тому nginx, можливо, доведеться поглянути на зміну своєї ліцензії, якщо вони хочуть реалізувати таку можливість і не можуть знайти код для цього в інших місцях.


0

Теоретично у вас може бути веб-сторінка, доступна через HTTP, яка може відкрити WebSocket на https: 443 у будь-якому місці, де вона захоче. Початкове рукостискання WebSocket - це HTTP. Отже, так, можна створити незахищену сторінку, яка насправді здатна забезпечити безпечне спілкування. Ви можете зробити це за допомогою бібліотеки Netty .


0

Зрештою, це можливо зробити належним чином з 1.15.2. Інформацію дивіться тут .

У свій nginx.conf додайте такий блок (поза блоком http):

stream {
    upstream http {
        server localhost:8000;
    }

    upstream https {
        server localhost:8001;
    }

    map $ssl_preread_protocol $upstream {
        default https;
        "" http;
    }

    server {
        listen 8080;
        listen [::]:8080;
        proxy_pass $upstream;
        ssl_preread on;
    }
}

Тоді ви можете створити свій звичайний серверний блок, але прослуховуючи ці різні порти:

server {
    listen 8000;
    listen [::]:8000;
    listen 8001 ssl;
    listen [::]:8001 ssl;
...

Таким чином, блок потоку здатний попередньо читати та виявляти, чи є він TLS чи ні (на порту 8080 у цьому прикладі), а потім проксі передає його на правильний порт сервера локально.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.