Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there a flag in nodejs runtime that is set when the bootstrap process is over and it starts executing userland code? #2259

Closed
sargemonkey opened this issue Oct 29, 2019 · 19 comments

Comments

@sargemonkey
Copy link

Hi,

I have a question related to the nodejs runtime. I am trying to distinguish in the nodejs executable code, whether there is a flag/environment/mechanism that I can check that signals whether the runtime has finished loading or not. Basically when the all the bootstrapping is done and the it starts loading and executing userland code.
I am trying to add a specific private feature where I can deny certain types of requests to the userland code, like for example, filesystem access, network access etc.
I want to allow the runtime bootup process (bootstrap folder, etc) to be able to access the fs and do its thing, but I would like to specifically block the userland js from doing so.

@devsnek
Copy link
Member

devsnek commented Oct 29, 2019

you shouldn't do that by time, but by what requests access.

@sargemonkey
Copy link
Author

Could you elaborate "what requests access"? Is there a way to figure out what module/codeblock accessed the requests? If I could figure out if it is a non userland access, that would be great.

@devsnek
Copy link
Member

devsnek commented Oct 30, 2019

if you're inside core (which it sounds like you are), you can hook NativeModule.require and Module.require.

@sargemonkey
Copy link
Author

from what I read, it looks like modules can be loaded by without NativeModule.require by calling process.binding directly from userland.

@devsnek
Copy link
Member

devsnek commented Oct 30, 2019

process.binding is native (c++) functionality, you can't stop those from using the filesystem/network.

@gireeshpunathil
Copy link
Member

IIRC, @jasnell has been championing perf_hooks and that has a flag which indicates bootstrap complete . Not sure how to use that in this scenario though.

@sargemonkey
Copy link
Author

process.binding is native (c++) functionality, you can't stop those from using the filesystem/network.

I can if I add a layer before it in C++ to check if the call has to be passed through or not. I am open to modifying JS/C++ runtime to implement the functionality, and it has to be in the nodejs executable so it cannot be overridden.

@sargemonkey
Copy link
Author

IIRC, @jasnell has been championing perf_hooks and that has a flag which indicates bootstrap complete . Not sure how to use that in this scenario though.

Thanks gireesh, let me read through it.

@sargemonkey
Copy link
Author

IIRC, @jasnell has been championing perf_hooks and that has a flag which indicates bootstrap complete . Not sure how to use that in this scenario though.

Thanks gireesh, let me read through it.

This seems promising, markBootstrapComplete(); is called after any of the execution mode is finished (interactive, main thread, syntax check etc). So if I check if the environment variable is set, that should give me a good indication of when the bootstrapping is over and userland starts. This is all assuming that nodejs startup is all single threaded, according to what I gather.

@gireeshpunathil
Copy link
Member

Yes, until the bootstrap is complete, Node.js runs on single thread (barring v8 and libuv which spawns a couple of threads for their internal functions). That should not be a cause of worry here as I guess the main thread takes control over their execution until the bootstrap is over, after which each are of their own. cc @addaleax and @joyeecheung to get finest details.

@joyeecheung
Copy link
Member

IIRC, --require initialized here is the point where the earliest possible user code execution can happen.

We could at least add a internal flag somewhere in here even just for internal assertions and documentation purposes. Then move on to discuss how or whether it should be exposed.

@joyeecheung
Copy link
Member

BTW, markBootstrapComplete is run after --require is handled, so it's not a particularly good place for guarding potential user code execution (the word bootstrap here is fairly ambiguous).

There is a has_run_bootstrapping_code_ flag in Environment which we check internally whether the environment-independent bootstrap process have has done. There is, still, a bit more environment-dependent bootstrap to be done after this flag has been set, so it is not really set as late as possible, but it is set in a fairly sensible early point in the process.

@sargemonkey
Copy link
Author

sargemonkey commented Nov 8, 2019

BTW, markBootstrapComplete is run after --require is handled, so it's not a particularly good place for guarding potential user code execution (the word bootstrap here is fairly ambiguous).

There is a has_run_bootstrapping_code_ flag in Environment which we check internally whether the environment-independent bootstrap process have has done. There is, still, a bit more environment-dependent bootstrap to be done after this flag has been set, so it is not really set as late as possible, but it is set in a fairly sensible early point in the process.

Thanks for the info! The problem I am trying to solve is enable restricted filesystem access to userland scripts...so I need all nodejs bootstrapping code like require('fs') to succeed, and then the userland require('fs') to fail (depending on a cmd line option). The bootstrapping process seems to have plenty of calls to fs, so that's why I'm trying to block calls as late as possible. If the nodejs bootstrap fails (because of my intervention in, say, GetInternalBinding in node_binding.cc), then none of the userland script blocks may be executed...

@gireeshpunathil
Copy link
Member

@surajjacob - that work sounds interesting! are you doing it in the open? are there links that I can refer? IIRC @addaleax did some work on a feature that addresses access control at the process level, not sure what is there in common, but just stating here in case if it helps.

@sargemonkey
Copy link
Author

@surajjacob - that work sounds interesting! are you doing it in the open? are there links that I can refer? IIRC @addaleax did some work on a feature that addresses access control at the process level, not sure what is there in common, but just stating here in case if it helps.

That would be great if I can build on the work of someone...thanks for adding! I plan to fork out and start work hopefully next week (yes, in public), but I wanted to know if the approach made sense with the local experts before I attempted it (this being my first attempt and all :))...

@addaleax
Copy link
Member

addaleax commented Nov 8, 2019

@surajjacob The PR that @gireeshpunathil was thinking about is most likely nodejs/node#22112 – It kind of stalled out because people were against using a mechanism like this to run untrusted code, whereas I was intentionally trying to work out something for that use case. Either way, I’d encourage you to take a look at the code and the discussion :)

@sargemonkey
Copy link
Author

@surajjacob The PR that @gireeshpunathil was thinking about is most likely nodejs/node#22112 – It kind of stalled out because people were against using a mechanism like this to run untrusted code, whereas I was intentionally trying to work out something for that use case. Either way, I’d encourage you to take a look at the code and the discussion :)

Thanks addaleax! I'll take a look and see if I can reuse it...certainly seems more thought out and granular than what I have been trying

@PoojaDurgad
Copy link

@surajjacob - is this issue resolved?

@PoojaDurgad
Copy link

answered. closing!! please feel free to reopen the issue if it is outstanding.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants