我们公司从我们的一位客户那里收到一份每日文件。它是一个翻译成 XML 并从 SFTP 服务器下载的 EDIFACT 文件。
我们开发了一个(c# 控制台)应用程序:
- 下载文件。
- 使用存储过程将其上传到一台 SQL Server (2005)。
- 从网络共享中删除此文件。
- 运行另一个存储过程以将其与我们的销售订单表集成。
SQL-Server 和 SQL-Server 代理服务作为LocalSystem
帐户启动。(它很快就会被替换,我宁愿让它保持原样,我知道它没有正确配置。)
将文件存储在我们的 SQL-Server 中的过程与此类似:
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
问题:
存储过程的工作方式取决于我们在 C# 应用程序中设置 ConnectionString 的方式。
如果它使用 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";
它可以正常工作而没有错误。
但如果它使用 IntegratedSecurity=true 连接(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;
它失败并出现错误:
操作系统错误 5 访问被拒绝。
AD 用户对网络共享拥有 MODIFY 权限。据我所知,“LocalSystem”帐户不应该能够访问网络共享。
- 为什么当 IntegratedSecurity=true 时出现此错误?
问题在于如何处理批量操作的安全性以及模拟的默认限制。
使用 SQL Server 登录连接时,没有外部 Windows SID/安全上下文,因此当需要访问 SQL Server 外部(即与操作系统、网络等交互)时,SQL Server 使用服务帐户的安全上下文。因此,如果 SQL Server 服务帐户是本地系统帐户,那么它就是用于外部访问的帐户。由于您的文件在网络上,因此“本地系统”必须有权访问该网络共享。这可以通过授予服务器本身访问权限(通过为 NTFS 权限指定<domain_name>\<computer_name>$ )来实现。在这种情况下,SQL Server 进程没有使用模拟:外部访问是在进程本身的安全上下文中进行的。
使用 Windows 登录(无论是 AD 帐户还是服务器上的本地帐户)连接到 SQL Server 时,有一个 SID 可供使用,SQL Server 将使用该 SID(指向 Windows / AD 帐户的安全 ID)进行外部访问. 这就是使用 "Integrated Security" / "Trusted_Connection" = 连接时发生的情况
true
。现在,使用登录到 SQL Server 的帐户的 SID 意味着 SQL Server 正在使用模拟使其网络请求看起来来自另一个 SID 而不是来自“本地系统”(请记住,服务帐户拥有sqlservr.exe
进程,发出网络请求的是那个进程,而不是登录到 SQL Server 的 SID)。默认情况下,模拟被隔离到启动进程的机器上(类似于EXECUTE AS user
默认情况下使用模拟通过被隔离到数据库的方式)。因此,BULK INSERT
/OPENROWSET(BULK...
命令应该能够在运行 SQL Server 的服务器上本地查看文件,但网络共享不是本地的,并且默认情况下模拟的安全上下文不会扩展那么远。您在这里看到的应该与臭名昭著的“双跳”问题相同。因此,您可以执行以下操作之一:
授予对该网络共享的权限
EVERYONE
继续通过 SQL Server Login 登录,不用担心使用集成安全/可信连接。
启用“委托”,虽然我不确定这是否适用于本地系统帐户(您提到 SQL Server 代理作为“本地系统”运行,所以这就是连接到 SQL Server 的内容)。它可能需要设置一个 AD 帐户并为“委托”启用该帐户,以便它可以超越本地服务器。无论哪种方式,“委托”都是如何配置 AD 以允许帐户(甚至可能是帐户/服务组合)权限超出默认隔离区。
PS @StrayCatDBA 提到使用网络服务帐户(即NT AUTHORITY\NETWORK SERVICE允许委派。此外,请参阅底部的“配置 Windows 服务帐户和权限”链接,因为该文档涵盖了您拥有的各种选项。
这里的一些信息: