web-dev-qa-db-fra.com

Autorisation d'exécution refusée sur l'objet sp_start_job

Je dois autoriser un utilisateur à lancer un travail d'agent spécifique sans avoir la possibilité d'en démarrer d'autres. Pour ce faire, j'ai créé la procédure suivante (simplifiée):

ALTER PROCEDURE [dbo].[RunJob]
    @job_name nvarchar(200)
WITH EXECUTE AS 'sysadminaccount'
AS
BEGIN
    --SET NOCOUNT ON;
    BEGIN TRY
        EXEC msdb.dbo.sp_start_job @job_name = @job_name 

        -- Wait for job to finish
        DECLARE @job_history_id AS INT = NULL
        DECLARE @job_result AS INT = NULL

        WHILE 1=1
        BEGIN
            SELECT TOP 1 @job_history_id = activity.job_history_id
            FROM msdb.dbo.sysjobs jobs
            INNER JOIN msdb.dbo.sysjobactivity activity ON activity.job_id = jobs.job_id
            WHERE jobs.name = @job_name
            ORDER BY activity.start_execution_date DESC

            IF @job_history_id IS NULL
            BEGIN
                WAITFOR DELAY '00:00:01'
                CONTINUE
            END
            ELSE
                BREAK
        END

        -- Check exit code
        SET @job_result = (SELECT history.run_status
        FROM msdb.dbo.sysjobhistory history
        WHERE history.instance_id = @job_history_id)

        RETURN @job_result;

    END TRY
    BEGIN CATCH
        THROW;
        RETURN;
    END CATCH
END

Cependant, lorsque j'appelle cette procédure (après avoir vérifié qu'elle s'exécute via "sysadminaccount"), j'obtiens le message d'erreur suivant:

Msg 229, niveau 14, état 5, procédure sp_start_job, ligne 1 L'autorisation EXECUTE a été refusée sur l'objet 'sp_start_job', base de données 'msdb', schéma 'dbo'.

Le compte est membre du rôle sysadmin, donc je comprends qu'il ne devrait pas y avoir de problèmes pour démarrer des emplois. J'ai vérifié qu'il est membre des trois rôles sqlagent dans msdb, et ces rôles ont tous une autorisation d'exécution sur sp_start_job.

Comment puis-je donner à ce compte les autorisations appropriées? Y a-t-il autre chose à faire à cause de l'emprunt d'identité?

8
Mansfield

Je n'aime pas l'option TRUSTWORTHY car elle augmente considérablement votre exposition à une variété de choses. Comme Remus l'explique dans cette réponse , cela élève essentiellement tout db_owner à sysadmin. Certaines autres choses qui valent la peine d'être lues sont une série sur TRUSTWORTHY par Sebastian Meine , la rubrique BOL et un article de la base de connaissances (même si les portions d'assemblage peuvent ne pas vous concerner dans ce scénario) :

(Et il y a des tonnes d'autres messages qui mettent en garde contre l'utilisation aveugle de cette propriété - ce n'est pas parce que cela fonctionne et que c'est facile que c'est la bonne chose à faire - en fait, cela devrait vous inciter à le remettre en question encore plus.) Je suggère donc une approche différente (et il y en a encore d'autres, comme la signature avec un certificat, mais cela a toujours fonctionné pour moi):

  1. Créez la procédure dans msdb.
  2. Créez un utilisateur pour la connexion de cet utilisateur dans msdb:

    USE msdb;
    GO
    CREATE USER floobarama FROM LOGIN floobarama;
    
  3. Accordez à l'utilisateur des privilèges d'exécution sur la procédure stockée:

    GRANT EXECUTE ON [dbo].[RunJob] TO floobarama;
    
  4. Testez-le - soit en appelant la procédure à partir d'une autre base de données:

    USE tempdb;
    GO
    EXECUTE AS LOGIN = N'floobarama';
    GO
    EXEC msdb.dbo.RunJob @job_name = N'whatever';
    GO
    REVERT;
    

    Ou un test plus simple, au cas où vous ne voudriez exécuter aucune tâche maintenant et ne voudriez pas attendre que cet utilisateur l'exécute pour savoir s'il a un accès suffisant à msdb:

    USE msdb;
    GO
    CREATE PROCEDURE dbo.whatever
    WITH EXECUTE AS N'sysadminaccount'
    AS
    BEGIN
      SET NOCOUNT ON;
      SELECT [I am really...] = SUSER_SNAME();
    END
    GO
    GRANT EXECUTE ON dbo.whatever TO floobarama;
    
    USE tempdb;
    GO
    EXECUTE AS LOGIN = N'floobarama';
    GO
    EXEC msdb.dbo.whatever;
    GO
    REVERT;
    

    Le résultat doit être:

    I am really...
    ---------------
    sysadminaccount
    
  5. Validez que cela n'expose rien d'autre dans msdb à cet utilisateur:

    USE tempdb;
    GO
    EXECUTE AS LOGIN = N'floobarama';
    GO
    SELECT job_id FROM msdb.dbo.sysjobs;
    GO
    REVERT;
    

    Le résultat devrait être ...

    Msg 229, niveau 14, état 5, ligne 21
    L'autorisation SELECT a été refusée sur l'objet 'sysjobs', base de données 'msdb', schéma 'dbo'.

    ... puisque la création d'un utilisateur dans une base de données ne leur donne pas de droits automatiques sur quoi que ce soit dans cette base de données; vous devez le faire explicitement soit pour cet utilisateur, soit pour un rôle ou un groupe dans lequel il se trouve (y compris public).

12
Aaron Bertrand

Je ne peux pas commenter car je n'ai pas assez de points, alors écrivez la réponse. On dirait que c'est un problème de chaînage de propriété de base de données croisée. Si cette procédure est créée en dehors de msdb, elle n'a aucun accès aux objets de la base de données référencée, même si "EXECUTE AS" emprunte l'identité de l'utilisateur en tant que membre du rôle sysadmin.

La solution la plus probable est donc:

  1. Rendez votre base de données TRUSWORTHY:

    ALTER DATABASE dbname SET TRUSTWORTHY ON;
    
  2. Activer le chaînage de propriété croisée de la base de données (peut ne pas être nécessaire)

    ALTER DATABASE dbname SET DB_CHAINING ON;
    
2
yahor

Je suis totalement d'accord avec le fait que TRUSTWORTHY n'est pas du tout digne de confiance d'une approche. Vous devez choisir une autre façon d'obtenir le résultat dont vous avez besoin.

J'ai récemment publié un moyen d'accorder le démarrage d'un travail d'agent SQL à un autre utilisateur ou groupe d'utilisateurs. Vous pouvez le voir sur:

Autoriser un non-administrateur système, non propriétaire d'un travail de l'Agent SQL Server à l'exécuter

La troisième option, une table de sécurité et une procédure stockée, vous offre le plus de flexibilité et le moins d'exposition.

0
RLF