Llamar a sp_start_job desde un procedimiento almacenado

8

Nuestros desarrolladores deben poder iniciar un trabajo del Agente SQL Server desde su código .Net. Sé que puedo llamar a msdb..sp_start_job para hacer exactamente eso, pero no quiero dar a las cuentas de usuario generales acceso directo para ejecutar trabajos.

Lo que me gustaría hacer es crear un procedimiento almacenado en la base de datos de la aplicación utilizando la cláusula WITH EXECUTE AS para suplantar una cuenta proxy. El procedimiento tal como lo tenemos es:

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

Sin embargo, cuando ejecutamos esto, recibimos el siguiente mensaje:

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

¿Algunas ideas? ¿Es esta la mejor manera de hacer esto en SQL2005?

Ed Leighton-Dick
fuente
1
Resuelto La solución tenía tres partes: el encadenamiento de propiedad debe estar habilitado en el servidor; el usuario utilizado en la instrucción EXECUTE AS debe ser sa o un usuario con permisos similares para ejecutar los trabajos xp_sqlagent_ *; y el trabajo debe ser propiedad del mismo usuario que figura en la declaración EJECUTAR COMO.
Ed Leighton-Dick
Un poco más de experimentación mostró una variación de esta solución. Si desea utilizar un usuario proxy que no sea SA para ejecutar el trabajo, puede otorgarle al usuario proxy EJECUTAR permisos en los procedimientos xp_sqlagent_ * en la base de datos maestra. (Los otros dos requisitos - de propiedad entre bases de datos de propiedad y trabajo - todavía se aplican.)
Ed Leighton-Dick

Respuestas:

5

¿Puso el inicio de sesión agentProxy en la base de datos msdb y le otorgó derechos para ejecutar sp_start_job? De lo contrario, deberá habilitar el encadenamiento de permisos de la base de datos para la base de datos msdb y su base de datos de usuario.

Probablemente sea mejor colocar el inicio de sesión en la base de datos msdb y otorgarle los derechos correctos.

mrdenny
fuente
Sí, comencé agregándolo a la función SQLAgentOperator y luego probé los permisos EXECUTE directos en sp_start_job. Ninguno de los dos ayudó. Parece arrojar este error independientemente de los permisos del proxy, incluso una cuenta de nivel de administrador de sistemas falla.
Ed Leighton-Dick
Use SQL Profiler y vea qué cuenta está realmente haciendo la llamada. Ahora que lo pienso más, Ejecutar como es usar un usuario de la base de datos, que probablemente no se esté traduciendo a la otra base de datos correctamente. Intente activar el encadenamiento de la base de datos y vea si eso funciona.
mrdenny
El encadenamiento de la propiedad fue una gran parte de la solución, así que estoy otorgando los puntos aquí. También resulta que hay otras dos piezas para esto; Notaré los de arriba.
Ed Leighton-Dick
8

Me alegro de que haya resuelto esto, pero el encadenamiento de propiedad no es la solución recomendada. Dado que parece estar válidamente preocupado por la seguridad y la granularidad adecuada de los derechos involucrados, agrego esta respuesta, aunque tarde, como referencia a lo que está sucediendo y cómo resolver estos problemas.

EJECUTAR COMO alcance de suplantación

Las cláusulas EJECUTAR COMO vienen en dos tipos: EJECUTAR COMO INICIAR SESIÓN y EJECUTAR COMO USUARIO. EJECUTAR COMO INICIAR SESIÓN está autenticado por el servidor y es un contexto de suplantación en el que confía toda la instancia de SQL (con ámbito de servidor):

Cuando se hace pasar por un principal utilizando la instrucción EXECUTE AS LOGIN, o dentro de un módulo con ámbito de servidor mediante la cláusula EXECUTE AS, el alcance de la suplantación abarca todo el servidor. Esto significa que después del cambio de contexto, se puede acceder a cualquier recurso dentro del servidor en el que el inicio de sesión suplantado tenga permisos.

EJECUTAR COMO USUARIO está autenticado por la base de datos y es un contexto de suplantación en el que solo confía esa base de datos (ámbito de base de datos):

Sin embargo, cuando se hace pasar por un principal utilizando la instrucción EXECUTE AS USER, o dentro de un módulo con ámbito de base de datos mediante la cláusula EXECUTE AS, el alcance de la suplantación se limita a la base de datos de forma predeterminada. Esto significa que las referencias a objetos fuera del alcance de la base de datos devolverán un error.

Un procedimiento almacenado que tiene una cláusula EXECUTE AS creará un contexto de suplantación de ámbito de base de datos y, como tal, no podrá hacer referencia a objetos fuera de la base de datos, en este caso, no podrá hacer referencia msdb.dbo.sp_start_jobporque está dentro msdb. Hay muchos otros ejemplos disponibles, como intentar acceder al DMV de alcance de un servidor, intentar usar un servidor vinculado o intentar entregar un mensaje de Service Broker en otra base de datos.

Habilitar la suplantación de una base de datos para acceder a un recurso en el que normalmente no se permitiría confiar en el autenticador del contexto de suplantación. Para una suplantación de ámbito de base de datos, el autenticador es la base de datos dbo. Esto se puede lograr de dos maneras posibles:

  • Al activar la propiedad TRUSTWORTHY en la base de datos que autenticó el contexto de suplantación (es decir, la base de datos donde se emitió la cláusula EXECUTE AS).
  • Mediante el uso de firmas de código.

Estos detalles se describen en MSDN: Extensión de la suplantación de bases de datos mediante EXECUTE AS .

Cuando resolvió el problema mediante el encadenamiento de propiedad cruzada de la base de datos, habilitó el encadenamiento cruzado de base de datos en todo el nivel del servidor, lo que se considera un riesgo de seguridad. La forma más controlada y precisa para lograr el resultado deseado es usar la firma de código:

  • En la base de datos de la aplicación, cree un certificado autofirmado
  • firmar dbo.StartAgentJobcon este certificado
  • soltar la clave privada del certificado
  • exportar el certificado al disco
  • importar el certificado a msdb
  • crear un usuario derivado del certificado importado en msdb
  • conceder permiso AUTENTICADO al usuario derivado en msdb

Estos pasos aseguran que dbo.StartAgentJobahora se confíe en msdbel contexto EXECUTE AS del procedimiento , porque el contexto está firmado por un principal que tiene permiso AUTHENTICATE msdb. Esto resuelve la mitad del rompecabezas. La otra mitad es otorgar el permiso EXECUTE msdb.dbo.sp_start_jobal contexto de suplantación ahora confiable. Hay varias formas de hacer esto:

  1. mapear el usuario suplantado agentProxyusuario en msdby le conceda permiso para ejecutarmsdb.dbo.sp_start_job
  2. conceder el permiso de ejecución al msdbusuario derivado del certificado de autenticación
  3. agregar una nueva firma al procedimiento, derivar un usuario para ello msdby otorgar el permiso de ejecución a este usuario derivado

La opción 1. es simple, pero tiene una gran desventaja: el agentProxyusuario ahora puede ejecutarla msdb.dbo.sp_start_jobpor su propia voluntad, realmente se le otorga acceso msdby tiene el permiso de ejecución.

La opción 3 es muy correcta, pero creo que es una exageración innecesaria.

Por lo tanto, mi opción preferida es la 2: otorgar el permiso EXECUTE msdb.dbo.sp_start_jobal usuario derivado del certificado creado en msdb.

Aquí está el SQL correspondiente:

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

Mi blog tiene algunos artículos que cubren este tema, escritos en el contexto de los procedimientos activados por Service Broker (ya que requieren una cláusula EXECUTE AS):

Por cierto, si estás tratando de probar mi guión y vives en el hemisferio oriental, o en el horario de verano del Reino Unido, definitivamente lee el último artículo que vinculé antes de probar.

Remus Rusanu
fuente
0

Como está intentando iniciar el Agente SQL Server desde el código .NET, ¿esta podría ser una mejor pregunta para StackOverflow?

http://www.stackoverflow.com

KPWINC
fuente
1
Creo que este es probablemente un problema de seguridad de la base de datos, pero intentaré StackOverflow si no podemos encontrar una respuesta aquí.
Ed Leighton-Dick
0

Comprobar una instancia SQL aleatoria en la red SQLAgentOperatorRole no le otorga los privilegios sp_start_job directamente, los hereda de SQLAgentUserRole.

Vuelva a verificarlo 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'

Ejecute esto en MSDB y verifique que no haya heredado ningún acceso de denegación explícito.

hth.

Andrés
fuente
El usuario es explícitamente un miembro de SQLAgentOperatorRole y SQLAgentUserRole.
Ed Leighton-Dick
0

Una forma de lograr esto sin otorgar permisos adicionales: no permita que el proceso almacenado inicie el trabajo directamente, solo permita que el proceso almacenado se voltee un poco en una tabla (en la base de datos de la aplicación); luego, deje que el trabajo se ejecute cada minuto más o menos, verifique si el bit está volteado y, de ser así, realice el trabajo y vuelva a voltear el bit. Si el trabajo ve que el bit no se voltea, el trabajo simplemente saldrá.

Funciona a las mil maravillas, si no le importa la demora (y el trabajo se ejecuta muy a menudo).

Robert van den Berg
fuente