web-dev-qa-db-ja.com

オブジェクトsp_start_jobで実行権限が拒否されました

ユーザーが他のジョブを開始することができなくても、特定のエージェントジョブを開始できるようにする必要があります。これを実現するために、次の手順を作成しました(簡略化)。

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

しかし、このプロシージャを呼び出すと( "sysadminaccount"を介して実行されていることが確認されている場合)、次のエラーメッセージが表示されます。

メッセージ229、レベル14、状態5、プロシージャsp_start_job、行1 EXECUTE権限は、オブジェクト 'sp_start_job'、データベース 'msdb'、スキーマ 'dbo'で拒否されました。

アカウントはsysadminロールのメンバーであるため、ジョブの開始に問題はないはずです。 msdbの3つのsqlagentロールのメンバーであることを確認しました。これらのロールはすべてsp_start_jobに対する実行権限を持っています。

このアカウントに適切な権限を与えるにはどうすればよいですか?なりすましのために他に必要なことはありますか?

8
Mansfield

TRUSTWORTHYオプションは、さまざまなものへの露出を大幅に増やすため、私は好きではありません。 Remusが この答え で説明しているように、本質的にdb_ownerからsysadminへ。他に読む価値のあるものとして、TRUSTWORTHYのシリーズ Sebastian Meine によるシリーズ、BOLトピック、およびKB記事があります(このシナリオでは、アセンブリの部分は関係がない場合もあります)。 :

(そして、このプロパティの盲目的な使用を警告する他の投稿がたくさんあります-それが機能し、それが簡単だからといって、それが正しいことを意味するわけではありません-実際、それはあなたにさらに疑問を抱かせるはずです。)だから私は別のアプローチを提案するでしょう(そして、証明書で署名するなど、他にもまだありますが、これは常に私にとってはうまくいきました):

  1. msdbにプロシージャを作成します。
  2. このユーザーのログイン用のユーザーをmsdbに作成します。

    USE msdb;
    GO
    CREATE USER floobarama FROM LOGIN floobarama;
    
  3. ストアドプロシージャの実行権限をユーザーに付与します。

    GRANT EXECUTE ON [dbo].[RunJob] TO floobarama;
    
  4. それをテストします-別のデータベースからプロシージャを呼び出すことによって:

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

    または、より簡単なテスト。ジョブをすぐに実行したくなく、このユーザーが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;
    

    結果は次のようになります。

    I am really...
    ---------------
    sysadminaccount
    
  5. これがmsdbの他の何もこのユーザーに公開しないことを確認します。

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

    結果は...

    メッセージ229、レベル14、状態5、行21
    オブジェクト 'sysjobs'、データベース 'msdb'、スキーマ 'dbo'に対するSELECT権限が拒否されました。

    ...データベースにユーザーを作成しても、そのデータベース内のすべてに対する自動権限は付与されません。そのユーザー、またはユーザーが属しているロールまたはグループ(publicを含む)に対して明示的に行う必要があります。

12
Aaron Bertrand

ポイントが足りないのでコメントできないので回答を書きます。これはデータベース間の所有権の連鎖の問題のようです。このプロシージャがmsdbの外部で作成された場合、 "EXECUTE AS"がsysadminロールのメンバーとしてユーザーを偽装しても、参照されるデータベースのオブジェクトにはアクセスできません。

したがって、考えられる解決策は次のとおりです。

  1. データベースをTRUSWORTHYにします。

    ALTER DATABASE dbname SET TRUSTWORTHY ON;
    
  2. データベースの複数データベースの組み合わせ所有権を有効にします(必要でない場合があります)

    ALTER DATABASE dbname SET DB_CHAINING ON;
    
2
yahor

私は、トラストワーシーがアプローチにそれほど信頼できるわけではないことに完全に同意します。必要な結果を得るには、別の方法を選択する必要があります。

最近、SQLエージェントジョブの開始を別のユーザーまたはユーザーグループに許可する方法を投稿しました。あなたはそれを見ることができます:

sysadmin以外、SQL Serverエージェントジョブの非所有者に実行を許可

3番目のオプションであるセキュリティテーブルとストアドプロシージャは、柔軟性を最大化し、危険性を最小限に抑えます。

0
RLF