SqlCmd все о SQL технологиях.





Все что необходимо для изучения и работы с СУБД Microsoft SQL Server, MySQL, MariaDB, MongoDB. Авторские статьи, библиотека фрагментов T-SQL кода, сборник полезных инструментов.



MS SQL Server, SQL Server, T-SQL, исходники, сниппеты, статьи, учебники SQL, утилиты SQL, инструменты SQL



Header RSS-подписка на обновления сайта eMail-подписка на обновления сайта

Нужен ли нам сервис SQL Server Browser? Часть 3/3.





  • Другие части статьи:
  • 1
  • 2
  • 3
  • вперед »

SQL Browser и именованный экземпляр.

Динамическое назначение порта именованному экземпляру.

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

Вновь начнем с того, что отключим изучаемый нами сервис SQL Browser, а сервис именованного экземпляра (напомню, что на машине автора его имя MSSQL2) напротив — включим. Настройка тест-клиента очевидна:

1
2
3
const string instName="MSSQL2";
const string ipNum="192.168.81.3";
const int portNum=0;

Запускаем, читаем ожидаемое:

Try connect to: 192.168.81.3\MSSQL2
Error detected: A network-related or instance-specific error occurred while
establishing a connection to SQL Server. The server was not found or was not
accessible. Verify that the instance name is correct and that SQL Server is configured
to allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error
Locating Server/Instance Specified)

Мы уже знаем, что всякие имена экземпляров не более чем «выдумлялки» самого SQL Server, в классической технологии обращения к удаленному сервису существуют IP-адрес/номер порта и ничего более. Однако, кажется, приходит час триумфа нашего изучаемого сервиса SQL Browser — запустим его и вновь, не меняя настроек, попробуем подключить наш тест-клиент. На этот раз:

Try connect to: 192.168.81.3\MSSQL2
Connection is established!

Сработал, наконец-таки! Если бы мы задумали в наших экспериментах опуститься до уровня трассировки отдельных сетевых пакетов (чего в статье мы делать не будем, однако как самостоятельная работа такая трассировка будет небесполезна), то выяснили бы что события разворачивались таким порядком:

  • подлежащая сетевая библиотека «увидела» бы, что подключение клиент пытается установить с экземпляром не по умолчанию, и что при этом номер порта в строке подключения отсутствует;
  • тогда она шлет запрос не на неизвестный порт, наудачу, а на порт вполне конкретный — 1434. Однако — тут тонкий момент, будьте внимательны, это не тот 1434 по которому мы «DAC-подключались» к экземпляру по умолчанию раздел назад! Тот 1434 относится к протоколу TCP, а этот, о котором идет речь, к гораздо менее распространенному (но от того не менее работоспособному) протоколу UDP. Вспомните, что IANA в свое время мудро зарегистрировала порт 1434 для обоих протоколов (впрочем, порт второй, 1433, постигла участь такой же «двойной регистрации»). Так вот порт TCP-1434 отдан под нужды DAC, как мы уже хорошо знаем. А порт UDP-1434 — под нужды, как раз таки, SQL Browser. Именно на последний порт клиент должен отправить запрос о мапировании «имя экземпляра» → «номер порта», и с него же ждать ответ. Итак, в нашем случае, сетевая библиотека отправляет на сервер по адресу 192.168.81.3 и на порт UDP-1434 последнего запрос, который в переводе на человеческий выглядит как «где у нас MSSQL2?»;
  • с того же порта, и с того же адреса библиотека получает ответ: «MSSQL2 у нас сегодня на порту 48229». Номер порта приведен для машины автора, у вас, наверняка, будет свое значение;
  • после чего следует переключение библиотеки на порт 48229 (это у нас, разумеется, вновь классический TCP протокол) и стандартная работа с сервисом на этом порту.

