nginx: como impedir que um bloco de servidor SSL exatamente nomeado atue como catchall para todos os SSL

17

Eu tenho um servidor web com muitos servidores virtuais. Apenas 1 deles é SSL. O problema é que, como não há nenhum bloco de servidor completo que atenda ao SSL, qualquer solicitação https para outros sites é atendida pelo bloco 1 SSL.

Minha configuração, basicamente, fica assim:

# the catch all
server {
  listen 80 default;

  # I could add this, but since I have no default cert, I cannot enable SSL,
  # and this listen ends up doing nothing (apparently).
  # listen 443; 

  server_name _;
  # ...
}

# some server
server {
  listen 80;
  server_name server1.com;
  # ...
}

# some other server ...
server {
  listen 80;
  server_name server2.com;
  # ...
}

# ... and it's https equivalent
server {
  listen 443;
  ssl on;
  server_name server2.com;
  # ...
}

Agora, como não há um ouvinte padrão para 443, uma solicitação como https://server1.comserá finalizada pelo server2.combloco https. Isto segue a lógica para server_namenos docs.

Se não houver correspondência, um bloco de servidor {...} no arquivo de configuração será usado com base na seguinte ordem:

  1. o bloco do servidor com uma diretiva de escuta correspondente marcada como [default | default_server]
  2. o primeiro bloco do servidor com uma diretiva de escuta correspondente (ou escuta implícita 80;)

Qual é a solução preferida para esse problema? Preciso configurar o certificado fictício para meu bloco catch all server apenas para que eu possa ouvir 443 e lidar com solicitações ruins? Existe um parâmetro que eu desconheço que força uma correspondência exata de nome de host server?

numbers1311407
fonte
O que você quer que aconteça quando as pessoas tentam acessar os outros sites usando https?
David Schwartz
Idealmente, eu gostaria que o nginx não servisse https, a menos que o nome do host corresponda, ou que ele seja redirecionado para http no mesmo host.
numbers1311407

Respostas:

9

Idealmente, eu gostaria que o nginx não servisse https, a menos que o nome do host corresponda, ou que ele seja redirecionado para http no mesmo host.

Nem é possível. A conexão de um cliente que acessa https://foo.example.com/ não pode ser aceita por nada além de um certificado SSL com "foo.example.com" como um de seus nomes. Não há oportunidade de redirecionar até que a conexão SSL seja aceita.

Se você configurar cada site para SSL, um usuário que clicar no erro de certificado obterá o site solicitado. Se você configurar um site "pegar tudo" para SSL que forneça apenas uma página de erro e configurar hospedagem virtual baseada em nome para o site que deve oferecer suporte a SSL, poderá servir uma página de erro aos clientes.

A hospedagem virtual SSL e HTTP simplesmente não funciona muito bem.

David Schwartz
fonte
Foi isso que reuni depois de ler os documentos. Eu só esperava que tivesse perdido alguma coisa. Não me importo com avisos SSL. Eu simplesmente não quero que alguém entre no server1.com e se veja na página inicial do server2.com ... Não há realmente nenhuma maneira de dizer ao nginx para não aceitar uma solicitação?
numbers1311407
Se não aceitar a solicitação, o primeiro site não funcionará. Ele precisa aceitar a solicitação para descobrir em qual site o usuário está tentando acessar.
David Schwartz
2
"A conexão de um cliente que acessa foo.example.com não pode ser aceita por nada além de um certificado SSL com" foo.example.com "como um de seus nomes." - Isso não está correto, o servidor aceitará a solicitação e cabe ao cliente verificar se o DN solicitado corresponde ao DN do certificado do servidor.
ColinM
4

A única maneira de fazer é criar um certificado SSL autoassinado e usá-lo para obter controle sobre solicitações https recebidas. Você pode criar seu certificado SSL autoassinado em algumas etapas simples descritas nesta postagem .

Digamos que você crie um certificado autoassinado com um nome de arquivo server.crt. Você acrescentaria o seguinte na sua configuração nginx:

server {
    listen  443;

    ssl    on;
    ssl_certificate         /etc/nginx/ssl/server.crt;
    ssl_certificate_key     /etc/nginx/ssl/server.key;

    server_name server1.com;

    keepalive_timeout 60;
    rewrite ^       http://$server_name$request_uri? permanent;
}

Você ainda receberá a mensagem de aviso SSL do navegador, mas pelo menos terá controle sobre o que acontece a seguir.

user1973679
fonte
1
Eu concordo com isto. Há o problema do navegador mostrando uma mensagem de aviso sobre certificados não confiáveis, mas se você estiver apenas tentando impedir que os usuários acessem https: // <endereço-ip> para obter no serverd um certificado igualmente inválido para um dos seus verdadeiros fantasmas (inválido porque o nome do host não corresponde), é melhor fornecer a eles um certificado fictício autoassinado inválido. Isso diz a eles "não há nada para ver aqui, nem mesmo um certificado de outro host".
Daniel F
2

Adicione um bloco de servidor abrangente e retorne o código de status 444. Ele informa ao nginx para fechar a conexão antes de enviar qualquer dado.

server {
    listen 443 default_server ssl;
    server_name _;
    return 444;
}
ATLief
fonte
1

Hoje em dia, você pode usar a extensão TLS Server Name Indication (SNI, RFC 6066). O ouvinte HTTPS poderá reconhecer o nome do domínio antes de fornecer o certificado apropriado.

Isso significa que você precisará ter certificados para TODOS os seus domínios e, quando o SNI for usado para reconhecer um dos outros domínios, você poderá usar o redirecionamento HTTP 301 para a versão não criptografada HTTP, a menos que o nome do servidor corresponda ao único que precisa criptografia.

Mais informações sobre o SNI disponíveis na documentação do nginx http://nginx.org/en/docs/http/configuring_https_servers.html

Evgeny
fonte
0

Mapeie o nome do host solicitado para nomes de host válidos no http {}bloco:

map $ssl_server_name $correct_hostname_example {
  default 0;
  example.com 1;
  www.example.com 1;
}

E então, no server {}bloco, mate as conexões com o nome do host errado:

if ($correct_hostname_example = 0) {
  return 444;
}

Use vários mapas, conforme necessário, para vários blocos de servidores. A conexão ainda será estabelecida usando um de seus certificados, mas se esse último bloco estiver presente em cada bloco de servidor que serve SSL, você "bloqueará" efetivamente as conexões com nomes de host inválidos. Pode ser necessário apenas no primeiro bloco do servidor, mas adicioná-lo a cada bloco do servidor garantirá que o pedido não seja importante.

A $ssl_server_namevariável está presente no nginx 1.7 ou superior.

ColinM
fonte
0

Foi assim que resolvi o problema:

  1. Crie um certificado autoassinado:

openssl req -nodes -x509 -newkey rsa:4096 -keyout self_key.pem -out self_cert.pem -days 3650

  1. Copie-o onde o NginX pode encontrá-lo:

cp self*.pem /etc/nginx/ssl/

  1. Configure uma rota abrangente:
server {
    listen 443 default_server ssl;

    ssl on;
    ssl_certificate /etc/nginx/ssl/self_cert.pem;
    ssl_certificate_key /etc/nginx/ssl/self_key.pem;

    return 301 http://$host;
}

O que isso fará: ele fornecerá um aviso (sem solução) em qualquer servidor que não tenha seu próprio certificado, mas o aviso não indicará o nome do certificado errado. Se o usuário clicar em "visitar de qualquer maneira", ele será redirecionado para a versão não SSL do site digitado.

advertência :

se o site habilitado para SLL definir apenas www.example.com(e não example.com), sua rota catch-all acabará servindo https://example.comcom o certificado autoassinado e o aviso correspondente.

ierdna
fonte
-2

Redirecionar para http:

server {
    listen       443;
    server_name  *.com;
    rewrite        ^ http://$host$request_uri? permanent;
}    

Retorno 404:

server {
    listen       443;
    server_name  *.com;
    return 404;
}    
freestyler
fonte
1
Isso ainda resultará em um aviso SSL, pois o túnel SSL precisa ser estabelecido antes que ocorra qualquer redirecionamento HTTP. Veja a resposta aceita por David Schwartz.
cjc