Обработка запросов http и https с использованием одного порта с помощью nginx

16

Мне было интересно, если nginx может обрабатывать запросы http и https на одном и том же порту . [*]

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

На уровне брандмауэра у меня может быть только один порт, перенаправляющий трафик на этот сервер . Итак, я хотел бы настроить nginx на этом сервере, чтобы он прослушивал запросы на один порт, а затем:

А) перенаправляет все запросы http://myhost.com/ *, чтобы они перешли на localhost: 8080 (где прослушивает lighttpd)

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

Как вы думаете, это может быть возможно? Если это так, как это можно сделать?

Я знаю, как это сделать, используя два разных порта. Проблема, с которой я сталкиваюсь, заключается в том, чтобы сделать это только с одним портом (к сожалению, я не могу контролировать конфигурацию брандмауэра в этой конкретной среде, поэтому я не могу обойти это ограничение). Использование методов, таких как переадресация портов через ssh для обхода брандмауэра, также не будет работать, потому что это должно работать для удаленных пользователей, у которых нет ничего, кроме веб-браузера и интернет-ссылки.

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

Заранее спасибо Алекс

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

alemartini
источник
HTTPS-запросы отправляются на порт 443 по умолчанию, поэтому даже если вы можете заставить это работать (и я думаю, что это возможно с небольшим количеством взлома), вам нужно будет использовать yourhost.com и yourhost.com:80 в качестве ссылок (или yourhost.com:443 и yourhost.com ).
Занчи
Хорошо, я новичок в Server Fault и не знаю, смогу ли я удалить свой вопрос. Поскольку кажется, что проблема сформулирована недостаточно четко, вместо этого я открою новый вопрос. В любом случае, большое спасибо всем, кто внес полезные предложения по этому вопросу.
Алемартини

Ответы:

18

Согласно статье в Википедии о кодах состояния, Nginx имеет собственный код ошибки при отправке http-трафика на порт https (код ошибки 497).

И в соответствии с документацией nginx на странице error_page вы можете определить URI, который будет отображаться для конкретной ошибки.
Таким образом, мы можем создать 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 через один и тот же порт

KOMU
источник
17

Для тех, кто может искать:

Добавьте ssl on;и error_page 497 $request_uri;к вашему определению сервера.

HoverHell
источник
8
Небольшое улучшение ответа HoverHells: добавьте ssl on;и error_page 497 =200 $request_uri;определение вашего сервера. Это изменит код состояния на 200.
Mawi12345
Этот ответ об ошибке объясняет, почему это решение работает.
SiliconMind
4

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

Zanchey
источник
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;
}
yaplik
источник
1

Я не думаю, что есть что-то, что может обрабатывать два разных протокола на одном порту ...

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

Уильям Хилсум
источник
Привет, Вил, и спасибо за ответ! Я думаю, что обслуживание всего через https могло бы быть вариантом, хотя я хотел бы иметь возможность настроить это так, как я описал. Возможно, это можно сделать, если передний веб-сервер (действующий в качестве обратного прокси-сервера) сможет установить нормальный сеанс http и затем обновить его до https без изменения портов. Я думаю, что это поведение описано в RFC2817 (обновление до TLS с использованием HTTP / 1.1), но я не уверен, что nginx или другие веб-серверы знают, как работать с этим стандартом.
Алемартини
У меня нет времени, чтобы прочитать весь RFC (и я не уверен, что достаточно умен, чтобы понять это!), Но вы говорите о стандартных переговорах до того, как будет установлен безопасный сеанс, или о полномасштабных различных сеансах? Я думаю, что понимаю немного больше - сервер обслуживает два порта, и именно прокси-сервер направляет запрос - звучит круто, но я никогда не видел, чтобы это было сделано. Возможно, решение могло бы быть путем создания одного безопасного сайта на одном порту и наличия целого виртуального каталога, который просто наследует / импортирует другой сайт? Это не
решает
1

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

Как подсказывает ваш комментарий к ответу Уилла, вы можете использовать обновление TLS (я полагаю, что более новые выпуски nginx поддерживают его, хотя я не пробовал), но это не работает HTTP и HTTPS, это просто работает HTTP с обновлением TLS. Проблема все еще в поддержке браузеров - большинство браузеров (до сих пор) не поддерживают ее. Если у вас ограниченный пул клиентов, то это возможно.

ombble
источник
1
Зашифрованный и незашифрованный HTTP-трафик может обрабатываться через один порт. Я хотел бы знать, возможно ли это с помощью nginx или других продуктов (например, lighttpd), действующих в качестве обратных прокси. Вполне возможно, что этот тип установки может быть обработан Apache, но я забыл упомянуть в своем первоначальном вопросе, что я бы предпочел не использовать Apache (хотя я бы сделал это, если на Linux нет другого выбора Платформа для достижения этой цели).
Алемартини
Как я сказал в своем ответе: «Я верю в поддержку более новой версии nginx [TLS upgrade], хотя я не пробовал». Если вам нужно, чтобы я прочитал руководство для вас, то вам не повезло.
Уомбл
Извините, если у меня сложилось впечатление, что мне нужен кто-то, кто прочитает для меня руководство. Просто кажется, что проблема (и вопросы о ней) не были описаны достаточно точно (моя ошибка), что привело к различным интерпретациям того, что я спрашивал или нуждался. Поэтому я решил открыть новый вопрос по этой проблеме и попытаться избежать любой возможной путаницы в отношении проблемы или конкретных вопросов по ней. В любом случае, спасибо за ваше время и за то, что поделились своим мнением.
Алемартини
0

Я не уверен, как это происходит, но CUPSD отвечает и на http, и на https через порт 631. Если nginx не может сделать это сейчас, возможно, они смогут узнать, как команда CUPS выполняет это, но CUPS находится под GPL, поэтому nginx, возможно, придется посмотреть на изменение своей лицензии, если они действительно хотят реализовать такую ​​возможность и не могут найти код для этого в другом месте.

Райан Грандж
источник
0

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

djangofan
источник
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 в этом примере), а затем прокси-сервер передает его на правильный порт сервера локально.

Сэм Булл
источник