Назад | Перейти на главную страницу

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

мне было интересно, может ли 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.

Согласно статье в Википедии о кодах состояния, 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 на одном и том же порту.

Для тех, кто ищет:

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

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

Да, это возможно, но требует исправления исходного кода 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;
}

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

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

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

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

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

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

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