Можем ли мы утверждать, что запущенный сервис SQL Browser это единственный путь подключиться к любому именованному экземпляру вообще, и к MSSQL2 в частности? Не можем мы такого утверждать, потому что это неправда! Вновь выключим указанный сервис, но, разумеется, оставим работать сервис самого экземпляра. Любым способом, какой вам больше нравится (а самый быстрый способ был описан в предыдущей части статьи) выясним номер порта прослушиваемый сервисом MSSQL2. Как мы уже знаем, на машине автора им оказывается порт 48229. Тогда, соответственно, мы берем тест-клиента и пишем:

1
2
3
const string instName="MSSQL2";
const string ipNum="192.168.81.3";
const int portNum=48229;

И запускаем:

Try connect to: 192.168.81.3\MSSQL2,48229
Connection is established!

И при этом, напомню, SQL Browser у нас выключен. Еще раз повторите для себя, что при обращении к удаленному сервису (любому) есть две физические сущности полностью определяющие успех/неуспех такого обращения:

  • IP-адрес того компьютера, что «хостит» целевой сервис;
  • номер порта на компьютере из первого пункта;

Все! Любые вещи в этот список не входящие будут максимум «синтаксическим сахаром». Вы можете спросить автора, «ну хорошо, если имя экземпляра, по сути, ровным счетом ничего не значит, и все определяет указанная выше пара значений, то зачем мы в настройках нашего тест-клиента продолжаем указывать это имя как значение константы instName»? И автор ответит вам — по инерции. :roll: То есть если мы выяснили и указали правильный номер порта, что значением константы instName могут быть:

  • MSSQL2
  • мой сервис (именно по-русски, и с пробелом посередине)
  • abc$#%^&
  • да и просто null

Иными словами вы можете указывать какое угодно имя экземпляра или не указывать его вовсе. И все будет подключаться, и к правильному экземпляру, уверяю вас! А это все потому, что имя экземпляра — ничто, номер верного порта — все.

Проведем еще один интересный эксперимент: укажем тест-клиенту правильное имя экземпляра и неправильный номер порта, например вот как:

1
2
3
const string instName="MSSQL2";
const string ipNum="192.168.81.3";
const int portNum=5555;

С выключенным сервисом SQL Browser следует вполне резонное:

Try connect to: 192.168.81.3\MSSQL2,5555
Error detected: A network-related or instance-specific error occurred while
establishing a connection to SQL Server. The server was not found or was not
accessible. Verify that the instance name is correct and that SQL Server is configured
to allow remote connections. (provider: TCP Provider, error: 0 - No connection could
be made because the target machine actively refused it.)

Опять же — не перебирать же сетевой библиотеке все существующие на машине порты «на удачу». Сказано ей что сервис на порту 5555 а там его нет — до свидания, ищите правильный порт. Однако — запустим изучаемый сервис и повторим попытку. Возможно SQL Browser по имени экземпляра сумеет вывести нас на «путь истины»? На самом же деле и с запущенным SQL Browser нас ждет ошибка буква-в-букву совпадающая с последней показанной. А это все потому, что обнаружив в строке подключения порт сетевая библиотека и не пытается обратиться к сервису SQL Browser. То есть никакие запросы на порт UDP-1434 и не отправляются. Да, вы все поняли правильно: хотите пользоваться услугами SQL Browser — не указывайте никаких (даже верных!) портов в строке подключения. Понятно, что указав верный порт вы точно подключитесь к целевому сервису, однако последнее предложение настаивает на «посредничестве» сервиса SQL Browser, а как раз этого в сценарии с правильным портом (да и вообще любым портом фигурирующем в строке подключения) и не случится.

Статическое назначение порта именованному экземпляру.

Данный раздел будет очень коротким: способ, каким именованный экземпляр получает назначенный ему порт не играет абсолютно никакой роли в контексте вопросов обсуждаемых данной статьей. Иными словами предыдущий раздел статьи на 100% применим как к именованному экземпляру получивший свой порт динамически, так и к нему же получившему тот же (или иной по номеру) порт статически.

Так есть ли жизнь без SQL Browser?

В качестве общего заключения и практических выводов давайте порассуждаем на тему как, все таки, лучше поступить с обсуждаемым сервисом — заблокировать ли его «намертво», запускать ли его автоматически, или запускать иногда, вручную? Аксиома от которой мы можем оттолкнуться и которая не может вызвать никаких споров — чем меньше в системе запущенных процессов, тем она оптимальней в работе и стабильней в поведении. Любой запущенный процесс/поток (thread) (а любой Windows service это он и есть):

  • «отжирает» часть ресурсов системы, пусть и объем этих затрачиваемых ресурсов смехотворен;
  • может содержать ошибки, они же «баги»;
  • является еще одной «точкой атаки» для потенциального злоумышленника;
  • и т.д.

Одним словом, хорошая система содержит так мало софта (по крайней мере софта «активного», вроде тех же сервисов), насколько это вообще возможно, однако без ущерба для основного функционала для которого система вообще создавалась. А поэтому изначально правильный порыв — заблокировать SQL Browser и забыть о нем. Обсудим правильность этого порыва для систем только с экземпляром по умолчанию и для систем с добавочными именованными экземплярами. Начнем с первых, для которых правильное поведение выкристаллизовывается моментально:

Если в вашей системе нет именованных экземпляров — полностью блокируйте сервис SQL Browser.

Аргументы в пользу этого правила:

  • самому экземпляру по умолчанию SQL Browser вообще помочь не в состоянии (имеется в виду, конечно, помощь в подключении клиентов к такому экземпляру). Причем даже не важно какой порт прослушивает указанный экземпляр — «от IANA» (1433), или свой, «кастомный». SQL Browser не работает и точка;
  • возможная помощь может проистекать только при попытке DAC-подключения к обсуждаемому экземпляру, да и то, только если такое подключение происходит не по стандартному порту 1434. Однако с учетом, что:
    • прием экземпляром по умолчанию DAC-подключения по любому порту помимо 1434 следует рассматривать как ситуацию очень «своеобычную», почти ошибочную. Строго технически DBA может сменить номер порта для целей DAC-подключений, но строго официально — нет;
    • DAC-подключение, по умолчанию, возможно только с того же компьютера (то есть локально). И это — правильно;
    • DAC-подключение обычно означает возникновение очень нестандартной ситуации когда необходимость вмешательства DBA практически не обсуждается. А последнему не повредит знание номера порта по которому вверенная ему система ожидает администраторского подключения. Ну так, на всякий случай, лучше если DBA этой информацией располагает;
    так вот с учетом означенных пунктов делаем вывод что весомость такой потенциальной помощи — ничтожна. Всегда DBA может указать правильный порт в строке подключения, а скорее всего и в 99% случаев, и этого делать не придется — порт 1434 не требует вообще никаких дополнительных «телодвижений».

Общий вывод — «вырубаем».

Что же до систем с наличием именованных экземпляров то тут, никто не спорит, однозначное правило вывести значительно сложнее, и более того, такого правила удовлетворяющего всех DBA скорее всего просто нет. С одной стороны, хорошо когда клиенты знают/видят ничего не означающее имя экземпляра и не знают/не видят номера порта. Безопасность системы однозначно повышается. Однако, что бы это работало, нужно запустить тот самый дополнительный сервис — SQL Browser, после чего безопасность не менее однозначно падает. С одной стороны, удобно что номер порта прослушиваемого именованным экземпляром можно сменить в любой момент, не затрагивая код/ini-файлы клиентов. С другой стороны, необходимость в такой смене возникает столь нечасто, что обычно это означает глобальную перестройку всей IT-инфраструктуры предприятия, когда и файлы поредактировать можно, да и перекомпиляции (хотя бы частичной) скорей всего не избежать. Так в какой же тип запуска лучше поставить сервис SQL Browser в таком сценарии? Сразу же отбросим ручной запуск как самый бестолковый — обсуждаемый сервис либо нужен нашим клиентам постоянно, либо не нужен им вовсе. Из двух оставшихся вариантов автор склоняется к такому:

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

