Skip to content

Instantly share code, notes, and snippets.

@fictorial
Created September 23, 2009 22:48
Show Gist options
  • Save fictorial/192360 to your computer and use it in GitHub Desktop.
Save fictorial/192360 to your computer and use it in GitHub Desktop.
/*
* This is a simple program to run Node.js as the
* specified user and in a chroot.
*
* BUILD:
* gcc -o chrooted-node chrooted-node.c
*
* USE:
* sudo chrooted-node <as-user> <chroot-dir> <file.js>
*
* Brian Hammond, Fictorial
*/
#include <stdio.h>
#include <pwd.h>
#include <errno.h>
#include <unistd.h>
#define NODE_BINARY_PATH "/usr/local/bin/node"
int main(int argc, char **argv)
{
struct passwd *userent;
if (geteuid() != 0)
{
fprintf(stderr, "must be run as root!\n");
return 1;
}
if (argc != 4)
{
fprintf(stderr, "USAGE: %s <user> <dir> <file.js>\n", argv[0]);
return 1;
}
userent = getpwnam(argv[1]);
if (!userent)
{
perror("getpwnam");
return 1;
}
/*
* Preload dynamically loaded objects
*
* Consider operations that require access to full-system resources and
* perform them before closing the jail door. These steps are often not
* entirely obvious at first and require some trial and error, but
* we've found several that qualify. Many systems load nameservice
* resolver clients dynamically at runtime, and they are not included
* in the shared objects bound to the executables. We have found that
* simply calling gethostbyname one time before the jail door is closed
* will load all the appropriate libraries required, so that later
* nameservice requests are handled properly:
*
* See http://unixwiz.net/techtips/chroot-practices.html
*/
(void) gethostbyname("localhost");
if (chdir(argv[2]))
{
perror("chdir");
return 1;
}
if (chroot(argv[2]))
{
perror("chroot");
return 1;
}
if (setuid(userent->pw_uid))
{
perror("setuid");
return 1;
}
execl(NODE_BINARY_PATH, "node", argv[3], NULL);
perror("execl");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment