Ошибка Kerberos в долго работающем скрипте PowerShell для Exchange через 10 часов - PullRequest
0 голосов
/ 04 июля 2018

Я реализовал скрипт powershell, который назначает настройки Exchange нашим почтовым ящикам пользователей (Exchange 2016). Поскольку у нас много почтовых ящиков и назначение параметров происходит медленно, скрипт будет работать более 15 часов. Однако примерно через 10 часов я получаю следующую ошибку:

Processing data for a remote command failed with the following error message: Error occurred during the Kerberos response. 
[Server=XXXXX, TimeStamp = 74/2018 01:25:49]
For more information, see the about_Remote_Troubleshooting Help topic.
At C:\Users\ACCOUNT\AppData\Local\Temp\tmp_cj3akhk4.osq\tmp_cj3akhk4.osq.psm1:77943 char:9
+         $steppablePipeline.End()
+         ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : OperationStopped: (XXXX:String) [], PSRemotingTransportException
+ FullyQualifiedErrorId : JobFailure
+ PSComputerName        : XXXX

Мой сценарий прекращает выполнение операции, и после двух попыток (которые не выполняются) отображается запрос на аутентификацию. Там я могу ввести пароль учетной записи службы и сценарий продолжается. Однако это диалоговое окно отображается только в том случае, если я запускаю сценарий в командной строке PS. Если скрипт запускается как Задача Windows, он просто зависает и не продолжается.

Соединение с Exchange открывается и импортируется со следующим кодом. Код может подключаться к нашей локальной Exchange или Exchange онлайн на основе переданного параметра. В настоящее время проблема возникает только при подключении к нашей локальной (локальной) инфраструктуре Exchange.

Function Connect-Exchange{
PARAM(
    [parameter(Mandatory=$false)]
    [String]$TargetExchange = 'Local'
)
BEGIN{}
PROCESS{
    if ($ExchangeSessionInfo.Session -and $ExchangeSessionInfo.Type -eq $TargetExchange -and $ExchangeSessionInfo.Session.State -eq 'Opened'){
        # Nothing to do, we are already connected.
        Write-Log "Exchange connection type $($TargetExchange) already established, nothing to do."
    } else {
        if ($ExchangeSessionInfo.Session -and $ExchangeSessionInfo.Type -ne $TargetExchange -and $ExchangeSessionInfo.Session.State -eq 'Opened'){
            # We have a open session with the wrong type. We close it.
            Remove-PSSession $ExchangeSessionInfo.Session
            $ExchangeSessionInfo.Session = $null
            $ExchangeSessionInfo.Status = 'undefined'
            $ExchangeSessionInfo.Type = ''
        }
        # We close all other existing Exchange sessions we created.
        get-pssession -Name "Exchange" -ErrorAction SilentlyContinue | remove-pssession

        # Now connect to the requestes Exchange infrastructure and import session.
        $Connected = $False
        $RetryCount = 5
        do{
            try {
                If ($TargetExchange -eq 'Local'){
                    $ExchangeServer = Get-Random -InputObject $LocalExchangeConfig.ExchangeServers
                    $ExchangeSessionInfo.Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "http://$($ExchangeServer)/PowerShell/" -Credential $EOCredentials -Authentication Kerberos -Name "Exchange"
                } else {
                    $ExchangeSessionInfo.Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri 'https://ps.protection.outlook.com/powershell-liveid/' -Credential $EOCredentials -Authentication Basic -AllowRedirection -Name "Exchange"
                }
                $Res = Import-PSSession $ExchangeSessionInfo.Session -WarningAction SilentlyContinue -AllowClobber

                # Store Exchange status in session variable.
                $Connected = $True
                $ExchangeSessionInfo.Status = 'connected'
                $ExchangeSessionInfo.Type = $TargetExchange 
            } catch {
                $err = Write-Error -err $error -msg "Could not connect to Exchange server type '$($TargetExchange)' (Retries left: $($RetryCount))." -Break $false
                get-pssession -Name "Exchange" -ErrorAction SilentlyContinue | remove-pssession
                $RetryCount -= 1
            }
        } while (!$Connected -and ($RetryCount -gt 0))          

        # If we do not have connection here, this is an error. 
        if (!$Connected) {
            $ExchangeSessionInfo.Session = $null
            $ExchangeSessionInfo.Status = 'undefined'
            $ExchangeSessionInfo.Type = ''
            throw "No connection to Exchange server (type: $($TargetExchange)) could be established."
        } else {
            # Get list of available mailbox DBs including mailbox count and create hashtable to store statistics. We only have to get it the first time.
            if (($MailboxDBList.count -eq 0) -and ($TargetExchange -eq 'Local')){
                Write-Log "Getting current Exchange DB configuration and mailbox count. Takes a moment."
                $MailboxDBList = Get-MailboxDBCount -Type $LocalExchangeConfig.DistributeMailboxes
            }
        }           
    }
}
END{
    return $ExchangeSessionInfo
}
}

Следующий код применяет предопределенный набор настроек Exchange:

...                 
$TryCount = 0
$Done = $false
do{
    # It takes a while after enabling mailbox until settings can be applied. So we need to retry.
    try{
        # If we need to execute a setting several times.
        if ($MailboxSetting.LoopOver){
            # We have a loop value (array).
            foreach ($LoopValue in $MailboxSetting.LoopOver){
                # Copy parameter as we have to change a value (loop value).
                $TempParams = $Params.PsObject.Copy()                               
                @($Params.getenumerator()) |? {$_.Value -match '#LOOPVALUE#'} |% {$TempParams[$_.Key]=$LoopValue} 
                $res = & $MailboxSetting.Command -ErrorAction Stop @TempParams -WhatIf:$RunConfig.TestMode
            }
        } else {
# THE PROBLEM HAPPENS HERE
            $res = & $MailboxSetting.Command -ErrorAction Stop @Params -WhatIf:$RunConfig.TestMode
        }
        # Write-Log "Setting command $($MailboxSetting.Command) executed successfully"
        $Done = $true
    } catch{
        $tryCount++
        $res = Write-Error -err $error -msg "Error applying mailbox settings, account: $($AccountDetails.sAMAccountName), retry count: $($TryCount)" -Break $false
        Start-Sleep -s $(($Retires-$TryCount) * 5)
    } 
} while ((!$done) -and ($tryCount -lt $Retires))
...

Я уверен, что ошибка не связана с кодом, потому что скрипт работает часами без проблем и применяет все настройки. Однако примерно через 10 часов кажется, что срок действия билета Kerberos истекает, и затем сценарий не может больше обращаться к Exchange без повторного входа в систему.

Есть ли способ предотвратить истечение срока действия билета Kerberos или его продление?

Любая помощь будет оценена.

Ответы [ 2 ]

0 голосов
/ 04 июля 2018

Спасибо за ваше предложение. В первой попытке я расширю свой код следующим образом и попытаюсь повторно подключить новое соединение Exchange. Требуется 10 часов запуска сценария, чтобы увидеть, работает ли он.

Я не могу влиять на Политику безопасности домена, кроме того, поскольку я не знаю, как долго выполняется скрипт, будет сложно установить значение.

В моей Windows 2016 команда "kinit" не распознается. Возможно, мне нужно установить дополнительные модули / роли.

...                 
$TryCount = 0
$Done = $false
do{
    # It takes a while after enabling mailbox until settings can be applied. So we need to retry.
    try{
        # If we need to execute a setting several times.
        if ($MailboxSetting.LoopOver){
            # We have a loop value (array).
            foreach ($LoopValue in $MailboxSetting.LoopOver){
                # Copy parameter as we have to change a value (loop value).
                $TempParams = $Params.PsObject.Copy()                               
                @($Params.getenumerator()) |? {$_.Value -match '#LOOPVALUE#'} |% {$TempParams[$_.Key]=$LoopValue} 
                $res = & $MailboxSetting.Command -ErrorAction Stop @TempParams -WhatIf:$RunConfig.TestMode
            }
        } else {
            $res = & $MailboxSetting.Command -ErrorAction Stop @Params -WhatIf:$RunConfig.TestMode
        }
        # Write-Log "Setting command $($MailboxSetting.Command) executed successfully"
        $Done = $true
    } catch{
        $tryCount++
        $res = Write-Error -err $error -msg "Error applying mailbox settings, account: $($AccountDetails.sAMAccountName), retry count: $($TryCount)" -Break $false
        Start-Sleep -s $(($Retires-$TryCount) * 5)

        try{
            # We may have lost the Kerberos ticket, reconnect to Exchange.
            $ConnectionType = $ExchangeSessionInfo.Type
            Disconnect-Exchange
            Connect-Exchange -TargetExchange $ConnectionType
        } catch {}          
    } 
} while ((!$done) -and ($tryCount -lt $Retires))
...
0 голосов
/ 04 июля 2018

Я думаю, что вы используете политику безопасности домена (объект групповой политики - GPO) => security settings/account policy/Kerberos policy ограничение.

Два действительных варианта для вас:

Maximum lifetime for user ticket => значение по умолчанию составляет 10 часов

Maximum lifetime for user ticket renewal => значение по умолчанию - 7 дней (это период, в течение которого билет может быть продлен).

  • Есть ли способ предотвратить истечение срока действия билета Kerberos или его продление?

Для первых вопросов вам «просто» нужно настроить значение maximum lifetime for user ticket на нужное значение.

Второй более хитрый. Я бы просто очистил все билеты Kerberos через PowerShell. Для более подробной информации - просмотр и очистка кэшированных билетов Kerberos , что даст вам новый.

Если билет может быть продлен, вы должны проверить флаг RENEABLE - вы хотите просмотреть его через kinit. Возможно, kinit -R может быть достаточно для обновления билета. (Я не делал это сам) Вы также можете обновить его через Kerberos для Windows

Редактировать - добавив klist purge, чтобы очистить все билеты Kerberos, чтобы его можно было возобновить.

Поскольку у вас есть klist, то вы можете очистить все билеты через, должны быть запущены в режиме повышенной мощности PowerShell. (все кредиты JaredPoeppelman ):

Get-WmiObject Win32_LogonSession | Where-Object {$_.AuthenticationPackage -ne 'NTLM'} | ForEach-Object {klist.exe purge -li ([Convert]::ToString($_.LogonId, 16))}  

Затем проверьте, обновился ли ваш TGT с помощью:

klist tgt

Примечание: вы должны использовать полное доменное имя везде!

...