Configurando adequadamente um servidor nginx "padrão" para https

71

Eu tenho vários servidores em execução na mesma máquina, alguns apenas com http, outros com http e https. Existem vários blocos de servidor definidos em arquivos separados, incluídos no arquivo de configuração principal.

Eu configurei um servidor "padrão" para http que servirá uma "página de manutenção" genérica para solicitações que não correspondem a nenhum dos outros nomes de servidor nos outros arquivos de configuração. O servidor padrão http funciona como esperado, ele usa o nome_do_servidor "_" e aparece primeiro na lista de inclusões (porque observei que, no caso de nomes de servidor duplicados nos servidores, é usado o que aparece primeiro). Isso funciona muito bem.

Eu esperaria o mesmo bloco de servidor exato (apenas alternando "listen 80 default_server" para "listen 443 default_server" e também em vez de exibir a página "return 444"), mas isso não acontece. Em vez disso, parece que o novo servidor https padrão está realmente capturando todas as conexões https recebidas e causando falhas, embora os outros blocos do servidor tenham nomes de servidor mais apropriados para as solicitações recebidas. A remoção do novo servidor https padrão fará com que o comportamento semi-correto seja retomado: todos os sites com https serão carregados corretamente; mas os sites sem https serão todos roteados para o primeiro servidor https nos arquivos de inclusão (que, de acordo com os documentos, se nenhum "servidor_servidor padrão" aparecer, o primeiro bloco de servidor a aparecer será "padrão").

Então, minha pergunta é: qual é a maneira correta de definir um "servidor padrão" no nginx para conexões SSL? Por que quando eu defino explicitamente um "default_server", ele fica ganancioso e agarra todas as conexões, enquanto quando eu deixo implicitamente o nginx decidir o "servidor padrão" funciona como eu esperaria (com o servidor incorreto definido como padrão e os outros servidores reais comportando-se corretamente)?

Aqui estão meus "servidores padrão". O HTTP funciona sem quebrar outros servidores. O Https quebra outros servidores e consome tudo.

server {
    listen 443 ssl default_server;
    server_name _;

    access_log /var/log/nginx/maintenance.access.log;
    error_log /var/log/nginx/maintenance.error.log error;

    return 444;
}

server {
    listen *:80 default_server;
    server_name _;
    charset utf-8;

    access_log /var/log/nginx/maintenance.access.log;
    error_log /var/log/nginx/maintenance.error.log error;

    root /home/path/to/templates;

    location / {
        return 503;
    }

    error_page 503 @maintenance;

    location @maintenance {
        rewrite ^(.*)$ /maintenance.html break;
    }
}

Algum de vocês vê o que pode estar errado aqui?


fonte

Respostas:

27

Você não possui nenhum ssl_certificate ou ssl_certificate_key definido no seu bloco https "padrão". Embora você não tenha ou deseje uma chave real para esse cenário padrão, ainda precisa configurar um ou o nginx terá o comportamento indesejado que você descreve.

Crie um certificado autoassinado com um Nome comum de * e conecte-o à sua configuração e ele começará a funcionar como você deseja.

O comportamento "padrão" dessa configuração seria que um navegador receberia um aviso de que o certificado não é confiável. Se o usuário adicionar o certificado como uma exceção, a conexão será eliminada pelo nginx e eles verão o padrão do navegador. mensagem de erro "não foi possível conectar".

Radmilla Mustafa
fonte
11
Eu tentei isso, mas ainda não funciona: todas as solicitações de SSL para o IP vão para meu outro host SSL. Mais alguma coisa que eu poderia tentar?
Michael Härtl
23

Consegui configurar uma hospedagem dedicada compartilhada em um único IP com o nginx. HTTP e HTTPS padrão que atendem a 404 para domínios desconhecidos recebidos.

1 - Crie uma zona padrão

Como o nginx está carregando vhosts em ordem ascii, você deve criar um 00-defaultarquivo / link simbólico no seu /etc/nginx/sites-enabled.

2 - Preencha a zona padrão

Preencha o seu 00-defaultcom vhosts padrão. Aqui está a zona que estou usando:

server {
    server_name _;
    listen       80  default_server;
    return       404;
}


server {
    listen 443 ssl;
    server_name _;
    ssl_certificate /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx.key;
    return       404;
}

3 - Crie certificado autoassinado, teste e recarregue

Você precisará criar um certificado autoassinado /etc/nginx/ssl/nginx.crt.

