Skip to content

Instantly share code, notes, and snippets.

@joelrbrandt
Last active December 14, 2015 13:19
Show Gist options
  • Save joelrbrandt/5092716 to your computer and use it in GitHub Desktop.
Save joelrbrandt/5092716 to your computer and use it in GitHub Desktop.
Notes on setting up a node server on ec2

Setting up a Node server on EC2

Basic EC2 setup

  1. Select your region in the upper right corner of the management console. I always use N. California, since that's where I live, but Oregon is a little cheaper.
  2. Launch a Ubuntu instance. I chose a "Ubuntu Server 12.04.1 LTS 64-bit" server using the Quick Launch wizard. If you don't already have a keypair, you'll have to create one.
  3. Optional: Immediately shut down the instance and change the instance type to "m1.medium" (or larger). This will make the setup steps much faster. You can always scale it back later. Note: When you change instance sizes, elastic IP mappings can get hosed. You may need to unassociate and reassociate them.
  4. Add an elastic IP that is mapped to the new instance.
  5. Log in to your new instance with something like: ssh -i yourkey.private ubuntu@your.elastic.ip
  6. Edit your security group for the instance to allow HTTP/HTTPS access and maybe ICMP (ping) access
  7. Optional: Set up an additional user on your instance with sudo adduser [username]. Once that is set up, add the new user to the "admin" group (so they can sudo) with sudo adduser [username] admin
  8. Optional: Enable password-based login via ssh by editing /etc/ssh/sshd_config. There should be a line that says PasswordAuthentication no. Change "no" to "yes". (Note that the comment above it is out of date and confusing). Restart sshd with sudo service ssh restart to make changes take effect.
  9. Optional: Log out and then try to ssh in with your new account and a password.
  10. Update installed packages with sudo apt-get update followed by sudo apt-get dist-upgrade
  11. Optional: Install emacs23-nox with sudo apt-get install emacs23-nox so Joel can use the computer.
  12. Optional: Make an image of the instance in the current state (or after setting up node).

Setting up node

Unfortunately, the version of node in apt is 0.6.x. More unfortunately, the Joyent guys don't distribute binaries for node. Fortunately, it's easy to build from scratch.

For security, ease of upgrading, and so-on, I recommend installing the node executable in a single user's home directory (not owned/installed by root).

  1. Install build essentials (like gcc) with sudo apt-get install build-essential

  2. Install git with sudo apt-get install git

  3. Make a new folder, e.g. "~/development" and clone node into that folder (git clone https://github.com/joyent/node.git)

  4. Switch to the node directory and check out a version tag (e.g. git checkout v0.8.21)

  5. Configure node to install to a local directory (e.g. ./configure --prefix=~/bin/node)

  6. Run make (using the option "-j8" seems to be about right on a medium instance)

  7. Run make install. That will put all of node in the "prefix" path specified above. This directory is completely self-contained and can be moved, copied to other users, etc.

  8. Optional: Put the node/bin directory in your path. Add something like the following to your .profile (and then logging back out and back in):

    # set PATH so it includes user's private node 'bin' directory if it exists
    if [ -d "$HOME/bin/node/bin" ] ; then
        PATH="$HOME/bin/node/bin:$PATH"
    fi

Running node as an unprivileged user on ports 80/443

Only privileged users (i.e. root) can open ports 80/443. However, running node as a privileged user is a bad idea from a security perspective. Apache uses a "dropping privileges" approach to get around this. However, this workflow isn't really worked out for node.

An alternative (that I very much prefer) is to set up iptables NAT rules that forward ports 80 and 443 to unprivileged ports (e.g. 3080 and 3443). This way, the node process can be run completely as an unpriviledged service. Node doesn't need to know anything about 80/443.

Adding these rules can be done as follows (in this example, port 3080 is being forwarded to 80 and 3443 is being forwarded to 443). Note that all of this will need to be done as root.

  1. Create an iptables "save" file that has the port mappings and put it someplace reasonable (e.g. /etc/network/redirect-80-443.iptables)

    # Generated by iptables-save v1.4.10 on Wed Dec 21 18:48:09 2011
    *nat
    :PREROUTING ACCEPT [0:0]
    :INPUT ACCEPT [69:4416]
    :OUTPUT ACCEPT [0:0]
    :POSTROUTING ACCEPT [0:0]
    -A PREROUTING -i eth0 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 3080
    -A PREROUTING -i eth0 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 3443
    COMMIT
    # Completed on Wed Dec 21 18:48:09 2011
    
  2. Add a script to the "/etc/network/if-pre-up.d" that installs these rules. For example, make a file called "nat-redirect-80-443" with the following content:

    #!/bin/sh
    
    iptables-restore < /etc/network/redirect-80-443.iptables
  3. Make the script executable with chmod a+x nat-redirect-80-443

  4. Reboot the instance to really test this works right, or just run the script if you want to live dangerously.

  5. Spin up a node server on port 3080 and see if you can access it from the outside world on port 80 (make sure your ec2 security group is set up to allow port 80):

    var http = require('http');
    http.createServer(function (req, res) {
        res.writeHead(200, {'Content-Type': 'text/plain'});
        res.end('Hello World\n');
    }).listen(3080, '0.0.0.0');
    console.log('Server running at http://0.0.0.0:3080/');

SSL with node

In this section, I give an example of how to create a static connect-based webserver that serves data over https with a self-signed certificate.

  1. Make a directory, (e.g. "example") to hold everything, and switch to that dir

  2. install connect with npm install connect

  3. make a self-signed certificate with the following commands:

    mkdir cert
    cd cert
    openssl req -new > certificate.csr
    # fill out info
    openssl rsa -in privkey.pem -out certificate.key
    openssl x509 -in certificate.csr -out certificate.cert -req -signkey certificate.key -days 365
  4. Add a "public" directory (for testing) to the "example" directory and put some html content in it. (e.g. an index.html file and some pictures)

  5. Set up a static connect server using the certificates from above. Something like:

    /*jslint vars: true, plusplus: true, devel: true, nomen: true,
    indent: 4, maxerr: 50, node: true */
    
    (function () {
        "use strict";
        
        var https = require("https");
        var fs = require("fs");
        var connect = require("connect");
        
        var options = {
            key: fs.readFileSync("cert/certificate.key"),
            cert: fs.readFileSync("cert/certificate.cert")
        };
        
        var app = connect()
            .use(connect.logger("dev"))
            .use(connect["static"]("public"))
            .use(function (req, res) {
                res.end("hello world\n");
            });
        
        var server = https.createServer(options, app)
            .listen(3443, "0.0.0.0");
        
        console.log("Server running at http://0.0.0.0:3443/");
    }());
  6. Run the server (node server.js) and visit your IP over https to verify that it works (obviously, you will get a certificate error, since it's self-signed).

Running node as a service

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment