我正在尝试让我的 MSSQL 数据库用户能够 BULK INSERT / OPENROWSET() 存储在我们的 DFS/cifs/smb 网络共享上的 CSV 文件。
初始设置
我已将 MSSQL 服务设置为以域用户帐户运行,EXAMPLE\svc_mssql
并将该用户添加到具有相关 DFS 共享的读取权限的安全组(例如\\example.org\myshare\data\path\to\mydata.csv
)。这允许通过 SQL 身份验证连接到数据库的用户成功读取共享上的文件,因为不存在 Kerberos 双跳问题:
C:\Temp>sqlcmd -N -S %DB_HOSTNAME% -U %DB_USERNAME% -P %DB_PASSWORD%
1> SELECT
2> CAST(CONNECTIONPROPERTY('auth_scheme') AS nvarchar(10)) AS auth_type,
3> COUNT(*) AS NumLines
4> FROM OPENROWSET(
5> BULK '\\example.org\myshare\data\path\to\mydata.csv'
6> , FORMATFILE = '\\example.org\myshare\data\path\to\mydata.fmt'
7> ) AS f;
8> GO
auth_type NumLines
---------- -----------
SQL 73
(1 rows affected)
凯尔伯罗斯
我有手动注册 SPN对于 AD 中的 MSSQLSvc,它们确实在该服务帐户上正确显示:
C:\Temp>setspn -Q MSSQLSvc/DBSERVER.example.org
Checking domain DC=example,DC=org
CN=svc_mssql,OU=ServiceAccounts,DC=example,DC=org
MSSQLSvc/DBSERVER.example.org
MSSQLSvc/DBSERVER.example.org:1433
Existing SPN found!
这使得我能够使用 Kerberos 身份验证成功连接到 SQL Server:
C:\Temp>sqlcmd -N -S %DB_HOSTNAME% -E
1> SELECT CAST(CONNECTIONPROPERTY('auth_scheme') AS nvarchar(10)) AS auth_type;
2> GO
auth_type
----------
KERBEROS
(1 rows affected)
无约束委派
如果我将服务帐户设置为受信任的委派(即不受约束的委派),如下所示:
PS C:\Temp> Get-ADUser svc_mssql -Properties TrustedForDelegation,TrustedToAuthForDelegation `
>> | Select-Object Name,TrustedForDelegation,TrustedToAuthForDelegation
Name TrustedForDelegation TrustedToAuthForDelegation
---- -------------------- --------------------------
svc_mssql True False
然后一切都运行正常:
C:\Temp>sqlcmd -N -S %DB_HOSTNAME% -E
1> SELECT
2> CAST(CONNECTIONPROPERTY('auth_scheme') AS nvarchar(10)) AS auth_type,
3> COUNT(*) AS NumLines
4> FROM OPENROWSET(
5> BULK '\\example.org\myshare\data\path\to\mydata.csv'
6> , FORMATFILE = '\\example.org\myshare\data\path\to\mydata.fmt'
7> ) AS f;
8> GO
auth_type NumLines
---------- -----------
KERBEROS 73
(1 rows affected)
数据包追踪 - 无约束委派
经过多次尝试和错误,并试图推测/猜测约束委派需要什么之后,我在数据库服务器上安装了 wireshark,并从正在运行的无约束设置中抓取了数据包跟踪。我不是网络专家,但这是我在该跟踪中看到的内容(我认为):
- TGS-REQ/REP 用于
cifs/DOMAINCONTROLLER01.example.org
- TGS-REQ/REP 用于
krbtgt/EXAMPLE.ORG
- SMB 树连接请求
\\DOMAINCONTROLLER01.example.org\IPC$
- SMB Ioctl FSCTL_DFS_GET_REFERRALS
\example.org\myshare
- TGS-REQ/REP 用于
cifs/FILESERVER01
- SMB 树连接请求
\\FILESERVER01\IPC$
- SMB Ioctl FSCTL_DFS_GET_REFERRALS
\FILESERVER01\myshare
- TGS-REQ/REP 用于
cifs/FILESERVER02
- SMB 树连接请求
\\FILESERVER02\IPC$
- SMB Ioctl FSCTL_DFS_GET_REFERRALS
\FILESERVER01\myshare\data
- TGS-REQ/REP 用于
cifs/NASCLUSTER01
- SMB 树连接请求
\\NASCLUSTER01\Data
- 更多 SMB 流量实际上读取文件......
我可以看到需要代理/模拟这两个cifs/FILESERVER
SPN,因为它们是 DFS 命名空间的底层节点。经过一番研究,我也能理解 SPN cifs/NASCLUSTER
,因为那是文件服务器实际存储数据的地方。
不过,前两个请求我不太理解。我猜第一个请求cifs/DOMAINCONTROLLER01.example.org
是为了让 SQL Server 能够查找托管所请求文件共享的 DFS 命名空间中的节点。如果是这样,我想我需要允许服务帐户在所有域控制器上进行委托cifs
,对吗?
剩下的就是krbtgt/EXAMPLE.ORG
。我不明白这是做什么的。为什么会得到 TGT?我是不是误读了跟踪?我看到它是一个 tgs-req,其 msg-type 为krb-tgs-req (12)
-- 与其他相同。这是协议转换吗?如果是,为什么在这种情况下会发生这种情况?
更新
svc_mssql
因此,我(再次)尝试为以下 SPN设置约束委派:
- cifs/FILESERVER01
- cifs/FILESERVER02
- cifs/NASCLUSTER
- cifs/DOMAINCONTROLLER01
查看数据包跟踪,我发现的数据包为 ,krb-tgs-req (12)
错误代码为。原始数据包为= ,wireshark 将其解码为:cifs/FILESERVER01
krb-error (30)
eRR-BADOPTION (13)
krb-tgs-req
kdc-options
40830000
kdc-options: 40830000
0... .... = reserved: False
.1.. .... = forwardable: True
..0. .... = forwarded: False
...0 .... = proxiable: False
.... 0... = proxy: False
.... .0.. = allow-postdate: False
.... ..0. = postdated: False
.... ...0 = unused7: False
1... .... = renewable: True
.0.. .... = unused9: False
..0. .... = unused10: False
...0 .... = opt-hardware-auth: False
.... 0... = unused12: False
.... .0.. = unused13: False
.... ..1. = constrained-delegation: True
.... ...1 = canonicalize: True
0... .... = request-anonymous: False
.0.. .... = unused17: False
..0. .... = unused18: False
...0 .... = unused19: False
.... 0... = unused20: False
.... .0.. = unused21: False
.... ..0. = unused22: False
.... ...0 = unused23: False
0... .... = unused24: False
.0.. .... = unused25: False
..0. .... = disable-transited-check: False
...0 .... = renewable-ok: False
.... 0... = enc-tkt-in-skey: False
.... .0.. = unused29: False
.... ..0. = renew: False
.... ...0 = validate: False
我将这些选项与无约束委派中的数据包进行了比较,唯一的区别是位constrained-delegation
被翻转了——正如预期的那样。我做错了什么?
帮助!?!?!?
答案1
因此,事实证明,在我们最初的故障排除过程中,我们DBSERVER$
在 AD 中为计算机帐户启用了 Kerberos 委派。这显然导致系统走上了寻找 RBCD 的不同路径,而我们并没有这样做。
最重要的是,将机器帐户重置为无委派,并在两个文件服务器和 NAS 集群的服务帐户上添加约束委派,彻底解决了该问题。