Проблемы с Java web start при обновлении до j7u45

Как можно догадаться из названия, пост будет посвящен вышедшему security обновлению джавы, которое наверняка сломает/сломало запуск вебстартового приложения. Всех не равнодушных — прошу под кат.

В нашей компании принята практика обновлять Java на всех серверах, как только выходят новые версии. Собственно, так мы и поступили в этот раз. Но что-то пошло не так, веб старт клиент перестал запускаться и приложение, без объявления войны, стало просто закрываться.
Засучив рукава, предстояло разобраться, что же стало причиной такого поведения.

Запустив клиент локально, появились ранее неведомые предупреждения о том, что в файлах манифеста отсутствуют свойства: Permissions, Application-Name, Codebase. Немного почитав о них, в качестве пробы было решено добавить их на дурачка во все манифесты, примерно в следующим виде:

  Permissions: all-permissions
  Application-Name: AppName
  Codebase: *


Не слишком красиво, от предупреждений избавило, но проблему не решило.
Далее, в ходе долгих дебагов (спасибо тебе Eclipse RCP за счастливое детство) было замечено, что системные параметры передаются как-то не так, что вывело на следующий пост на stackoverflow: stackoverflow.com/questions/19400725/with-java-update-7-45-the-system-properties-no-more-set-from-jnlp-tag-property.
Резюмируя его, можно сказать, что теперь нельзя просто так взять и передать проперти в JNLP файле.

Было найдено 3 пути решения:
1) Подписать JNLP файл — docs.oracle.com/javase/7/docs/technotes/guides/jweb/signedJNLP.html#signedJnlp — вкратце — просто подложить jnlp в подписанный джарик в папку JNLP-INF с именем APPLICATION.JNLP и будет счастье.
Главная проблема в этом способе заключалась в том, что у нас JNLP для каждого клиента разный и вообще говоря генерится на лету.

2) Создать подписанный шаблон JNLP файла — blogs.oracle.com/thejavatutorials/entry/signing_jar_files_with_a
Изначально я остановился на этом способе, ничего сложного: тоже самое, что и в предыдущим пункте, только имя файла APPLICATION_TEMPLATE.JNLP и те места, что могут изменятся заменяем на '*'.
Но все оказалось не так радужно, ничего не заработало. Пришлось немного по шаманить и влезть во внутренности веб старта, чтобы посмотреть как же сравнивается шаблон и сам jnlp файл. В итоге был обнаружен следующий код в com.sun.javaws.jnl.XMLFormat:

  public static boolean isBlacklisted(XMLNode paramXMLNode)
  {
    if (paramXMLNode == null) {
      return false;
    }
    if (paramXMLNode.getName() != null)
    {
      XMLAttribute localXMLAttribute;
      String str;
      if ((paramXMLNode.getName().equals("java")) || (paramXMLNode.getName().equals("j2se"))) {
        for (localXMLAttribute = paramXMLNode.getAttributes(); localXMLAttribute != null; localXMLAttribute = localXMLAttribute.getNext()) {
          if (localXMLAttribute.getName().equals("java-vm-args"))
          {
            str = localXMLAttribute.getValue();
            if ((str != null) && (str.indexOf("*") >= 0))
            {
              Trace.println("Blacklisted - a = " + localXMLAttribute, TraceLevel.SECURITY);
              return true;
            }
          }
        }
      } else if (paramXMLNode.getName().equals("property")) {
        for (localXMLAttribute = paramXMLNode.getAttributes(); localXMLAttribute != null; localXMLAttribute = localXMLAttribute.getNext())
        {
          str = localXMLAttribute.getValue();
          if ((str != null) && (str.indexOf("*") >= 0))
          {
            Trace.println("Blacklisted - a = " + localXMLAttribute, TraceLevel.SECURITY);
            return true;
          }
        }
      }
    }
    if (isBlacklisted(paramXMLNode.getNested())) {
      return true;
    }
    return isBlacklisted(paramXMLNode.getNext());
  }


