Практическое руководство: Запуск и настройка OPC UA сервера iotechsys/opc-ua-sim в Docker
Цель: Краткая выжимка по запуску рабочего OPC UA сервера с поддержкой авторизации (пароль/сертификаты), настройкой нод и примерами Lua-скриптов.
1. Базовый запуск сервера
Минимальная команда (без безопасности)
docker run -d --name opc-ua-sim \
-p 4840:49947 \
-v /mnt/iscsi_disk/opcua:/data \
iotechsys/opc-ua-sim:1.2 \
-l /data/sensors.luaКлючевые параметры командной строки
docs.iotechsys.com
Параметр | Кратко | Значение | Описание |
|---|---|---|---|
|
|
| Lua-скрипт для создания нод |
|
|
| Политика шифрования канала |
|
|
| Режим безопасности (подпись/шифрование) |
|
|
| Сертификат сервера (обязателен при политике ≠ None) |
|
|
| Приватный ключ сервера |
|
|
| Адрес Local Discovery Server |
⚠️ Важно: При использовании политик безопасности отличных от None, обязательно указывать --certificate и --private-key
2. Настройка аутентификации
Сервер поддерживает три метода аутентификации пользователей
Вариант 1: Анонимный доступ (по умолчанию)
# Просто запускаем без дополнительных настроек
docker run -d --name opc-ua-sim -p 4840:49947 iotechsys/opc-ua-sim:1.2 -l /data/sensors.lua
Вариант 2: Логин/пароль (Username/Password)
Для включения аутентификации по паролю необходимо использовать конфигурационный файл сервера (не Lua-скрипт), так как управление пользователями реализуется на уровне компонента OPC-UA Server, а не симулятора.
Пример config.json для Edge XRT OPC-UA Server Component
{
"AccessControl": {
"AllowAnonymous": false,
"EnableX509": false,
"AccessControlUserPassList": {
"admin": "SecureP@ss123",
"operator": "Op3r@tor!2024"
}
},
"SecurityPolicies": [
{
"SecurityPolicy": "Basic256Sha256",
"Certificate": "/certs/server_cert.der",
"PrivateKey": "/certs/server_key.der"
}
],
"Endpoints": [
{
"SecurityPolicy": "Basic256Sha256",
"MessageSecurityModes": ["SignEncrypt"]
}
]
}Запуск с конфигурацией:
docker run -d --name opc-ua-sim \
-p 4840:49947 \
-v /mnt/iscsi_disk/opcua:/data \
-v /mnt/iscsi_disk/opcua/certs:/certs:ro \
iotechsys/opc-ua-sim:1.2 \
-l /data/sensors.lua \
--security-policy Basic256Sha256 \
--security-mode SignEncrypt \
--certificate /certs/server_cert.der \
--private-key /certs/server_key.der
Вариант 3: Аутентификация по сертификатам (X.509)
- Сгенерируйте сертификат сервера (если нет):
# Создаём приватный ключ и самоподписанный сертификат
openssl req -x509 -newkey rsa:2048 -days 365 -nodes \
-keyout server_key.pem -out server_cert.pem \
-subj "/CN=opc-ua-server/O=MyOrg/C=RU"
# Конвертируем в DER-формат (требование OPC UA)
openssl x509 -in server_cert.pem -outform DER -out server_cert.der
openssl rsa -in server_key.pem -outform DER -out server_key.der- Настройте доверенные клиентские сертификаты (опционально):
{
"CertificateVerification": {
"TrustList": ["/certs/trusted/client1_cert.der"],
"IssuerList": ["/certs/ca/root_ca.der"],
"RevocationList": []
},
"AccessControl": {
"EnableX509": true,
"AllowAnonymous": false
}
}- Подключайтесь с клиента, предоставив свой пользовательский сертификат.
3. Структура Lua-скрипта: основные паттерны
Создание пространства имён и папок
-- Создаём новое пространство имён (индекс 2+)
ns = Server.addNamespace("MySensors")
-- Корневая папка для нод
root_folder = ObjectNode.newRootFolder("Sensors", ns)
Server.addObjectNode(root_folder)Переменные (VariableNode)
-- Создаём переменную с начальным значением
variant = Variant.new(DataType.DOUBLE)
variant:setScalar(42.5)
temp_node = VariableNode.new(
NodeId.newString("Temperature", ns), -- NodeId
"Temperature", -- DisplayName
root_folder:getNodeId(), -- Parent
variant, -- Initial value
AccessLevel.READ | AccessLevel.WRITE -- Права доступа
)Server.addVariableNode(temp_node)
Методы (MethodNode)
-- Объявляем аргументы
input_arg = Argument.newStandardType("value", DataType.INT32, ValueRank.SCALAR, nil)
output_arg = Argument.newStandardType("result", DataType.STRING, ValueRank.SCALAR, nil)
-- Функция-обработчик
function my_method(inputs, outputs)
local val = inputs[1]:getScalar()
outputs[1]:setScalar("Вы ввели: " .. tostring(val))
end
-- Регистрируем метод
method_node = MethodNode.new("ProcessValue", ns, root_folder:getNodeId(),
my_method, {input_arg}, {output_arg})
Server.addMethodNode(method_node)События (Event)
-- Создаём тип события
event_type_id = NodeId.newString("AlarmEvent", ns)
base_event_id = NodeId.newNumeric(ObjectType.BASEEVENTTYPE, 0)
event_type = ObjectTypeNode.new(event_type_id, "AlarmEvent", base_event_id)
-- Источник событий (должен иметь EventNotifier=true)
event_source = ObjectNode.newFolder("AlarmSource", ns, root_folder:getNodeId())
event_source:setEventNotifier(true)
-- Функция генерации события
function trigger_alarm(inputs, outputs)
Server.triggerEvent(event_type, event_source, 2, "High Temperature!", "LuaScript")
end
-- Метод для вызова
alarm_method = MethodNode.new("TriggerAlarm", ns, root_folder:getNodeId(),
trigger_alarm, {}, {})
-- Регистрация
Server.addObjectTypeNode(event_type)
Server.addObjectNode(event_source)
Server.addMethodNode(alarm_method)Периодическое обновление (Update())
last_update = os.time()
wait_time = 1 -- интервал в секундах
function should_update()
if os.time() - last_update < wait_time then return false end
last_update = os.time()
return true
end
function Update()
if not should_update() then return end
-- Обновляем значение переменной
local current = temp_node:getVariant():getScalar() + 0.1
temp_node:getVariant():setScalar(current)
temp_node:updateValue() -- Важно: уведомляем клиентов об изменении!
end4. Пример структуры проекта
/mnt/iscsi_disk/opcua/
├── sensors.lua # Основной скрипт нод
├── config.json # Конфигурация безопасности (опционально)
├── certs/
│ ├── server_cert.der # Сертификат сервера
│ ├── server_key.der # Приватный ключ
│ └── trusted/
│ └── client1_cert.der # Доверенные клиентские сертификаты
└── logs/ # Опционально: лог-файлы5. Чеклист безопасности
Задача | Решение |
|---|---|
🔒 Шифрование канала | Использовать |
👤 Аутентификация пользователей | Настроить |
🪪 Проверка клиентских сертификатов | Заполнить |
🚫 Отключение анонимного доступа | Установить |
📜 Ротация сертификатов | Использовать внешние скрипты + перезапуск контейнера |
💡 Совет: Для продакшена всегда используйте SignEncrypt и отключайте Anonymous. Для тестов допустимо None + Anonymous