Pull to refresh

Как передать зашифрованные параметры в DataStage

Reading time 5 min
Views 2.7K
Не секрет, что зашифрованные параметры (т.е. имеющие тип Encrypted), используемые в IBM DataStage в версиях до 8.7 очень легко расшифровать. Эти зашифрованные параметры часто используются для передачи паролей, необходимых для соединения с базами данных.
При постороении корпоративных ODS (а в некоторых случаях даже и в случае хранилищ данных) имеет смысл создавать универсальные джобы — так называемые генерики, которые полностью конфигурируются извне и не содержат специфичной для каждой таблицы информации, а поэтому их можно использовать для многих ETL процессов. Особенно это необходимо при извлечении данных из баз данных источников (Extraction). В таком случае необходимо хранить в файлах конфигураии пароли для каждого источника данных. И вам приходится, прогибаясь под политики безопасности различных предприятий, делать вид, что это надежный алгоритм шифрования и хранить пароли к корпоративным данным в зашифрованном DataStage виде.
Но проблемы возникают, если вы захотите передать такие параметры в джоб. Какие проблемы и как их решать я и напишу в этой статье.


А в чем, собственно, проблема?


Предположим, у вас есть конфигурационный файл, в которым вы описываете свой ETL процесс. Не важно в каком виде он хранится. Мы, например, используем XML. В этом конфиге вы хотите сохранить пароль к базе данных в зашифрованном виде, например так:
<export>
   <parameters>
      <parameter name="SQL" handleQuotes="Y">
            <value><![CDATA[select * from STAGING.TABLE]]></value>
       </parameter>
       <parameter name="DB" value="SAMPLEDB"/>
       <parameter name="USER" value="USER"/>
       <parameter name="PASSWORD" value="L<I@@9V8M=;M07GILIJLBK96BLN"/>
    </parameters>
</export>


Вы считываете конфигурацию, извлекаете необходимые параметры генерику. Ну и вот если этот пароль вы попытаетесь передать в качестве Encrypted параметра джобу, то DataStage расценит его как незашифрованный пароль и зашифрует повторно. Причем, не важно как вы передадите: в секвенсере через JobActivity или через фукнцию Basic DSSetParam.
DSXchange и прочие StackOverflow содержат кое-какую информацию о способах как это сделать. Но все это как-то очень посредственно относится к делу. Варианты вроде использования внешних средств шифрования/дешифрования и последующей передачи параметров в открытом (String) виде нас не устроят, так как пароли будут светиться в логе DataStage Director (мы же помним, что относительно надежности внутреннего алгоритма мы молчим и храним секрет Полишинеля).

Возможные пути решения проблемы


  1. Использовать DataStage C API;
  2. Создать файл параметров и использовать его с dsjob -run -paramfile ;
  3. Дешифровать пароль перед отправкой его джобу;
  4. Создать ParameterSet для каждого источника данных.


Кратко: ни один из этих способов не работает. Ну или нас не устраивает. И вот почему.
  1. Действительно, в API содержится структура DSPARAM, описанная следующим образом
    typedef struct _DSPARAM {
    int paramType;
    union {
       char *pString;
       char *pEncrypt;
       int pInt;
       float pFloat;
       char *pPath;
       char *pListValue;
       char *pDate;   
       char *pTime;
    } paramValue;
    } DSPARAM;
    

    которая содержит указатель на зашифрованный параметр char *pEncrypt. Поле paramType должно содержать тип параметра, в данном случае — DSJ_PARAMTYPE_ENCRYPTED.
    Я не пробовал этот способ. Дело в том, что, на мой взгляд, это слишком неоправданно затратный способ всего лишь запустить джоб, кроме того, придется реализовывать всю логику работы с джобом: запуск, передача всех параметров, отслеживание его состояния и возвращение статуса в секвенсер с обратной пропагацией аварийного завершения в случае чего (Error Handler не поймает исключение в таком случае). Читаемость ETL процесса упадет и поддерживать такой проект уже будет под силу только довольно скилованным перцам (да, не все могут нанимать сеньоров, обладающих помимо знания DataStage еще и знаниями C). Помимо всего прочего, не всегда клиент вам предоставит право на запись каталога Server/PXEngine/lib (Server/PXEngine/user_lib) куда необходимо будет положить скомпилированный объект.
    Резюмируя: похоже, что этот паровоз полетит, но пробовать не всегда имеет смысл
  2. Этот вариант не работает. Вообще. DataStage также шифрует параметры повторно как это делает и в случае DSSetParam. И опять же, запуск джоба вне секвенсера вещь довольно неприятная с точки зрения поддерживаемости решения. Отметаем и этот вариант.
  3. Да, мы можем легко-прелегко расшифровать пароли и потом их также легко шифровать в случае надобности. Но компания IBM сменила алгоритм шифрования в версиях, начиная с 8.7, сменив его на более стойкий AES. Т.е. если мы будем мигрировать наше решениe (а, поверьте, это случится рано или поздно), оно перестанет работать в новых версиях DataStage. Отметаем этот вариант также.
  4. Первое, что приходит в голову: это некрасиво. Второе что приходит: это неудобно. В боевой среде придется релизить каждый раз новый ParameterSet при добавлении очередного источника данных. Ну и в третьих — это не будет работать, потому что мы не можем динамически сменить имя набора параметров при запуске джоба.