Проверяет примерно следующее: при сравнении шаблона с файлом, смотрит не находится ли текущий тег в черном списке, если да, то не пропускает такой jnlp файл. В текущей реализации нельзя добавлять шаблоны в свойство java-vm-args тэгов java или j2se, а так же нельзя передавать параметры, содержащие в значение шаблон.

И опять провал, многие свойства для каждого клиента уникальны и передаются как раз через параметры в виде:
<property 
  name="client.specific.property"
  value="client.specific.value"/>


И мы не можем просто написать в шаблоне:
<property 
  name="client.specific.property"
  value="*"/>


Остается только последний(на тот момент) вариант:

3) В соответствии с bugs.openjdk.java.net/browse/JDK-8023821, свойства можно передавать, добавив к ним префикс 'jnlp', они будут успешно перекладываться в системные свойства и далее в нашем коде можно будет переложить их на свое место. Поскольку другой альтернативы не было найдено, в самое начало запуска приложения был добавлен код, найденый здесь:

  private static void initializeSystemProperiesFromJnlp() {
    //Hack for not signing JNLP file
    Properties properties = System.getProperties();
    // copy properties to avoid ConcurrentModificationException
    Properties copiedProperties = new Properties();
    copiedProperties.putAll(properties);
    Set<Object> keys = copiedProperties.keySet();
    for (Object key : keys) {
      if (key instanceof String) {
        String keyString = (String) key;
        if (keyString.startsWith("jnlp.")) { //$NON-NLS-1$
          // re set all properties starting with the jnlp-prefix 
          // and set them without the prefix
          String property = System.getProperty(keyString);
          String replacedKeyString = keyString.replaceFirst("jnlp.", ""); //$NON-NLS-1$ //$NON-NLS-2$

          System.setProperty(replacedKeyString, property);
        }
      }
    }
  }


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

Кроме того, есть еще один, более тернистый, но правильный способ, о котором я узнал позже. Он описан вот здесь.

Надеюсь данный пост будет кому-нибудь полезен.

Комментарии 10

    –9
    Вот уж геморроя-то у вас. В C# все намного проще.
      +3
      В детском саду тоже было просто. Поел/поспал/поел — романтика
      0
      После фразы — «В нашей компании принята практика обновлять Java на всех серверах, как только выходят новые версии», дальше стало страшно читать.
        +2
        Но интересно, согласитесь.
          0
          Процесс практически автоматизирован и на каждом сервере/клиенте все пути стандартны, процесс занимает не много времени. Главное проблема — убедиться, что все работает после обновления и вовремя откатить, пока никто не видел, если что-то пошло не так.
            0
            Дело не в автоматизации, а в том, что проблемы могут всплыть не сразу, а через какое-то время. И потребуется время на поиск проблемы, откатывание всех серверов и т.д.
              0
              смотря что для вас важнее: безопасность или работоспособность. Вы будете удивлены, но первое иногда (иногда!) важнее.
                0
                Да, но аксиома «новая версия безопаснее старой» не верна. Т.е. количество закрытых ошибок не всегда больше количества новых ошибок от версии к версии.
                  +1
                  Хорошая практика — не торопиться с обновлениями. Во-первых, не всегда обновление принесёт что-то нужное для конкретно вашего проекта. И тогда риск, связанный с обновлением, вообще ничем не будет оправдан. Во-вторых, как это было правильно замечено, обновления могут не только закрывать уязвимости, но и создавать новые. Как минимум, стоит выждать некоторое время и посмотреть, что напишет PutPixel и ему подобные отважные люди. Ну а потом уже решать, стоит обновляться, или лучше пропустить.
            0
            Доброе время суток!
            Может кто подскажет что делать когда ничего нельзя сделать с приложением на стороне сервера (в моей ситуации это Web Tools от Brocade). Отключал всё что можно по секюрити и сертификатам в Java control panel.
            С какого update 7-ки перестало работать проверить не было возможности, помог просто откат на 6u45 на стороне клиента.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое