Nossa empresa recebe um arquivo diário de um de nossos clientes. É um arquivo EDIFACT traduzido para XML e baixado de um servidor SFTP.
Desenvolvemos um aplicativo (console c#) que:
- Baixe o arquivo.
- Carregue-o em um SQL Server (2005) usando um procedimento armazenado.
- Exclui este arquivo do compartilhamento de rede.
- Executa outro procedimento armazenado para integrá-lo às tabelas do nosso pedido de venda.
O serviço de agente SQL-Server e SQL-Server é iniciado como LocalSystem
conta. (Ele será substituído em breve e prefiro deixá-lo como está, sei que não está configurado corretamente.)
O procedimento para armazenar o arquivo em nosso SQL-Server é semelhante a este:
CREATE PROCEDURE [dbo].[sp_ProviderName_Download]
(
-- FQN of XML file
@file_name nvarchar(260)
)
AS
BEGIN
DECLARE @xml_data XML,
@cmd nvarchar(max);
BEGIN TRY
SET @cmd = 'SELECT @xmlText = BulkColumn FROM OPENROWSET(BULK '
+ '''' + @file_name + ''''
+ ', SINGLE_BLOB) x;';
EXEC sp_executesql @cmd, N'@xmlText XML OUTPUT', @xmlText = @xml_data OUTPUT;
INSERT INTO [dbo].[ProviderXML_Register] (..., xml_data)
VALUES (..., @xml_data);
RETURN 0;
END TRY
BEGIN CATCH
EXEC [dbo].[vsp_error_handler];
RETURN -1;
END CATCH
END
A questão :
O procedimento armazenado funciona dependendo de como definimos o ConnectionString em nosso aplicativo C#.
Se usar IntegratedSecurity=false:
this.scsb = new SqlConnectionStringBuilder();
this.scsb.ApplicationName = "ProviderXML_Download";
this.scsb.WorkstationID = Environment.MachineName;
this.scsb.DataSource = "xxx.xxx.xxx.xxx";
this.scsb.InitialCatalog = "my_db";
this.scsb.IntegratedSecurity = false;
this.scsb.Password = "*******";
this.scsb.UserID = "SqlServerLogin";
Funciona corretamente sem erros.
Mas se conectar usando IntegratedSecurity=true (usuário AD)
this.scsb = new SqlConnectionStringBuilder();
this.scsb.ApplicationName = "ProviderXML_Download";
this.scsb.WorkstationID = Environment.MachineName;
this.scsb.DataSource = "SQL_INSTANCE_NAME";
this.scsb.InitialCatalog = "my_db";
this.scsb.IntegratedSecurity = true;
ele falha com erro:
Erro do sistema operacional 5 Acesso negado.
O usuário do AD tem direitos MODIFICAR no compartilhamento de rede. E até onde eu sei, a conta 'LocalSystem' não deve poder acessar o compartilhamento de rede.
- Por que estou recebendo este erro quando IntegratedSecurity=true?
O problema é devido à forma como a segurança é tratada para operações em massa e as limitações padrão de representação.
Ao se conectar com um logon do SQL Server, não há SID/contexto de segurança externo do Windows, portanto, o SQL Server usa o contexto de segurança da conta de serviço quando precisar acessar fora do SQL Server (ou seja, interagir com o SO, rede etc.). Portanto, se a conta de serviço do SQL Server for a conta do sistema local, essa será a conta usada para acesso externo. Como seu arquivo está na rede, teria que ser que "Sistema Local" tenha acesso a esse compartilhamento de rede. Isso é possível dando acesso ao próprio servidor (especificando <domain_name>\<computer_name>$ para as permissões NTFS). Nesse caso, o processo do SQL Server não está usando a representação: o acesso externo está sendo feito no contexto de segurança do próprio processo.
Ao se conectar ao SQL Server usando um Login do Windows (seja conta AD ou conta local no servidor), há um SID a ser usado, e o SQL Server usará esse SID (ID de segurança que aponta para a conta Windows/AD) para acesso externo . Isto é o que acontece ao conectar com "Integrated Security" / "Trusted_Connection" =
true
. Agora, usar o SID da conta que faz login no SQL Server significa que o SQL Server está usando a representação para fazer com que sua solicitação de rede pareça estar vindo desse outro SID em vez de "Sistema Local" (lembre-se, a conta de serviço possui osqlservr.exe
processo, e é esse processo, não o SID que faz login no SQL Server, que está fazendo a solicitação de rede). A representação, por padrão, é colocada em quarentena na máquina em que o processo foi iniciado (semelhante a como o uso de representação viaEXECUTE AS user
é, por padrão, colocado em quarentena no banco de dados). Portanto, os comandosBULK INSERT
/OPENROWSET(BULK...
devem ser capazes de ver arquivos localmente no servidor em que o SQL Server está sendo executado, mas os compartilhamentos de rede não são locais e o contexto de segurança representado, por padrão, não se estende tão longe. O que você está vendo aqui deve ser a mesma coisa que o infame problema do "salto duplo".Então, você pode fazer um dos seguintes:
dar permissão nesse compartilhamento de rede para
EVERYONE
continue fazendo login através do SQL Server Login e não se preocupe em usar a Segurança Integrada/Conexão Confiável.
Habilite "Delegação", embora não tenha certeza se isso funcionará em uma conta do Sistema Local (você mencionou o SQL Server Agent em execução como "Sistema Local", então é isso que estaria se conectando ao SQL Server). Pode ser necessário configurar uma conta do AD e habilitar essa conta para "Delegação" para que possa ir além do servidor local. De qualquer forma, "Delegação" é como se configura o AD para permitir que uma permissão de conta (talvez até mesmo uma combinação de conta/serviço) vá além da quarentena padrão.
PS @StrayCatDBA mencionou que usar a conta de serviço de rede (ou seja, NT AUTHORITY\NETWORK SERVICE permite delegação. Além disso, consulte o link "Configurar contas e permissões de serviço do Windows" na parte inferior, pois a documentação aborda as várias opções que você tem.
Algumas informações aqui: