Sentinel applies a two-level check for outbound network access: a capability gate that controls whether a package may make network calls at all, and a URL allowlist that controls which destinations are reachable.
The two-level check
- Capability gate — does the package hold
network:http (or network:fetch)? If not, the call is blocked immediately.
- URL allowlist — if
sentinel.networkPolicy.allowlist is configured, is the destination URL in the list? If not, the call is blocked.
Both checks must pass for an HTTP or fetch call to succeed.
network:http — outbound HTTP/HTTPS
Triggered when a node calls http.request(), https.request(), http.get(). Gates outbound calls through the http/https built-in modules.
// Node-RED log when blocked:
// [@allanoricil/nrg-sentinel] BLOCKED http.request() — my-node lacks network:http
sentinel: {
allow: {
"my-node": ["registry:register", "network:http"],
},
}
URL allowlist
sentinel.networkPolicy.allowlist controls which URLs are reachable for network:http and network:fetch. It does not apply to network:socket or network:dns.
sentinel: {
allow: {
"my-node": ["registry:register", "network:http"],
},
networkPolicy: {
allowlist: [
"https://api.example.com/",
"https://metrics.internal/",
],
},
}
If no allowlist is configured, a package with network:http can reach any HTTP/HTTPS destination.
network:socket — raw TCP/UDP sockets
Triggered by net.createConnection(), tls.connect(), dgram.createSocket(). These bypass the HTTP URL allowlist entirely — a package with only network:http cannot open raw sockets.
// Node-RED log when blocked:
// [@allanoricil/nrg-sentinel] BLOCKED net.createConnection() — my-node lacks network:socket
sentinel: {
allow: {
"my-node": ["registry:register", "network:socket"],
},
}
network:socket has no allowlist equivalent. The allowlist only applies to HTTP/fetch. A package granted network:socket can connect raw TCP/UDP to any host and port with no further restriction. A host/port allowlist for sockets is not yet designed.
network:dns — DNS lookups
Triggered by require('dns').lookup(), resolve(), and all other dns methods, including dns/promises variants. DNS is a known data-exfiltration channel: subdomains can encode data to an attacker-controlled nameserver (DNS tunneling).
// Node-RED log when blocked:
// [@allanoricil/nrg-sentinel] BLOCKED dns.lookup() — my-node lacks network:dns
sentinel: {
allow: {
"my-node": ["registry:register", "network:dns"],
},
}
network:dns has no allowlist equivalent. DNS queries go to the system resolver and cannot be restricted by the HTTP allowlist. A domain allowlist for DNS would require a separate mechanism.
network:fetch — browser-side fetch
Gates globalThis.fetch() calls. In the browser context, this is enforced by the Service Worker that Sentinel registers in the editor. The same URL allowlist configured in sentinel.networkPolicy.allowlist applies.
// Blocked log:
// NRG Sentinel: network:fetch not granted — my-node
sentinel: {
allow: {
"my-node": ["registry:register", "network:fetch"],
},
}
network:listen — inbound connections
Gates http.createServer(), https.createServer(), net.createServer(). Opening a listening port on the host is a backdoor vector — a malicious package could open an unauthenticated port that bypasses all Node-RED auth.
sentinel: {
allow: {
"my-node": ["registry:register", "network:listen"],
},
}
Complete settings.js example
// settings.js
module.exports = {
sentinel: {
allow: {
// Only permitted to reach the listed URLs.
"node-red-contrib-http-request": ["registry:register", "network:http"],
},
networkPolicy: {
allowlist: [
"https://api.example.com/",
"https://metrics.internal/",
],
},
},
};