Вызов sp_start_job из хранимой процедуры

8

Наши разработчики должны иметь возможность запустить задание агента SQL Server из своего кода .Net. Я знаю, что могу вызвать msdb..sp_start_job, чтобы сделать это, но я не хочу давать обычным учетным записям прямой доступ к выполненным заданиям.

Я хотел бы создать хранимую процедуру в базе данных приложения, используя предложение WITH EXECUTE AS для олицетворения учетной записи прокси. Процедура, как мы имеем это:

CREATE PROCEDURE dbo.StartAgentJob 
    WITH EXECUTE AS 'agentProxy'
AS
BEGIN
    EXEC msdb.dbo.sp_start_job N'RunThisJob';
END

Когда мы запускаем это, мы получаем следующее сообщение:

The EXECUTE permission was denied on the object 'sp_start_job', database 'msdb', schema 'dbo'.

Любые идеи? Это даже лучший способ сделать это в SQL2005?

Эд Лейтон-Дик
источник
1
Решаемые. Решение состояло из трех частей: цепочка владения должна быть включена на сервере; пользователь, используемый в операторе EXECUTE AS, должен быть sa или пользователем с аналогичными правами доступа для запуска заданий xp_sqlagent_ *; и задание должно принадлежать тому же пользователю, который указан в инструкции EXECUTE AS.
Эд Лейтон-Дик
Немного больше экспериментов показало один вариант этого решения. Если вы хотите использовать прокси-пользователя не-SA для запуска задания, вы можете предоставить прокси-пользователю разрешение EXECUTE для процедур xp_sqlagent_ * в базе данных master. (Другие два требования - владение несколькими базами данных и владение работой - все еще применяются.)
Эд Лейтон-Дик,

Ответы:

5

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

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

mrdenny
источник
Да, я начал с добавления его в роль SQLAgentOperator, а затем попробовал прямые разрешения EXECUTE для самого sp_start_job. Ни то, ни другое не помогло. Кажется, выдает эту ошибку независимо от разрешений прокси-сервера - даже учетная запись уровня системного администратора дает сбой.
Эд Лейтон-Дик,
Используйте SQL Profiler и посмотрите, какая учетная запись фактически выполняет вызов. Теперь, когда я больше об этом думаю, «Выполнить как» использует пользователя базы данных, который, вероятно, неправильно переводит в другую базу данных. Попробуйте включить цепочку базы данных и посмотрите, работает ли это.
Мрденный
Цепочка владения была большой частью решения, поэтому я присуждаю баллы здесь. Оказывается также, что есть еще две части к этому; Я отмечу это выше.
Эд Лейтон-Дик
8

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

ВЫПОЛНИТЬ КАК сфера олицетворения

Предложения EXECUTE AS представлены в двух вариантах: EXECUTE AS LOGIN и EXECUTE AS USER. EXECUTE AS LOGIN аутентифицируется сервером и является контекстом олицетворения, которому доверяет весь экземпляр SQL (в области сервера):

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

EXECUTE AS USER аутентифицируется базой данных и является контекстом олицетворения, которому доверяет только эта база данных (в области баз данных):

Однако при олицетворении принципала с помощью оператора EXECUTE AS USER или в модуле с областью базы данных с помощью предложения EXECUTE AS область олицетворения по умолчанию ограничивается базой данных. Это означает, что ссылки на объекты вне области базы данных будут возвращать ошибку.

Хранимая процедура с предложением EXECUTE AS создаст контекст олицетворения в области базы данных, и, таким образом, не сможет ссылаться на объекты вне базы данных, в данном случае вы не сможете ссылаться, msdb.dbo.sp_start_jobпотому что она находится в msdb. Есть много других доступных примеров, таких как попытка доступа к DMV области сервера, попытка использовать связанный сервер или попытка доставить сообщение компонента Service Broker в другую базу данных.

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

  • Включив свойство TRUSTWORTHY в базе данных, которая аутентифицировала контекст олицетворения (т. Е. В базе данных, в которой было выдано предложение EXECUTE AS).
  • Используя кодовые подписи.

Эти подробности описаны в MSDN: Расширение олицетворения базы данных с помощью EXECUTE AS .

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

  • В базе данных приложения создайте самозаверяющий сертификат
  • подписать dbo.StartAgentJobс этим сертификатом
  • сбросить закрытый ключ сертификата
  • экспортировать сертификат на диск
  • импортировать сертификат в msdb
  • создать производного пользователя из импортированного сертификата в msdb
  • предоставить разрешение AUTHENTICATE производному пользователю в msdb

Эти шаги гарантируют, что контексту EXECUTE AS dbo.StartAgentJobпроцедуры теперь доверяют msdb, потому что контекст подписан участником, имеющим разрешение AUTHENTICATE msdb. Это решает половину головоломки. Другая половина - фактически предоставить разрешение EXECUTE msdb.dbo.sp_start_jobдля доверенного контекста олицетворения. Есть несколько способов, как это можно сделать:

  1. карта олицетворяемых пользователя agentProxyпользователя в msdbи предоставить ему разрешение на выполнениеmsdb.dbo.sp_start_job
  2. предоставить разрешение на выполнение msdbпроизводному пользователю сертификата аутентификатора
  3. добавьте новую сигнатуру в процедуру, создайте для нее пользователя msdbи предоставьте разрешение на выполнение этому производному пользователю

Вариант 1. прост, но имеет большой недостаток: agentProxyпользователь теперь может выполнить msdb.dbo.sp_start_jobего по собственному желанию, он действительно имеет доступ msdbи имеет разрешение на выполнение.

Вариант 3, безусловно, правильный, но я чувствую, что это излишнее излишество.

Поэтому я предпочитаю вариант 2: предоставить разрешение EXECUTE для получателя msdb.dbo.sp_start_jobсертификата, созданного в msdb.

Вот соответствующий SQL:

use [<appdb>];
go

create certificate agentProxy 
    ENCRYPTION BY PASSWORD = 'pGFD4bb925DGvbd2439587y'
    with subject = 'agentProxy'
   , start_date='01/01/2009';
go

ADD SIGNATURE TO OBJECT::[StartAgentJob]
      BY CERTIFICATE [agentProxy]
        WITH PASSWORD = 'pGFD4bb925DGvbd2439587y';
go

alter certificate [agentProxy] 
  remove private key;
go

backup certificate [agentProxy] 
 to file='c:\temp\agentProxy.cer';
go

use msdb
go

create certificate [agentProxy] 
  from file='c:\temp\agentProxy.cer';
go

create user [agentProxyAuthenticator] 
 from certificate [agentProxy];
go

grant authenticate to [agentProxyAuthenticator];
grant execute on msdb.dbo.sp_start_job to [agentProxyAuthenticator];
go

use [<appdb>];
go

exec dbo.StartAgentJob;
go

В моем блоге есть несколько статей на эту тему, написанных в контексте активированных процедур Service Broker (поскольку для них требуется условие EXECUTE AS):

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

Ремус Русану
источник
0

Поскольку вы пытаетесь запустить агент SQL Server из кода .NET, это может быть лучшим вопросом для StackOverflow?

http://www.stackoverflow.com

KPWINC
источник
1
Я думаю, что это, вероятно, проблема безопасности базы данных, но я попробую StackOverflow, если мы не сможем найти ответ здесь.
Эд Лейтон-Дик,
0

Проверка случайного экземпляра SQL в сети SQLAgentOperatorRole не дает непосредственных привилегий sp_start_job, а наследует их от SQLAgentUserRole.

Дважды проверьте это с помощью:

select dp.NAME AS principal_name,
                 dp.type_desc AS principal_type_desc,
                 o.NAME AS object_name,
                 p.permission_name,
                 p.state_desc AS permission_state_desc 
    from    sys.database_permissions p
    left    OUTER JOIN sys.all_objects o on p.major_id = o.OBJECT_ID
    inner   JOIN sys.database_principals dp on p.grantee_principal_id = dp.principal_id
    where o.name = 'sp_start_job'

Запустите это в MSDB и дважды проверьте, что вы не унаследовали явный отказ в доступе.

НТН.

Эндрю
источник
Пользователь явно является членом SQLAgentOperatorRole и SQLAgentUserRole.
Эд Лейтон-Дик
0

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

Работает как шарм, если вы не против задержки (и работа выполняется очень часто).

Роберт ван ден Берг
источник