Crie um certificado autoassinado padrão:

sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt

Apenas um lembrete:

  • Teste a configuração do nginx antes de recarregar / reiniciar: nginx -t
  • Recarregue uma diversão: sudo service nginx reload

Espero que ajude.

Se não
fonte
2
Não resolve o aviso do navegador de visitar um site não seguro.
Gdbj
5
Não pode ser resolvido, pois um catch-all deve corresponder a qualquer domínio. Nenhum SSL pode curinga todos os domínios. Imagine se você estiver falsificando o endereço google.fr no seu servidor, você poderá autenticar o servidor como google.fr. Seria um grave problema de segurança :(
Caso contrário
Sim, acho que faz sentido. Infelizmente, no Chrome, você recebe um aviso assustador antes de ver a página 404, o que é pior do que o servidor simplesmente rejeitar o tráfego. Faz parecer que o servidor está configurado incorretamente.
Gdbj #
Muito obrigado. Isso funcionou para mim, exceto que eu tive que adicionar default_serverpara escutar 443 e adicionei o endereço IPv6 [::]: 80 e [::]: 443 com default_server.
chmike 17/04
16

Basicamente, queremos evitar a todo custo que a primeira definição de servidor em nosso arquivo de configuração seja servida como um servidor catch-all-server para conexões SSL. Todos sabemos que faz isso (em oposição ao http e usando a configuração default_server que funciona muito bem).

Isso não pode ser alcançado declarativamente para SSL (ainda), portanto, precisamos codificá-lo com um IF ...

A variável $hosté o nome do host da linha de solicitação ou do cabeçalho http. A variável $server_nameé o nome do bloco do servidor em que estamos agora.

Portanto, se esses dois não forem iguais, você serviu esse bloco do servidor SSL para outro host, para que ele devesse ser bloqueado.

O código não contém referências específicas aos endereços IP do servidor, para que possa ser reutilizado facilmente para outras configurações do servidor sem modificação.

Exemplo:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    ###
    ### Section: SSL

    #
    ## Check if this certificate is really served for this server_name
    ##   http://serverfault.com/questions/578648/properly-setting-up-a-default-nginx-server-for-https
    if ($host != $server_name) {
        #return 404 "this is an invalid request";
        return       444;
    }

    ...
Rolf
fonte
você poderia explicar o que listen [::]:443 ssl http2;faz? Estou tendo problemas para encontrar documentação para isso.
Andrew Brown
Acho que encontrei, só precisava saber o que procurar. Diretivas IPV4 e IPV6.
Andrew Brown
7

Para elaborar mais sobre a resposta de Radmilla Mustafa:

O Nginx usa o cabeçalho 'Host' para a correspondência server_name. Ele não usa TLS SNI. Isso significa que, para um servidor SSL, o nginx deve poder aceitar a conexão SSL, que se resume a ter certificado / chave. O certificado / chave pode ser qualquer um, por exemplo, autoassinado.

Ver documentação

Portanto, a solução é:

server {
    server_name _;
    listen 80 default_server;
    listen 443 ssl default_server;

    ## To also support IPv6, uncomment this block
    # listen [::]:80 default_server;
    # listen [::]:443 ssl default_server;

    ssl_certificate <path to cert>;
    ssl_certificate_key <path to key>;
    return 404; # or whatever
}
andreycpp
fonte
Eu sinto que esta deve ser a resposta aceita. Muito Obrigado.
aggregate1166877
2

Para quem perdeu tanto cabelo por isso quanto eu (passei quase o dia inteiro nisso hoje). Eu tentei quase tudo, e o que o fez finalmente funcionar corretamente foi esta frase estúpida:

ssl_session_tickets off;

Com base na resposta do Ifnot , meu exemplo de trabalho é:

server {
    listen 443 ssl http2 default_server;
    listen [::]:443 ssl http2 default_server;
    server_name _;

    ssl_certificate /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx.key;
    ssl_session_tickets off;

    return 404;
}

Não tenho idéia do por que isso era necessário, o único princípio que deduzi disso foi que o nginx se comporta de maneira estranha quando não damos a ele o que ele quer.

Marek Lisý
fonte
11
você é uma lenda.
Nizar Blond
1

Se você quiser ter certeza absoluta, use endereços IP separados para hosts que não devem responder em HTTPS e hosts que devem. Isso também resolve o problema de aviso do navegador "certificado inválido".

Michael Hampton
fonte