Внутренний сервер обновления Adobe Flash Player

Предыстория


Начальство поставило задачу: нужно поддерживать в актуальном состоянии Flash Player. Масштабы: ~15000 компов, на половине из которых FP действительно нужен.

Казалось бы, в чём проблема — делаем доменную политику, запихиваем MSI пакет и радуемся… Но не тут-то было! Структура компании сильно распределена, т.е. в удалённые точки с каналом ~1мбит на 20 компов пропихнуть политиками даже 15мб уже проблема — утром сотрудники включают компьютер и по полчаса ждут загрузки, пока всё скачается и поставится (или просто отвалится по таймауту). Не говоря уже о том, что компы в подобных офисах имеют неприятное свойство периодически из домена выпадать.

Нужно было другое решение, которое будет работать независимо от политик, никак не напрягать пользователя, минимально использовать канал (хотя бы чтобы качали не все одновременно). Варианты со скриптами по очевидным причинам тоже не подошли.

Выручил, как обычно, Гугл: оказалось, что можно поднять внутренний сервер обновления и настроить на него встроенное средство обновления FP. При этом на клиенты нужно будет только распространить файл настроек. Подробности под катом.

Настройка сервера обновлений


1. Получаем лицензионное соглашение

Для распространения своего ПО Adobe требует получить лицензионное соглашение. Не будем нарушать условия использования и получим лицензию (благо, это совсем не сложно): FlashPlayer: Adobe Runtimes / Reader Distribution License Agreement. Лицензия выдаётся сроком на год. По истечении можно отправить запрос ещё раз.

2. Поднимаем веб-сервер

Платформа роли не играет, в моём случае вертится N-ным сайтом на IIS под Win2012. Ресурсов оно практически не жрёт даже при том, что уже ~3000 компьютеров настроены на этот сервер.

Настройки сервера:

  • Доступ по портам 80, 443 (http, https соответственно).
    Первый нужен, собственно, для скачивания, по второму FP будет ходить за XML-кой актуальной версии.
  • Валидный сертификат https.
    Я выписывал сертификат на основе корневого корпоративного, который по умолчанию есть на всех машинах.
  • Листинг директорий.
    Не проверял работу без него — в документации просят, я решил сделать как написано.

Подробно останавливаться на настройке сервера не буду.

Для наглядности назовём сервер FlashPlayerUpdate.domain.local.

3. Скачиваем ресурсы и выкладываем на сервер

В корне веб-сервера создаём дерево директорий: /pub/flashplayer/update/current/sau/.

Дерево директорий на моём сервере:



Если вы запросили лицензию на первом шаге, то в ответ должно прийти письмо со ссылкой, откуда скачивать FlashPlayer — проходим по этой самой ссылке. Если не пришло, или не запрашивали, то идём сюда: https://www.adobe.com/products/flashplayer/distribution3.html и скачиваем архив по ссылке "Download Background Update Resources":



Extended Support Release или Public Release
Тут нужно сделать ремарку. На страничке 2 варианта загрузок: стандартный (Public) и Extended Support Release. В моём случае важна стабильность работы и не нужны новые фичи, поэтому был выбран вариант ESR. При этом я добавил себе некоторое количество геморроя: паблик версию можно напрямую выкачивать скриптом с сайта Macromedia. Как выкачивать ESR, я так и не нагуглил, поэтому в моём случае обновление контента на внутреннем сервере происходит в ручном режиме.

В конце статьи приложил 2 скрипта PowerShell: для автоматического обновления (только для стандартной версии; легко портируется на bash), для проверки обновлений и оповещении по e-mail (для любой версии, в т.ч. ESR).

Скачанный архив распаковать в папку /pub/flashplayer/update/current/sau/ на сервере.

4. Распространяем на клиенты файл конфигурации

В зависимости от разрядности системы:

  • 32-bit: C:\Windows\System32\Macromed\Flash\mms.cfg
  • 64-bit: C:\Windows\SysWOW64\Macromed\Flash\mms.cfg

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

