【问题标题】:Powershell Invoke-Command remote works manually, but not from JenkinsPowershell Invoke-Command 远程手动工作,但不是来自 Jenkins
【发布时间】:2018-04-09 19:29:53
【问题描述】:

我有一个非常奇怪的情况,当从 Windows Server 2012 R2 手动运行相同的命令时,但不能从运行在同一服务器上的 Jenkins 从进程运行时。

首先,手动运行的输出,一个管理 PowerShell 窗口:

PS C:\Users\Administrator> whoami
win-cm8utd1qfnc\administrator
PS C:\Users\Administrator> Invoke-Command -computername web.sandbox.MUNGED.com -scriptblock {iisreset /restart}
Attempting stop...
Internet services successfully stopped
Attempting start...
Internet services successfully restarted

太好了。现在,Jenkins流水线代码的相关sn-p:

pipeline {
    stages {
        stage('Deploy web') {
            agent { label 'windows-server-2012' }
            environment {
                SERVER = 'web.sandbox.MUNGED.com'
            }
            steps {
                powershell """
                    whoami
                    Invoke-Command -computername ${SERVER} -scriptblock {iisreset /restart}
                """

            }
        }
    }
}

以及从 Jenkins 运行时的输出:

07:37:29 win-cm8utd1qfnc\administrator
07:37:29 [web.sandbox.MUNGED.com] Connecting to remote server web.sandbox.MUNGED.com failed with the following error message : Access is denied. For more information, see the 
07:37:29 about_Remote_Troubleshooting Help topic.
07:37:29     + CategoryInfo          : OpenError: (web.sandbox.MUNGED.com:String) [], PSRemotingTransportException
07:37:29     + FullyQualifiedErrorId : AccessDenied,PSSessionStateBroken

Windows 服务器(Jenkins 从服务器和 Web 服务器)不是域的一部分,但具有相同的管理员密码,这似乎使身份验证工作良好。

不管怎样,这里是 Jenkins slave 的 winrm 配置:

PS C:\Users\Administrator> winrm get winrm/config
Config
    MaxEnvelopeSizekb = 500
    MaxTimeoutms = 1800000
    MaxBatchItems = 32000
    MaxProviderRequests = 4294967295
    Client
        NetworkDelayms = 5000
        URLPrefix = wsman
        AllowUnencrypted = false
        Auth
            Basic = true
            Digest = true
            Kerberos = true
            Negotiate = true
            Certificate = true
            CredSSP = false
        DefaultPorts
            HTTP = 5985
            HTTPS = 5986
        TrustedHosts = *
    Service
        RootSDDL = O:NSG:BAD:P(A;;GA;;;BA)(A;;GR;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)
        MaxConcurrentOperations = 4294967295
        MaxConcurrentOperationsPerUser = 1500
        EnumerationTimeoutms = 240000
        MaxConnections = 300
        MaxPacketRetrievalTimeSeconds = 120
        AllowUnencrypted = true
        Auth
            Basic = true
            Kerberos = true
            Negotiate = true
            Certificate = false
            CredSSP = false
            CbtHardeningLevel = Relaxed
        DefaultPorts
            HTTP = 5985
            HTTPS = 5986
        IPv4Filter = *
        IPv6Filter = *
        EnableCompatibilityHttpListener = false
        EnableCompatibilityHttpsListener = false
        CertificateThumbprint
        AllowRemoteAccess = true
    Winrs
        AllowRemoteShellAccess = true
        IdleTimeout = 7200000
        MaxConcurrentUsers = 10
        MaxShellRunTime = 2147483647
        MaxProcessesPerShell = 4096
        MaxMemoryPerShellMB = 8192
        MaxShellsPerUser = 30

来自网络服务器:

PS C:\Users\Administrator> winrm get winrm/config
Config
    MaxEnvelopeSizekb = 500
    MaxTimeoutms = 1800000
    MaxBatchItems = 32000
    MaxProviderRequests = 4294967295
    Client
        NetworkDelayms = 5000
        URLPrefix = wsman
        AllowUnencrypted = false
        Auth
            Basic = true
            Digest = true
            Kerberos = true
            Negotiate = true
            Certificate = true
            CredSSP = false
        DefaultPorts
            HTTP = 5985
            HTTPS = 5986
        TrustedHosts = *
    Service
        RootSDDL = O:NSG:BAD:P(A;;GA;;;BA)(A;;GR;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)
        MaxConcurrentOperations = 4294967295
        MaxConcurrentOperationsPerUser = 1500
        EnumerationTimeoutms = 240000
        MaxConnections = 300
        MaxPacketRetrievalTimeSeconds = 120
        AllowUnencrypted = true
        Auth
            Basic = true
            Kerberos = true
            Negotiate = true
            Certificate = false
            CredSSP = false
            CbtHardeningLevel = Relaxed
        DefaultPorts
            HTTP = 5985
            HTTPS = 5986
        IPv4Filter = *
        IPv6Filter = *
        EnableCompatibilityHttpListener = false
        EnableCompatibilityHttpsListener = false
        CertificateThumbprint
        AllowRemoteAccess = true
    Winrs
        AllowRemoteShellAccess = true
        IdleTimeout = 7200000
        MaxConcurrentUsers = 10
        MaxShellRunTime = 2147483647
        MaxProcessesPerShell = 25
        MaxMemoryPerShellMB = 1024
        MaxShellsPerUser = 30

编辑:我在一段时间后让它工作了。首先,在 Jenkins slave 上,我必须运行:

winrm set winrm/config/client '@{AllowUnencrypted="true"}'

然后我将管道更改为:

powershell """
    \$creds = Import-CliXml \$home\\creds.xml
     Invoke-Command -computername ${SERVER} -scriptblock {iisreset /restart} -Authentication Basic -Credential \$creds
"""

其中creds.xml 是先前使用Get-Credentials | Export-CliXml creds.xml 生成的文件。

这仍然不能解释为什么手动 PowerShell 和 Jenkins slave 的行为不同。这有点烦人的解决方法,但至少我可以继续。

【问题讨论】:

  • Jenkins slave 是否以较低的权限运行?出于安全原因,可以在同一个用户帐户中运行,但只使用部分权限(例如,Chrome 浏览器运行其浏览器选项卡的方式比其运行的用户帐户具有更多限制)。
  • 我还以为管理员就是管理员。我如何查看您所说的这些特权?
  • 我检查过了,这似乎不是问题。我运行了([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"),无论是从 Jenkins 从站还是在 PowerShell 中手动运行时,它都返回 True。接下来我应该检查什么?
  • 我在 OP 的底部添加了一个解决方法,但它仍然没有解释为什么手动 PowerShell 和 Jenkins slave 之间的行为不同。很高兴收到任何有线索的人的来信。

标签: powershell jenkins windows-server-2012-r2 winrm


【解决方案1】:

您可能遇到了 Jenkins 的远程执行脚本限制(安全性是这里的原因)。您需要配置 Jenkins 服务器才能“正常”运行脚本,但您始终必须添加凭据。

您从 powershell 命令行运行的脚本使用您的 win-cm8utd1qfnc\administrator 的默认凭据,因此以下内容将按照您所写的那样工作:

PS C:\Users\Administrator> whoami
win-cm8utd1qfnc\administrator
PS C:\Users\Administrator> Invoke-Command -computername web.sandbox.MUNGED.com -scriptblock {iisreset /restart}
Attempting stop...
Internet services successfully stopped
Attempting start...
Internet services successfully restarted

但是,当从 Jenkins 运行 Powershell(本质上是一个插件)时,您会遇到安全设计限制。您不想使用您的管理帐户运行“狂野”脚本。

我在这个主题上找到的最合理的指南是here(以下摘自页面:

远程执行 Powershell 脚本/命令

上述作业在 Jenkins 服务器本身上创建了一个文本文件。到 设置远程 Powershell 脚本 我们首先需要配置 Jenkins 用于远程 Powershell 脚本执行的服务器。启用远程 Windows 机器进入 Jenkins 服务器上的 WS-Man 受信任列表。 在 Jenkins 服务器上的 Powershell 窗口中执行以下命令。这 命令会将所有远程机器添加到信任列表中。

Set-Item WSMan:\localhost\Client\TrustedHosts *

除了命令,我们还需要启用远程脚本 执行也。启用远程执行 Powershell 脚本 在 Jenkins 服务器的 Powershell 窗口中执行以下命令。

Set-ExecutionPolicy RemoteSigned –Force

我们必须安装一个名为 EnvInject Plugin 的新插件 传递变量,例如密码。

Login to Jenkins and navigate to Manage Jenkins > Manage Plugins
Click on the Available tab and Enter EnvInject in the filter box
Select the plugin showing by name PowerShell Plugin
Select Download now and install after restart

创建作业以重新启动 Windows 时间服务:

On Jenkins interface, click New Item
Enter Remote Powershell scripts for the job name. Select Freestyle project
Tick This build is parameterized. Create following parameters
    Type: String Parameter
    Name: ServerIp/Hostname
    Description: Remote machine’s IP address.
    Type: String Parameter
    Name: UserName
    Type: Password Parameter
    Name: Password
Now, Click Add Parameter list and select the Choice Parameter. Enter the options on new lines inside the Choices text box. Also,

提供上述选项的描述:

以下脚本基于上面的链接,但我不喜欢使用的纯文本,所以我决定重写它以使用 Powershell 的SecureString

首先存储您的管理员密码:

read-host -AsSecureString | ConvertFrom-SecureString | Out-File C:\<your_path>\securestring.txt

然后有脚本:

# Configure build failure on errors on the remote machine similar to set -x on bash script 
$ErrorActionPreference = 'Stop'  

# Create a PSCredential Object using the "Username" and "Password" variables created on job 
$password = Get-Content 'C:\<your_path>\securestring.txt' | ConvertTo-SecureString
$creddentials = New-Object System.Management.Automation.PSCredential -ArgumentList $env:UserName, $password

# It depends on the type of job you are executing on the remote machine as to if you want to use "-ErrorAction Stop" on your Invoke-Command. 
Invoke-Command -ComputerName $env:Computer -Credential $credentials -ScriptBlock { Restart-Service -Name W32Time }

【讨论】:

  • 感谢您的回答,但我不确定那里有什么新东西。您可以在 OP 中看到我已经拥有TrustedHosts = *。你对这个“来自 Jenkins 的远程执行脚本限制”有更详细的信息吗?我找不到太多。运行 Powershell 命令时,它看起来像 Jenkins sets a very liberal execution policy
  • @topper Part1 评论:对于 Powershell,你必须做一些额外的步骤——这是插件的问题,而不是 Jenkins。如果您检查原始插件的更改日志 (wiki.jenkins.io/display/JENKINS/PowerShell+Plugin),您将看到一条注释 PowerShell now runs with ExcecutionPolicy 设置为“Bypass”以避免执行策略问题,这表明必须“强制”插入" 才能正确运行。
  • @topper Part2 评论:要运行正常运行管理 PowerShell 提示,您必须执行 Set-ExecutionPolicy RemoteSigned –Force。你这样做了吗?您还可以将凭证存储在 Jenkins 本身中,这可以更好地解决您的问题 - hodgkins.io/…
  • @topper 以与您在 shell (powershell.exe) 中并使用 Powershell ISE 相同的方式想象它。 Jenkins powershell 是一个插件,你没有直接使用 shell。该插件实施了自己的安全措施。
【解决方案2】:

看到这个问题:Remote Access with Powershell and Jenkins

需要将服务用户从本地系统更改为管理员。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多