2017-10-11 477 views
0

我的网络中的每个人都可以读取包含需要安装的证书的SMB共享。我也以域管理员身份执行此代码。有什么必要克服这个错误?调用命令导入证书访问被拒绝

Access is denied. 0x80070005 (WIN32: 5 ERROR_ACCESS_DENIED) 

这是我想要在每台服务器上安装证书的代码。

$vms = "App1","App2","App3" 

Invoke-Command -ComputerName $vms -ScriptBlock { 
    Import-Certificate ` 
     -FilePath \\dc1\CertificateShare\17C37D0A655B89967398C9E7E8185F78541B1589.cer ` 
     -CertStoreLocation 'Cert:\LocalMachine\Root' 

} 
+0

- 固定双跃点身份验证问题“*什么是需要克服这个错误*?”;可能重复的[Powershell服务器网络驱动器](https://stackoverflow.com/questions/24903878/powershell-server-network-drive) – TessellatingHeckler

回答

0

您遇到了一个称为双跳问题。在本质上,您要求远程会话使用相同的Windows身份验证访问另一个外部资源。

由于安全原因,Microsoft默认情况下不允许这种情况。

Double hop issue

如果您想了解更多关于它,请阅读我的整个后PowerShell remoting caveats

的解决方案是不容易的,definetely不是唯一的,因为它主要有Kerberos身份验证以及如何做域控制器想要控制这个。例如,域控制器可以指定两台服务器之间允许使用特定的双跳,否则您必须允许每台服务器使用双跳。

更好的解决方案是使用域配置,但您需要联系您的域管理员并说服他。

我将通过脚本参考显示如何为每个服务器执行此操作。例如从上面的图片中,我们必须配置ServerA以允许将凭证委托给ServerB

我必须解决这些问题,我已经构建了一个自动化存储库。自动化名称为ISHBootstrap,我将参考此存储库中的脚本。并非一切都适合这里。请记住,所有脚本都可以在本地或远程执行,这就是为什么您会注意到使用包装程序cmdlet的原因:Invoke-CommandInvoke-CommandWrap。只需查看受尊重的脚本块并提取代码即可。

首先,我们需要在ServerA上安装一些前提条件。

我使用这个脚本Install-WinRMPrerequisites.ps1,但它是作为此命令

Get-WindowsFeature |Where-Object -Property Name -EQ "WinRM-IIS-Ext"|Add-WindowsFeature 

然后,我们需要启用和配置WSManCredSSP一样简单。这需要一个安全的远程会话,这就是为什么我们需要通过SSL设置winrm。此脚本运行整个过程Enable-WSManCredSSP.ps1

  1. 启用WSManCredSSP。
  2. 安装自签名证书并使用安全端点配置WSMan。
  3. 重新启动WSMan。
  4. 打开防火墙端口。

下面是从脚本中提取出代码:

#region Enable WSManCredSSP 
Enable-WSManCredSSP -Role Server -Force | Out-Null 
#endregion 

#region Configure a secure WinRMListener 
$winRmListeners=& winrm enumerate winrm/config/listener 
$httpsLine= $winRmListeners -match "HTTPS" 
Write-Debug "httpsLine=$httpsLine" 
if(-not $httpsLine) 
{ 
    $certificate=Get-ChildItem -Path Cert:\LocalMachine\My |Where-Object -Property Thumbprint -EQ $Thumbprint 
    $hostname=(($certificate.Subject -split ', ')[0] -split '=')[1] 
    Write-Debug "Adding winrm https listener" 
    & winrm create winrm/config/Listener?Address=*+Transport=HTTPS "@{Hostname=""$hostname"";CertificateThumbprint=""$Thumbprint""}" 
    Write-Verbose "Added winrm https listener" 

    Write-Debug "Configuring ACL" 
    # Specify the user, the permissions and the permission type 
    $permission = "NETWORK SERVICE","Read,FullControl","Allow" 
    $accessRule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $permission; 

    $keyPath = $env:ProgramData + "\Microsoft\Crypto\RSA\MachineKeys\"; 
    $keyName = $certificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName; 
    $keyFullPath = Join-Path $keyPath $keyName; 

    # Get the current acl of the private key 
    # This is the line that fails! 
    $acl = Get-Acl -Path $keyFullPath; 

    # Add the new ace to the acl of the private key 
    $acl.AddAccessRule($accessRule); 

    # Write back the new acl 
    Set-Acl -Path $keyFullPath -AclObject $acl; 
    Write-Verbose "Configured ACL" 
} 
else 
{ 
    Write-Warning "winrm https listener detected. Skipped" 
} 
#endregion 

#region restart WinRm service 
Write-Debug "Restarting winrm service" 
Get-Service -Name WinRM |Restart-Service| Out-Null 
while((Get-Service -Name WinRM).Status -ne "Running") 
{ 
    Start-Sleep -Milliseconds 500 
} 
Write-Verbose "Restarted WINRM service" 
#endregion 


#region Configure windows firewall 
$ruleName="WinRM-HTTPS" 
$rulePort=5986 
Write-Debug "Querying if firewall port for winrm https is open" 
if(-not (Get-NetFirewallRule|Where-Object {($_.DisplayName -eq $ruleName) -and ($_.Direction -eq "Inbound")})) 
{ 
    Write-Verbose "Adding firewall port for winrm https is open" 
    New-NetFirewallRule -DisplayName $ruleName -Direction Inbound -Action Allow -Protocol "TCP" -LocalPort $rulePort|Out-Null 
} 
Write-Host "Winrm https firewall port is ok"   
#endregion 

此时ServerA准备好您的授权委托给另一台服务器,但客户仍然需要明确指出的是,远程会话必须能够做到这一点。使用CredSSP认证类型时这样

$session=New-PSSession -ComputerName $Computer -Credential $Credential -UseSSL -Authentication Credssp 

公告创建一个会话两个重要的事情:

  • -Credentials必须明确指定,即使他们是为登录同一用户。
  • 使用-UseSSL使用winrm安全。

现在使用$session与你原来的代码,它应该工作

Invoke-Command -ComputerName $vms -Session $session -ScriptBlock { 
    Import-Certificate ` 
     -FilePath \\dc1\CertificateShare\17C37D0A655B89967398C9E7E8185F78541B1589.cer ` 
     -CertStoreLocation 'Cert:\LocalMachine\Root' 

}