Setting up a Rails server from scratch can be a daunting task. I remember my first attempt; it was a multi-day process full of frustration, things not working, me not understanding why, and a whole lot of googling. In an effort to make this experience less painful for those new to Rails, I’d like to share the process I went through to deploy Phindee to a VPS (Virtual Private Server).
Choosing a VPS
Phindee is currently running on DigitalOcean servers, but there are other options available as well, like Linode, which was my previous VPS provider. If you’re new to deployment, I recommend DigitalOcean because it’ll be ideally suited to your needs, due their more flexible billing policy and cheaper plans, but any VPS provider will do.
Once you decide on a VPS, you’ll then signup for a plan. If you’re just starting out, the cheapest plan available will be enough; otherwise, choose a plan that fits your needs. Once you have an account, you’ll be able to create your server, and typically, you’ll have a list of operating systems to choose from. DigitalOcean offers a wide variety of Linux distributions; I chose the latest 32-bit version of Ubuntu for Phindee, and I recommend you do the same if you’re new to deployment.
The reason why I chose the 32-bit version was because it uses less memory than the 64-bit one. This is something you should consider if you chose one of the cheaper plans with a lower amount of memory, but if memory is not an issue, go with the 64-bit since you’ll have better performance (see this page to learn more).
Once you create your instance, you’ll be given your server’s IP address and password. If you’re on Linux or a Mac, open up Terminal and login. (If you’re on Windows, you’ll need to download Putty.) To login using Terminal, use the following command, replacing the Xs with your own IP address:
This command uses SSH to connect to your server as the user
root. If you’re unfamiliar with SSH, it stands for Secure Shell, and it’s basically a network protocol that allows two computers to securely communicate with one another. There are many other protocols out there, such as HTTP, which allows browsers to communicate with web servers.
The first time you attempt to login, you’ll be asked if you’re sure you want to continue connecting; type “yes”. Then enter the password for the
root user, and you’ll be logged in.
Groups and Privileges
Now that you’re in, the first thing we’ll do is change the password for
root using the following command:
This will prompt you to enter a new password twice. Next, we’ll create a new group called
deployers, which will allow us to easily manage the users with deployment privileges:
Now we’ll create a new user called
bob, and assign him to the
deployers group we just created above:
This command will prompt you to enter a password for this user, in addition to some other information afterwards, but after you enter the password twice, feel free to just press “Enter” for the other fields, as they’re not strictly necessary. By the way, don’t use the same password for both
root and the user you just created above or bad things will happen.
Next thing we’ll do is open the
sudoers file containing a list of users and groups who have root privileges:
and we’ll add the following line into it (we use “%” to indicate that this is a group name):
You can then exit the nano text editor by typing “Control-X” and typing “Y” when asked if you want to save. In case you’re wondering, the line we just added above will give the users in the
deployers group the ability to run commands as
root. If this is new to you, let me explain.
Running commands while logged in as
root is considered bad practice because, as the superuser,
root can run any and all commands, and since there is no undo functionality in Unix, one accidental bad command and your system can be seriously disrupted. That’s why we created a separate user called
bob, which will have deployment privileges and nothing else.
But why did we create a
deployers group and added
bob into it? Well, first of all, we could’ve avoided creating a group altogether and just added
bob to the
sudoers file and given him
root privileges instead. But let’s say I’m working on a project with a friend and she wants to be able to deploy as well. I would have to then add her to the
sudoers file too (to give her
root privileges), and the file would keep growing every time a new user with deployment privileges needed to be added. This would be a nightmare to maintain.
A better way to go about this is to create a group called
deployers, give the group
root privileges, and then add users to this group. This way, whenever I’d need to add new users with deployment privileges, I would just need to add them to the
deployers group. This keeps the
sudoers file clean and organized, while allowing me to easily manage the members of the group as well. I could, for example, easily revoke some rights for all members of the
deployers group at the same time, instead of doing it one user at a time, or I could simply remove a user from the
deployers group if I discover, for example, that he still creates “1234” passwords for his accounts.
Okay, but why is it necessary for users and groups to have
root privileges? Well, these privileges allow a user, say
bob, to run commands he otherwise would not be able to run due to not having the necessary permissions, which arises from the fact that the user is not
root and therefore has limited privileges. But given
root privileges, or being part of a group with
root privileges, enables
bob to run these commands simply by preceding the command with
sudo. He’ll then be prompted to enter his password, and the command will run.
That’s the reasoning behind giving the
root privileges and adding
bob into it. Later on,
bob will need these privileges during the deployment process.
Configuring SSH Access
Now we’re ready for the next step in securing our server, and we’ll start by opening the
This file contains a number of rules that define who can login to the server and in what way. The first thing we’ll do is change the port number with which users will login; the default port that servers listen on is 22, but it’s wise to change it to another value so that any potential hackers have some extra work to do in figuring out the correct one; you can choose any port number from 1025 to 65536. Once you have your number, look for a line that looks like the following:
and change its port number to the one you picked. Make sure you make a note of the new port number because you’ll need it for future login.
Next, look for another line in the file that looks like this:
and change the “yes” to a “no”; this prevents
root user login, which means that any potential hackers will need to know the name of one of the users on the server to actually login.
We can even go a step further and define exactly which existing users are able to login. Since I only want
bob to have login access, I’ll add the following line to the end of the file:
You could even specify a space-separated list of users here, if you have more than one user in need of login access.
All right, there is one final line that we’ll add to the end of our file:
This line disables hostname lookup, which can lead to a delay of up to 30 seconds when logging in with
ssh. Disabling it will save you time and do no harm.
To put these changes into effect, we’ll restart SSH, like so:
Now we’re ready to test the configurations we just made to make sure they work. I’ll open a new shell in Terminal, without closing my current one, and try to login as the user
bob on the port I specified in
Make sure you change the above command to match the user and port number you specified in your own
sshd_config file, or it obviously won’t work. The above command will then prompt you to enter that user’s password. If you login successfully, congratulations! Your configuration is correct! You can close your previous shell and just continue using the current one; otherwise, you’ll need to go back and double check your
sshd_config file configurations.
Enabling SSH Authentication
The final thing we’ll do to secure our server is enable SSH authentication, which will allow us to use SSH keys to authenticate with the server, instead of the traditional password authentication. This is a more secure approach because password authentication involves sending your password over the network, and this makes it vulnerable to being intercepted and cracked. It’s also more convenient since you won’t need to enter it every time you want to login. But before we move on, I’d like to briefly explain how SSH keys work and what makes them more secure.
All SSH keys come in pairs: one private and the other public. The private key is stored locally and needs to be carefully guarded, while the public key is stored on the remote server to which you will be logging in. Anytime you want to connect to the server, it will use the public key to create a challenge, which it will then send over to you, and only you, the holder of the private key, will be able to correctly understand and solve the challenge. Your response is then sent back to the server, and if it’s correct, it’ll grant you access.
You can see if you already have an SSH key by running:
If you see any files with the
.pub extension, then you already have a key generated; otherwise, you can generate one with the following command:
Note that we’re using the
-C flag to create a label for our key for easy identification, and it’s typical to set it to your email address. When the command runs, it’ll prompt you to enter a path and passphrase, but the default path is fine, and since we won’t be setting up a passphrase, you can just press “enter” for both. This will store both the private and public keys in the
~/.ssh/ directory, and they will be named according to the type of encryption used, the default being RSA authentication. Your private key will be stored in a file called
id_rsa.pub will hold your public key.
We’ll then need to add the newly generated keys to
ssh-agent, which is a program that caches your private key and provides it to the SSH client program on your behalf. You can do so with the following command:
It’ll then ask you for a passphrase, but since we didn’t set one up, you’ll just need to press “enter.”
Having our keys generated, we’re now ready to copy our public key over to the remote server using the
ssh-copy-id command. (If you’re on a Mac, and you don’t have
ssh-copy-id installed, you can install it using Homebrew with
brew install ssh-copy-id.) Below is the full
ssh-copy-id command that will copy our key over to the server:
This will create a new file called
authorized_keys on your remote server inside the
~/.ssh directory and store your public key in it. If you now try to
ssh into your server, you should be authenticated and logged in without entering your password.
Going through this process might seem a bit tedious and time consuming at first, but after you’ve done it a couple times, it will get easier and hopefully become second nature. Security is important, and the time you spend learning and implementing it is time well spent.
To summarize, we made our server more secure by:
rootprivileges to just members of the
- setting a custom port with which to connect
- specifying exactly which user is able to login
- enabling SSH authentication
Of course, this doesn’t mean our server is “unhackable” by any means, but it is significantly more secure than it was before. You can now sleep more peacefully knowing that any future hackers have at least some of their work cut out for them.
In part 2, we’ll start setting up the server by installing the technology stack behind Phindee. If you’d like to be notified when its out, feel free to subscribe, and you’ll get the complete post delivered right to your inbox as soon as it’s released.