Chamando sp_start_job de um procedimento armazenado

8

Nossos desenvolvedores precisam poder iniciar um trabalho do SQL Server Agent a partir do código .Net. Eu sei que posso chamar msdb..sp_start_job para fazer exatamente isso, mas não quero dar acesso direto às contas de usuário em geral para executar tarefas.

O que eu gostaria de fazer é criar um procedimento armazenado no banco de dados do aplicativo usando a cláusula WITH EXECUTE AS para representar uma conta proxy. O procedimento como o temos é:

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

Porém, quando executamos isso, recebemos a seguinte mensagem:

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

Alguma ideia? Essa é a melhor maneira de fazer isso no SQL2005?

Ed Leighton-Dick
fonte
11
Resolvido. Havia três partes na solução: o encadeamento de propriedade deve estar ativado no servidor; o usuário usado na instrução EXECUTE AS deve ser sa ou um usuário com permissões semelhantes para executar os trabalhos xp_sqlagent_ *; e o trabalho deve pertencer ao mesmo usuário listado na instrução EXECUTE AS.
Ed Edt Leighton-Dick
Um pouco mais de experimentação mostrou uma variação dessa solução. Se você desejar usar um usuário proxy não-SA para executar a tarefa, poderá conceder ao usuário proxy permissões EXECUTE nos procedimentos xp_sqlagent_ * no banco de dados mestre. (Os outros dois requisitos - de propriedade entre bancos de dados e de propriedade de trabalho - ainda se aplicam.)
Ed Leighton-Dick

Respostas:

5

Você colocou o logon agentProxy no banco de dados msdb e concedeu direitos para executar sp_start_job? Caso contrário, será necessário ativar o encadeamento de permissões do banco de dados para o banco de dados msdb e o banco de dados do usuário.

Provavelmente, é melhor colocar o logon no banco de dados msdb e conceder os direitos corretos.

Mrdenny
fonte
Sim - comecei adicionando-o à função SQLAgentOperator e tentei permissões diretas EXECUTE no próprio sp_start_job. Nem ajudou. Parece lançar esse erro independentemente das permissões do proxy - até mesmo uma conta no nível de administrador de sistema falha.
Ed Leighton-Dick
Use o SQL Profiler e veja qual conta está realmente fazendo a chamada. Agora que penso mais sobre isso, o Executar como é usar um usuário de banco de dados, que provavelmente não está sendo traduzido para o outro banco de dados corretamente. Tente ativar o encadeamento de banco de dados e veja se isso funciona.
mrdenny
O encadeamento de propriedade foi uma grande parte da solução, então estou premiando os pontos aqui. Acontece também que existem duas outras peças para isso; Vou anotar aqueles acima.
Ed Leighton-Dick
8

Estou feliz que você tenha resolvido isso, mas o encadeamento de propriedade não é a solução recomendada. Como você parece validamente preocupado com a segurança e a granularidade adequada dos direitos envolvidos, estou adicionando esta resposta, embora atrasada, como uma referência ao que está acontecendo e como resolver esses problemas.

EXECUTAR COMO escopo de representação

As cláusulas EXECUTE AS têm dois tipos: EXECUTE AS LOGIN e EXECUTE AS USUÁRIO. O EXECUTE AS LOGIN é autenticado pelo servidor e é um contexto de representação confiável para toda a instância SQL (com escopo no servidor):

Ao representar uma entidade usando a instrução EXECUTE AS LOGIN ou dentro de um módulo com escopo do servidor usando a cláusula EXECUTE AS, o escopo da representação é de todo o servidor. Isso significa que, após a alternância de contexto, qualquer recurso no servidor em que o logon representado tenha permissões pode ser acessado.

EXECUTE AS USER é autenticado pelo banco de dados e é um contexto de representação confiável apenas por esse banco de dados (com escopo no banco de dados):

No entanto, ao representar um principal usando a instrução EXECUTE AS USER, ou dentro de um módulo com escopo de banco de dados usando a cláusula EXECUTE AS, o escopo da representação é restrito ao banco de dados por padrão. Isso significa que as referências a objetos fora do escopo do banco de dados retornarão um erro.

Um procedimento armazenado que possui uma cláusula EXECUTE AS criará um contexto de representação no escopo do banco de dados e, como tal, não poderá fazer referência a objetos fora do banco de dados, caso em que você não poderá fazer referência msdb.dbo.sp_start_jobporque está dentro msdb. Existem muitos outros exemplos disponíveis, como tentar acessar uma DMV do escopo do servidor, tentar usar um servidor vinculado ou tentar entregar uma mensagem do Service Broker em outro banco de dados.

A habilitação de uma representação no escopo do banco de dados para acessar um recurso que normalmente não seria permitido no qual o autenticador do contexto de representação deve ser confiável. Para uma representação no escopo do banco de dados, o autenticador é o dbo do banco de dados. Isso pode ser alcançado por dois meios possíveis:

  • Ativando a propriedade TRUSTWORTHY no banco de dados que autenticou o contexto de representação (ou seja, o banco de dados em que a cláusula EXECUTE AS foi emitida).
  • Usando assinaturas de código.

Esses detalhes são descritos no MSDN: Estendendo a representação de banco de dados usando o EXECUTE AS .

Quando você resolveu o problema por meio do encadeamento de propriedade entre bancos de dados, ativou o encadeamento entre bancos de dados em todo o nível do servidor, o que é considerado um risco de segurança. A maneira mais controlada e refinada de obter o resultado desejado é usar a assinatura de código:

  • No banco de dados do aplicativo, crie um certificado autoassinado
  • assine o dbo.StartAgentJobcom este certificado
  • solte a chave privada do certificado
  • exportar o certificado para o disco
  • importar o certificado para msdb
  • crie um usuário derivado do certificado importado em msdb
  • conceda permissão AUTHENTICATE ao usuário derivado em msdb

Essas etapas garantem que o contexto EXECUTE AS do dbo.StartAgentJobprocedimento agora seja confiável msdb, porque o contexto é assinado por uma entidade com permissão AUTHENTICATE msdb. Isso resolve metade do quebra-cabeça. A outra metade é realmente conceder a permissão EXECUTE msdb.dbo.sp_start_jobpara o contexto de representação agora confiável. Existem várias maneiras de como isso pode ser feito:

  1. mapear o usuário agentProxyusuário representado msdbe conceder a ele permissão de execução emmsdb.dbo.sp_start_job
  2. conceda a permissão de execução ao msdbusuário derivado do certificado de autenticador
  3. adicione uma nova assinatura ao procedimento, obtenha um usuário para ele msdbe conceda a permissão de execução a esse usuário derivado

A opção 1. é simples, mas tem uma grande desvantagem: o agentProxyusuário agora pode executar o que msdb.dbo.sp_start_jobquiser, ele realmente tem acesso msdbe permissão de execução.

A opção 3 está positivamente correta, mas acho que é um exagero desnecessário.

Portanto, minha preferida é a Opção 2: conceda a permissão EXECUTE msdb.dbo.sp_start_jobao usuário derivado do certificado criado em msdb.

Aqui está o SQL correspondente:

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

Meu blog possui alguns artigos que abordam esse tópico, escritos no contexto dos procedimentos ativados pelo Service Broker (uma vez que exigem uma cláusula EXECUTE AS):

BTW, se você está tentando testar meu roteiro e mora no hemisfério leste ou no horário de verão do Reino Unido, leia definitivamente o último artigo que vinculei antes de testar.

Remus Rusanu
fonte
0

Como você está tentando iniciar o SQL Server Agent a partir do código .NET, essa pode ser uma pergunta melhor para o StackOverflow?

http://www.stackoverflow.com

KPWINC
fonte
11
Acho que esse é provavelmente um problema de segurança do banco de dados, mas tentarei o StackOverflow se não encontrarmos uma resposta aqui.
Ed Leighton-Dick
0

A verificação de uma instância SQL aleatória na rede SQLAgentOperatorRole não fornece privilégios sp_start_job diretamente, ela os herda de SQLAgentUserRole.

Verifique-o usando:

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'

Execute isso no MSDB e verifique se você não herdou nenhum acesso negado explícito.

hth.

Andrew
fonte
O usuário é explicitamente um membro de SQLAgentOperatorRole e SQLAgentUserRole.
Ed Leighton-Dick
0

Uma maneira de conseguir isso sem conceder permissões adicionais: não permita que o processo armazenado inicie o trabalho diretamente, mas permita que o processo armazenado inverta um pouco em uma tabela (no banco de dados do aplicativo); então, deixe o trabalho ser executado a cada minuto, verifique se o bit está invertido e, se estiver, execute o trabalho e inverta o bit novamente. Se o trabalho perceber que o bit não está invertido, o trabalho será encerrado.

Funciona como um encanto, se você não se importa com o atraso (e o trabalho sendo executado com muita frequência).

Robert van den Berg
fonte