域功能级别2008 R2下远程会话复制文件的Kerberos委托问题
你遇到的是典型的Kerberos双跳权限问题:当你通过PowerShell远程连接到目标机器(第一跳)后,在远程会话中试图访问另一台服务器的共享(第二跳)时,默认情况下你的域凭据不会被传递到第二台服务器,导致访问拒绝。由于域功能级别是2008 R2,确实无法使用2012及以上林级别才支持的Kerberos约束委派Cmdlet,但还有以下几种可行的方法:
方法1:使用CredSSP进行凭据委派(支持2008 R2环境)
CredSSP允许将你的本地凭据直接委派给远程服务器,从而解决双跳问题。需要在本地客户端和所有目标远程机器上配置:
步骤1:在本地客户端启用CredSSP作为客户端
Enable-WSManCredSSP -Role Client -DelegateComputer "*.yourdomain.com" # 替换为你的域或具体机器名
步骤2:在远程目标机器启用CredSSP作为服务器
你可以通过组策略批量配置,或者手动在每台机器上运行:
Enable-WSManCredSSP -Role Server
(如果是批量操作,可以先通过初始的PowerShell会话(不带CredSSP)执行这个命令,因为此时还不需要双跳)
步骤3:使用CredSSP创建远程会话
修改你的代码,添加-Authentication Credssp参数:
$Cred = Get-Credential DOMAIN\USER $Computers = Get-Content C:\tab.txt | Where-Object { $_ } ForEach ($Computer in $Computers) { if (Test-Connection -ComputerName $Computer -BufferSize 16 -Count 1 -Quiet) { # 使用CredSSP认证创建会话 $Session = New-PSSession $computer -Credential $cred -Authentication Credssp Invoke-Command -Session $Session -ScriptBlock { # 现在可以正常访问共享了 Copy-Item -Path "\\print-server\pcclient\win\*" -Destination "c:\pcclient" -Force -Recurse -Verbose Start-Process "\\Print-Server\PCClient\win\client-local-install.exe" -ArgumentList "/SILENT" -Wait } Remove-PSSession -Session $Session } }
注意:CredSSP会将凭据以明文形式存储在远程服务器内存中,存在一定安全风险,建议仅在可信网络环境中使用。
方法2:先将文件下载到本地,再传到远程机器(绕过双跳)
这种方法不需要修改任何委派配置,通过本地中转文件来避免双跳:
$Cred = Get-Credential DOMAIN\USER $Computers = Get-Content C:\tab.txt | Where-Object { $_ } # 先将共享文件下载到本地临时目录 $localTemp = "C:\temp\pcclient" New-Item -Path $localTemp -ItemType Directory -Force | Out-Null Copy-Item -Path "\\print-server\pcclient\win\*" -Destination $localTemp -Force -Recurse ForEach ($Computer in $Computers) { if (Test-Connection -ComputerName $Computer -BufferSize 16 -Count 1 -Quiet) { $Session = New-PSSession $computer -Credential $cred # 将本地文件传到远程机器 Copy-Item -Path "$localTemp\*" -Destination "c:\pcclient" -Force -Recurse -ToSession $Session -Verbose # 在远程执行安装 Invoke-Command -Session $Session -ScriptBlock { Start-Process "c:\pcclient\client-local-install.exe" -ArgumentList "/SILENT" -Wait } Remove-PSSession -Session $Session } } # 清理本地临时文件 Remove-Item -Path $localTemp -Recurse -Force
这个方法的优势是安全性更高,不需要委派凭据,但如果文件体积较大,会增加本地磁盘占用和网络传输时间。
方法3:在远程会话中显式映射共享并提供凭据
在远程会话中使用net use命令显式指定共享的访问凭据,这样不需要依赖Kerberos委派:
$Cred = Get-Credential DOMAIN\USER $Computers = Get-Content C:\tab.txt | Where-Object { $_ } ForEach ($Computer in $Computers) { if (Test-Connection -ComputerName $Computer -BufferSize 16 -Count 1 -Quiet) { $Session = New-PSSession $computer -Credential $cred Invoke-Command -Session $Session -ScriptBlock { param($sharePath, $destPath, $username, $password) # 映射共享驱动器 net use Z: $sharePath $password /USER:$username /PERSISTENT:NO # 复制文件 Copy-Item -Path "Z:\*" -Destination $destPath -Force -Recurse -Verbose # 卸载共享驱动器 net use Z: /DELETE /YES # 执行安装 Start-Process "$sharePath\client-local-install.exe" -ArgumentList "/SILENT" -Wait } -ArgumentList @("\\print-server\pcclient\win", "c:\pcclient", $Cred.UserName, $Cred.GetNetworkCredential().Password) Remove-PSSession -Session $Session } }
注意:这种方法会在远程会话中明文传递密码,虽然
net use会处理,但仍有一定安全风险,建议仅在必要时使用。
方法4:使用PsExec工具(无需PowerShell远程)
如果你不想依赖PowerShell远程,可以使用Sysinternals的PsExec工具直接在远程机器上执行命令,它可以直接传递凭据并访问共享:
$Cred = Get-Credential DOMAIN\USER $Computers = Get-Content C:\tab.txt | Where-Object { $_ } $sharePath = "\\print-server\pcclient\win" ForEach ($Computer in $Computers) { if (Test-Connection -ComputerName $Computer -BufferSize 16 -Count 1 -Quiet) { # 使用PsExec复制文件并执行安装 .\psexec.exe \\$Computer -u $Cred.UserName -p $Cred.GetNetworkCredential().Password ` cmd /c "xcopy `"$sharePath\*`" `"c:\pcclient`" /E /I /Y && `"$sharePath\client-local-install.exe`" /SILENT" } }
PsExec的优势是配置简单,不需要提前启用PowerShell远程,但需要在本地下载PsExec工具,且同样存在明文传递密码的风险。
内容的提问来源于stack exchange,提问作者Royston

