Почему 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;
    [...]
}

и т.д

** РЕДАКТИРОВАТЬ **

Кажется, что некоторые пользователи немного смущены этим примером и думают, что он ограничен одним файлом conf и т. Д.

Обратите внимание, что вышеприведенный пример является простым примером для разработки OP по мере необходимости.

Лично я использую отдельные файлы 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 директива в другом месте.

В этом случае алфавитный порядок имен файлов conf-файлов для других серверов становится неактуальным.

Кроме того, эта схема дает большую гибкость в том смысле, что можно определить несколько значений по умолчанию.

В моем конкретном случае у меня 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.

На самом деле у меня есть четыре таких серверных блока, и это упрощенный вариант использования.

Dayo
источник
1
Nginx требует чувствительности к регистру - «Сервер» должен быть сервером, а «Возврат» должен быть возвращен. Надеюсь, что это спасет несколько проблем при копировании этого кода.
Capaj
2
статический, см. ответ Олега Неумывкина - если на сайтах доступно несколько конфигурационных файлов, то по умолчанию используется первый сервер в первом файле в алфавитном порядке. Я подозреваю, что это может быть проблемой. Кроме того, во многих дистрибутивах запускается 'nginx -t' для проверки конфигурации перед перезапуском - может возникнуть ошибка, препятствующая перезапуску.
jwhitlock
2
Это неполно и не должно быть принятым ответом. Чтобы это работало, вы также должны удалить default_server из любых директив прослушивания.
Бен
@ben Во-первых, у OP не было «default_server» в его примере задачи, и ответ был адаптирован к специфике этого вопроса. Во-вторых, почему кто-то может определить default_server на отдельном сервере, если следуя инструкциям, чтобы сделать первый определенный сервер сервером по умолчанию, я не знаю. В любом случае, если сервер default_server был специально определен, то этот набор вопросов и ответов не является тем, на что следует обращать внимание, чтобы разрешить любые проблемы, с которыми они могут столкнуться.
Dayo
1
@Dayo Вы правы, что вы указали конкретную конфигурацию, опубликованную OP. Но для полного ответа на поставленный вопрос, я думаю, необходимо упомянуть default_server. Очень возможно прочитать ваш ответ и не понять, как default_server будет мешать ему. Это даже более вероятно, потому что некоторые дистрибутивы поставляются с default_server, определенным в файле, который может быть неочевиден для пользователя.
Бен
62

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

server {
    listen       80  default_server;
    server_name  _; # some invalid name that won't match anything
    return       444;
}
ITech
источник
Работал у меня для nginx 1.8.0. У server_name _;меня не было в более ранних версиях для nginx, но это работало. Теперь для новых версий nginx кажется, что вам нужно server_name _;. Спасибо
Даниэль
Решаемые. Я забыл точку с запятой; _;
user1201917
2
@iTech: почему-то возвращается 444 для всех запросов. Есть какие-нибудь подсказки?
Дивик
Отличный совет 444, и imho гораздо более чистое решение, чем возвращение кода ошибки.
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 Буквально в третьей строке этой статьи говорится, что этот вариант использования в порядке. Я понимаю необходимость быть осторожным, но давайте не будем вилять пальцами при разумных случаях использования.
Усилен
На мой взгляд, это самое простое и лаконичное решение, так как для этого требуется всего 3 строки кода, которые вы можете с радостью вставить в любой vhost-файл, изменив впоследствии только сравниваемый $ host.
Акито
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, и не найдет сертификат / ключ.

andreycpp
источник
3
Я понятия не имею, почему этот ответ так далеко внизу списка. Это тот, который отвечает на вопрос, не отвлекаясь на блестящие вещи по пути.
MMC
Это помогло мне решить проблему, когда я пытался перенаправить с не-www на www, что я должен был включить свой сертификат ssl в этот маршрут перенаправления, иначе пытался получить мой сертификат ssl по умолчанию, который был для другого домена.
конец
1
Это кажется лучшим ответом для большинства конфигураций ... не уверен, кто в наши дни использует nginx без SSL, но тот факт, что это единственный, который покрывает SSL, чрезвычайно показателен.
Усиление
Кажется, что server_name _;это даже не нужно.
Жюльен Салинас
26

Есть несколько способов указать сервер по умолчанию.

Первый способ - укажите сервер по умолчанию первым в списке, если вы храните настройки сервера в одном файле конфигурации, как показано выше в Dayo.

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

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

Больше информации здесь: Nginx doc / Listen

Этот способ более полезен, когда вы храните конфигурации сервера в отдельных файлах и не хотите называть эти файлы в алфавитном порядке.

Павел
источник
3
Это должен быть правильный ответ. Принятый ответ вводит в заблуждение. Простое добавление другого сервера в конфигурацию не решит проблему, если другой сервер настроен в качестве сервера по умолчанию.
Бен
1
@Pavel нет причин, по которым предоставленный ответ не может работать с несколькими отдельными файлами vhost. Кроме того, благодаря этому я знаю, что мой сервер по умолчанию всегда находится в главном файле nginx.conf, и мне не нужно помнить, какой из моих многочисленных отдельных файлов vhost содержит это.
Dayo
Не забудьте также прослушать 443 для ssl (см. Ответ @ AndreyT ниже).
Константинос
8

Маленький комментарий, чтобы ответить:

если у вас есть несколько виртуальных хостов на нескольких IP-адресах в нескольких файлах конфигурации в sites-available /, то домен «по умолчанию» для IP будет взят из первого файла в алфавитном порядке.

И, как сказал Павел, есть аргумент «default_server» для директивы «listen» http://nginx.org/en/docs/http/ngx_http_core_module.html#listen

Олег Неумывакин
источник