Skip to content

Commit

Permalink
permission,net,dgram: add permission for net
Browse files Browse the repository at this point in the history
  • Loading branch information
theanarkh committed Jun 20, 2024
1 parent ce531af commit 29e2adc
Show file tree
Hide file tree
Showing 16 changed files with 981 additions and 10 deletions.
54 changes: 54 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,57 @@ Error: Access to this API has been restricted
}
```

### `--allow-net-udp`

<!-- YAML
added: REPLACEME
-->

> Stability: 1.1 - Active development
When using the [Permission Model][], the process will not be able to create,
receive, and send UDP packages by default. Attempts to do so will throw an
`ERR_ACCESS_DENIED` unless the user explicitly passes the `--allow-net-udp` flag
when starting Node.js.

The argument format is `--allow-net-udp=domain_or_ip[/netmask][:port]`.
The valid arguments are:

* `*` - To allow all operations.
* `--allow-net-udp=nodejs.org`
* `--allow-net-udp=127.0.0.1`
* `--allow-net-udp=127.0.0.1:8888`
* `--allow-net-udp=127.0.0.1:*`
* `--allow-net-udp=*:9999`
* `--allow-net-udp=127.0.0.1/24:*`
* `--allow-net-udp=127.0.0.1/255.255.255.0:*`
* `--allow-net-udp=127.0.0.1:8080 --allow-net-udp=127.0.0.1:9090`
* `--allow-net-udp=127.0.0.1:8080,localhost:9090`

Example:

```js
const dgram = require('node:dgram');
dgram.createSocket('udp4').bind(9297, '127.0.0.1')
```

```console
$ node --experimental-permission --allow-fs-read=./index.js index.js
node:events:498
throw er; // Unhandled 'error' event
^

Error [ERR_ACCESS_DENIED]: Access to this API has been restricted. Permission: bind to 127.0.0.1/9297
at node:dgram:379:18
at process.processTicksAndRejections (node:internal/process/task_queues:77:11)
Emitted 'error' event on Socket instance at:
at afterDns (node:dgram:337:12)
at node:dgram:379:9
at process.processTicksAndRejections (node:internal/process/task_queues:77:11) {
code: 'ERR_ACCESS_DENIED'
}
```

### `--build-snapshot`

<!-- YAML
Expand Down Expand Up @@ -1012,6 +1063,7 @@ following permissions are restricted:
* Child Process - manageable through [`--allow-child-process`][] flag
* Worker Threads - manageable through [`--allow-worker`][] flag
* WASI - manageable through [`--allow-wasi`][] flag
* UDP - manageable through [`--allow-net-udp`][] flag

### `--experimental-require-module`

Expand Down Expand Up @@ -2804,6 +2856,7 @@ one is included in the list below.
* `--allow-child-process`
* `--allow-fs-read`
* `--allow-fs-write`
* `--allow-net-udp`
* `--allow-wasi`
* `--allow-worker`
* `--conditions`, `-C`
Expand Down Expand Up @@ -3356,6 +3409,7 @@ node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
[`--allow-child-process`]: #--allow-child-process
[`--allow-fs-read`]: #--allow-fs-read
[`--allow-fs-write`]: #--allow-fs-write
[`--allow-net-udp`]: #--allow-net-udp
[`--allow-wasi`]: #--allow-wasi
[`--allow-worker`]: #--allow-worker
[`--build-snapshot`]: #--build-snapshot
Expand Down
4 changes: 4 additions & 0 deletions doc/api/permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,8 @@ using the [`--allow-child-process`][] and [`--allow-worker`][] respectively.
To allow native addons when using permission model, use the [`--allow-addons`][]
flag. For WASI, use the [`--allow-wasi`][] flag.

For UDP, use [`--allow-net-udp`][] flag.

#### Runtime API

When enabling the Permission Model through the [`--experimental-permission`][]
Expand Down Expand Up @@ -575,6 +577,7 @@ There are constraints you need to know before using this system:
* Inspector protocol
* File system access
* WASI
* UDP
* The Permission Model is initialized after the Node.js environment is set up.
However, certain flags such as `--env-file` or `--openssl-config` are designed
to read files before environment initialization. As a result, such flags are
Expand All @@ -598,6 +601,7 @@ There are constraints you need to know before using this system:
[`--allow-child-process`]: cli.md#--allow-child-process
[`--allow-fs-read`]: cli.md#--allow-fs-read
[`--allow-fs-write`]: cli.md#--allow-fs-write
[`--allow-net-udp`]: cli.md#--allow-net-udp
[`--allow-wasi`]: cli.md#--allow-wasi
[`--allow-worker`]: cli.md#--allow-worker
[`--experimental-permission`]: cli.md#--experimental-permission
Expand Down
3 changes: 3 additions & 0 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ Allow execution of WASI when using the permission model.
.It Fl -allow-worker
Allow creating worker threads when using the permission model.
.
.It Fl -allow-net-udp
Allow create, receive, and, send UDP packages when using the permission model.
.
.It Fl -completion-bash
Print source-able bash completion script for Node.js.
.
Expand Down
63 changes: 54 additions & 9 deletions lib/dgram.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const {
ErrnoException,
ExceptionWithHostPort,
codes: {
ERR_ACCESS_DENIED,
ERR_BUFFER_OUT_OF_BOUNDS,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_FD_TYPE,
Expand Down Expand Up @@ -81,6 +82,7 @@ const {

const dc = require('diagnostics_channel');
const udpSocketChannel = dc.channel('udp.socket');
const permission = require('internal/process/permission');

const BIND_STATE_UNBOUND = 0;
const BIND_STATE_BINDING = 1;
Expand Down Expand Up @@ -327,12 +329,9 @@ Socket.prototype.bind = function(port_, address_ /* , callback */) {
else
address = '::';
}

// Resolve address first
state.handle.lookup(address, (err, ip) => {
const afterDns = (err, ip) => {
if (!state.handle)
return; // Handle has been closed in the mean time

if (err) {
state.bindState = BIND_STATE_UNBOUND;
this.emit('error', err);
Expand Down Expand Up @@ -372,7 +371,18 @@ Socket.prototype.bind = function(port_, address_ /* , callback */) {

startListening(this);
}
});
};
if (permission.isEnabled()) {
const resource = `${address}/${port || '*'}`;
if (!permission.has('net.udp', resource)) {
process.nextTick(() => {
afterDns(new ERR_ACCESS_DENIED(`bind to ${resource}`));
});
return this;
}
}
// Resolve address first
state.handle.lookup(address, afterDns);

return this;
};
Expand Down Expand Up @@ -413,13 +423,27 @@ function _connect(port, address, callback) {
this.once('connect', callback);

const afterDns = (ex, ip) => {
if (!ex && !address && permission.isEnabled()) {
const resource = `${ip}/${port}`;
if (!permission.has('net.udp', resource)) {
ex = new ERR_ACCESS_DENIED(`connect to ${resource}`);
}
}
defaultTriggerAsyncIdScope(
this[async_id_symbol],
doConnect,
ex, this, ip, address, port, callback,
);
};

if (address && permission.isEnabled()) {
const resource = `${address}/${port}`;
if (!permission.has('net.udp', resource)) {
process.nextTick(() => {
afterDns(new ERR_ACCESS_DENIED(`connect to ${resource}`));
});
return;
}
}
state.handle.lookup(address, afterDns);
}

Expand All @@ -430,9 +454,13 @@ function doConnect(ex, self, ip, address, port, callback) {
return;

if (!ex) {
const err = state.handle.connect(ip, port);
if (err) {
ex = new ExceptionWithHostPort(err, 'connect', address, port);
try {
const err = state.handle.connect(ip, port);
if (err) {
ex = new ExceptionWithHostPort(err, 'connect', address, port);
}
} catch (e) {
ex = e;
}
}

Expand Down Expand Up @@ -663,6 +691,13 @@ Socket.prototype.send = function(buffer,
}

const afterDns = (ex, ip) => {
// If we have not checked before dns, check it now
if (!ex && !connected && !address && permission.isEnabled()) {
const resource = `${ip}/${port}`;
if (!permission.has('net.udp', resource)) {
ex = new ERR_ACCESS_DENIED(`send to ${resource}`);
}
}
defaultTriggerAsyncIdScope(
this[async_id_symbol],
doSend,
Expand All @@ -671,6 +706,16 @@ Socket.prototype.send = function(buffer,
};

if (!connected) {
// If address is not empty, check it
if (address && permission.isEnabled()) {
const resource = `${address}/${port}`;
if (!permission.has('net.udp', resource)) {
process.nextTick(() => {
afterDns(new ERR_ACCESS_DENIED(`send to ${resource}`));
});
return;
}
}
state.handle.lookup(address, afterDns);
} else {
afterDns(null, null);
Expand Down
1 change: 1 addition & 0 deletions lib/internal/process/pre_execution.js
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,7 @@ function initializePermission() {
'--allow-child-process',
'--allow-wasi',
'--allow-worker',
'--allow-net-udp',
];
ArrayPrototypeForEach(availablePermissionFlags, (flag) => {
const value = getOptionValue(flag);
Expand Down
2 changes: 2 additions & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
'src/permission/permission.cc',
'src/permission/wasi_permission.cc',
'src/permission/worker_permission.cc',
'src/permission/net_permission.cc',
'src/pipe_wrap.cc',
'src/process_wrap.cc',
'src/signal_wrap.cc',
Expand Down Expand Up @@ -280,6 +281,7 @@
'src/permission/permission.h',
'src/permission/wasi_permission.h',
'src/permission/worker_permission.h',
'src/permission/net_permission.h',
'src/pipe_wrap.h',
'src/req_wrap.h',
'src/req_wrap-inl.h',
Expand Down
4 changes: 4 additions & 0 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,10 @@ Environment::Environment(IsolateData* isolate_data,
options_->allow_fs_write,
permission::PermissionScope::kFileSystemWrite);
}
if (!options_->allow_net_udp.empty()) {
permission()->Apply(
this, options_->allow_net_udp, permission::PermissionScope::kNetUDP);
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,11 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
"allow worker threads when any permissions are set",
&EnvironmentOptions::allow_worker_threads,
kAllowedInEnvvar);
AddOption("--allow-net-udp",
"allow host:port or ip:port to bind and connect by UDP socket "
"when any permissions are set",
&EnvironmentOptions::allow_net_udp,
kAllowedInEnvvar);
AddOption("--experimental-repl-await",
"experimental await keyword support in REPL",
&EnvironmentOptions::experimental_repl_await,
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class EnvironmentOptions : public Options {
bool allow_child_process = false;
bool allow_wasi = false;
bool allow_worker_threads = false;
std::vector<std::string> allow_net_udp;
bool experimental_repl_await = true;
bool experimental_vm_modules = false;
bool expose_internals = false;
Expand Down
Loading

0 comments on commit 29e2adc

Please sign in to comment.