Para quem pode pesquisar:
Adicione ssl on;
e error_page 497 $request_uri;
à sua definição de servidor.
Eu queria saber se o nginx é capaz de lidar com solicitações http e https na mesma porta . [*]
É isso que estou tentando fazer. Estou executando um servidor da web (lighttpd) manipulando solicitações http e um programa C que atende a uma seção específica da árvore de documentos por meio de https. Esses dois processos são executados no mesmo servidor.
No nível do firewall, eu posso ter apenas um tráfego de encaminhamento de porta para esse servidor . Então, o que eu gostaria de fazer é configurar o nginx neste servidor para que ele atenda a solicitações em uma única porta e, em seguida:
A) redireciona todos os pedidos de link * para que eles acessem o localhost: 8080 (onde o lighttpd está escutando)
B) se um usuário solicitar uma URL começando com, por exemplo, https: // myhost.com/app, ele envia essa solicitação para localhost: 8008 (programa C). Observe que, nesse caso, o tráfego entre o navegador remoto e o nginx deve ser criptografado.
Você acha que isso poderia ser possível? Se for, como pode ser feito?
Eu sei como fazer isso usando duas portas diferentes. O desafio que eu enfrento é fazer isso com apenas uma única porta (infelizmente, eu não tenho controle sobre a configuração do firewall neste ambiente em particular, então essa é uma restrição que não posso evitar). Usar técnicas como redirecionamento de porta reversa através do ssh para ignorar o firewall também não funcionará, porque isso deve funcionar para usuários remotos que não tenham nada além de um navegador da Web e um link da Internet.
Se isso estiver além dos recursos do nginx, você conhece algum outro produto que possa atender a esses requisitos? (até agora eu não tive sucesso em configurar isso com lighttpd e pound). Eu também prefiro evitar o Apache (embora eu esteja disposto a usá-lo se for a única opção possível).
Obrigado antecipadamente, Alex
[*] Só para esclarecer, estou falando sobre a manipulação de conexões HTTP criptografadas e não criptografadas através da mesma porta. Não importa se a criptografia é feita através de SSL ou TLS.
Para quem pode pesquisar:
Adicione ssl on;
e error_page 497 $request_uri;
à sua definição de servidor.
De acordo com o artigo da wikipedia sobre códigos de status, o Nginx tem um código de erro personalizado quando o tráfego http é enviado para a porta https (código de erro 497)
E, de acordo com os documentos nginx na error_page , você pode definir um URI que será exibido para um erro específico.
Assim, podemos criar um uri para o qual os clientes serão enviados quando o código de erro 497 for gerado.
#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;
}
}
No entanto, se um cliente fizer uma solicitação por meio de qualquer outro método, exceto um GET, essa solicitação será transformada em GET. Assim, para preservar o método de pedido que o cliente entrou via; Usamos redirecionamentos de processamento de erros, conforme mostrado em docs nginx em error_page
E é por isso que usamos o 301 =307
redirect.
Usando o arquivo nginx.conf mostrado aqui, podemos ter http e https escutando na mesma porta
Se você quisesse ser realmente inteligente, você poderia usar um proxy de conexão para farejar os primeiros bytes do fluxo de dados de entrada e entregar a conexão com base no conteúdo do byte 0: se for 0x16 (o SSL / TLS 'handshake' byte), passe a conexão para o lado do SSL, se for um caractere alfabético, faça o HTTP normal. Meu comentário sobre a numeração de portas é aplicável .
Sim, é possível, mas precisa corrigir o código-fonte do nginx (o HoverHell tem solução sem correção). O Nginx trata isso como configuração incorreta, em vez de configuração válida.
A variável $ ssl_session_id pode ser usada para diferir entre a conexão simples e ssl.
Patch contra 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)
Configuração do servidor:
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;
}
Eu não acho que exista algo que possa lidar com dois protocolos diferentes em uma única porta ...
Estou curioso para saber por que você só pode encaminhar uma porta, mas isso de lado ... não é o ideal, mas se eu estivesse no seu lugar, eu serviria tudo em https.
Você não pode suportar HTTP e HTTPS na mesma porta, porque ambas as extremidades da conexão esperam falar um determinado idioma, e não são espertas o suficiente para funcionar se a outra extremidade estiver falando outra coisa.
Como seu comentário para a resposta de Wil sugeriu, você poderia usar a atualização TLS (acredito que versões mais recentes do nginx o suportam, embora eu não tenha tentado), mas isso não está rodando HTTP e HTTPS, executando HTTP com atualização TLS. O problema ainda é o suporte ao navegador - a maioria dos navegadores (ainda) não o suporta. Se você tem um pool limitado de clientes, então esta é uma possibilidade, no entanto.
Não tenho certeza de como isso funciona, mas o CUPSD responde tanto a http quanto a https na porta 631. Se o nginx não puder fazer isso agora, talvez eles possam aprender como a equipe do CUPS faz isso, mas o CUPS está sob a GPL, então o nginx pode ter que mudar sua licença se quiser implementar tal recurso e não conseguir encontrar o código para fazê-lo em outro lugar.
Em teoria, você poderia ter uma página da Web acessível via HTTP, que é capaz de abrir um WebSocket em https: 443 para qualquer lugar que quiser. O handshake inicial do WebSocket é HTTP. Então, sim, é possível fazer uma página de aparência insegura realmente capaz de comunicação segura. Você pode fazer isso com a Biblioteca Netty .
Isto é finalmente possível fazer corretamente desde 1.15.2. Veja as informações aqui .
No seu nginx.conf, adicione um bloco como este (fora do bloco 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;
}
}
Em seguida, você pode criar seu bloco de servidor normal, mas ouvindo essas portas diferentes:
server {
listen 8000;
listen [::]:8000;
listen 8001 ssl;
listen [::]:8001 ssl;
...
Dessa forma, o bloco de fluxo pode pré-ler e detectar se é TLS ou não (na porta 8080 neste exemplo) e, em seguida, o proxy a transmite para a porta do servidor correta localmente.