Чому nginx відповідає на будь-яке доменне ім’я?


139

У мене є nginx і працює з додатком Ruby / Sinatra, і все добре. Однак зараз я намагаюся створити другу програму з того самого сервера, і я помітив щось дивне. По-перше, ось мій nginx.conf:

pid /tmp/nginx.pid;
error_log /tmp/nginx.error.log;

events {
  worker_connections 1024;
  accept_mutex off;
}

http {
  default_type application/octet-stream;
  access_log /tmp/nginx.access.log combined;

  sendfile on;
  tcp_nopush on;
  tcp_nodelay off;

  gzip on;
  gzip_http_version 1.0;
  gzip_proxied any;
  gzip_min_length 500;
  gzip_disable "MSIE [1-6]\.";
  gzip_types text/plain text/xml text/css
             text/comma-separated-values
             text/javascript application/x-javascript
             application/atom+xml;

  upstream app {
    server unix:/var/www/app/tmp/sockets/unicorn.sock fail_timeout=0;
  }

  server {
    listen 80;
    client_max_body_size 4G;
    server_name FAKE.COM;

    keepalive_timeout 5;

    root /var/www/app/public;

    location / {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;

      if (!-f $request_filename) {
        proxy_pass http://app;
        break;
      }
    }

    error_page 500 502 503 504 /500.html;
    location = /500.html {
      root /var/www/app/public;
    }
  }
}
                                                          68,0-1        B

Зверніть увагу, як server_nameвстановлено, FAKE.COMщо сервер відповідає на всі хости, які потрапили на цей сервер через інші доменні імена. Як я можу змусити конкретний сервер відповідати лише на запити FAKE.COM?


listen fake.com | something.com:80 команда фільтрує, що не server_name.
Олексій Марченко

Відповіді:


202

Перший блок сервера в конфігурації nginx є типовим для всіх запитів, що потрапляють на сервер, для якого немає конкретного блоку сервера.

Отже, у вашому конфігурації, припускаючи, що ваш реальний домен - це REAL.COM, коли користувач вводить цей вхід, він вирішить ваш сервер, і оскільки для цієї установки немає блоку сервера, блок серверів для FAKE.COM є першим серверний блок (лише серверний блок у вашому випадку) буде обробляти цей запит.

Ось чому правильні конфігурації Nginx мають специфічний серверний блок для встановлення за замовчуванням, перш ніж слідкувати за іншими для конкретних доменів.

# Default server
server {
    return 404;
}

server {
    server_name domain_1;
    [...]
}

server {
    server_name domain_2;
    [...]
}

тощо

** редагувати **

Здається, що деякі користувачі трохи плутають цей приклад і вважають, що він обмежений одним файлом конф і т.д.

Зауважте, що вищезазначене - простий приклад розвитку ОП в міру необхідності.

Я особисто використовую окремі файли vhost conf з цим так (CentOS / RHEL):

http {
    [...]
    # Default server
    server {
        return 404;
    }
    # Other servers
    include /etc/nginx/conf.d/*.conf;
}

/etc/nginx/conf.d/ буде містити domain_1.conf, domain_2.conf ... domain_n.conf, який буде включений після блоку сервера в головному файлі nginx.conf, який завжди буде першим і завжди буде за замовчуванням, якщо його не буде замінено на default_server директива в іншому місці.

Алфавітний порядок назв файлів конф-файлів для інших серверів у цьому випадку стає неактуальним.

Крім того, таке розташування дає велику гнучкість в тому, що можна визначити кілька за замовчуванням.

У моєму конкретному випадку я слухаю Apache на порту 8080 лише у внутрішньому інтерфейсі, і я проксі скрипти PHP та Perl до Apache.

Однак я запускаю два окремих програми, які обидва повертають посилання з ": 8080" у вихідному html-файлі, який додається, коли вони виявляють, що Apache не працює на стандартному порту 80 і намагаються "допомогти" мені.

Це спричиняє проблему в тому, що посилання стають недійсними, оскільки Apache неможливо отримати через зовнішній інтерфейс, а посилання повинні вказувати на порт 80.

Я вирішую це, створивши сервер за замовчуванням для порту 8080 для перенаправлення таких запитів.

http {
    [...]
    # Default server block for undefined domains
    server {
        listen 80;
        return 404;
    }
    # Default server block to redirect Port 8080 for all domains
    server {
        listen my.external.ip.addr:8080;
        return 301 http://$host$request_uri;
    }
    # Other servers
    include /etc/nginx/conf.d/*.conf;
}

Оскільки нічого у звичайних блоках серверів не слухається на порту 8080, блок сервера перенаправлення за замовчуванням прозоро обробляє такі запити в силу свого положення в nginx.conf.

У мене фактично є чотири таких блоки сервера, і це спрощений випадок використання.


1
Nginx вимагає чутливості регістру - "Сервер" повинен бути сервером, а "Повернення" - поверненням. Сподіваємось, це заощадить кілька проблем при копіюванні цього коду.
Капай

2
статично, дивіться відповідь Олега Неумівкіна - якщо у вас є кілька конфігураційних файлів на доступних сайтах, то перший сервер у першому файлі за алфавітом є вашим замовчуванням. Я підозрюю, що це може бути проблемою. Крім того, у багатьох дистрибутивах запущено 'nginx -t' для тестування конфігурації перед перезапуском - у вас може виникнути помилка, яка запобігає перезапуску.
jwhitlock

2
Це неповно і не повинно бути прийнятою відповіддю. Щоб це працювало, вам також доведеться видалити default_server з будь-яких директив про прослуховування.
Бен

@ben По-перше, ОП не мав "default_server" у своєму прикладі проблеми, і відповідь адаптована до специфіки цього питання. По-друге, чому хтось визначив default_server на окремому розташуванні сервера, виконуючи вказівки, щоб зробити перший визначений сервер за замовчуванням, не в мене. У будь-якому випадку, якщо сервер за замовчуванням був конкретно визначений, то цей Q / Набір - це не те, на що слід дивитися, щоб вирішити будь-які проблеми, які вони можуть виникнути.
Дайо

1
@Dayo Ви неправі, що ви звернулися до конкретної конфігурації, розміщеної ОП. Але для повної відповіді на поставлене запитання, я вважаю, що потрібно згадати про default_server. Дуже можна прочитати свою відповідь і не зрозуміти, як заважаючий сервер заважатиме їй. Це ще ймовірніше, тому що деякі файли дистрибуції з доставкою за допомогою default_server визначені у файлі, який може не бути очевидним для користувача.
Бен-

61

У вас повинен бути сервер за замовчуванням для загального доступу , ви можете повернутися 404або краще взагалі не відповідати (це дозволить заощадити деяку пропускну здатність), повернувши 444який є nginx-відповідь HTTP, що просто закриває з'єднання і нічого не повертає

server {
    listen       80  default_server;
    server_name  _; # some invalid name that won't match anything
    return       444;
}

Працював для мене для nginx 1.8.0. У server_name _;попередніх версіях для nginx у мене не було, але він працював. Тепер для нових версій nginx здається, що вам потрібна server_name _;. Спасибі
Даніель

Вирішено. Я забув крапку з комою; _;
user1201917

2
@iTech: чомусь він повертає 444 для всіх запитів. Будь-які підказки?
Divick

Чудова порада про 444, і їм набагато більш чисте рішення, ніж повернення коду помилки.
qqilihq

1
Важливо вказати сертифікат / ключ, інакше всі SSL-з'єднання будуть збігатися та виходити з ладу, на що вказував @AndreyT у своїй відповіді нижче.
Марк Флетчер

35

Я не зміг вирішити свою проблему жодною з інших відповідей. Я вирішив проблему, перевіривши, чи збігається хост і повернув 403, якщо він не відбувся. (У мене був якийсь випадковий веб-сайт, який вказує на вміст моїх веб-серверів. Я здогадуюсь захопити рейтинг пошуку)

server {
    listen 443;
    server_name example.com;

    if ($host != "example.com") {
        return 403;
    }

    ...
}

1
Якщо це погана практика: nginx.com/resources/wiki/start/topics/depth/ifisevil
Esolitos

1
Є випадки, коли ви просто не можете уникнути використання значень if, наприклад, якщо вам потрібно протестувати змінну, яка не має еквівалентної директиви.
Едвард

4
@Esolitos Буквально третій рядок цієї статті говорить про те, що цей випадок використання нормальний. Я розумію, що потрібно бути обережними, але не давайте махати пальцями при розумних випадках використання.
mpowered

На мій погляд, найпростіше і найкоротше рішення, оскільки для нього потрібні лише 3 рядки коду, які ви можете із задоволенням вставити у будь-який файл vhost, лише змінивши згодом $ хост.
Акіто

28

Щоб відповісти на ваше запитання - nginx вибирає перший сервер, якщо немає відповідності. Дивіться документацію :

Якщо його значення не відповідає жодному імені сервера або запит взагалі не містить цього заголовкового поля, то nginx направить запит на сервер за замовчуванням для цього порту. У конфігурації вище сервер за замовчуванням є першим ...

Тепер, якщо ви хотіли мати сервер загальноприйнятих серверів, який, скажімо, відповідає 404 на всі запити, то ось як це зробити:

server {
    listen 80 default_server;
    listen 443 ssl default_server;
    server_name _;
    ssl_certificate <path to cert>
    ssl_certificate_key <path to key>
    return 404;
}

Зауважте, що вам потрібно вказати сертифікат / ключ (який можна підписати самостійно), інакше всі з’єднання SSL не вдасться, оскільки nginx спробує прийняти з'єднання за допомогою цього сервера default_server і не знайде cert / key.


3
Я поняття не маю, чому ця відповідь так далеко в списку. Це той, хто відповідає на питання, не відволікаючись на блискучі речі по дорозі.
mmc

Це допомогло мені вирішити проблему, коли я намагався перенаправити з не-www на www, що мені довелося включити свій сертифікат ssl в цей маршрут переадресації, інакше намагався схопити мій сервер ssl за замовчуванням, який був для іншого домену.
endyourif

1
Це здається найкращою відповіддю для більшості конфігурацій ... не впевнений, хто використовує nginx без SSL в наші дні, але факт, що це єдиний, що охоплює SSL, надзвичайно показовий.
mpowered

Здається, server_name _;це навіть не потрібно.
Жульєн Салінас

26

Є кілька способів вказати сервер за замовчуванням.

Перший шлях - спочатку вкажіть сервер за замовчуванням у списку, якщо ви зберігаєте конфігурації свого сервера в одному конфігураційному файлі, як показано Dayo вище.

Другий спосіб (краще) Більш гнучкий - надайте default_serverпараметр для listenінструкції, наприклад:

server {
    listen  *:80 default_server;
    root /www/project/public/;
}

Більше інформації тут: Nginx doc / Слухай

Цей спосіб більш корисний, коли ви зберігаєте конфігурації сервера в окремих файлах і не бажаєте називати ці файли в алфавітному порядку.


3
Це має бути правильна відповідь. Прийнята відповідь вводить в оману. Просто додавання іншого сервера до конфігурації не вирішить проблему, якщо інший сервер налаштований як сервер за замовчуванням.
Бен

1
@ Paavel, немає жодної причини, коли надана відповідь не може працювати з декількома окремими файлами vhost. Також, маючи це, я знаю, що мій сервер за замовчуванням завжди знаходиться в головному файлі nginx.conf і мені не потрібно пам’ятати, який з моїх багатьох окремих файлів vhost містить це.
Дайо

Не забудьте послухати і 443 для ssl (див. @ Відповідь AndreyT нижче).
Константинос

8

Маленький коментар для відповіді:

якщо у вас декілька віртуальних хостів на декількох IP-адресах у кількох конфігураційних файлах у доступних сайтах /, домен "за замовчуванням" для IP буде взято з першого файлу за алфавітом.

І як сказав Павло, для директиви "прослуховування" є аргумент "default_server" http://nginx.org/en/docs/http/ngx_http_core_module.html#listen

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.