В файле включаем тихое автообновление, прописываем интервал обновлений (в днях), путь к нашему серверу, и на всякий случай логирование, чтобы проще было диагностировать проблемы, если они возникнут:

AutoUpdateDisable=0
SilentAutoUpdateEnable=1
AutoUpdateInterval=2
SilentAutoUpdateServerDomain=FlashPlayerUpdate.domain.local
SilentAutoUpdateVerboseLogging=1

Если всё было сделано верно, то Flash Player на клиентских машинах должен начать обновляться по расписанию (согласно приведённому файлу выше — раз в 2 дня). Обычно сервис обновления Adobe запускается раз в час для проверки условий обновления — в это время Updater должен увидеть файл конфигурации, перенастроить обновления согласно прописанным настройкам и сходить на новый сервер проверить версию.

То есть примерно через час после распространения файла конфигурации можно смотреть логи на сервере на предмет запросов на проверку версии.

Автоматизация


Как классический представитель айтишного братства, я терпеть не могу рутинную ручную работу и просто ну никак не мог не автоматизировать процесс проверки и выкачивания новой версии. Однако, как отмечено выше, пока я не нашёл способа выкачивать версии ESR с сайта Macromedia, потому скриптом только проверяю обновления. Предложения приветствуются.

Скрипт для автоматического скачивания обновлений

Только для публичной версии!

Логика работы: скрипт в тупую скачивает файлы обновления напрямую с Macromedia для версий 11,15,16,17,18,19 (если какую-то версию уже убрали с сайта — скрипт просто ругнётся, что не смог скачать и пропустит) и кладёт с заменой на сервер обновления. Никаких проверок версий. На этапе тестирования я использовал этот скрипт: запускал через шедулер на сервере по ночам.

При желании можно скрестить этот скрипт и следующий и получить полную автоматизацию с проверкой версий, оповещениями и скачиванием только при наличии обновлений.

Параметры скрипта:

  • *FPRoot — путь к корневой папке сервера обновлений. Локальный, или сетевой. Естественно, у пользователя, от которого будет запущен скрипт, должны быть права на запись в эту папку.
  • FPDownloadRoot — путь на сайте Macromedia. Задан по умолчанию, но можно изменить при необходимости.
  • DownloadProxy — прокси сервер, если используется в компании. Писать полностью: http://proxy.domain.local.
  • ProxyCreds — имя пользователя для авторизации на прокси.
  • UserAgent — для изменения юзерагента, с которым PowerShell пойдёт качать. Например, у нас на прокси ограничение по UserAgent-ам, я хожу с агентом Internet Explorer.
  • Force — отключить проверку сертификатов командлета Invoke-Webrequest (точнее, заставить доверять всем сертификатам).

* — обязательный параметр

Пример использования:

powershell.exe -command "& '.\FPUpdater.ps1' -FPRoot '\\FlashPlayerUpdate\pub\flashplayer\update\current\sau' -DownloadProxy 'http://proxy.domain.local' -ProxyCreds 'DOMAIN\UserName' -UserAgent 'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko'"

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



Скрипт для проверки обновления и оповещения по e-mail

Проверка обновлений идёт по стандартной версии, но поскольку обновляются они одновременно (security-фиксы никто не отменял), то прокатит и для ESR. Для работы скрипта нужно создать в корне веб-сервера (рядом с папкой pub) файл CurrentPublic, в который вписать текущую публичную версию для ActiveX (для проверки используется именно версия ActiveX).

Логика работы: скрипт сравнивает версию, полученную из файла CurrentPublic с вашего сервера с версией на сервере Macromedia. Версию на сервере смотрит по логике автообновлялки: сначала ищет в XML текущий мажорный билд, идёт в папку с мажорным и там смотрит полный билд.

