How to setup a Linux web server using the command line, Part 1 : Securing Your Server
So you’ve got a fresh new install of a Linux distribution such as Ubuntu Gutsy or Fedora 8 that you plan to use as a web application hosting server - sweet. The only thing is - you only have root access via SSH, no point-and-click GUI. That sounds perfect. Using only the command line will take us about 20 minutes to setup a complete, secure Web 2.0 Linux Apache MySQL PHP (LAMP) Ruby on Rails Django TurboGears stack on a slice (once you get the hang of things, of course). Take deep breaths.
This multi-part tutorial will show you step-by-step instructions for securing your new dedicated server or virtual private server (a.k.a. Slice or VPS), loading the programs you need to run it, and setting the Linux server up to host multiple Ruby on Rails and other web 2.0 framework applications, all using your local workstation’s bash shell command line, uh, your (Mac) terminal.
This tutorial has been thoroughly tested to work with the following Linux distros:
For this tutorial, I’ll be using “nano”, a cool in-shell text editor, but others may like to use “mate” to call TextMate or some other text editor. To get nano, visit the GNU Nano homepage here. To check out TextMate, visit the Macromates homepage here.
I’ll also be using the username “super” throughout this tutorial. You may substitute this username for whatever unique username you are actually using.
Step 0: SSH Known_Hosts
We want to make sure we can login from our local machine to our remote server via SSH without having to enter the password every time. To accomplish this, we need to initially login from our local machine using a standard SSH command as the Unix root user. But before we set that up, let’s make sure our remote server IP address and/or the domain are not already registered in the local SSH Known_Hosts file.
On your local machine, edit file: ~/.ssh/known_hosts
nano ~/.ssh/known_hosts
Step 1: Login to Remote Server
Now that we’ve made sure there are no existing entries linking your local machine to your remote server, you can go ahead and login to your server as the “root” user.
On your local machine, in a terminal window:
ssh root@222.22.33.333
password: <enter_your_ssh_password>
If you’ve logged in successfully, you should see your shell prompt text look something like root@222.22.33.333:~$
Step 2: Change “root” User Password
Now that you are logged into your remote server, it’d be smart to change the ssh password for your “root” user. Unix has a lot of cool one-word commands to administer user accounts. Let’s change the password now.
passwd
Step 3: Add Super User
adduser super
If your Linux distribution does not prompt you to set the new user’s password upon creation (with most non-Debian-based distros), you’ll need to set it with this command:
passwd super
Once you’ve added the new user and assigned the new user a Unix password, go ahead and assign “sudo” permissions to this new user by opening the visudo file:
visudo
(If you see an error like “-bash: visudo: command not found“, your Linux distro, like Gentoo, does not already have “sudo” installed. You’ll need to install the sudo package before continuing. To install sudo on Gentoo, which is the only Linux distro I’ve found to not have “sudo” pre-installed, use the command: “emerge sudo”. Please see this Gentoo Wiki page for more information on installing sudo in Gentoo.)
At the bottom of the visudo file, add a line to allow the new “super” user access to the “sudo” commands:
…
root ALL=(ALL) ALL
super ALL=(ALL) ALL # add this line
If you are editing the visudo file using “nano”, you can hit ^O (that’s control+oh) to save our changes, then ^X (control-x) to close the file and return to the terminal prompt.
If you are editing the visudo file using “vi”, you’ll want to hit the “I” key to enable text insertion, hit the “ESC” key to disable text insertion, and when you’re happy with your changes, type “:wq” to save the visudo file and return to the prompt.
Note to Gentoo users: Gentoo does not automatically set up a “/home/<username>” directory for new users. No problem, you’ll just need to run the following commands to set the “super” user’s home directory up and assign it permissions:
mkdir /home/super
chown -R super:super /home/super
chmod -R 755 /home/super
chmod 750 /home/super
Step 4: Generate SSH Public and Private Keys
One effective way of securing SSH access to your remote server is to use a public/private key. This means that a “public” key is placed on the remote server and the “private” key is on our local machine. Now it is impossible for anyone to login using just your password - they must have the private key located on their computer, i.e., they must enter the correct password only from your local machine.
On your local machine now, open a new terminal window and make a directory for our ssh keys:
mkdir ~/.ssh
Now let’s generate the necessary ssh keys using “ssh-keygen” command:
ssh-keygen -t rsa
It’s up to you if you want to enter a passphrase, just in case you forget your password later. The default is to leave the passphrase blank.
Step 5: Upload Private Key to Remote Server
The “ssh-keygen” command generates 2 files on our local machine, the private key (”/home/super/.ssh/id_rsa”) and the public key (”/home/super/.ssh/id_rsa.pub”). We need to now upload the newly-generated public key “id_rsa.pub” to the remote server. To do this, I like to use a very fast, simple, and secure file transfer command called “scp”, which is pretty much just a “cp” copy command but using ssh.
From your local machine still:
scp ~/.ssh/id_rsa.pub super@222.22.33.333:/home/super/
When prompted, enter the “super” user’s password you created in Step 3 with the “adduser” command.
If the upload went well, you should see output like this:
id_rsa.pub 100% 421 0.4KB/s 00:00
The other file “id_rsa” without the .pub extension is your private key. You’ll want to keep this file safe, i.e., not available to the public.
Step 6: Set SSH Key Permissions
We made a directory on our local machine called ~/.ssh, now let’s make one on our remote server. Then we’ll move the public key to the remote ~/.ssh directory.
Go back to the other open terminal window, which should be still connected to your remote server as “root”, and enter these commands:
mkdir /home/super/.ssh
mv /home/super/id_rsa.pub /home/super/.ssh/authorized_keys
Now we can set the correct permissions on the key:
# Arch users enter this command:
chown -R super:users /home/super/.ssh
# All others enter this command:
chown -R super:super /home/super/.ssh# All users enter these 2 commands:
chmod 700 /home/super/.ssh
chmod 600 /home/super/.ssh/authorized_keys
Step 7: Change Default SSH Configuration
Next we’ll change the default SSH configuration to make it more secure:
nano /etc/ssh/sshd_config
Make sure these settings are in the sshd_config file and are uncommented:
Port 33333 # anything but default port 22
Protocol 2
PermitRootLogin no
PasswordAuthentication no
X11Forwarding no
UsePAM no
UseDNS no # you may have to add this line to the file
AllowUsers super # you will have to add this line to the file
Here we are telling the SSH configuration file that we only want to be allow user “super” to connect via SSH on Port 33333 (you can make this anything you like, just remember it!). The “root” user will no longer be able to sign in using SSH, which is what we want. Hint: if you want to add more users to “AllowUsers”, just separate their names with a space (not a comma!)
Alright! We have now set up basic security settings for logging in to our remote server using SSH. Although we could stop here and SSH would work (after a “/etc/init.d/sshd reload” command), but we really should set up a simple firewall at this point. Let’s do that now.
Step 8: Setup a Basic Firewall using “iptables”
Right now our remote server is wide open for potential hacker attacks. Let’s take a few moments to setup a simple firewall to block unused and hacker-happy ports. We’ll do this by setting up the “iptables” rules for IPs and ports. After this is done, our new Linux will be much more secure with only 3 ports open - http, https, and ssh.
We’re going to create two new files, /etc/iptables.test.rules and /etc/iptables.live.rules. The first contains a set of temporary testing rules, the second set are the actual iptables rules that we want to use for the remote server, and be reinstated after remote server reboot.
If you are currently logged in as a user other than “root” at this time:
sudo -i
Gentoo users will need to install “iptables” using the Portage package manager command “emerge”:
emerge iptables
CentOS users will need to install “iptables” using the Yum package manager command “yum install”:
yum install iptables
Now, signed in or acting as the root user, and knowing that we have iptables installed, let’s see what rules are running now:
iptables -L
You should see something like this:
Chain INPUT (policy ACCEPT)
target prot opt source destinationChain FORWARD (policy ACCEPT)
target prot opt source destinationChain OUTPUT (policy ACCEPT)
target prot opt source destination
Let’s go ahead and save all existing rules to a new file called /etc/iptables.live.rules:
iptables-save > /etc/iptables.live.rules
Now create the file /etc/iptables.test.rules and add some rules to it. Make sure to set the port number to the port number you set in the sshd_config file:
nano /etc/iptables.test.rules
Now that we’ve defined our (test) rules, let’s apply those rules to our remote server:
iptables-restore < /etc/iptables.test.rules
Now let’s see if there is a difference:
iptables -L
You should see an output like this:
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT tcp — anywhere anywhere state NEW tcp dpt:33333
ACCEPT 0 — anywhere anywhere
REJECT 0 — anywhere 127.0.0.0/8 reject-with icmp-port-unreachable
ACCEPT 0 — anywhere anywhere state RELATED,ESTABLISHED
ACCEPT tcp — anywhere anywhere tcp dpt:www
ACCEPT tcp — anywhere anywhere tcp dpt:https
ACCEPT icmp — anywhere anywhere icmp echo-request
LOG 0 — anywhere anywhere limit: avg 5/min burst 5 LOG level debug`prefix `iptables denied: ‘
REJECT 0 — anywhere anywhere reject-with icmp-port-unreachableChain FORWARD (policy ACCEPT)
target prot opt source destination
REJECT 0 — anywhere anywhere reject-with icmp-port-unreachableChain OUTPUT (policy ACCEPT)
target prot opt source destination
ACCEPT 0 — anywhere anywhere
If there is no change in your output from before, a step was missed somewhere. No problem, just try again from Step 8. But if your screen looks similar to this, that’s good - well done so far!
Once you are happy with the (test) rules, it’s time to save/overwrite our rules permanently to /etc/iptables.live.rules:
iptables-save > /etc/iptables.live.rules
We’ve saved our iptables rules, but when we reboot the server, we’ll lose all our rules. So let’s add a runlevel command to restore our iptables.live.rules at boot up.
Automatic Loading of “iptables” Rules at System Boot
Runlevel commands are handled a little differently in each Linux distro base, some having rc management scripts pre-installed, like Gentoo’s “rc-update”, Red Hat/Fedora/CentOS’s “chkconfig”, “service” and “lokkit” scripts, and Debian/Ubuntu’s “update-rc”, but all of this can get confusing! So I like to take the BSD approach and keep it as_simple_as_possible by manually editing the /etc/rc.local file to set my init commands. This way, no matter which Linux distribution I’m using, I’ll know where to set my custom runlevel commands - nice!
Gentoo users will need to add a symlink to Gentoo’s “rc.local” equivalent file called “/etc/conf.d/local.start” before making any edits to it:
ln -s /etc/conf.d/local.start /etc/rc.local
Now let’s open and edit the file /etc/rc.local:
nano /etc/rc.local
Add a single line to the file at the bottom, then save and exit the text editor (^O return ^X):
iptables-restore < /etc/iptables.live.rules
As you can see, this line will restore the iptables rules from the /etc/iptables.live.rules file. By putting this in the common Linux file “rc.local”, we are assured that the script is run after all other init scripts. This is a simple and effective way to reload your iptables on system reboot with almost any Linux distro.
There are some great tutorials that go into much more detail about iptables. There are two that I find myself referencing a lot: Linux Home Networking .com has a great iptables wiki tutorial, and Gentoo’s wiki has HOWTO_iptables_for_newbies. For more information on the Linux rc.d system, check out this page.
Step 9: Reload SSH To Activate Firewall
We need to test our new firewall by reloading ssh so it uses the new ports and configurations in the rules file. Don’t worry, you won’t be logged out of your ssh session as the “root” user, which is good because it allows you to stay logged in as “root” in case there are errors in the visudo, sshd_config, and/or iptables files that we have edited in this tutorial.
For Debian-based distros, including Ubuntu:
/etc/init.d/ssh reload
For Red Hat-based (Fedora, CentOS), and Gentoo-based distros:
/etc/init.d/sshd reload
For Arch-based distros:
/etc/rc.d/sshd restart
Make sure you keep your current terminal window open. We may need it just in case something goes wrong. Let’s open a new terminal window (splat-T) and leave this one alone for the time being.
Step 10: Login As Super User (without a password!)
For this final step, please open a fresh terminal window on your local machine.
Now that we have all the remote server settings, we can login from our local machine to our remote server with the new “super” user account we’ve added. Make sure to use the port number you specified in the sshd_config file in Step 7, as well as the correct “super” user name you created and remote server IP address.
ssh -p 33333 super@222.22.33.333
If everything is successful, you should immediately see your remote server terminal prompt, and you didn’t have to enter a password! Woohoo!
Last login: Sat Mar 02 16:24:55 on ttys000
super@222.22.33.333:~$
Done!
We now have a working firewall on our newly-installed Linux server distro that we can securely log in to from our local machine without having to enter a password every time.
Now on to Part 2 of the series “How to setup a Ruby on Rails on Linux web hosting server” where we apply some updates and start installing some standard programs…
























Marty said,
Wrote on March 22, 2008 @ 6:00pm
This tutorial is great! Thanks for being so thorough and clear. I reference this page immediately after I install a Linux distro on a new “slice” or a client’s vps. Makes it very SIMPLE! :-)