SQL 对 运行 作业的证书权限
SQL Certificate Permissions to Run Jobs
我们需要权限较低的用户可以通过 proc 启动 1 个作业。所以,我们:
- 创建了一个 proc(在 master db 中)来启动作业
- 创建了一个证书并用证书签署了过程
- 从证书创建了一个登录名并授予了对该登录名更高级别的访问权限
- 在 master 数据库中创建了一个角色,并为该角色授予了 EXECUTE 权限
- 将权限较低的初始用户放入这个新角色
我的问题 为什么这行不通?我们使用类似的东西来允许用户在我们的一台服务器上恢复数据库。现在让这个为我工作的唯一方法是在 msdb db 中为低权限用户提供更高的权限。这显然是不理想的,并且完全取消了我们试图避免的事情。
下面是示例权限代码,下面是 proc 代码。
USE [master];
GO
CREATE CERTIFICATE [TestCert]
ENCRYPTION BY PASSWORD = N'ComplicatedPassword'
WITH SUBJECT = N'Certificate to allow user to kick off one job';
GO
ADD SIGNATURE
TO [dbo].[ProcThatCallsJob]
BY CERTIFICATE [TestCert]
WITH PASSWORD = N'ComplicatedPassword';
GO
CREATE LOGIN [TestLoginFromCert] FROM CERTIFICATE [TestCert]
GO
ALTER SERVER ROLE [sysadmin] ADD MEMBER [TestLoginFromCert]
GO
CREATE ROLE [RoleToExecuteProc]
GO
GRANT EXECUTE ON OBJECT::[dbo].[ProcThatCallsJob]
TO [RoleToExecuteProc];
GO
CREATE USER [TestUser_LowPermissions]
FOR LOGIN [TestUser]
GO
ALTER ROLE [RoleToExecuteProc] ADD MEMBER [TestUser_LowPermissions]
GO
USE [msdb]
GO
CREATE USER [TestUserFromCert]
FOR LOGIN [TestLoginFromCert]
GO
ALTER ROLE [db_datareader] ADD MEMBER [BDWReportUserRunJobs]
GO
ALTER ROLE [SQLAgentOperatorRole] ADD MEMBER [BDWReportUserRunJobs]
GO
进程,[dbo].[ProcThatCallsJob]:
USE [master]
GO
CREATE PROCEDURE [dbo].[ProcThatCallsJob]
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE @sql NVARCHAR(MAX);
SET @sql = N'USE msdb
EXEC msdb.dbo.sp_start_job @job_name = ''10171_TestJob''
'
EXECUTE sp_executesql @sql;
END
问题是否与 proc 使用 master db 中的证书签名(与具有 sysadmin 权限的登录相关联)这一事实有关?我可以将证书放在 msdb 数据库中,但我没有任何东西可以在那里签名。
编辑 - 我在 msdb db 中创建了 proc 和证书,以测试我的假设,即更改数据库引起了一些担忧。没有运气。 [TestUser_LowPermissions] 仍然收到无法执行 sp_start_job 的错误。所以,我仍然不知道为什么签名的过程没有继承 sysadmin 的 [TestLoginFromCert] 权限。
来自MSDN forum:
到目前为止你所做的一切本身都是正确的。问题出在嵌套系统存储过程本身。
事情是:sp_start_job 正在做另一个 permission/role 检查,hard-coded:
-- 检查超出 sysjobs_view 检查的权限
-- SQLAgentReader 角色可以看到所有作业,但是
-- 不能 start/stop 他们不拥有的工作
IF (@job_owner_sid <> SUSER_SID() -- 不拥有该工作
AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) -- 不是系统管理员
AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 0)) -- 不是 SQLAgentOperatorRole
开始
RAISERROR(14393, -1, -1);
RETURN(1) -- 失败
结束
既然不想乱系统代码,那么剩下的选项不多了:
1. 使用 Impersonation to sa: 高风险
2. 使用可信赖的数据库——我个人不太喜欢公开这么说,因为很难教育普通用户不要使用这个概念。但如果它是您自己的 code-only 数据库,您可以保护它
3. 结合使用 [SQLAgentOperatorRole] 和您自己的过程。含义:使用户成为该角色的成员,然后拒绝在 sp_startjob(和其他过程)上执行。这样你就可以通过你自己的 proc 授予执行并通过给他 [SQLAgentOperatorRole].
来绕过 hard-coded 检查
我们需要权限较低的用户可以通过 proc 启动 1 个作业。所以,我们:
- 创建了一个 proc(在 master db 中)来启动作业
- 创建了一个证书并用证书签署了过程
- 从证书创建了一个登录名并授予了对该登录名更高级别的访问权限
- 在 master 数据库中创建了一个角色,并为该角色授予了 EXECUTE 权限
- 将权限较低的初始用户放入这个新角色
我的问题 为什么这行不通?我们使用类似的东西来允许用户在我们的一台服务器上恢复数据库。现在让这个为我工作的唯一方法是在 msdb db 中为低权限用户提供更高的权限。这显然是不理想的,并且完全取消了我们试图避免的事情。
下面是示例权限代码,下面是 proc 代码。
USE [master];
GO
CREATE CERTIFICATE [TestCert]
ENCRYPTION BY PASSWORD = N'ComplicatedPassword'
WITH SUBJECT = N'Certificate to allow user to kick off one job';
GO
ADD SIGNATURE
TO [dbo].[ProcThatCallsJob]
BY CERTIFICATE [TestCert]
WITH PASSWORD = N'ComplicatedPassword';
GO
CREATE LOGIN [TestLoginFromCert] FROM CERTIFICATE [TestCert]
GO
ALTER SERVER ROLE [sysadmin] ADD MEMBER [TestLoginFromCert]
GO
CREATE ROLE [RoleToExecuteProc]
GO
GRANT EXECUTE ON OBJECT::[dbo].[ProcThatCallsJob]
TO [RoleToExecuteProc];
GO
CREATE USER [TestUser_LowPermissions]
FOR LOGIN [TestUser]
GO
ALTER ROLE [RoleToExecuteProc] ADD MEMBER [TestUser_LowPermissions]
GO
USE [msdb]
GO
CREATE USER [TestUserFromCert]
FOR LOGIN [TestLoginFromCert]
GO
ALTER ROLE [db_datareader] ADD MEMBER [BDWReportUserRunJobs]
GO
ALTER ROLE [SQLAgentOperatorRole] ADD MEMBER [BDWReportUserRunJobs]
GO
进程,[dbo].[ProcThatCallsJob]:
USE [master]
GO
CREATE PROCEDURE [dbo].[ProcThatCallsJob]
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE @sql NVARCHAR(MAX);
SET @sql = N'USE msdb
EXEC msdb.dbo.sp_start_job @job_name = ''10171_TestJob''
'
EXECUTE sp_executesql @sql;
END
问题是否与 proc 使用 master db 中的证书签名(与具有 sysadmin 权限的登录相关联)这一事实有关?我可以将证书放在 msdb 数据库中,但我没有任何东西可以在那里签名。
编辑 - 我在 msdb db 中创建了 proc 和证书,以测试我的假设,即更改数据库引起了一些担忧。没有运气。 [TestUser_LowPermissions] 仍然收到无法执行 sp_start_job 的错误。所以,我仍然不知道为什么签名的过程没有继承 sysadmin 的 [TestLoginFromCert] 权限。
来自MSDN forum:
到目前为止你所做的一切本身都是正确的。问题出在嵌套系统存储过程本身。
事情是:sp_start_job 正在做另一个 permission/role 检查,hard-coded:
-- 检查超出 sysjobs_view 检查的权限
-- SQLAgentReader 角色可以看到所有作业,但是
-- 不能 start/stop 他们不拥有的工作
IF (@job_owner_sid <> SUSER_SID() -- 不拥有该工作
AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) -- 不是系统管理员
AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 0)) -- 不是 SQLAgentOperatorRole
开始
RAISERROR(14393, -1, -1);
RETURN(1) -- 失败
结束
既然不想乱系统代码,那么剩下的选项不多了: 1. 使用 Impersonation to sa: 高风险 2. 使用可信赖的数据库——我个人不太喜欢公开这么说,因为很难教育普通用户不要使用这个概念。但如果它是您自己的 code-only 数据库,您可以保护它 3. 结合使用 [SQLAgentOperatorRole] 和您自己的过程。含义:使用户成为该角色的成员,然后拒绝在 sp_startjob(和其他过程)上执行。这样你就可以通过你自己的 proc 授予执行并通过给他 [SQLAgentOperatorRole].
来绕过 hard-coded 检查