Понятно, что в отличии от первого правила это не истина в последней инстанции, и даже не приближение к ней. «Неубиваемый» контраргумент, к примеру, может быть таким: «а у нас у клиентов строка подключения жестко прошита в коде, а исходников нет». Тогда автор, сильно удивившись такой... гм... неоднозначной архитектуре клиента, вынужден будет согласится — «да, тогда включайте SQL Browser, ничего не попишешь». Однако для усредненной системы с вменяемыми клиентами и не менее вменяемыми программистами оных, совет его приведен выше — «вырубайте» и забивайте порт в строку подключения. Логика автора тут такая: как показывают предыдущие рассуждения на каждое «за» включение сервиса SQL Browser находится не менее весомое «против» такого поступка. Но есть один момент перевешивающий именно «против». Это тот самый 1% функционала сервиса SQL Browser, многократно упомянутый автором по ходу изложения материала, но пока никак не объясненный им. Исправим ситуацию.

Стало быть — о чем вообще речь? Напомню, что на тестовом стенде автора SQL Server проинсталлирован в трех экземплярах — по умолчанию, MSSQL2 и MSSQL3. А у знакомой нам утилиты командной строки sqlcmd, к которой автор данного блога питает (по понятным причинам) особо нежные чувства, есть такая хитрая опция -L. Которая (цитируя BOL) «выводит список локально настроенных серверов и имена серверов, осуществляющих передачу данных в сети». А если по нашему, по простецки, то запустив указанную утилиту с указанной же опцией мы увидим (при соблюдении одного условия, которое станет ясным далее) все экземпляры SQL сервера имеющих быть проинсталлированными хоть когда нибудь на данном компьютере. Вот что получается при выключенном сервисе SQL Browser:

c:\>sqlcmd -L

Servers:
    WINR2

Обнаруживается единственный «дефолтный» экземпляр, да и тот по всем известному порту 1433, где никаких услуг SQL Browser не требуется. А вот что меняется если обсуждаемый сервис запустить:

c:\>sqlcmd -L

Servers:
    WINR2
    WINR2\MSSQL2
    WINR2\MSSQL3

Как видите — SQL Browser услужливо подсказывает всем желающим (в том числе потенциальному злоумышленнику) имена экземпляров размещенных на целевом сервере. Но кабы такая информация отдавалась именно в таком формате и только утилите sqlcmd это б еще пол-беды было б. А на самом деле мы (и любой желающий) можем взять в руки любую утилиту из довольно обширной их группы по имени «сканеры портов». Всем начинающим злоумышленникам :) автор рекомендует утилиту PortQry. Во-первых, она вышла из под пера программистов Microsoft, что само по себе внушает доверие и настраивает нас на деловой лад. Во-вторых, в ней всего-то 146Kb а удовольствия масса ее помощь в изучении «всяких таких штучек» может быть просто неоценима. В третьих, в лучших традициях Microsoft, даже столь мелкая утилита имеет вполне вменяемую документацию, a.k.a. user manual, вот он. Ну и наконец, последнее по порядку но не по значимости — все совершенно бесплатно, a.k.a. freeware. Забрать экземпляр этой утилиты, способной дать массу пищи для размышлений пытливому уму, очень просто. Для нас на текущем этапе обсуждения важна лишь одна из множества функциональностей встроенных в утилиту: PortQry способен обратиться к указанному в командной строке порту (причем протокол TCP/UDP тоже можно выбрать), послать на него запрос и показать нам тот ответ, что на данный запрос пришел с указанного порта.

Одним словом, вновь выключив сервис SQL Browser и напечатав в консоли:

c:\PortQryV2>PortQry -n 192.168.81.3 -e 1434 -p UDP

мы имеем удовольствие наблюдать вот такой результат:

Querying target system called:

 192.168.81.3

Attempting to resolve IP address to a name...


IP address resolved to WinR2

querying...

UDP port 1434 (ms-sql-m service): NOT LISTENING

Все логично — порт UDP-1434 должен прослушиваться сервисом SQL Browser, но в данный момент он выключен, а потому мы имеем законный NOT LISTENING. А теперь включим сервис и повторим совершенно ту же команду. Искать 10 отличий в ее результатах нам не придется:

Querying target system called:

 192.168.81.3

Attempting to resolve IP address to a name...


IP address resolved to WinR2

querying...

UDP port 1434 (ms-sql-m service): LISTENING or FILTERED

Sending SQL Server query to UDP port 1434...

Server's response:

ServerName WINR2
InstanceName MSSQLSERVER
IsClustered No
Version 10.50.2500.0
tcp 1433

ServerName WINR2
InstanceName MSSQL2
IsClustered No
Version 10.50.2500.0
tcp 48229

ServerName WINR2
InstanceName MSSQL3
IsClustered No
Version 10.50.2500.0
tcp 1159

==== End of SQL Server query response ====

UDP port 1434 is LISTENING

Если в случае экспериментов с утилитой sqlcmd автор не был уверен в резонности сообщать всем желающим просто имена экземпляров проинсталлированных на сервер, то в резонности сообщать имена и плюс точные версии (!) SQL Server он сомневается еще больше. Ведь из скромных циферок 10.50.2500.0 можно извлечь кучу информации, как о том повествует статья Версия сервера, версия базы данных и уровни совместимости. А уж идея сообщать «всему миру» не только имена и их версии, но и номера портов (!!) прослушиваемых этими экземплярами (и, как следствие, открытых в корпоративном файрволе) кажется автору и вообще дьявольски сомнительной с точки зрения безопасности. Тот факт, что для обретения всей этой информации злоумышленнику не надо ничего (то есть абсолютно, кроме знания того где взять утилиту PortQry. Ему не нужно знать правильное название хотя бы одного экземпляра. Ему не нужно знать хотя бы один правильный TCP-порт. В отличии от сервисов самого SQL Server, сервис SQL Browser всегда слушает порт UDP-1434, это даже не настраивается и информация о том известна любому начинающему но уважающему себя хакеру)... Да, так вот, тот факт что столь ценная информация добывается столь ненапряжным способом и доступна с любого компьютера, способного в принципе послать сетевой пакет порту 1434 — все это так же не добавляет спокойствия автору. И приводит его вот к такому финальному заключению:

Если в вашей системе нет именованных экземпляров — полностью блокируйте сервис SQL Browser без всяких дальнейших размышлений. В случае их наличия так же склоняйтесь к такому же решению, если только у вас нет очень веских доводов для решения противоположного. Аккуратно оцените все потенциальные риски следующие за включением указанного сервиса, помните, что изрядную долю начальной (а потому наиболее важной) информации о конфигурации вашей системы в этом случае может получить практически любой сотрудник компании.

Однако же, будет технически корректно сделать и такое замечание:

Весь материал данной статьи, включая и ее финальное заключение, является точным и правильным при условии если ваши пользователи нуждаются в услугах только «главного» сервиса SQL Server, то бишь Database Engine. При использовании прочих сервисов так же входящих в группу SQL Server возможно следует учесть их специфику и неявные связи с сервисом SQL Browser. Например: ваши пользователи работают не только с Database Engine, но и с Analysis Services и желают пользоваться службой перенаправления (Analysis Services redirector) последнего. В этом случае следует учитывать, что эта самая служба перенаправления является составной частью SQL Server Browser и без последнего работать не будет. Подобные вторичные нюансы связанные со вспомогательными сервисами SQL Server очень незначительны по своему количеству, но они — есть. Что же касается сервиса Database Engine, то автор постарался учесть в своем рассказе и практических выводах совершенно все нюансы связанные со взаимоотношением последнего с сервисом SQL Browser.

Спасибо всем читателям блога за их внимание, письма и комментарии к материалам размещенным на сайте. Автор желает всем быстрых запросов и безопасных систем (еще раз проверьте состояние сервиса SQL Browser в вашей). Увидимся, пока! ;)





  • Другие части статьи:
  • 1
  • 2
  • 3
  • вперед »