Skip to content

Latest commit

 

History

History
executable file
·
198 lines (129 loc) · 7.65 KB

sysvinit-and-nvm.md

File metadata and controls

executable file
·
198 lines (129 loc) · 7.65 KB

Back to Table of Contents

Start/Stop the Node Daemon Using nvm and SysVinit

SysVinit is the first process executed and as such is the parent of all processes. SysVinit scripts are found in the /etc/init.d directory on debian-based systems. Maybe the following command is familiar to you?

$ sudo service apache2 restart

We'll write our own SysVinit script to run on Ubuntu 14.04 using nvm. But note that mainline linux distributions are moving towards systemd and sysvinit is becoming obsoleted. When this setup moves beyond Ubuntu 14.04, I will update these examples with a migrate to systemd example (such as found here).

This example assumes installation of nvm-global following the prior example.

Preparing a new sysvinit script

Create a new sysvinit script file in /etc/init.d. The file needs root ownership and executable permissions.

$ sudo touch /etc/init.d/node-app
$ sudo chmod a+x /etc/init.d/node-app

Update the system service definitions. Note the number at the end of the command. A low number gets run first. I usually set my node apps with higher run-levels because of a time I could not get one program to run with the default 20.

$ sudo update-rc.d node-app defaults 92

For reference, remove update-rc.d linkages to the sysvinit script by runing

$ sudo update-rc.d -f node-app remove 

Open the new script file in an editor.

$ sudo vim /etc/init.d/node-app

And edit this template, replacing the example values with your own.

Required variables to edit

  • APPNAME: This should be recognizable, perhaps the same name as the sysvinit script. It also becomes the name of the pidfile and logfile.
  • DESC: Can be more detailed than APPNAME. Used in messages.
  • APPROOT: The working directory. The daemon execution command will first cd into this directory before executing nvm
  • DAEMON_ARGS: The target javascript file that node should run against first, followed by any arguments. (eg server.js --port 8080)
  • NODE_VERSION: The version of node that the daemon should use. First check that this has been installed with sudo nvm ls and if not install with sudo nvm install VERSION
  • NODEUSER: The user:group that the daemon process should be run as. See the section below for help.

Running as a secluded user

Did you see the variable NODEUSER? This refers to the user:group that the daemon process should be run as. You could run the process as root:root but this is unsafe should a vulnerability allow an attacker to gain control of the application and suddenly have root permissions. You could run the process with your own userid but most likely you'll have other files that you want protected in case the worst happens. You should run production node in its own unprivileged user account.

We will tell our node-app to run using both an userid and groupid named node-app-ps.

Does this groupid exist?

$ getent group | cut -d: -f1 | grep node-app-ps

Does this userid exist?

$ awk -F':' '{ print $1}' /etc/passwd | grep node-app-ps

If these did not return values, then create the new node-app-ps user and group. This useradd command creates both.

$ sudo useradd -s /bin/bash -m -d /home/node-app-ps -c "safe node app process" node-app-ps

Create a password for the user. (Enter twice)

$ sudo passwd node-app-ps

Add the user to the sudo group. The user can execute root commands but only with a password.

$ sudo usermod -aG sudo node-app-ps

Within the new home directory, I create a prod folder. Run these commands by ssh'ing into the server or running as the user via sudo.

# sudo
$ su node-app-ps
$ mkdir ~/prod

# or from a remote device
# ssh
$ ssh node-app-ps@svr1.example.com
$ mkdir ~/prod

The prod folder contains the source code (eg server.js). Each username created is unique for each production node application that has its own sysvinit script. The app's user credentials are used to scp files to the production server from source control or are obtained via git clone by the admin on the server.

Ownership of the production directory

Who should the user:group owner be of the prod directory? The user is node-app-ps and the group is node-app-ps. But is that correct? Shouldn't I do something more with chmod or chown to harden my system?

Note: Some file ownership strategies can be found at Digital Ocean, examples from Securing Apache and Ruby on Apache, and nvm sudo errors.

For my node-apps, I use Digital Ocean's model of creating a safeuser and running the node-process under that user. If the site were breached, the user would not be able to manipulate files outside of the production directory. Individual folders within the production directory could have different ownership or read-write permissions, such as making all files readable but only certain folders writable. Going to greater lengths to harden the file structure is done on a case-by-case basis.

Testing

Start the server.

$ sudo service node-app start

View the process. node is the uid.

$ sudo ps aux | grep nvm

# OUTPUT
UID              PID
node-app-ps     13256  0.0  0.0  26760  2140 ?        S    17:31   0:00 /bin/bash /usr/local/bin/nvm run 0.10.32 server.js
node-app-ps     13287  0.0  0.0 658912 11520 ?        Sl   17:31   0:00 /usr/local/nvm/v0.10.32/bin/node server.js

And let's look at the process tree.

$ sudo pstree -p 13256

# OUTPUT
nvm(13256)───node(13287)───{node}(13288)

View by ip:port.

$ sudo netstat -ntulp | grep LISTEN | grep node

# OUTPUT
tcp        0      0 127.0.0.1:1337          0.0.0.0:*               LISTEN      13287/node

So far so good. Let's stop the server.

$ sudo service node-app stop

View the process.

$ sudo ps aux | grep nvm

# OUTPUT
(NOTHING)

Excellent. It works fine, though I will follow-up with a last section of what I am watching with this script.

What I'm watching

The script works fine. I have not had issues. However, in my script, I send the TERM signal to children (node) of the parent pid (nvm). If I didn't, these children would continue to run. Also the built-in sysvinit function start-stop-daemon does not seem to work for the child processes but pkill does. I'm mentioning this because if you do run into an issue where you think the process should have stopped but it appears to be running, then this is the culprit. This is how to identify and fix it.

Find the nvm processes running.

$ sudo ps aux | grep nvm

# OUTPUT - orphaned process. Remember that in the example above there were two records returned!
UID              PID
node-app-ps     13287  0.0  0.0 658912 11520 ?        Sl   17:31   0:00 /usr/local/nvm/v0.10.32/bin/node server.js

Kill the specific process

$ sudo kill -TERM 13287

View the process.

$ sudo ps aux | grep nvm

# OUTPUT
(NOTHING)

Good results. The process has completely ended.