Параметры скрипта:

  • *FPIntServerRoot — Адрес нашего сервера. Например: FlashPlayerUpdate.domain.local
  • FPDownloadRoot — путь на сайте macromedia. Задан по умолчанию, но можно изменить при необходимости.
  • ESR — проверять ESR версию (без этого флага будет проверять публичную).
  • DownloadProxy — прокси сервер, если используется в компании. Писать полностью: http://proxy.domain.local.
  • ProxyCreds — имя пользователя для авторизации на прокси.
  • UserAgent — для изменения юзерагента, с которым PowerShell пойдёт качать. Например, у нас на прокси ограничение по UserAgent-ам, я хожу с агентом Internet Explorer.
  • Force — отключить проверку сертификатов командлета Invoke-Webrequest (точнее, заставить доверять всем сертификатам).
  • *MailTo — e-mail адреса, на которые будут приходить уведомления.
  • *MailFrom — от кого будут приходить уведомления. Например: FPUpdater@company.com
  • SmtpServer — smtp-сервер, через который будет производиться отправка сообщения.

* — обязательный параметр

Пример использования:

.\FPCheckUpdate.ps1 -FPIntServerRoot 'fp-update.domain.local' -ESR -Proxy 'http://proxy.domain.local' -UserAgent InternetExplorer -Force -MailTo 'admin@company.com','support@company.com' -MailFrom 'FP@company.com' -SmtpServer 'smtp.company.com'


Использованные ресурсы





UPD


Слегка запоздалый апдейт от 17.06.16.

С момента написания статьи Adobe успели 2 раза поменять порядок доступа к странице загрузки FlashPlayer'а. В итоге теперь чтобы получить доступ к странице для скачивания, нужно сперва авторизоваться в Adobe ID. Т.е. вариант с парсингом страницы на предмет версии ESR больше не прокатывает.

Морочиться с авторизацией, получением-отправкой cookie через PowerShell пока не стал. В итоге переделал скрипт для проверки ESR на страницу distribution3, которая может пропасть в любой момент. Пока так, дальше будет видно.

Я ещё в начале года задавал вопрос на форуме Adobe на тему проверки обновлений версии ESR. Обещают что-то придумать, но пока воз и ныне там.

UPD2


На днях на странице распространения Flash Player (ссылку на которую вы получили, получив лицензию на распространение) появилась следующая информация:

ВНИМАНИЕ! Важные изменения с Extended Support Release



Изначально выпуск Extended Support Release создавался с целью свести к минимуму время, которое тратят ИТ-администраторы на сертифицирование каждого нового выпуска Flash Player: Extended Support Release ограничивал изменения, вносимые в выпуск, до изменений в системе безопасности и исправлений критических функциональных неполадок. На тот момент функциональных изменений в каждом выпуске Flash Player было намного больше, чем изменений в системе безопасности. Однако сейчас ситуация изменилась: число упреждающих и ответных мер безопасности намного превышает функциональные изменения. На практике Extended Support Release больше не ограждает ИТ-организации от огромного количества изменений, вносимых в стандартные выпуски Flash Player, а лишь снижает функциональные риски. В связи с этим было принято решение прекратить выпуск Extended Support Release и сконцентрироваться на разработке только стандартных выпусков.Ориентация только на стандартные выпуски позволит нам проявлять больше гибкости в принятии ответных мер безопасности и в дальнейшем работать над тестированиями и новыми техническими решениями в сфере безопасности.

Чтобы предоставить организациям достаточно времени на тестирование и сертификацию, поддержка и обновление Extended Support Release продлятся до 11 октября 2016 г. Затем организациям потребуется перейти на стандартный выпуск.
Share post

