eu tenho essa configuração
- Criei dois bancos de dados (origem e destino).
- O banco de dados de origem possui um procedimento armazenado que está acessando a tabela no banco de dados de destino.
- O procedimento armazenado é executado como conta de serviço
- O login da conta de serviço tem conta de usuário em ambos os bancos de dados
- A conta de serviço tem permissão de conexão e autenticação ao banco de dados de destino
- A conta de serviço tem permissão de leitura no esquema de banco de dados de destino
- Ambos os bancos de dados têm confiança em
mesmo assim deu erro
O principal do servidor "ServiceAccount" não pode acessar o banco de dados "TargetDatabase" no contexto de segurança atual.
script SQL
-- Create logins
CREATE LOGIN SourceDatabaseOwner WITH PASSWORD = 'Pa$$w0rd'
CREATE LOGIN TargetDatabaseOwner WITH PASSWORD = 'Pa$$w0rd'
CREATE LOGIN ServiceAccount WITH PASSWORD = 'Pa$$w0rd'
-- Create databases
CREATE DATABASE SourceDatabase
CREATE DATABASE TargetDatabase
-- Setup trustworthy
ALTER DATABASE SourceDatabase SET TRUSTWORTHY ON;
ALTER DATABASE TargetDatabase SET TRUSTWORTHY ON;
-- Setup database owners
USE SourceDatabase
GO
EXEC dbo.sp_changedbowner @loginame = N'SourceDatabaseOwner'
USE TargetDatabase
GO
EXEC dbo.sp_changedbowner @loginame = N'TargetDatabaseOwner'
-- Add ServiceAccount to source database
USE SourceDatabase
GO
CREATE USER ServiceAccount FOR LOGIN ServiceAccount;
-- Add ServiceAccount to target database
USE TargetDatabase
GO
CREATE USER ServiceAccount FOR LOGIN ServiceAccount;
-- Enable ServiceAccount to authenticate to target database
USE TargetDatabase
GO
GRANT AUTHENTICATE TO ServiceAccount;
-- Grant permissions
USE TargetDatabase
GO
GRANT SELECT ON SCHEMA::dbo TO ServiceAccount
-- Create table with data
USE TargetDatabase
GO
create table dbo.InterestingData (Id int identity primary key, Content nvarchar(255))
insert into dbo.InterestingData (Content) values ('Foo'), ('Bar')
-- Create stored procedure executing under service account and accessing target database
USE SourceDatabase
GO
create or alter procedure dbo.GetData
with execute as 'ServiceAccount' as
begin
select id, content from TargetDatabase.dbo.InterestingData
end
-- Execution does not work under service account
-- The server principal "ServiceAccount" is not able to access the database "TargetDatabase" under the current security context.
exec dbo.GetData
Então continuei e tentei assinar o procedimento com o certificado e configurar permissões na tabela de destino para o login desse certificado
-- create certificate in master
use master
go
CREATE CERTIFICATE SignProcedureCert
ENCRYPTION BY PASSWORD = 'Pa$$w0rd'
WITH SUBJECT = 'Certificate for signing stored procedures'
GO
-- backup certificate
BACKUP CERTIFICATE SignProcedureCert TO FILE ='C:\Certs\SignProcedureCert.cer'
WITH PRIVATE KEY
(
FILE = 'C:\Certs\SignProcedureCert.pvk',
DECRYPTION BY PASSWORD = 'Pa$$w0rd',
ENCRYPTION BY PASSWORD = 'Pa$$w0rd'
)
GO
-- create login from the certificate
create login SignProcedureLogin from certificate SignProcedureCert
-- Import certificate to the source database
CREATE CERTIFICATE SignProcedureCert
FROM FILE = 'C:\Certs\SignProcedureCert.cer'
WITH PRIVATE KEY (FILE = 'C:\Certs\SignProcedureCert.pvk',
ENCRYPTION BY PASSWORD = 'Pa$$w0rd',
DECRYPTION BY PASSWORD = 'Pa$$w0rd')
-- Sign the procedure
ADD SIGNATURE TO dbo.GetData
BY CERTIFICATE SignProcedureCert
WITH PASSWORD = 'Pa$$w0rd';
GO
-- Setup permission for SignProcedureLogin to target database
USE TargetDatabase
GO
CREATE USER SignProcedureLogin FOR LOGIN SignProcedureLogin;
GRANT SELECT ON SCHEMA::dbo TO SignProcedureLogin
então agora eu deveria ter essa configuração
mas quando eu executo
USE SourceDatabase
GO
exec dbo.GetData
eu tenho o mesmo erro
O principal do servidor "ServiceAccount" não pode acessar o banco de dados "TargetDatabase" no contexto de segurança atual.
o que estou perdendo?
Atualizar
Quando eu removo a with execute as
cláusula, ela funciona
-- Setup trustworthy
ALTER DATABASE SourceDatabase SET TRUSTWORTHY OFF;
ALTER DATABASE TargetDatabase SET TRUSTWORTHY OFF;
USE SourceDatabase
GO
create or alter procedure dbo.GetData
--with execute as 'ServiceAccount'
as
begin
select id, content from TargetDatabase.dbo.InterestingData
end
GRANT EXECUTE ON SCHEMA::dbo TO ServiceAccount
execute as login = 'ServiceAccount'
exec dbo.GetData
revert
mas ainda é possível executar de alguma forma o procedimento com a with execute as
cláusula?
Você estava realmente muito perto de sua configuração inicial. Você estava apenas faltando 1 passo (e tem 2 passos estranhos). Porém, antes de explicar como fazer o cenário original funcionar, preciso mencionar/avisar que o cenário original não é o ideal e não deve ser utilizado, mesmo que corrigido, pois existe um método melhor e mais seguro: Module Signing. Expliquei a solução preferida para o problema geral abordado pela configuração original na minha resposta à sua pergunta relacionada: A opção Confiável deve ser desativada, mas qual é uma alternativa? .
Como você descobriu (com base em sua atualização na pergunta), o problema é devido ao uso da
WITH EXECUTE AS ...
cláusula (ou seja, representação) na definição do proc. Isso coloca o processo em quarentena no banco de dados atual. Sim, habilitarTRUSTWORTHY
(como você tentou inicialmente) pode ajudar a superar isso, mas as regras nem sempre são diretas. Primeiro,TRUSTWORTHY
afeta apenas onde um processo está sendo iniciado, portanto, não há motivo para habilitarTRUSTWORTHY
no banco de dados de destino (que é uma das duas etapas estranhas). Segundo, ao habilitarTRUSTWORTHY
, o foco da segurança passa a ser o proprietário do banco de dados atual (ou seja, onde o processo está sendo iniciado). Portanto, a questão é seSourceDatabaseOwner
tem ou não permissão para atuar como autenticador em[TargetDatabase]
, nãoServiceAccount
(ou seja, a etapa ausente é concederAUTHENTICATE
aSourceDatabaseOwner
- o que exigirá a adiçãoSourceDatabaseOwner
como usuário[TargetDatabase]
- e a segunda etapa estranha é concederAUTHENTICATE
aServiceAccount
).Aqui está uma demonstração de trabalho, com base no seu código de exemplo da pergunta:
Configuração inicial
Teste
FIXAR ...
para permitir o acesso a bancos de dados individuais
— ??? —
para permitir o acesso a todos os bancos de dados
Teste novamente
Se, por algum motivo, você realmente precisava/querer usar
WITH EXECUTE AS
(embora normalmente não haja motivo para isso, já que a assinatura de módulo permite que o proc/função/gatilho funcione de forma consistente, independentemente de quem o executa), você ainda pode fazer isso sem habilitarTRUSTWORTHY
via assinatura de módulo. Assim como na configuração original, você estava novamente perto das etapas adicionais de assinatura de módulo, mas havia novamente uma etapa ausente e uma etapa irrelevante. A assinatura de módulo muda o foco da segurança para o certificado (e, portanto, para o logon e/ou usuário baseado em certificado). Portanto, a etapa que faltava não era concederAUTHENTICATE
ao usuário baseado em certificado em[TargetDatabase]
. E a etapa estranha foi criar um Login para o Certificado, pois não há permissões de nível de servidor concedidas neste cenário.Aqui está uma demonstração de trabalho, continuando a demonstração de cima (ou seja, não independente):
Desfaça a etapa extra da demonstração acima que fez as coisas funcionarem (para mostrar que a assinatura de módulo também corrige esse cenário)
Teste novamente (verifique se o proc não funciona mais)
Configurar a assinatura do módulo
Teste novamente (verifique se o proc ainda não funciona)
A configuração de assinatura de módulo que funcionou na pergunta relacionada (vinculada ao topo) não funciona aqui.
O problema é a
WITH EXECUTE AS
cláusula na definição do proc.FIXAR
Teste novamente
Para obter mais informações sobre a assinatura de módulo em geral, visite meu site:
https://ModuleSigning.Info/