nginx: Comment empêcher un bloc de serveur SSL exactement nommé d'agir comme le fourre-tout pour tous les SSL

17

J'ai un serveur Web avec de nombreux serveurs virtuels. Seul 1 d'entre eux est SSL. Le problème est, car il n'y a pas de bloc serveur catchall à l'écoute de SSL, toute demande https vers les autres sites est servie par le bloc 1 SSL.

Ma configuration ressemble essentiellement à ceci:

# 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;
  # ...
}

Maintenant qu'il n'y a pas d'écouteur par défaut pour 443, une requête comme celle- https://server1.comci finira par être servie par le server2.combloc https. Cela fait suite à la logique server_namedans la documentation.

S'il n'y a pas de correspondance, un bloc de serveur {...} dans le fichier de configuration sera utilisé selon l'ordre suivant:

  1. le bloc serveur avec une directive d'écoute correspondante marquée comme [default | default_server]
  2. le premier bloc serveur avec une directive d'écoute correspondante (ou écoute implicite 80;)

Quelle est la solution préférée pour ce problème? Dois-je configurer un certificat factice pour mon bloc de serveur catch all juste pour pouvoir écouter sur 443 et gérer les mauvaises demandes? Y a-t-il un paramètre que je ne connais pas qui force une correspondance exacte avec le nom d'hôte server?

numéros1311407
la source
Que voulez-vous qu'il se passe lorsque les gens essaient d'accéder aux autres sites en utilisant https?
David Schwartz
Idéalement, soit je voudrais que nginx ne serve pas du tout https à moins que le nom d'hôte ne corresponde, soit qu'il redirige vers http sur le même hôte.
numéros1311407

Réponses:

9

Idéalement, soit je voudrais que nginx ne serve pas du tout https à moins que le nom d'hôte ne corresponde, soit qu'il redirige vers http sur le même hôte.

Ni l'un ni l'autre n'est possible. La connexion d'un client qui va à https://foo.example.com/ ne peut pas être acceptée par autre chose qu'un certificat SSL avec "foo.example.com" comme l'un de ses noms. Il n'y a aucune possibilité de rediriger jusqu'à ce que la connexion SSL soit acceptée.

Si vous configurez chaque site pour SSL, un utilisateur qui clique sur l'erreur de certificat obtiendra le site demandé. Si vous configurez un site «catch all» pour SSL qui ne fournit qu'une page d'erreur et configurez l'hébergement virtuel basé sur le nom pour le seul site censé prendre en charge SSL, vous pouvez servir une page d'erreur aux clients.

L'hébergement virtuel SSL et HTTP ne fonctionne tout simplement pas bien ensemble.

David Schwartz
la source
C'est ce que j'ai rassemblé après avoir lu les documents. J'espérais juste avoir raté quelque chose. Je ne me soucie pas du tout des avertissements SSL. Je ne veux tout simplement pas que quelqu'un entre sur server1.com et se retrouve sur la page d'accueil de server2.com ... N'y a-t-il vraiment aucun moyen de dire à nginx de ne pas accepter une demande?
numéros1311407
S'il n'accepte pas la demande, le premier site ne fonctionnera pas. Il doit accepter la demande pour savoir à quel site l'utilisateur tente d'accéder.
David Schwartz
2
"La connexion d'un client qui va à foo.example.com ne peut pas être acceptée par autre chose qu'un certificat SSL avec" foo.example.com "comme l'un de ses noms." - Ce n'est pas correct, le serveur acceptera la demande et il appartient au client de vérifier que le DN demandé correspond au DN du certificat du serveur.
ColinM
4

La seule façon de le faire est de créer un certificat SSL auto-signé et de l'utiliser pour prendre le contrôle des demandes https entrantes. Vous pouvez créer votre certificat SSL auto-signé en quelques étapes simples décrites dans cet article .

Supposons que vous créez un certificat auto-signé avec un nom de fichier server.crt. Vous devez ensuite ajouter les éléments suivants dans votre configuration 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;
}

Vous obtiendrez toujours le message d'avertissement SSL du navigateur, mais au moins vous aurez le contrôle sur ce qui se passera ensuite.

user1973679
la source
1
Je suis d'accord avec ça. Il y a le problème du navigateur affichant un message d'avertissement sur les certificats non approuvés, mais si vous essayez seulement d'empêcher les utilisateurs d'aller sur https: // <ip-address> pour obtenir à serverd un certificat tout aussi invalide pour l'un de vos vrais vhosts (invalide parce que le nom d'hôte ne correspondra pas), alors vous feriez mieux de leur servir un certificat factice auto-signé invalide. Ce genre de leur dit "il n'y a rien à voir ici, même pas un certificat d'un autre hôte".
Daniel F
2

Ajoutez un bloc serveur fourre-tout et renvoyez le code d'état 444. Il indique à nginx de fermer la connexion avant d'envoyer des données.

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

De nos jours, vous pouvez utiliser l'extension TLS Server Name Indication (SNI, RFC 6066). L'écouteur HTTPS pourra reconnaître le nom de domaine avant de servir le certificat approprié.

Cela signifie que vous devrez disposer de certificats pour TOUS vos domaines, et lorsque SNI est utilisé pour reconnaître l'un des autres domaines, vous pouvez simplement utiliser la redirection HTTP 301 vers la version HTTP non chiffrée, sauf si le nom du serveur correspond à celui qui a besoin chiffrement.

Plus d'informations sur SNI disponibles dans la documentation nginx http://nginx.org/en/docs/http/configuring_https_servers.html

Evgeny
la source
0

Mappez le nom d'hôte demandé sur des noms d'hôte valides dans le http {}bloc:

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

Et puis dans le server {}bloc tuer les connexions avec le mauvais nom d'hôte:

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

Utilisez plusieurs cartes si nécessaire pour plusieurs blocs de serveur. La connexion sera toujours établie à l'aide de l'un de vos certificats, mais si ce dernier bloc est présent dans chaque bloc serveur qui sert SSL, vous "bloquerez" efficacement les connexions avec des noms d'hôte invalides. Il peut être nécessaire uniquement dans le premier bloc serveur, mais l'ajouter à chaque bloc serveur garantira que l'ordre n'a pas d'importance.

La $ssl_server_namevariable est présente dans nginx 1.7 ou supérieur.

ColinM
la source
0

Voici comment j'ai résolu le problème:

  1. Créer un certificat auto-signé:

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

  1. Copiez-le où NginX peut le trouver:

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

  1. Configurer un itinéraire fourre-tout:
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;
}

Qu'est-ce que cela va faire: il vous donnera un avertissement (sans contournement) sur tout serveur qui n'a pas son propre certificat, mais l'avertissement ne dira pas le mauvais nom de certificat. Si l'utilisateur clique sur "visiter quand même", il sera redirigé vers la version non ssl du site qu'il a tapé.

mise en garde :

si votre site compatible SLL définit uniquement www.example.com(et non example.com), votre itinéraire fourre-tout finira par servir https://example.comavec le certificat auto-signé et l'avertissement correspondant.

ierdna
la source
-2

Rediriger vers http:

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

Retour 404:

server {
    listen       443;
    server_name  *.com;
    return 404;
}    
freestyler
la source
1
Cela entraînera toujours un avertissement SSL, car le tunnel SSL doit être établi avant qu'une redirection HTTP ne se produise. Voir la réponse acceptée de David Schwartz.
cjc