Решение проблемы



Я нашел только один способ решения этой проблемы. Может быть существует и другое, более очевидное решение. Но я о нем не знаю.
Я обратил внимание в диалоге конфигурации ParameterSets на вкладку Values

Я никогда ранее не использовал эту вкладку и смею предположить, мало кто ее использовал и вообще знает зачем она нужна. В этой вкладке можно указать имя текстового файла, в котором будут храниться значения созданного вами набора параметров.
Этот файл хранится в каталоге
${PROJECT_DIR}/ParameterSets/ИМЯ_НАБОРА_ПАРАМЕТРОВ/
Я не мог поверить, что Encrypted параметры будут храниться в этом файле в открытом виде. А раз это не так, то DataStage не будет их шифровать повторно. Проверяем гипотезу.

Отлично! Если заменить теперь содержимое этого файла на другие параметры (подставив нужный нам зашифрованный пароль) и проверить работоспособность джоба с этим набором параметров то мы увидим, что все работает как нужно.
Теперь, для того, чтобы передать параметры для нескольких независимых инстанций одного джоба (если он Multiple Instance), нужно будет выполнить следующие шаги:
  1. Создать ParameterSet для с необходимыми полям. Нам не потребуется создавать много наборов. Только один. Один набор может использовать несколько файлов для значений своих полей
  2. Указать какое-нибудь имя файла во вкладке Values конфигурации ParameterSets. Любое. Это нужно, чтобы DataStage создал каталог для параметра
  3. В параметры джоба добавьте этот набор параметров

  4. Перед тем как запускать джоб с зашифрованным параметром, создайте процесс, который бы добавлял (и перезаписывал в случае необходимости) файл с уникальным именем для каждого инстанса джоба. Напомню, что файл необходимо записать в каталог ${PROJECT_DIR}/ParameterSets/ИМЯ_НАБОРА_ПАРАМЕТРОВ/
  5. Для запуска джоба необходимо указать имя файла в качестве значения параметра вашего ParameterSet

    Обратите внимание, что при запуске джоба, DataStage помечает примененные параметры как From value file.


Вместо заключения



Указанный выше способ не тестировался нами в версиях >8.5, но по идее должен работать, так как ничего сверхъестественного мы здесь не использовали. Сами значения параметров лучше объявлять на уровне проекта в DataStage Administrator и получать их в процессе выполнения. В DSParams все Encrypted параметры хранятся также в зашифрованном виде, поэтому все приведенные выше размышления касаются и этого случая. Например, мы используем такой способ конфигурирования наших процессов:
<export>
   <parameters>
       <parameter name="SQL" handleQuotes="Y">
              <value><![CDATA[select count(*) from STAGING.TABLE]]></value>
       </parameter>
       <parameter name="PASSWORD">
              <value><![CDATA[${SOURCE_PASSWORD}]]></value>
       </parameter>
       <parameter name="DB">
              <value><![CDATA[${SOURCE_DB}]]></value>
        </parameter>
        <parameter name="USER">
             <value><![CDATA[${SOURCE_USER}]]></value>
        </parameter>
    </parameters>
</export>

где SOURCE_* — это переменные проекта.
Tags:
Hubs:
+3
Comments 0
Comments Leave a comment

Articles