Do you need to access data from your OPC UA servers from web applications or IT systems easily and securely? This Gateway acts as a robust bridge, offering a modern REST API and advanced monitoring over OPC UA.
graph TD
A[OPC UA Server] -- Secure Connection --> B(OPC UA Gateway);
B -- REST API --> D[Web Application];
B -- REST API --> E[Python/Node Script];
B -- REST API --> F[IoT Platform];
B -- REST API --> G[Kepware IoT GW Client];
subgraph Consumer Clients
direction LR %% Force internal layout if necessary %%
D
E
F
G
end
- Simplifies OPC UA Access: Forget the complexities of the OPC UA protocol. Interact using a simple REST API.
- IT/OT Integration: Facilitates the connection between the operational technology (OT) world and information technology (IT) systems.
- Centralized Security: Manage OPC UA connection security and API security in one place.
- Standard Monitoring: Use SNMP (v1/v2c/v3) and/or the REST API to monitor the gateway's status and performance.
- Compatibility:
/iotgateway
endpoints designed to ease migration from or coexistence with Kepware IoT Gateway. - Open Source: Completely free, open-source code (MIT License) with the possibility to contribute.
- Modern and Lightweight: Built with Node.js, ideal for efficient deployments.
- Visualize production data from PLCs (via OPC UA) on web dashboards (Grafana, etc.) in real-time.
- Integrate OPC UA alarms with ticketing systems, databases, or notification services (Email, Slack).
- Store historical process data from a SCADA system in time-series databases (InfluxDB, TimescaleDB).
- Allow scripts (Python, Node.js) to read/write data in control systems in a controlled and secure manner.
- Migrate client applications that used Kepware IoT Gateway to an open-source solution.
- Quick Start (Docker)
- Main Features
- Installation and Deployment
- Configuration
- Detailed Security
- Metrics and Monitoring
- API Endpoints
- Project Structure
- Error Handling
- Security Best Practices
- Requirements
- Code of Conduct
- Contributing
- License
- Author
The fastest and recommended way to run the gateway using Docker and the official Docker Hub image:
-
Create
docker-compose.yml
file: Create a file nameddocker-compose.yml
in a folder of your choice with the following content:version: '3.8' services: opcua-gateway: image: exacross/opcua-gateway:latest # Use the image from Docker Hub container_name: opcua-gw ports: - '3000:3000' # Map host port to container port env_file: - .env # Load environment variables from .env file restart: unless-stopped # Optional: Mount volume for certificates if using secure modes # volumes: # - ./certificates:/app/certificates
-
Create
.env
file: In the same folder where you createddocker-compose.yml
, create a file named.env
. Copy and paste the necessary configuration variables from the Environment Variables section. At a minimum, you need to configure:# Required for basic connection OPC_ENDPOINT=opc.tcp://YOUR_OPCUA_SERVER:4840 # Required for API security (choose one or both) API_KEY=A_SECURE_API_KEY_HERE # or AUTH_USERNAME=your_basic_user AUTH_PASSWORD=your_basic_password # Recommended (adjust as needed) LOG_LEVEL=info LOG_TO_CONSOLE=true # ... other variables you need to modify ...
Replace the example values with your actual configuration.
-
Start the Container: Open a terminal in the folder where you created the files and run:
docker-compose up -d
Docker will automatically download (
pull
) theexacross/opcua-gateway:latest
image if you don't have it locally and then start the container. -
Verify Status: Wait a few seconds and check the health endpoint:
curl http://localhost:3000/health # You should see a JSON response indicating "UP" and "CONNECTED" status
Done! The gateway is running and accessible at http://localhost:3000
, using the official image and your local configuration.
- π Secure Connection: Support for different OPC UA security modes and policies.
- π Modern REST API: Intuitive endpoints for reading and writing OPC UA values.
- π€ Kepware Compatibility:
/iotgateway
endpoints for easy integration/migration. - π Connection Pooling: Efficient management of OPC UA sessions.
- π Automatic Reconnection: Robust handling of disconnections with configurable retries.
- π‘οΈ Complete API Security: Dual authentication (Basic/API Key), Rate Limiting, CORS, Helmet.
- π Advanced Monitoring: Detailed metrics via REST API and SNMP (v1/v2c/v3) with Zabbix template.
- π Configurable Logging: Log levels, output to console and/or files.
- βοΈ Modular and Maintainable: Clear and organized project structure.
- π³ Easy Deployment: Ready to use with Docker and Docker Compose.
- π Open Source (MIT): Freedom to use, modify, and distribute.
See the Quick Start (Docker) section.
To build the image manually:
docker build -t opcua-gateway .
To run the container manually (make sure you have your .env
file ready):
docker run -d \
-p 3000:3000 \
--name opcua-gw \
--env-file .env \
opcua-gateway
# 1. Clone the repository (if you haven't already)
git clone https://github.com/tinroad/opcua-gateway
cd opcua-gateway
# 2. Install dependencies
npm install
# 3. Configure environment variables
cp .env.example .env
# Edit .env with your development settings
# 4. Start development server (with nodemon for auto-reloading)
npm run dev
# 1. Make sure you have Node.js >= 14.0.0 on your server
# 2. Clone or copy the source code to your server
# 3. Install ONLY production dependencies
npm install --production
# 4. Configure environment variables in `.env`
# MAKE SURE TO USE SECURE CREDENTIALS AND SETTINGS FOR PRODUCTION!
# 5. Start the server (recommended to use a process manager like pm2)
npm start
# Or with pm2:
# pm2 start src/server.js --name opcua-gateway
The gateway is fully configured via environment variables defined in a .env
file in the project root. Copy .env.example
to get started.
# === Core OPC UA Configuration ===
OPC_ENDPOINT=opc.tcp://127.0.0.1:4840 # OPC UA Server URL
OPC_SECURITY_MODE=1 # 1:None, 2:Sign, 3:SignAndEncrypt
OPC_SECURITY_POLICY=None # None, Basic128Rsa15, Basic256, Basic256Sha256, Aes128_Sha256_RsaOaep, Aes256_Sha256_RsaPss
OPC_NAMESPACE=2 # Default namespace for Node IDs (if not specified)
OPC_APPLICATION_URI=urn:CLIENT:NodeOPCUA-Client # Client application URI
# === OPC UA Certificate Configuration (only for secure modes > 1) ===
OPC_CERTIFICATE_FILE=./certificates/client_cert.pem
OPC_PRIVATE_KEY_FILE=./certificates/client_key.pem
OPC_TRUSTED_FOLDER=./certificates/trusted # Trusted server certificates
OPC_REJECTED_FOLDER=./certificates/rejected # Rejected certificates
# === OPC UA Connection Configuration ===
CONNECTION_RETRY_MAX=5 # Max retries per connection attempt
CONNECTION_INITIAL_DELAY=1000 # Initial delay before first attempt (ms)
CONNECTION_MAX_RETRY=10 # Global max reconnection attempts (-1 for infinite)
CONNECTION_MAX_DELAY=10000 # Max delay between retries (ms)
CONNECTION_RETRY_DELAY=5000 # Base delay between retries (ms)
# === Web Server Configuration ===
SERVER_PORT=3000 # Port the gateway will listen on
# === API Security Configuration ===
API_KEY=your_api_key_here # Secret key for X-API-Key authentication
AUTH_USERNAME=admin # User for Basic Authentication
AUTH_PASSWORD=your_secure_password # Password for Basic Authentication
ALLOWED_ORIGINS=http://localhost:3000,[https://your-frontend-domain.com](https://your-frontend-domain.com) # Allowed CORS origins (comma-separated)
CORS_MAX_AGE=600 # CORS preflight cache time (seconds)
RATE_LIMIT_WINDOW_MS=900000 # Rate limit time window (15 minutes default)
RATE_LIMIT_MAX=100 # Max requests per IP in the window
# === Logging Configuration ===
LOG_LEVEL=info # Log level: error, warn, info, http, verbose, debug, silly
LOG_FILE_ERROR=error.log # File for error logs
LOG_FILE_COMBINED=combined.log # File for all logs
LOG_TO_CONSOLE=true # Log to console? (true/false)
# === SNMP Configuration ===
ENABLE_SNMP=true # Enable SNMP agent (true/false)
SNMP_PORT=161 # SNMP agent port
SNMP_COMMUNITY=public # Community for SNMP v1/v2c
SNMP_VERSION=3 # SNMP version: 1, 2c, or 3
# --- SNMPv3 Configuration (if SNMP_VERSION=3) ---
SNMP_SECURITY_NAME=opcgwuser # Main SNMPv3 user
SNMP_SECURITY_LEVEL=authPriv # Level: noAuthNoPriv, authNoPriv, authPriv
SNMP_AUTH_PROTOCOL=SHA256 # Auth protocol: MD5, SHA1, SHA224, SHA256, SHA384, SHA512
SNMP_AUTH_KEY=opcgw_auth_key # Auth key (min 8 chars)
SNMP_PRIV_PROTOCOL=AES128 # Privacy protocol: DES, AES128, AES192, AES256, AES192C, AES256C
SNMP_PRIV_KEY=opcgw_priv_key # Privacy key (min 8 chars)
# --- Additional SNMPv3 User (Optional) ---
# SNMP_USER_2_NAME=zabbix
# SNMP_USER_2_LEVEL=authPriv
# ... (full configuration for user 2)
Security is paramount. This gateway implements several layers:
You can protect the /iotgateway
and /api
endpoints using one or both methods simultaneously:
-
Basic Authentication:
- Uses username and password (
AUTH_USERNAME
,AUTH_PASSWORD
). - Ideal for quick tests or human access.
- Example with
curl
:curl -X GET "http://localhost:3000/iotgateway/read?ids=ns=2;s=MiVariable" \ -u "admin:your_secure_password"
- Uses username and password (
-
API Key Authentication:
- Uses a secret key (
API_KEY
) sent in theX-API-Key
header. - Recommended for machine-to-machine (M2M) communication.
- Example with
curl
:curl -X GET "http://localhost:3000/iotgateway/read?ids=ns=2;s=MiVariable" \ -H "X-API-Key: your_api_key_here"
- Uses a secret key (
- Protects against brute force and DoS attacks by limiting the number of requests per IP (
RATE_LIMIT_WINDOW_MS
,RATE_LIMIT_MAX
). - Responds with
429 Too Many Requests
if the limit is exceeded.
- Controls which origins (web browsers) can make requests to the API (
ALLOWED_ORIGINS
). - Configurable to allow credentials and specific methods.
- Automatically applies various security-related HTTP headers to protect against common attacks (XSS, clickjacking, etc.).
Gain visibility into the gateway's performance and status:
Endpoints under /api/metrics
(require authentication) expose metrics in JSON format:
/api/metrics
: All metrics./api/metrics/opcua
: OPC UA specific metrics./api/metrics/http
: HTTP request metrics./api/metrics/system
: System metrics (CPU, memory, etc.).
If ENABLE_SNMP=true
, the gateway acts as an SNMP agent.
- Supports SNMP v1, v2c, and v3.
- Exposes metrics using OIDs under the enterprise base
1.3.6.1.4.1.12345
.
- Configure the version, port, community (v1/v2c), or security credentials (v3) using the
SNMP_*
environment variables. - SNMPv3 is recommended for production due to its enhanced security (authentication and encryption).
Simplify Zabbix integration!
- Make sure
ENABLE_SNMP
is active in.env
. - Generate the Zabbix XML template:
npm run generate:zabbix # Options to customize SNMP version and security: # node src/tools/generateZabbixTemplate.js --help
- Import the generated
tools/zabbix_template.xml
file into your Zabbix server. - Add the gateway as a Host in Zabbix, configure the SNMP interface (IP, port, version, community/credentials), and link the imported template.
Key metrics are monitored across three categories (non-exhaustive list):
- OPC UA: Active connections, errors, retries, read/write latency.
- HTTP: Request counts (by status code), errors, latency, rate limit blocks.
- System: CPU usage, memory utilization, uptime.
Check the logs on startup with SNMP enabled or the source code for a detailed OID mapping.
GET /iotgateway/read?ids=<node-id1>,<node-id2>,...
- Authentication: Basic Auth (
-u user:pass
) OR API Key (-H "X-API-Key: key"
). - Query Parameters:
ids
(required): One or more OPC UA Node IDs, comma-separated. (E.g.,ns=2;s=MyVariable,ns=3;i=1001
)
- Successful Response (200 OK):
{ "readResults": [ { "id": "ns=2;s=MyVariable", "s": true, // Success (OPC UA status code) "r": "Good", // Reason / Status description "v": "123.45", // Read value "t": 1678886400000 // Timestamp (OPC UA source timestamp) } // ... more results ] }
- Error Response (e.g., 400 Bad Request if
ids
is missing):{ "error": "Parameter 'ids' is required" }
POST /iotgateway/write
Content-Type: application/json
- Authentication: Basic Auth OR API Key.
- Request Body (JSON): An array of objects to write.
[ { "id": "ns=2;s=MyVariable", "value": "NewValue" // "dataType": "String" // Optional: Specify OPC UA data type (e.g., Double, Int32, Boolean) }, { "id": "ns=3;i=1002", "value": true, "dataType": "Boolean" } // ... more values to write ]
- Successful Response (200 OK):
{ "writeResults": [ { "id": "ns=2;s=MyVariable", "success": true, "message": "Good" // OPC UA status of the write operation } // ... more results ] }
- Error Response (e.g., 400 Bad Request if body is invalid):
{ "error": "Invalid or empty request body" }
These endpoints provide additional functionality or direct access (require authentication):
GET /api/opcua/status
: OPC UA connection status.GET /api/opcua/read/:nodeId
: Direct read of a single node (URL-encoded ID).POST /api/opcua/write/:nodeId
: Direct write to a single node (URL-encoded ID, value in JSON body{"value": ...}
).GET /api/metrics/...
: Detailed metrics endpoints.
GET /health
: Basic health status of the gateway (no authentication required). Ideal for load balancers or service checks.{ "status": "UP", "opcClient": "CONNECTED", // or "DISCONNECTED", "CONNECTING" "opcEndpoint": "opc.tcp://127.0.0.1:4840", "time": 1678886500000 }
project/
βββ src/
β βββ config/ # Configuration files (central, CORS)
β βββ services/ # Main business logic (OPC UA Service)
β βββ middleware/ # Express Middlewares (Auth, Logging, Rate Limit)
β βββ routes/ # API Route definitions (Express)
β βββ utils/ # Utilities (Logger, etc.)
β βββ tools/ # Tools (Zabbix template generator)
β βββ app.js # Express application setup
β βββ server.js # Entry point, server start
βββ certificates/ # OPC UA Certificates (client, trusted, rejected)
βββ public/ # Static files (if any)
βββ .env # Environment variables (DO NOT version control)
βββ .env.example # Example environment variables
βββ Dockerfile # Docker image definition
βββ docker-compose.yml # Docker Compose service definition
- Robust automatic reconnection to OPC UA with exponential backoff.
- Detailed logging of OPC UA and HTTP errors.
- Centralized middleware to catch Express application errors.
- Consistent JSON error responses for the API (4xx/5xx).
401 Unauthorized
: Authentication required or invalid.403 Forbidden
: Authenticated but not permitted (not currently applicable).429 Too Many Requests
: Rate limit exceeded.400 Bad Request
: Malformed request (missing parameters, invalid JSON).500 Internal Server Error
: Unexpected server error.503 Service Unavailable
: OPC UA connection error or other critical service failure.
- Credentials: Use strong API keys and rotate them periodically. Use robust passwords for Basic Auth. Never commit the
.env
file! - HTTPS: In production, ALWAYS run this gateway behind a reverse proxy (Nginx, Traefik, Caddy) that handles HTTPS/TLS.
- Network: Limit access to the gateway port (
SERVER_PORT
) only to necessary IPs/networks (firewall). - CORS: Configure
ALLOWED_ORIGINS
restrictively to only the frontend domains needing access. - Rate Limiting: Adjust limits (
RATE_LIMIT_*
) based on expected traffic to prevent abuse. - SNMPv3: If using SNMP in production, prefer SNMPv3 with
authPriv
for maximum security. - Updates: Keep dependencies (Node.js, npm libraries) updated to patch vulnerabilities.
- Node.js >= 14.0.0
- Accessible OPC UA server on the network.
- (Optional) Valid OPC UA certificates if using Sign or SignAndEncrypt security modes.
- (Recommended) Docker and Docker Compose for easy deployment.
This project adheres to a Code of Conduct. By participating, you agree to abide by its terms.
Contributions are welcome! If you want to help improve this project, here are some ideas:
- Report bugs or suggest new features by creating a GitHub Issue.
- Check the open issues, especially those tagged
good first issue
orhelp wanted
. - Improve the documentation.
- Submit Pull Requests with fixes or new features.
This project uses Conventional Commits for commit messages. This helps maintain a clean history and automatically generate changelogs. The basic format is:
<type>(<scope>): <description>
Where <type>
can be feat
, fix
, docs
, style
, refactor
, perf
, test
, build
, ci
, chore
, revert
, config
.
For more details on how to contribute, please see CONTRIBUTING.md.
Distributed under the MIT License. See LICENSE for more information.