Хотя прилинкованные серверы представляют собой неотъемлемую часть механизма, обеспечивающего взаимодействие серверов SQL Server (и других серверов), использование их от случая к случаю или некорректная настройка могут привести к серьезным проблемам в сфере безопасности.

В этой статье мы рассмотрим вопрос о настройке определений прилинкованных серверов таким образом, чтобы два или несколько серверов, входящих в одну и ту же группу доступности AlwaysOn, могли обмениваться данными, а также проверять состояние заданий, выполняемых на сервере-партнере. Впрочем, идея состоит в том, что, поскольку SERVERA может видеть и запрашивать данные на SERVERB (при этом в качестве дополнения — или альтернативы — SERVERB может видеть и запрашивать данные на SERVERA), мы можем создать регулярно запускаемое задание агента SQL Server, которое может выполняться на одном из серверов (как это определять, мы установим в одной из следующих статей), а затем позаботиться о том, чтобы все задания агента SQL Server, указанные на SERVERA, во всех деталях зеркально отображались или дублировались/синхронизировались на SERVERB (либо в обратном направлении).

Это не будет равнозначно установлению такого порядка, при котором обеспечивается или принудительно осуществляется обязательное одновременное изменение заданий на обоих серверах. Тем не менее, подобное решение в значительной степени поможет смягчить последствия реализации некоторых сценариев. Например, администратор уточняет график или детали некоего задания на «основном» сервере, а затем забывает внести аналогичные изменения в код на «вторичном» сервере. Поскольку в сети на регулярной основе выполняется задание, которое может осуществлять проверки деталей синхронизации заданий, все сотрудники будут по крайней мере оповещены о том, что при реализации сценариев, когда детали выполнения задания изменяются на одном сервере, но не изменяются на другом, возникает проблема в сфере синхронизации.

Об использовании псевдонимов для прилинкованных серверов

В этой и последующих статьях я буду использовать простые абстракции или псевдонимы, которые помогают обслуживать коды, когда дело доходит до опроса объектов на удаленных серверах. Идея состоит в следующем. Вместо того чтобы SERVERA обращался к прилинкованному серверу, называемому SERVERB, мы придем к использованию псевдонима или абстракции (PARTNER), что облегчит обслуживание кода. Иначе говоря, SERVERA будет располагать определением для «подключения» к прилинкованному серверу SERVERB, но будет называть его PARTNER, а SERVERB, в свою очередь, будет иметь обратное подключение к SERVERA и тоже будет называть его PARTNER. И все это для того, чтобы мы могли писать код с целью его развертывания как на SERVERA, так и на SERVERB, и в нем код на втором сервере будет именоваться кодом на PARTNER. Таким образом, нам не придется жестко задавать логику или код для обращения по имени к реальному серверу.

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

— Code run on SERVER_A:
SELECT job_id, name FROM PARTNER.msdb.dbo.sysjobs;
— Code run on SERVER_B:
SELECT job_id, name FROM PARTNER.msdb.dbo.sysjobs;

С другой стороны, если бы мы не пользовались псевдонимами и абстракциями, нам пришлось бы применить код, подобный следующему:

— Code run on SERVER_A:
SELECT job_id, name FROM SERVERB.msdb.dbo.sysjobs;
— Code run on SERVER_B:
SELECT job_id, name FROM SERVERA.msdb.dbo.sysjobs;

Понятно, что речь здесь не идет о внесении в код принципиальных изменений, но как бы то ни было, наша задача несколько упрощается. Увы, этот подход не работает в ситуациях, когда число серверов достигает 3 и более («не работает» в том смысле, что вам придется жестко задавать имена серверов или вносить в код иную номенклатуру).

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

USE [master];
GO
EXEC master.dbo.sp_addlinkedserver
@server = N'PARTNER',
@srvproduct = N'',
@provider = N'SQLNCLI',
@datasrc = N'tcp:OTHER_SERVER_NAME_HERE',
@catalog = N'master';
EXEC master.dbo.sp_addlinkedsrvlogin
@rmtsrvname = N'PARTNER',
@useself = N'True',
@locallogin = NULL,
@rmtuser = NULL,
@rmtpassword = NULL;
GO
EXEC master.dbo.sp_serveroption
@server = N'PARTNER',
@optname = N'rpc',
@optvalue = N'true';
GO
EXEC master.dbo.sp_serveroption
@server = N'PARTNER',
@optname = N'rpc out',
@optvalue = N'true';
GO

При выполнении приведенного выше кода вам придется на каждом сервере заменять заполнитель «OTHER_SERVER_NAME_HERE» именами реальных серверов (так, при выполнении кода на SERVER1 нужно будет указать 'SERVER2' или на NNN02 нужно будет указать NNN01 и т.д.).

Кроме того, обратите внимание на то обстоятельство, что помещенные выше сценарии активизируют режим RPC и RPC out, то есть обеспечивают для локального сервера возможность вызовов удаленных процедур Remote Procedure Calls (RPC) на удаленном сервере, так что мы можем выполнять хранимые процедуры на удаленном сервере по мере необходимости. При условии, что это действительно проблема безопасности, так что вопрос о том, кто сможет использовать данную функцию, становится для нас приоритетным.

Обзор настроек безопасности для прилинкованных серверов

Если после выполнения приведенного выше кода вы откроете узел Server Objects > Linked Servers, то увидите определение для нового прилинкованного сервера, именуемого Partner (см. экран 1).

 

Определение нового прилинкованного сервера Partner
Экран 1. Определение нового прилинкованного сервера Partner

При этом, если вы щелкните на слове PARTNER правой кнопкой мыши и в открывшемся меню выберете пункт Properties, а затем перейдете на вкладку Security, то увидите настройку, которая выглядит как на экране 2.

 

Свойства прилинкованных серверов
Экран 2. Свойства прилинкованных серверов

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

Таким образом, фраза «производится с использованием текущего контекста безопасности применяемого для регистрации в системе учетного имени» означает, что если я вошел в систему на SERVERA как 'sa', я буду использовать тот же контекст на SERVERB, когда буду регистрироваться там. Подобным же образом, если я зарегистрировался на SERVERA как DOMAIN\Mike и подключился к SERVERB, я буду зарегистрирован на этом сервере как DOMAIN\Mike (если средства Kerberos функционируют корректно). Идея состоит в том, что все разрешения, которые у меня были на первом сервере, «переходят» вслед за мной на второй сервер и далее. Но это рассуждение базируется на предпосылке, что у меня уже имеются идентичные разрешения на обоих серверах. Если же у меня не было или нет таких разрешений, то я, возможно, и смогу создать прилинкованный сервер, но не смогу подключиться к нему. Но еще важнее другое заключение: учетные записи для регистрации, не располагающие существенными разрешениями на SERVERA, не позволят повысить уровень разрешений при подключении к SERVERB.

К сожалению, мне сплошь и рядом приходится видеть такие настройки прилинкованных серверов, где выбирается последний из предлагаемых вариантов («Производится с использованием данного контекста безопасности»), где указываются 'sa' + пароль. А это означает, что подключиться к SERVERA может кто угодно или что угодно — любой пользователь, который имеет всего лишь общий доступ к незаполненной или не имеющей значения базе данных. И такой пользователь в конце концов получит статус системного администратора (с полным набором разрешений) на SERVERB, поскольку этот прилинкованный сервер был настроен абсолютно некорректно.

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

Все разрешения, которые вы имеете на SERVERA, неприкосновенны. Сказанное справедливо и в отношении разрешений (или отсутствия таковых), которыми вы располагаете на SERVERB. Помещенный выше сценарий выполняет только две задачи: он «связывает» два сервера и позволяет вам выполнять код, а также направлять запросы с одного сервера на другой, если, конечно, у вас есть на то необходимые разрешения.

О протоколе Kerberos и более сложных потребностях в сфере безопасности

Разумеется, вам необходимо иметь разрешения на выполнение целого ряда операций на серверах A и B. Но ваша задача ограничивается действиями в контексте агента SQL Server. По этой причине вы должны убедиться, что данный агент располагает на обоих серверах достаточным уровнем разрешений. Таким образом, вы, вероятно, захотите, чтобы учетная запись вашего агента SQL Server имела статус наименее привилегированной доменной учетной записи (то есть учетной записи на уровне домена, которая не входит в состав какой-либо группы или роли и не используется для запуска агента SQL Server на обоих серверах в вашей группе доступности). Разумеется, некоторые специалисты по проблемам безопасности сочли бы запуск такого объекта, как агент SQL Server, на нескольких системах под одной учетной записью грубым нарушением общепринятых методов работы (вам, возможно, потребуется завести отдельную учетную запись для каждого сервера). Тогда вам придется каким-то образом разрешить и агенту SQL Server вступить во взаимодействие с базой данных msdb на прилинкованных серверах.

Кроме того, не забывайте и о том, что Kerberos порой без предварительной настройки функционирует не лучшим образом.

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

Дабы убедиться, что все компоненты, судя по внешним признакам, не обещают неприятных сюрпризов (по крайней мере, для вас и для вашего контекста безопасности), откройте узел 'PARTNER', углубитесь в его структуру до уровня System Catalogs > msdb database и найдите таблицы msdb 'system' на удаленном сервере (см. экран 3).

 

Просмотр удаленного сервера
Экран 3. Просмотр удаленного сервера

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

Если вы не в состоянии увидеть msdb, причина этого либо в том, что используемая вами учетная запись не имеет необходимых разрешений (что маловероятно, если вы в данный момент настраиваете прилинкованные серверы), либо в том, что система Kerberos (или какой-то иной компонент) вышла из строя. При выяснении причин неполадок воспользуйтесь следующими материалами: The Biggest Mistake: Service Principal Names (blogs.iis.net/brian-murphy-booth/archive/2007/03/09/the-biggest-mistake-serviceprincipalname-s.aspx) и Register a Server Principal Name for Kerberos Connections (msdn.microsoft.com/en-us/library/ms191153(v=sql.110).aspx).

Если в перечисленных статьях вы не найдете ответов на свои вопросы, то я вам сочувствую: диагностика проблем Kerberos — занятие не из приятных (просто потому, что здесь все должно быть выстроено идеально, и малейший промах в работе с именами SPN может повлечь за собой полный выход из строя системы Kerberos).

Иначе, если вы просто окажетесь не в состоянии заставить Kerberos работать на себя (или позднее не справитесь с агентами SQL Server), возможно, вам потребуется подойти к проблеме с другой стороны. Однако данный способ самый безопасный, и именно по этой причине я излагаю его здесь. В сущности, вы создадите специализированную, одноразовую, наименее привилегированную учетную запись для выполнения проверок синхронизации задач, а затем будете выполнять задания с помощью посредников агента SQL Server Agent Proxies. Сделать это можно следующим образом:

  • На основном сервере SQL Server создайте новую учетную запись с минимальным уровнем привилегий (если есть такая возможность, я бы рекомендовал использовать имя SQL Auth — в результате получится учетная запись, скажем, с именем JobsSyncCheckupAccount).
  • Предоставьте ей доступ к базе данных msdb и членство в надлежащих и необходимых ролях SQLAgent, определенных здесь. Конкретные роли, которые вам понадобятся, будут определены в следующих статьях; это будет зависеть от того, каким образом вы решите выполнять операции, которые ваш код сможет осуществлять при выполнении проверок.
  • Создайте для данной учетной записи новую комбинацию «учетные данные + посредник».
  • Настройте ваше задание таким образом, чтобы оно выполнялось с использованием созданной вами учетной записи посредника.

Теперь перейдите на вкладку Security и создайте сопоставления вашего прилинкованного сервера для определенного вами конкретного учетного имени, опираясь на экран 4, где представлены некоторые примеры вариантов высокого уровня для осуществляемых явным образом добавления/связывания сопоставлений учетных имен для SQL Auth и интегрированной версии аналогичной идеи.

 

Примеры высокоуровневых вариантов для осуществляемого явным образом добавления сопоставлений учетных имен
Экран 4. Примеры высокоуровневых вариантов для осуществляемого явным образом добавления сопоставлений учетных имен

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

В этот момент (то есть когда средства для организации взаимодействия между серверами уже активированы) вы будете готовы к настройке некоторых регулярных заданий для проверки того, синхронизированы ли задания должным образом. Но это уже тема следующей статьи.