Invoke-Command导入 - 证书访问被拒绝

时间:2017-10-11 23:21:36

标签: powershell

我网络中的每个人都对包含需要安装的证书的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'

} 

1 个答案:

答案 0 :(得分:0)

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

出于安全原因,Microsoft默认不允许此操作。

Double hop issue

如果您想了解更多相关信息,请阅读我的整个帖子PowerShell remoting caveats

解决方案并不容易,并且并非唯一,因为它主要与Kerberos身份验证以及域控制器如何控制它有关。例如,域控制器可以指定两个服务器之间允许特定的双跳,或者您必须允许每个服务器双跳。

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

我将通过脚本引用显示如何在每台服务器上执行此操作。例如,从上图中我们必须配置ServerA以允许凭据授权到ServerB

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

首先,我们需要在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. 打开防火墙端口。
  5. 以下是从脚本中提取的代码:

    #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已准备好将您的授权委托给另一台服务器,但客户端仍需要明确声明远程会话必须具备此功能。创建一个这样的会话

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

    使用CredSSP身份验证类型时注意两件重要的事情:

      必须明确指定
    • -Credentials,即使它们是针对登录的同一用户。
    • 使用-UseSSL使用winrm secure。

    现在使用$session和原始代码一起使用

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