Comments 14

    0
    Добро пожаловать, полезный член сообщества :)
      0
      Спасибо! Хочется верить, что не бесполезный, да :)

      Планирую переложить скрипты на гитхаб, т.к. уже нашёл несколько мелких, но неприятных косяков и исправил проверку версии, а править пост по 10 раз не хочется…
      0
      Обновления:
      1. Переместил скрипты на гитхаб, чтобы не редактировать тут пост при обновлениях кода.
      2. Обновил описание к скриптам (в частности, набор параметров).


      Если кто-то успел забрать старые скрипты — лучше взять новые с гитхаба. Они стали умнее :)
      Обратите внимание: в старом скрипте для скачивания обновлений требовалось указать в параметре 'FPRoot' путь к папке 'sau', в новой версии — к корневой папке сервера обновлений. Скрипт теперь создаёт требуемую структуру директорий сам, если её нет.
        0
        А как заставить Invoke-WebRequest работать через squid
        Пробывал:
            #$cred = Get-Credential $ProxyCreds
            $SecPass = ConvertTo-Securestring $ProxyPass -AsPlainText -Force 
            $cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $ProxyUser, $SecPass
            $WebrequestParams['ProxyCredential']=$cred
        


        Не пускает прокси.
        Getting ADOBE server version (ESR)...FAIL!
        Can't verify ADOBE server version! Error: [The remote server returned an error:
        (407) Proxy Authentication Required.].
        
          0
          Сам отвечу на свой же вопрос. Получилось только так

          $ProxyUser = "user"
          $ProxyPass = "pass"
          $Proxy = "http://xxx.xxx.xxx.xxx:xxxx"
          
          $url = 'http://www.adobe.com/ru/products/flashplayer/distribution3.html'
          
          #Auth
          $pair = "$($ProxyUser):$($ProxyPass)"
          $encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
          
          #Proxy
          $prox = new-object net.WebProxy;
          $prox.Address = $Proxy
          
          # Request
          $req = [net.webRequest]::Create($url)
          $req.method = "GET"
          $req.Host = "www.adobe.com"
          $req.UserAgent = "Mozilla/5.0 (Windows NT 5.1; rv:42.0) Gecko/20100101 Firefox/42.0"
          $req.Proxy = $prox
          $req.Headers['Proxy-Authorization'] = "Basic $encodedCreds";
          
          
          #Responce
          $resp = $req.GetResponse()
          $stream = new-object io.streamreader $resp.GetResponseStream()
          $res = $stream.readtoend()
          $res
          
            0
            Во-первых, я ярый противник передачи пароля в открытом виде, а тем более, хранения его в скрипте.
            Уж если брать пароль открытым текстом, то хотя бы так:
            $cred = Get-Credential $ProxyCreds
            $UserName = $cred.UserName
            $Password = $cred.GetNetworkCredential().Password
            

            Во-вторых, мне кажется, у вас как-то неправильно настроен сквид, если он не принимает на входе ProxyCredential. Посмотреть бы на лог WireShark — где и почему он отфутболивает.

            Я положил свой скрипт в шедулер на сервер выполняться от моей доменной учётки с ключом «ProxyUseDefaultCredentials» — используются учётные данные записи, от которой запускается скрипт (в моём скрипте достаточно опустить параметр ProxyCreds для использования этого ключа автоматически). На прокси настроена доменная авторизация.
              0
              Согласен с вами по поводу пароля. «Расчехлил» tcpdump, попробывал еще 3proxy с basic авторизацией.
              Провел 2 теста с 3proxy и squid.

              Тест 1.
              $Proxy = "http://xxx.xxx.xxx.xxx:xxxx"
              $ProxyCreds = "user"
              
              $cred = Get-Credential $ProxyCreds
              $UserName = $cred.UserName
              $Password = $cred.GetNetworkCredential().Password
              
              try {
                Invoke-WebRequest -Uri "http://ya.ru" -Proxy $Proxy -ProxyUseDefaultCredentials 
              } catch {
                      Write-Host -f Red "Error: [$($_.Exception.Message)]."
                      break
              }
              


              3proxy и squid ругнулись — 407, в HTTP заголовке отсутсвует Proxy-Authorization
              HTTP заголовок в wireshark
              image

              Тест 2.
              $Proxy = "http://xxx.xxx.xxx.xxx:xxxx"
              $ProxyCreds = "user"
              
              $cred = Get-Credential $ProxyCreds
              $UserName = $cred.UserName
              $Password = $cred.GetNetworkCredential().Password
              
              $pair = "$($UserName):$($Password)"
              $encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
              
              $Headers = @{
                  'Proxy-Authorization' = "Basic $encodedCreds"
              }
              
              try {
                Invoke-WebRequest -Uri "http://ya.ru" -Proxy $Proxy -ProxyUseDefaultCredentials -Headers $Headers
              } catch {
                      Write-Host -f Red "Error: [$($_.Exception.Message)]."
                      break
              }
              


              Ура! Заработало. В HTTP заголовке уже появился Proxy-Authorization и basic авторизация прошла.
              HTTP заголовок в wireshark
              image

              На этом варианте похоже я и остановлюсь.
                0
                В первом случае не заработало ожидаемо — $creds просто не используется, т.е. вы действительно не передаёте данные для авторизации :-)

                Ещё раз, флаг -ProxyUseDefaultCredentials использует для авторизации на прокси УЗ, от имени которой выполняется скрипт. Если сквозная авторизация на прокси не настроена — этот флаг для вас бесполезен — только явная передача данных для авторизации. Соответственно, нужно передавать авторизацию либо через -ProxyCredential, либо как-то по-другому, если прокси не принимает авторизацию от PowerShell стандартным способом. Ваш вариант — тоже вполне себе вариант.

                Можно добавить в скрипт ещё один флаг (о да, я люблю тысячи параметров запуска для скриптов!) наподобие ProxyBasicAuth, и добавить для него вариант отправки авторизации в Basic формате.
          0
          Ок. Понял, причесал код. В заголовке пусто. Странно конечно, может он по NTML пытается авторизоваться.
          try {
            Invoke-WebRequest -Uri "http://ya.ru" -Proxy "http://xx.xx.xx.xxx:xxxx" -ProxyCredential(Get-Credential)
          } catch {
                  Write-Host -f Red "Error: [$($_.Exception.Message)]."
                  break
          }
          

          Значит, так правильнее.
          Invoke-WebRequest -Uri "http://ya.ru" -Proxy $Proxy -Headers $Headers
          
            0
            Значит, так правильнее.
            В вашем случае — видимо, да.

            Я проверял скрипт на WebSense и BlueCoat. На обоих работают оба варианта: и ProxyCredential, и ProxyUseDefaultCredentials.
            0
            Adobe отказывается от Extended Support Release. Обратите внимание на UPD2.
              0
              Буквально на днях сделал у нас в конторе такое, автовыкачивание пока не делал, но файл с контентом пока прекрасно выкачивается по ссылке https://fpdownload.macromedia.com/pub/flashplayer/current/licensing/win/fp_background_update.cab
                0
                Да, но в этом файле нет апдейта, например, с 15-й версии. Скрипт автовыкачивания качает всё, что доступно с версии 11 по актуальную (сейчас это 11,15...23). Правда, в текущей версии моего скрипта не учтён FP для PPAPI (Chrome и подобные).
                Кроме того, я пересадил сервер на Ubuntu Server, в связи с чем состряпал скрипт автовыкачивания на bash с отправкой уведомления по e-mail. Как будет время, обновлю скрипт на PowerShell для выкачивания PPAPI и выложу дополнительно скрипт на bash.
                0
                В любом случае — респект за статью. Это мой уже третий заход в этом направлении, на SSL в документации вообще не обратил внимания, а directory listing — таки да, нужен (ух, какой же изврат с его включением в server 2012… — кто будет там поднимать (удобно, например, на одном хосте со wsus) — обратите внимание, что включать его надо через roles and features).

                Only users with full accounts can post comments. Log in, please.