ГлавнаяАкадемияNode-RED: установка, flows, msg/JSON, отладка → Использование Credentials Service для паролей и ключей API

Использование Credentials Service для паролей и ключей API

Урок 5 · Node-RED: установка, flows, msg/JSON, отладка · 30 мин · theory

Введение в Credentials Service

> ⚠️ Внимание: Никогда не храните пароли, ключи API или любые другие секретные данные в виде открытого текста в своих потоках. Это создает серьезную уязвимость в системе безопасности.

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

Самый распространенный, но и самый опасный анти-паттерн — это хранение этих данных в виде обычного текста непосредственно в логике потока.

Проблема хранения секретных данных в открытом виде

Представьте себе поток, который отправляет данные в защищенный API. Начинающий инженер может решить эту задачу, поместив API-ключ прямо в узел `function` или `change`:

// НЕПРАВИЛЬНО: хранение ключа в коде

const API_KEY = "super-secret-key-12345";

msg.headers = {

"Authorization": "Bearer " + API_KEY

};

// ... остальная логика

return msg;

Или, что еще хуже, в узел `inject`, чтобы "удобно" запускать поток с нужными параметрами. Почему это представляет серьезную угрозу?

  • Утечка при экспорте: Когда вы экспортируете свой поток для резервного копирования, переноса на другой контроллер или для того, чтобы поделиться им с коллегой, все эти секретные данные экспортируются вместе с ним в виде простого текста в файле `flows.json`. Любой, кто получит доступ к этому файлу, получит доступ и ко всем вашим секретам.
  • Несанкционированный доступ: Если злоумышленник получит доступ к интерфейсу редактора Node-RED, ему будет достаточно просто открыть свойства узла, чтобы увидеть и скопировать все учетные данные.
  • Сложность управления: Если вам нужно изменить пароль или API-ключ, придется вручную искать все места в потоках, где он используется, и изменять их. Это трудоемко и чревато ошибками. При большом количестве устройств или сервисов такая задача становится практически невыполнимой.
  • Решение: Node-RED Credentials Service

    Для решения этой фундаментальной проблемы безопасности в Node-RED встроен специальный механизм — Credentials Service. Это стандартная служба, предназначенная для безопасного хранения и управления секретными данными.

    Принцип ее работы основан на разделении конфигурации и учетных данных:

    Таким образом, вы можете безопасно делиться файлом `flows.json`, не опасаясь утечки паролей. Для полноценной работы системы на другом контроллере потребуется перенести оба файла: `flows.json` и `flows_cred.json`, а также ключ шифрования, если он был задан. Этот механизм обеспечивает изоляцию секретов, упрощает их администрирование и значительно повышает общий уровень безопасности системы.

    ---

    Определение и регистрация Credentials в нодах

    Вы наверняка замечали, что при настройке таких узлов, как `mqtt in`, `http request` или `influxdb out`, поля для ввода пароля или токена ведут себя особенно: введенные символы отображаются как точки, а после развертывания (Deploy) поле выглядит пустым. Это прямое проявление работы Credentials Service. Нода "знает", что определенные поля содержат секретную информацию и должны обрабатываться особым образом.

    > 💡 Подсказка: Для разработчиков собственных нод: правильное определение credentials на уровне регистрации ноды — ключевой аспект создания безопасных и удобных в использовании компонентов. Понимание этого механизма полезно и для инженеров-интеграторов, так как позволяет глубже понять принципы работы платформы.

    Это поведение не является магией, а определяется разработчиком узла на этапе его создания. Рассмотрим, как это устроено на примере стандартного узла для настройки MQTT-брокера (`mqtt-broker`). Каждый узел в Node-RED состоит из как минимум двух файлов:

  • `.js`-файл: определяет серверную логику узла (что он делает при получении сообщения, как подключается к сервисам и т.д.).
  • `.html`-файл: определяет внешний вид узла в редакторе Node-RED (панель настроек, иконка, описание).
  • Определение Credentials в `.js`-файле узла

    В серверном `.js`-файле узла, при его регистрации в системе Node-RED с помощью функции `RED.nodes.registerType`, разработчик добавляет специальный объект `credentials`. Этот объект перечисляет все свойства, которые должны считаться секретными.

    // Фрагмент .js файла узла конфигурации MQTT брокера
    

    module.exports = function(RED) {

    function MQTTBrokerNode(n) {

    RED.nodes.createNode(this, n);

    // ... логика узла

    // this.credentials содержит расшифрованные значения

    // например, this.credentials.user и this.credentials.password

    }

    RED.nodes.registerType("mqtt-broker", MQTTBrokerNode, {

    credentials: {

    user: {type: "text"},

    password: {type: "password"}

    }

    });

    }

    В этом примере объект `credentials` указывает, что узел `mqtt-broker` имеет два секретных поля: `user` и `password`.

    Когда вы вводите данные в эти поля в редакторе, Node-RED не сохраняет их в основном потоке `flows.json`, а помещает в `flows_cred.json`, связывая с уникальным ID узла.

    Отображение полей в `.html`-файле узла

    Соответствующий `.html`-файл должен содержать HTML-разметку для этих полей. Чтобы редактор Node-RED "понял", что эти `input`-поля соответствуют определенным в `.js`-файле credentials, их `id` должен иметь префикс `node-config-input-`.

    
    
    
    

    Обратите внимание на два ключевых момента:

  • `id="node-config-input-user"`: Идентификатор поля ввода точно соответствует имени свойства, определенного в `credentials` в `.js`-файле (`user`).
  • `type="password"`: Для поля пароля используется стандартный HTML-тип `password`. Это заставляет браузер автоматически маскировать вводимые символы, обеспечивая базовую защиту от "подглядывания через плечо".
  • Таким образом, связка между определением в `RED.nodes.registerType` и HTML-разметкой позволяет Node-RED бесшовно и безопасно управлять секретными данными, предоставляя пользователю интуитивно понятный интерфейс.

    ---

    Практика: Использование Credentials в ноде Function

    Стандартные ноды, как мы увидели, имеют предопределенные поля для учетных данных. Но что делать, если вам нужно использовать API-ключ или токен в собственной логике внутри узла `function`? К счастью, узел `function` также имеет встроенную поддержку Credentials Service.

    Это позволяет вам определять собственные именованные секреты и безопасно получать к ним доступ в коде, не "засвечивая" их в теле функции или в объекте `msg`.

    Добавление Credentials в узел Function

    Чтобы добавить секретное поле в узел `function`, выполните следующие шаги:

  • Поместите узел `function` на рабочую область.
  • Дважды щелкните по нему, чтобы открыть редактор.
  • Перейдите на вкладку "Настройка" (Setup).
  • В нижней части окна вы найдете раздел "Учетные данные" (Credentials). Нажмите кнопку "+ добавить" (+ add).
  • Откроется небольшая форма, где нужно задать "Свойство" (Property) — это имя, по которому вы будете обращаться к секрету в коде, и выбрать его "Тип" (Type) — `text` (текст) или `password` (пароль).
  • Давайте создадим два поля для гипотетического API:

    После добавления этих свойств в интерфейсе редактора появятся соответствующие поля для ввода. Введите в них произвольные значения (например, `mySecretKey123` и `admin`). Нажмите "Готово" (Done), а затем "Развернуть" (Deploy).

    Доступ к Credentials в коде

    Теперь, когда учетные данные сохранены, к ним можно получить доступ внутри кода узла `function` через специальный объект `this.credentials`. Это свойство узла, которое Node-RED автоматически заполняет расшифрованными значениями при инициализации потока.

    Давайте напишем код, который формирует заголовки для HTTP-запроса, используя сохраненные секреты.

    // Код для вкладки "Функция" (Function)
    

    // Получаем доступ к ранее определенным credentials

    // Важно: this.credentials доступен только в этом контексте

    // и не передается в объекте msg.

    const apiKey = this.credentials.apiKey;

    const apiUser = this.credentials.apiUser;

    // Проверяем, что credentials были установлены

    if (!apiKey || !apiUser) {

    node.error("API Key или API User не настроены! Проверьте вкладку 'Настройка' в узле.", msg);

    node.status({fill:"red", shape:"dot", text:"Ошибка: нет credentials"});

    return null; // Прерываем выполнение потока

    }

    // Формируем структуру для следующего узла (например, http request)

    // مطابق قرارداد сообщения, которое мы рассмотрели ранее

    msg.headers = {

    'Content-Type': 'application/json',

    'X-Auth-User': apiUser,

    'Authorization': 'Bearer ' + apiKey

    };

    msg.payload = {

    "source": "HI-Controller-01",

    "value": 25.5,

    "ts": Date.now()

    };

    node.status({fill:"green", shape:"dot", text:"Credentials OK"});

    return msg;

    Проверка безопасности

    Самое главное — убедиться, что наши секреты не утекают.

  • Подключите к выходу узла `function` узел `debug`.
  • Разверните поток и запустите его.
  • В панели отладки вы увидите объект `msg`. Внимательно изучите его. Вы увидите `msg.headers` и `msg.payload`, но нигде не найдете объект `this.credentials` или его содержимого. Он существует только внутри контекста выполнения узла `function`.
  • Теперь выделите ваш узел `function` и экспортируйте его (Меню -> Экспорт -> Выбранные узлы).
  • Вы получите примерно такой JSON:

    [
    

    {

    "id": "a1b2c3d4.e5f6g7",

    "type": "function",

    "z": "...",

    "name": "Формирование API запроса",

    "func": "// Код для вкладки \"Функция\" (Function)\n// ...",

    "outputs": 1,

    "noerr": 0,

    "initialize": "",

    "finalize": "",

    "libs": [],

    "credentials": {

    "apiKey": "",

    "apiUser": ""

    },

    "x": 350,

    "y": 200,

    "wires": [["h8i9j0k1.l2m3n4"]]

    }

    ]

    Обратите внимание на объект `"credentials"`. Имена свойств (`apiKey`, `apiUser`) присутствуют, но их значения пусты. Сами значения надежно хранятся в зашифрованном файле `flows_cred.json` и не попадают в экспорт. Это доказывает, что данный метод является безопасным для хранения секретной информации.

    ---

    Активация шифрования: настройка `credentialSecret`

    По умолчанию Node-RED не шифрует, а лишь обфусцирует (делает нечитаемым) содержимое файла `flows_cred.json`. Это защищает от случайного просмотра, но не является надежной криптографической защитой. Для активации полноценного AES-шифрования необходимо задать секретный ключ в главном конфигурационном файле Node-RED.

    > ⚠️ Внимание: Потеря ключа `credentialSecret` приведет к невозможности расшифровать существующие credentials. Всегда создавайте резервные копии `settings.js` и `flows_cred.json` после его установки или изменения.

    Файл, управляющий глобальными настройками Node-RED на вашем контроллере HI, называется `settings.js`.

    Поиск и редактирование `settings.js`

    На стандартной Linux-системе, которую использует контроллер HI, этот файл обычно располагается в домашней директории пользователя, от имени которого запущен Node-RED, в подпапке `.node-red`.

    Для редактирования файла подключитесь к контроллеру по SSH и используйте любой текстовый редактор, например `nano`:

    nano ~/.node-red/settings.js
    

    Пролистайте файл вниз до секции, касающейся безопасности потоков (`Flow security`). Вы найдете закомментированную строку:

    // ... другие настройки
    

    module.exports = {

    // ...

    /* The following property can be used to enable básico-auth for the editor /

    //adminAuth: {

    // ...

    /* The following property can be used to encrypt credentials in storage /

    //credentialSecret: "a secret key",

    // ...

    }

    Ваша задача — раскомментировать строку `credentialSecret` и заменить значение `"a secret key"` на ваш собственный, надежный и уникальный ключ.

    Генерация надежного ключа

    Использовать простую фразу в качестве ключа небезопасно. Ключ должен быть длинной, случайной строкой символов. На контроллере HI, работающем на Debian, можно легко сгенерировать такой ключ с помощью стандартных утилит.

    Способ 1: Использование `openssl` (рекомендуемый)

    Эта команда сгенерирует 24 случайных байта и закодирует их в формат Base64, что идеально подходит для ключа.

    openssl rand -base64 24
    

    Пример вывода: `wJ/k9sL+r7dE6gZ5hXNqR8mF3tYjCgO+`

    Способ 2: Использование `/dev/urandom`

    Эта команда читает случайные данные из системного генератора, фильтрует их и кодирует в Base64.

    head -c 24 /dev/urandom | base64
    

    Пример вывода: `bVpGk8tQjLzR7sYf+NqWpXzHgCjE/wA=`

    Выберите любой из этих способов, сгенерируйте ключ, скопируйте его и вставьте в `settings.js`:

    // ...
    

    /* The following property can be used to encrypt credentials in storage /

    credentialSecret: "wJ/k9sL+r7dE6gZ5hXNqR8mF3tYjCgO+",

    // ...

    Сохраните файл (в `nano` это `Ctrl+O`, `Enter`, затем `Ctrl+X`).

    Перезапуск Node-RED и последствия

    После изменения `settings.js` необходимо перезапустить службу Node-RED, чтобы изменения вступили в силу.

    # Команда может отличаться в зависимости от конфигурации системы
    

    sudo systemctl restart nodered

    > ℹ️ Информация: Если вы установили `credentialSecret` на системе, где уже были сохранены какие-либо credentials, они станут нечитаемыми. Node-RED попытается расшифровать их старым методом (обфускация) и потерпит неудачу. Вам придется заново ввести все пароли и ключи API в соответствующих узлах. После этого они будут сохранены в `flows_cred.json` уже в зашифрованном виде с использованием вашего нового ключа. То же самое произойдет, если вы когда-либо измените или потеряете `credentialSecret`.

    Именно поэтому критически важно сделать резервную копию файла `settings.js` (с вашим ключом) и файла `flows_cred.json` и хранить их в надежном месте. Без ключа зашифрованные данные превращаются в бесполезный набор символов.

    ---

    Пример: Защита подключения к MQTT-брокеру

    Давайте применим полученные знания на практике и защитим подключение к MQTT-брокеру, который требует аутентификации.

    Сценарий: Наш контроллер HI должен публиковать данные в топик на защищенном MQTT-брокере. У нас есть следующие учетные данные:

    Пошаговая настройка

  • Добавьте узел `mqtt out` на рабочую область.
  • Дважды щелкните по нему, чтобы открыть панель настроек.
  • В поле "Брокер" (Broker) нажмите на иконку карандаша, чтобы отредактировать или создать новую конфигурацию.
  • В окне настроек брокера перейдите на вкладку "Безопасность" (Security).
  • Введите имя пользователя `hi_controller` в поле "Имя пользователя" (Username).
  • Введите пароль `P@$$w0rd_mqtt_!2024` в поле "Пароль" (Password). Обратите внимание, что символы маскируются.
  • Нажмите "Обновить" (Update), а затем "Готово" (Done).
  • Разверните (Deploy) поток.
  • На этом настройка завершена. Узел будет использовать сохраненные учетные данные для подключения к брокеру.

    Анализ файлов конфигурации

    Теперь самое интересное. Посмотрим, что произошло "под капотом". Мы проанализируем (упрощенно) содержимое файлов `flows.json` и `flows_cred.json`.

    Файл `flows.json`:

    В этом файле вы найдете два объекта: один для самого узла `mqtt out` и один для узла конфигурации `mqtt-broker`, который мы только что создали.

    [
    

    {

    "id": "abc12345.def678",

    "type": "mqtt out",

    "z": "...",

    "name": "Отправка телеметрии",

    "topic": "telemetry/office/temp",

    "qos": "1",

    "retain": "false",

    "broker": "xyz98765.uvw432", // <-- Ссылка на узел конфигурации

    "x": 450,

    "y": 300,

    "wires": []

    },

    {

    "id": "xyz98765.uvw432", // <-- Уникальный ID узла конфигурации

    "type": "mqtt-broker",

    "name": "Secure Broker",

    "broker": "192.168.1.10",

    "port": "1883",

    "clientid": "HI-Controller-Main"

    // ЗАМЕТЬТЕ: здесь нет ни имени пользователя, ни пароля!

    }

    ]

    Как видите, `flows.json` содержит все настройки, кроме секретных. Узел `mqtt out` (`id: abc12345...`) просто ссылается на узел `mqtt-broker` (`id: xyz98765...`) через поле `"broker"`.

    Файл `flows_cred.json`:

    Если вы откроете этот файл, его содержимое будет выглядеть примерно так (если `credentialSecret` был настроен):

    {
    

    "xyz98765.uvw432": {

    "user": "hi_controller",

    "password": "b1a2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2g3h4i5j6k7l8m9n0o1p2q3r4s5t6u7v8w9x0y1z2a3b4c5d6e7f8g9h0i1j2k3l4m5n6o7p8"

    },

    "$": "a1a4b6b8c9c0d3d5e2e8f1f5g8g4h7h6i1i9j3j2k6k7l5l0m9m4n8n2o6o3p1p7q..."

    }

    Этот пример наглядно демонстрирует принцип разделения: `flows.json` определяет структуру, а `flows_cred.json` — секреты, привязанные к этой структуре через ID узлов.

    ---

    Итоги и лучшие практики

    > 🔗 Связанный материал: Для повышения уровня организации проекта, рассмотрите возможность инкапсуляции логики работы с API, включая получение credentials, в Subflow. Это позволит создать переиспользуемый и легко настраиваемый компонент. Подробнее о создании подпотоков мы говорили в уроке `COURSE-06-M06-L03`.

    В этом уроке мы рассмотрели один из самых важных аспектов создания безопасных и профессиональных систем на базе Node-RED — управление секретными данными с помощью Credentials Service. Неправильное хранение паролей и ключей является одной из самых серьезных и распространенных уязвимостей в системах автоматизации.

    Давайте закрепим ключевые принципы и лучшие практики:

  • Разделяйте конфигурацию и секреты: Credentials Service — это стандартный механизм, который отделяет логику потока (`flows.json`) от чувствительных данных (`flows_cred.json`). Всегда используйте его.
  • Активируйте шифрование: На любой системе, используемой в реальных условиях (production), обязательно задайте `credentialSecret` в файле `settings.js`. Это превращает простую обфускацию в надежное AES-шифрование.
  • Используйте встроенные возможности: Для стандартных узлов (`mqtt`, `http request` и др.) всегда используйте предопределенные поля для ввода учетных данных на их панелях настроек.
  • Применяйте для `function`: Когда вам нужны секреты в кастомной логике, добавляйте их через вкладку "Настройка" узла `function` и получайте доступ через `this.credentials`.
  • Не раскрывайте секреты: Никогда не копируйте значения из `this.credentials` в объект `msg` или в переменные контекста (`flow`/`global`). Это сведет на нет всю пользу от безопасного хранения.
  • Создавайте резервные копии: Регулярно делайте бэкапы как минимум трех файлов: `flows.json`, `flows_cred.json` и `settings.js` (содержащего ваш `credentialSecret`). Храните их в надежном, защищенном месте. Без этих трех компонентов восстановление системы будет неполным или невозможным.
  • Что дальше

    Освоив безопасное хранение учетных данных, мы готовы перейти к следующему важному аспекту организации крупных проектов — версионированию и управлению изменениями. В следующем уроке мы рассмотрим, как интегрировать проекты Node-RED с системой контроля версий Git, что позволит отслеживать все изменения, откатываться к предыдущим версиям и организовывать совместную работу над проектом.