Running your own web server is the ultimate level of technological freedom. Having a server to call your own allows you to do all kinds of things:
- Keep all of your files backed up in the cloud
- Host an email server
- Open a remote connection to your home network
- Run a private or public API
- And so much more!
In this tutorial, you’ll get a CentOS server set up with Linode, a cloud hosting company similar to Rackspace or Amazon Web Services. You’ll configure your server with a few bells and whistles and implement some simple security measures to help minimize your risk to outside attackers.
Note that Linode is a paid service and you’ll need to sign up for an account and pay to provision a new server instance to continue.
Why Linode and CentOS?
In contrast to Amazon’s AWS, Linode is a straightforward, fixed-cost hosting package. It fills the need for people who just need a small server up and running in minutes. Contrast this to getting an AWS EC2 instance for computing coupled with an EBS instance for disk storage, configuring them to talk to each other, routing traffic correctly, securing them, and so forth.
And most importantly, Linode is faster for basic operations.
CentOS stands for “Community Enterprise Operating System” and is considered to be an Open Source alternative to the highly popular Red Hat Enterprise Linux distribution used in most large companies. It offers the robustness and scale of Red Hat with the user-friendly features and great out-of-the-box support that Fedora and Ubuntu are known for.
Head to linode.com and click Sign Up in the top right corner of the header bar below:
Fill out the standard account details, such as user name, address and credit card info.
Select the first plan available: Linode 1024. This base option is enough to power a simple web site and is enough to get you started for the purposes of this tutorial. You can always instantly upgrade later should you run out of storage or bandwidth.
Click Continue, agree to the Terms of Service – after reading them, of course :] – and complete your order!
Creating your account can happen instantly, or it may take up to 15 minutes, depending on the options you selected during set up. Once you are notified that your account is ready to go, log in to Linode again and you’ll be prompted to pick an initial setup for your server.
The first question you’ll come across is picking a Data Center. It’s important to pick a location that is relevant to your usage. Typically you’ll want a location close to your users or close to your office to get the best response times.
Click Place this Linode here on the location you prefer, as shown below:
If you can’t be troubled to decide, pick Dallas as it’s middle of the road in the USA!
Next you’re provided with several options for customizing your server. Pick CentOS 6.4 from the Distribution drop-down. Next, since you’re paying for this server, you want to get the most out of it to meet your needs so set the Swap Disk to 512MB and max out the remaining Deployment Disk Size as shown in the screenshot below:
This configuration sets up your server with the most disk space possible for your configuration level.
Finally, create a root password for your server — make sure it’s a a good one! — then click Rebuild.
Your server is now being built to order! Watch the Host Job Queue section; once all of the tasks have completed with a “Success” result and the spinner dust settles, click the big Boot button in the Dashboard section shown below:
You now have your very own piece of real estate on the Internet! :]
You’re now ready to configure the server to your liking, including connecting to it remotely, securing the system, setting up LAMP, and getting a basic webpage up and running.
Time to get to work!
Connecting to Your Server
Click on the Remote Access menu at the top of the page; under the Public Network section you’ll see a list of instructions on how to connect to your server. Fire up Terminal (on a Mac) or a program like PuTTy (on a PC) and type in the appropriate command shown in the SSH Access row.
Note: SSH stands for Secure Shell; it’s a protocol just like HTTP and FTP that’s used to securely connect to another computer. Typically, you’ll use Terminal and SSH to connect to other computers that don’t provide a visual interface to the OS.
Each time you connect to a server, SSH checks that the server’s key is the same as last time. Since this is the first time you’ve logged into your server, SSH prompts you to check the key and add it to its key database. Since you’re an expert computer user and nothing could possibly go wrong — famous last words, perhaps? — just type yes and hit Enter, like so:
Now, simply enter the root password you created when you set up the server, and you’ll be sitting at the command prompt, as shown below:
Naming Your Server
The first thing you need to do is give your server a meaningful name — like a newborn baby!
The name can be anything, but it should be unique. Many times servers are named after collection of things that are near and dear to the creator of the server – like planets, Roman or Greek gods, cities, or Pokemon characters.
In corporate environments, server names are typically more boring but their name serves a purpose as it usually includes the server location and function. For example, “prodweb01″ could mean “production web server #1″.
Choose a server name that has meaning to you. This tutorial pays homage to the lovable fallen planet, Pluto, so just replace ‘pluto’ with your server name in the examples that follow. There are three steps to set your server name — make sure you execute them in order and don’t skip one by mistake!
Enter the following command at your shell prompt:
echo "HOSTNAME=pluto" >> /etc/sysconfig/network
This above command updates a critical network file /etc/sysconfig/network on your server with your chosen server name.
Enter the second command as follows:
This sends an alert to all processes listening for updates from the
hostname process on the server that the servername has changed.
Finally, you’ll need to edit a configuration file on the server. Enter the third and final command as below:
This launches the nano text editor. /etc/hosts controls how Internet traffic is routed into and through your server.
Using the text editor, arrow down to the bottom of the file and add a new line. Enter the IP address of your server, hit the Tab key, then enter the name of your server. You can find your server’s IP address from the Linode dashboard (under the Remote Access tab).
For example, if your server’s IP is 192.168.99.42, enter
192.168.99.42 pluto in the hosts file. Hit Control-X (not Command-X) to exit the editor; press y to save your changes, then hit Enter to overwrite the existing hosts file.
Note: If your server will host a domain (like howtosetuplinode.com), then you’ll have to specify your servername in the hosts file as “server-name.domain-name.extension”. This is known as a fully qualified domain name, or FQDN. This tutorial does not require a FQDN, so if you don’t have a domain yet, don’t worry.
To test your new setup, exit your current SSH session:
Then SSH back in again, using the same command you used earlier. Your prompt should now show your new server name, like so:
Also try to ping your server like this:
It should ping your local machine, proving it works. Hit Ctrl-C to stop ping and return to your SSH session.
Final Server Setup
By default, the server timezone is set to GMT; it’s up to you to set your local time zone.
Enter the following command:
This launches the following text-based Timezone Select Tool:
Simply answer the questions about your location one by one, and at the end of the process the server prompts you to confirm that the time is setup correctly.
Of course, you’ll want to have the latest and greatest versions of all software packages installed on your shiny new system.
Run the following command and accept the updates one by one as the system prompts you for confirmation:
yum is the gateway to package management and installation on CentOS. You should run the update command on a weekly or monthly basis to keep your system safe by eliminating known vulnerabilities in installed software packages.
Creating a Second User
Up to this point, you’ve been using
root to perform all changes on your server.
root is the super-admin account on your server and has permission to do anything and everything/ This makes it really easy to configure your server, but it also makes it easy to do bad things and cause disaster down the road.
A tenet of good security is to use the lowest level of permissions possible for an action. This protects the system in case an account gets compromised. Hence, it’s a best practice to create a second user account for most tasks, and then to switch to the root user only when needed. This adds one more level of safety and security to your server.
Enter the following command:
This creates a new user with the name
Next, enter the following command:
The passwd program both unlocks and sets a new password for the user. Just as you did when setting the root password while creating the server, make sure this password is complicated and difficult to guess or crack.
User accounts that are allowed to act as
root have what are known as sudo permissions. The name comes from the program of the same name, which performs a temporary upgrade to your permissions to perform an action, and then returns to your ordinary permissions when you’re done.
Enter the following command at the prompt to start the text editor:
Go about two-thirds of the way down the page and find a line that looks like
root ALL=(ALL) ALL. Add the following line below that:
remote_user ALL=(ALL) ALL
Your file should look like the following:
Save and close the file by hitting Control-X.
It’s time to say goodbye to your omnipotent days of using
root and embrace the loving embrace of your newest user:
remote_user. Log out of the Linode server by typing
exit in Terminal.
Securing Your Server Connections
The next step to a more secure server experience is setting up an SSH key pair. This means the SSH connection knows who you are based on your secret key. The key is also protected with a passphrase which you’ll need to unlock your key.
Ensure that you’ve logged out of your Linode server and that you’re back to your local machine in Terminal.
The good new is that you may already have a key if you use SSH, or use a key to push to services like GitHub.
You can find out if you already have a key by executing the following command:
If you see id_rsa and id_rsa.pub listed, then you’re all set! You can skip the next step of creating a key pair.
However, if you don’t see any files listed or get a “No such file or directory” error – you’ll need to create your own key.
Enter the following command at the shell prompt:
Hit Enter to save the files in the default location provided and be sure to use a good passphrase. A passphrase is much like a password, but as the name suggests, can be a phrase or sentence for extra security.
The above command creates two files in a hidden directory on your file system in ~/.ssh named id_rsa and id_rsa.pub. id_rsa.pub is your public key and can be given out freely. id_rsa is your private or secret key and it’s critical that you not share this file with anyone! As an extra line of defence, the private key is secured with the passphrase.
Now that your keys are generated, the next step is to let your Linode server know about the public key.
Enter the following command:
scp ~/.ssh/id_rsa.pub firstname.lastname@example.org:
Replace the IP address above with the IP address of your sever, and don’t forget about the colon at the end of the command. Usually after the colon you’d list the path to save the file, but if you leave it blank it will save the file in the user’s home directory.
scp stands for “secure copy” and copies the public key file to
remote_user‘s home directory.
Now reconnect to your server, but as your new user this time:
Again, fill in your server’s actual IP address instead of 127.0.0.1 above.
Enter the following commands to set up your key file on the server:
mkdir .ssh mv id_rsa.pub .ssh/authorized_keys
Just as the key files on your computer were in the
.ssh folder, the key files on the server need to live in a matching directory.
mkdir stands for “make directory” and creates a new .ssh folder on your server.
em moves the public key file from your computer into authorized_keys, which contains a list of all the public keys that can be used to log in as this user. You can add more keys here later to allow other other people to log in to this server.
Enter the command below:
chown -R remote_user:remote_user .ssh chmod 700 .ssh chmod 600 .ssh/authorized_keys
Here you ensure only the right people have access to the .ssh folder and its contents.
The first line uses
chown (“change owner”) to recursively set the owner of all files in the .ssh folder to
remote_user. The next two lines use
chmod (“change mode”) to set the permissions on .ssh to only be accessible by remote_user, and likewise for the authorized_keys file.
Note: For more details on Linux file permissions, check out the official documentation from Linux.org.
Okay — here’s the acid test. Type
exit in Terminal and log back in: you should no longer be prompted for your server password! As an added bonus, Keychain can save your passphrase, so simply logging into your server as
remote_user account will log you in automatically.
As you expand your server empire, you can just copy your public key to each server using the same method; that way, your single key and passphrase will give you access to all of your servers.
Lock Down Remote Access
Log back in to your Linode server. Enter this command at the prompt:
sudo nano /etc/ssh/sshd_config
This step makes it more difficult for malicious users to find your server and make their way in.
Notice the command in front: sudo. This tells the system you’d like to temporarily escalate your permission to execute this next command.
In this case, you’re opening a text editor to modify the SSH configuration file, which only the root user is allowed to do. Modify the file as instructed below:
- Find the setting
PasswordAuthenticationand change it from
no. This disables password logins so everyone will need to log in with key pairs.
- Find the setting
#PermitRootLogin, remove the leading
#character, then change the setting from
no. This prevents the root user from logging in to the server directly.
- Find the line with
#Portand remove the leading
#character. Change the value from
22to any value between
65536; I chose
23456as it’s easy to remember. This changes the default port to make it more difficult for strangers out there to find your server.
- Go to the very bottom of the file and add the line
AllowUsers remote_user. This explicitly tells the SSH service to let your user in regardless of other settings.
Save and exit, then restart the SSH daemon to reload your configuration file as follows:
sudo /etc/init.d/sshd reload
To test that it works, open a new Terminal tab with Command-T (you want to keep your old tab open still logged in to the server, in case you made a typo with your SSH configuration). Then make sure that you can no longer log in as root by issuing the following command:
ssh -p 7415 email@example.com
Replace 127.0.0.1 with your server’s IP as usual. You should now get an error that says “Permission denied (publickey,gssapi-keyex,gssapi-with-mic).”.
Now repeat this with the user that is allowed to log in with SSH:
ssh -p 23456 firstname.lastname@example.org
Replace 127.0.0.1 with your server’s IP, and 23456 with the port you chose. If you successfully get in, your SSH is now locked down!
Setting up Your Firewall
Once you’ve taken care of server access, your next step is to set up a firewall to filter out undesirable network traffic such as bots and people trying to gain unauthorized access to your server. A tool called Fail2Ban lets you to setup rules to ban undesirable network traffic from even talking to your server – the traffic your mother warned you about. :]
Execute the following command:
You could edit the firewall rules directly by hand, but it’s quite complicated and the wrong move can open your server to attacks — or worse, prevent you from logging in! Using
system-config-firewall-tui to make changes is slightly easier to use as it’s a text-based GUI. You use the tab and arrow keys to navigate and the space bar to make a selection – press once to select, press again to de-select.
The first screen has a fairly obvious option to enable the firewall, as shown below:
Press the spacebar to select this option; you’ll see a * appear beside the option. Use the arrow keys to reach the Customize button and hit the spacebar again to select it.
Select the following protocols to allow through the firewall:
- IPSec – allows the IP Security protocol to authenticate/authorize certain connections
- SSH – allows you to remote into your server (like you are doing now)
- Secure WWW (HTTPS) – allows HTTPS traffic
- WWW (HTTP) – allows HTTP traffic
As your server needs grow, you may revisit this list and allow other protocols through; for example, SMTP and IMAP/POP3 for email hosting, or DNS to run your own name server. Select Forward and move onto the next page,
Arrow over and select Add to add a custom port. Now that you have SSH running on port 23456, you need to tell the firewall to allow connections on that port — otherwise, you won’t be able to log in. Set the Port to 23456 and the Protocol to tcp like so:
Select OK to add the new custom port and then select Forward to the next page.
Because this is a fairly basic server setup for web hosting, you don’t need to expose any networks on the Trusted Interfaces screen. You’d typically allow eth+ if this server was going to act as a traffic filter or router for other servers on your network, but you don’t need to change anything for this tutorial. Select Forward to get to the next page.
The same is true for this screen – all options can be left blank. Masquerading lets multiple servers appear to be coming from one address, known as NAT, or Network Address Translation) Again, this is a simple server so you don’t need this. Forward on!
You won’t change anything on this page either but it’s important to know what this screen is for. You can add an entry on this page if you need to map one port to another.
Why would you do this? Well, say you have an application that only looks for an SSH connection on port 22, but you changed yours to 23456. With an entry on this page, you could say port 23456 actually maps to port 22. Any traffic that came from port 23456 would be sent to port 22 and your application would work as expected.
You don’t need to set this up right now, so select Forward to move on.
The ICMP Filter page actually has something you’ll change! Yippee!
This screen lists several types of protocols used by your server to communicate information about itself to the outside world. Enable the Destination Unreachable and Source Quench protocols; the first one tells other servers that your server is not available for communicating. The second one helps optimize bandwidth and is used by network routers to request that servers speed up or slow down their data rates.
Select Forward from this screen and then Close. You’re returned back to the main screen, so hit OK and accept the warning about overriding Firewall rules.
Exit your SSH session and log in again to make sure your access is still OK with these new firewall rules.
Whew – another piece finished!
Fail2Ban automatically prevents people from connecting when they have too many failed login attempts. Malicious automated bots out there often try thousands of username and password combinations and tie up your server; Fail2Ban stops these attempts early on.
Enter the following commands at the shell prompt:
sudo rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm sudo yum install fail2ban
Fail2Ban isn’t available in the usual package repositories that CentOS ships with. The first line lets the system know about the Extra Packages for Enterprise Linux (or EPEL) repository, which includes many useful third-party libraries and utilities.
The second line then installs Fail2Ban itself. Accept the prompts to install the package and associated dependencies.
Next, set up the configuration file with the following commands:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local sudo nano /etc/fail2ban/jail.local
Fail2Ban comes with some default configuration settings in jail.conf, but you don’t want to modify it directly because upgrading Fail2Ban later may overwrite this configuration file. To keep things clean, make a copy called jail.local which Fail2Ban reads as local customizations.
Once jail.local opens in the editor, find the line starting with
ignoreip. If you have a static IP, or at least one that doesn’t change very often on your end, you can add it to this list to stop Fail2Ban from blocking you if you have too many failed logon attempts. You can Google “what is my ip address” and it will give it to you in the search results. Google FTW! :]
The line will look similar to the following once you’re done editing:
ignoreip = 127.0.0.1/8 184.108.40.206
Next, find the line starting with
bantime and change it as follows:
bantime = 3600
If a system fails to authenticate itself after a certain number of attempts, Fail2Ban locks them out for 600 seconds by default. 10 minutes is not a lot of time for a persistent attacker, so this new value of 3600 bumps that up that to an hour.
Save and close your file. Since this is a fresh install of Fail2Ban, start it manually with the following command:
sudo service fail2ban start
In the future, you can edit the configuration file and use the command above with “restart” in place of “start” to have the Fail2Ban service start automatically.
If you want some late night reading material, the jail.local file defines ban parameters for every protocol on the server (Apache, FTP, etc) and examples of how to set up different options for each one for the ultimate customization possibilities.
LAMP servers – Linux, Apache, MySQL, and PHP – are the de facto configuration these days and power everything from simple blogs and websites like the one you are building in this tutorial all the way up to infrastructure that companies like Facebook and Twitter built their legacy on.
Enter the following command at the prompt:
sudo yum install httpd
This installs the Apache web server. CentOS names the package httpd after the name of the executable, while other Linux distributions might call it Apache. Don’t worry, the names all refer to the same thing. :]
Next, you’ll need to set up the configuration file. Execute the commands below:
cp /etc/httpd/conf/httpd.conf ~/httpd.conf.backup sudo nano /etc/httpd/conf/httpd.conf
Just like you did for Fail2Ban, first copy the default configuration to a backup file in case you need it in the future. Once you have the backup, you then open httpd.conf in a text editor.
In httpd.conf file, find the section for the
prefork module as shown below:
<IfModule prefork.c> StartServers 2 MinSpareServers 6 MaxSpareServers 12 ServerLimit 128 MaxClients 128 MaxRequestsPerChild 3000 </IfModule>
Change the values in your file to match the list above. These values are optimized for Apache running on the base Linode server and allow for a low number of active services/threads but allow the server to ramp up when the traffic starts flowing in.
Finally, find the
ServerName line in your file, and uncomment it by removing the
# character at the start of the line and change the default value to
localhost. Your line should look like the following:
Save the file and exit the editor by hitting Control-X. Then start Apache with the following command:
sudo service httpd start
Open up your favorite web browser and browse to the IP address of your server to see your new web site in all its glory!
That’s the L and the A — there’s only two more letters to go in LAMP.
Enter these commands at the prompt to install MySQL:
sudo yum install mysql-server sudo service mysqld start sudo /usr/bin/mysql_secure_installation
Getting MySQL up and running requires only these three lines. It doesn’t get much easier than that! :]
The first line above installs the package and any required dependencies. The second line starts up the MySQL service. And finally, the third line runs a configuration script to secure the installation of MySQL.
When running through the
mysql_secure_installation steps, you’ll be prompted for a root password for MySQL — just hit enter as you don’t have one yet. You’ll be prompted to create one, so do so. Say yes to all of the installation prompts, such as remove anonymous users, drop test database, and all others.
You might be wondering whether to allow incoming MySQL connections through your firewall. This is generally a bad idea for security reasons. The only thing connecting to your database should be your applications on your server. Allowing random strangers from the Internet to touch your MySQL database directly is a bad idea.
Up to this point, you’ve been installing software and starting it up manually. If your server is rebooted though, you’d have to manually start all the services you installed such as Apache and MySQL.
To save you the headache, enter these commands at the shell prompt to set your services to automatically start:
sudo chkconfig fail2ban on sudo chkconfig httpd on sudo chkconfig mysqld on
The chkconfig program is short for “check configuration” and sets up the services that should start automatically.
PHP is just as easy to install. It doesn’t have its own server, but it’s called from Apache so you don’t have to worry about starting up another service.
Install PHP with the following command:
sudo yum install php php-mysql php-pear
Notice that there are a couple packages specified in the
yum call above:
- php – base install of PHP.
- php-mysql – PHP components specifically for interacting with MySQL.
- php-pear – an extension repository that offers many helper components.
PHP requires some tuning in order to be more secure, developer friendly, and performant. Open up the default PHP configuration with the following command:
sudo nano /etc/php.ini
Edit the three lines shown below in your version of php.ini:
error_reporting = E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR error_log = /var/log/php.log max_input_time = 30
Each of these changes are found in different locations in php.ini, but instead of scrolling around the file looking for them, you can hit Control-W in the text editor to search and find the configuration keys such as “error_reporting” and “max_input_time”.
Here’s what each change above does:
- error_reporting – sets what kind of errors to show. There are several categories of errors, notices, and warnings; you can combine them with the
|character as shown.
- error_log – sets the location of the log file. While your web site is running, you can check this file to check for errors.
- max_input_time – sets the maximum time a script to run. Setting a value here helps prevent runaway scripts from taking up all the processor time and slowing down the server.
Save and close the file when you’re done. Again, PHP will be called from Apache so there’s no separate PHP service to start. However, you do need to restart the web server so that it detects the new PHP installation, so restart the web server with the following command:
sudo service httpd restart
To confirm that your LAMP stack is running correctly, you need a basic web page to replace the generic Apache test page.
Open up the default web page in a text editor:
sudo nano /var/www/html/index.php
Modify the contents of the file to look like the following:
<h1>Hello World!</h1> <p>LAMP on Linode checking in! :]</p> <?php echo "<p>And don't forget about PHP.</p>"; ?> <p>- Love, RayWenderlich.com</p>
Be creative here and show off your HTML and PHP skills! Enter your server’s IP address in a browser and you’ll see this message instead of the Apache start page.
Setting Up Your Domain
No one these days says “check out 127.0.0.1″. Although 127.0.0.1 is where the ♥ is…ha! Okay, sorry, bad geek joke.
People want to use domain names like HowToSetupLinode.com instead. It’s also good for business, branding, and easy to remember.
Luckily, getting a domain name is quick and simple!
Once you’ve registered the domain name, view the domain properties by selecting the Launch button on the right under the
Action section, shown below:
Select the DNS Zone File tab and click the Edit button, like so:
Edit the A (Host) record and change the IP address to the address of your Linode server as shown below:
NS (Nameserver) section and add 4 entries for Linode’s name servers as
Set the Points To address to an “@” sign – this tells the Nameserver to go look at what is defined for the “@” address set as the A (Host) record in the previoius step — this points to your Linode server.
Your configuration should resemble the screenshot below:
Save the changes via the big black button in the top center of the window and we’re done with GoDaddy.
Head to Linode.com and log in. On the DNS Manager tab, click the
Add a domain zone option, like so:
Enter the domain name you want routed to your server and add a contact email in the event there are any issues with routing traffic, as such:
Finally, click the
Add a Master Zone button, and that’s it! A new domain name can take up to 48 hours to propagate over the internet, if your domain doesn’t resolve right away, be patient; you can always use the IP address in the meantime.
Where to Go From Here?
Congratulations! You now have your very own LAMP server configured with some security best-practices, and you can host a basic website on your own. From here, the sky’s the limit!
If you want to create a more robust website but don’t have the necesasry HTML/JS/CSS chops, Twitter’s Bootstrap framework is extremely popular and comes ready for mobile, tablet, and desktop websites out of the box. Likewise, WordPress offers the ability to host your own blog on your server.
If you want to learn more about CentOS system administration, the CentOS 6 Linux Server Cookbook offers a no non-sense look at building and running a CentOS server. Alternatively, if you’d prefer a straight-from-the-source guide, Red Hat has created a System Administration Guide that discusses every detail of working with CentOS.
Hopefully you enjoyed the tutorial; if you have any questions or comments, please let us know in the forum discussion below!