Search code examples
node.jsubuntumacos-sierra

Become root after app starts


On occasion, the user initiates an action in my Node app that requires escalated administrator or root privileges. Rather than ask users to run the app with sudo, I would like to prompt the user for their password and escalate the privileges of the already-running Node process.

I am not interested in my app executing a child process with sudo (as is already possible with sudo-prompt). I want the node process itself to gain root privileges after having been started by a non-root user without sudo.

One example of an app that displays behavior exhibiting the problem:

var process = require('process');
var http = require('http');
var server = http.createServer(...);
// Several steps here that are unsafe to run as root
promptUserForAdminPassword();
server.listen(80); // Fails, needs to be root

I would like to write the function promptUserForAdminPassword(), which would prompt the user for their password, escalating the privileges of Node so it can run server.listen(80) with root privileges, but run everything prior with user privileges.


Solution

  • You are essentially wanting to change the uid of the node process to 0, the id for root. This is done using Node's process.setuid(0), but only root or processes run with sudo will be successful with that call, so this is not possible.

    It is not possible for a process with a uid of a non-privileged user to change its uid to 0.

    Alternatives

    Start Another Process

    // Prompts user for password in terminal running Node process
    child_process.spawn('sudo', ['node', 'serverlistener.js']);
    
    // Prompts user for password using UI element
    child_process.spawn('gksudo', ['node', 'serverlistener.js']);
    

    This question has some options for the missing gksudo on macOS.

    Effective User ID

    If starting the app with sudo is a possibility, you can reduce the exposure of root by:

    1. Starting as root
    2. Immediately changing the effective user id to a safer user
    3. Later change the effective user back to root as needed

    Example:

    var userid = require('userid');
    var sudoUserId = userid.uid(process.env.SUDO_USER);
    process.seteuid(sudoUserId);
    // Do things
    process.seteuid(0);
    server.listen(80);
    

    Uses userid module.