How to setup Ubuntu Hardy 8.04 web server hosting multiple domains
Let’s get up and running with a secure Ubuntu Hardy server that hosts multiple domains, all from the command line via ssh. This tutorial goes through the complete setup of everything you need to host and run your database-driven Web 2.0 applications on Ruby on Rails, PHP, Python, and more.
—————————————————
INITIAL SETUP - SECURE UBUNTU SERVER
Log in to your Ubuntu Hardy command line via ssh:
ssh root@123.45.67.890
Change “root” user’s password:
passwd
Add a non-root user. This can be anything, but for this and most crazyrails.com tutorials, we’ll create a user named “super”:
adduser super
Optional:
If you run a list command on a fresh install of Ubuntu 8.04 from here, it looks like this. You’ll notice only “hidden” files - your bash environment stuff and your aptitude package manager for application installations:
ls -al
drwxr-xr-x 3 root root 4096 Apr 23 12:57 .
drwxr-xr-x 21 root root 4096 Jul 9 17:11 ..
drwx—— 2 root root 4096 Jul 9 11:42 .aptitude
-rw——- 1 root root 28 Aug 15 20:02 .bash_history
-rw-r–r– 1 root root 2227 Oct 20 2007 .bashrc
-rw-r–r– 1 root root 141 Oct 20 2007 .profile
Optional:
Set “nano” as your default text editor. This way you can use nano to edit the visudo file (instead of “vi”, which sucks). First, you need to open your “.bashrc” profile in a text editor - hey, how about “nano”. Second, you need to set your $EDITOR environment variable to “nano”. Third and finally, you need to refresh your environment variables by calling source on your bash profile. Note: Ubuntu Hardy comes with “nano” preinstalled - woohoo!
nano .bashrc # to edit your bash profile
export EDITOR=nano # add this line, then save and close
source .profile # to refresh bash profile environment variables
Optional:
Mac users take note: If you want “nano” to recognize your “delete” button as a “backspace” button (you do, trust me), you can edit your “nano” configuration file. Open the “nanorc” file under /etc, scroll down a ways to where it says “## Fix Backspace/Delete confusion problem. # set rebinddelete” and simply take out the # sign in the “# set rebinddelete” line, then save and close the file:
nano /etc/nanorc # open nano config file
set rebinddelete # uncomment this line, then save and close
Add user privileges for your non-root user to your “visudo” file:
visudo
Scroll down to the bottom and add this user privilege specification, then save and close:
super ALL=(ALL) ALL
While we’re here, let’s generate our locale and set it:
# generate
locale-gen en_US.UTF-8
# set it and forget it
/usr/sbin/update-locale LANG=en_US.UTF-8
—————————————————
SSH
Now we need to do some things to securely login via SSH.
First, on the Ubuntu REMOTE MACHINE, we need to create a “.ssh” directory for this super user and set Unix permissions on it:
mkdir /home/super/.ssh
mkdir /home/super/.ssh/authorized_keys
chown -R super:super /home/super/.ssh
chmod 700 /home/super/.ssh
chmod 600 /home/super/.ssh/authorized_keys
Second, on your LOCAL MACHINE, we need to pretty much do the same.
If you don’t already have a “~/.ssh” directory on your local machine, you’ll need to make one:
# ON LOCAL MACHINE
mkdir ~/.ssh
If you don’t already have an id_rsa keypair on your local machine, you’ll need to create one:
# ON LOCAL MACHINE
ssh-keygen -t rsa
Now you need to upload your id_rsa.pub file to your Ubuntu Hardy system from your local machine:
# ON LOCAL MACHINE
scp ~/.ssh/id_rsa.pub super@123.45.67.890:/home/super/.ssh# output should look something like this:
> id_rsa.pub 100% 421 0.4KB/s 00:00
Back on your Ubuntu REMOTE MACHINE, make sure the “id_rsa.pub” file has been uploaded to the correct directory, and go there:
cd /home/super/.ssh && ls -al
At this point, you’ll want to move the freshly-uploaded key to the “authorized_keys” subdirectory. (Why didn’t we just upload directly to this directory? Well, if you think about it, it’s more secure this way - a human has to control if an uploaded key “makes it” to the authorized_keys path - keeps hackers from being able to upload/transfer files directly to your so-so-secure key path :-)
mv id_rsa.pub authorized_keys/id_rsa.pub
Now we need to edit a few settings in the SSH configuration file:
nano /etc/ssh/sshd_config
# things to set
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# Optional - if you wish to change your port from the default 22 here, make sure to match it in the iptables_rules file (below, as iptables -dport variable), and also keep in mind that changing your SSH communication port effects other communication applications you may use as well, like FTP, SCP, SVN, GIT, etc. Some (most) programmers (hackers) would care to say (laugh) that changing your port won’t increase security and mostly it just creates more pain for the intended programmers, but it’s up to you if you feel more secure :-)
Port 33333
—————————————————
IPTABLES
Now we need to setup a basic firewall so that the world doesn’t hack our Ubuntu Web Server. I want to keep this section as simple as possible, so if you’d like more information on the use of “iptables”, check out this other crazyrails.com blog post.
First, save the live iptables_rules file (which is probably blank):
iptables-save > /etc/iptables_rules.live
Second, create an iptables rules file for testing:
touch /etc/iptables_rules.test
Third, copy+paste from this iptables_rules.test example file and make sure your iptables_rules.test file looks like it. This can be (is) a pain in the ass…
Why? If you try to copy+paste into nano from a text file, nano has a tendency to rewrite your text (bummer). I also tried uploading my own iptables_rules.test file and moving it into the /etc directory as root, but that doesn’t work either (it fails). I’ve tried everything I can think of to make this step as simple as possible, so the only way I’ve been able to successfully write an iptables_rules.test file AND get it to work with iptables happily is to copy and paste into nano and then edit the quirks that nano throws up. So a normal copy and paste probably won’t work, but a Copy+Paste+Fix should do the job. Hint: all iptables lines start with either a comment # sign or a directive “-A”. There can only be one “-A” directive per line!!!! Also, (hint hint) lines cannot start with – hyphens either, only “#” or “-A” !!!!
# 1. Open iptables_rules.test example file in a separate window
# 2. Select all content with control+A, then copy it with control+C
# 3. Open your iptables_rules.test file with “nano”
nano /etc/iptables_rules.test
# 4. Paste the content with control+P
# 5. Make sure your “-dport” variable matches the port used for SSH (above)
# 6. Edit your file to match the example file, then save it and close nano
Fourth, load your test rules into iptables, making them ACTIVE:
iptables-restore < /etc/iptables_rules.test
Fifth, check active iptables to make sure they are there:
iptables -L
Sixth, once we are happy with our active iptables, we should save them to our “live” rules file:
iptables-save > /etc/iptables_rules.live
Seventh, although our iptables are now tested, active, and saved, if we reboot the server, they’ll die. So now we should point Ubuntu to our iptables_rules.live file upon boot-up. Ubuntu makes this very easy by loading the “rc.local” file upon every boot. We just have to add one line to /etc/rc.local and we are done:
nano /etc/rc.local
# comment out the line “exit 0″, or simply delete it
# exit 0
# add this line, then save the file and close nano
iptables-restore < /etc/iptables_rules.live
Finally, we can REFRESH SSH on our remote server and TEST IT by logging in as “super” using SSH on our local machine.
/etc/init.d/ssh reload
—————————————————
DON’T LOGOUT OF “ROOT”
UNTIL SIGNING IS AS “SUPER” SUCCEEDS
—————————————————
# ON LOCAL MACHINE
ssh super@123.45.67.890
# or, if you’ve changed the default port
ssh -p 33333 super@123.45.67.890
If you are able to login from your local machine to your remote Ubuntu server without it prompting you to enter a password, then you’ve succeeded. Now you may REBOOT your Ubuntu Hardy server by typing “reboot”.
Once your server is finished booting, you should be able to login (again) via SSH using this simple sweet command from your LOCAL MACHINE:
ssh super@123.45.67.890
# or, if you’ve chosen to change the default port
ssh -p 33333 super@123.45.67.890
—————————————————
LOGIN TO SECURE SERVER: DONE!
NOW INSTALL STUFF
This tutorial is meant to fit on one page, so I’ll try to create blog sub-posts to go into further detail on these applications we’re installing and configuring. If you’d like me to create a new blog that goes into more detail on any of these subjects, just let me know and I’ll see what I can do.
—————————————————
CHOOSE WEB SERVER: APACHE or NGINX?
I have chosen to focus on the supercool Nginx web server application. Why not Apache? Well, Apache’s web server, called Httpd, is very cool and has been my sidekick for years, but Apache Httpd had children, and one of them grew up to be Nginx, so out with the old and on with the new (and simpler and faster!) Nginx web server (pronounced “Engine X”).
Note for newbees, middlebees, and some oldbees:
Apache and Nginx are “web application server applications”, meaning they are system applications that serve (point to) your web applications (like crazyrails.com) and your web applications’ servers (like Mongrel). Apache and Nginx serve your PHP, Ruby, Python, Java, applications by pointing them to where your applications are located on your server. Often, if you’re using anything other than HTML, you will need an additional language-specific server like Mongrel (Ruby), Thin (Ruby), Tomcat (Java), Spyce (Python), etc. Apache and Nginx configure and “point” to these backend servers as well, so they are triggered to activate and load your web application with it’s language-specific server (like the crazyrails.com application on a Mongrel server).
In simpler terms, consider this mock flow:
Client browser call to domain name (http://crazyrails.com) => Nginx/Apache (Linux) => Locate web app (CrazyRails.com) => Start a Mongrel/Thin (Ruby) server => Return CrazyRails.com (Rails) to client browser
First, and foremost, let’s install Nginx using Ubuntu’s famous package manager called “aptitude”. (Although it’s pretty easy to install Nginx from source code, it is mucho grande easier to install from the Debian package - 10 steps or 3 steps - I choose 3. Also, by installing using the Ubuntu (Debian) package instead of building from the source code, you get an Apache-like layout of “sites-available” and “sites-enabled” folders, and it’s much easier to start/stop and command your domain virtual hosts as well :-)
sudo aptitude install nginx
Second, clear out default Nginx config file and make it look like this:
user www-data www-data;
worker_processes 4;error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;events {
worker_connections 1024;
}http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
sendfile on;
tcp_nopush on;
tcp_nodelay off;
keepalive_timeout 3;gzip on;
gzip_comp_level 2;
gzip_proxied any;
gzip_types text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;include /etc/nginx/sites-enabled/*;
}
Third, start Nginx web server:
sudo /etc/init.d/nginx start
Last, go to your default website!
And just like with Apache, we can stop, start, and restart Nginx with the following respective commands. (Note, you don’t automatically get these commands when installing Nginx from source code, so that’s a major plus for choosing to install from Ubuntu’s aptitude package manager.)
sudo /etc/init.d/nginx stop
sudo /etc/init.d/nginx start
sudo /etc/init.d/nginx restart
—————————————————
WEB SERVER NGINX INSTALLED: DONE!
NOW ADD MULTIPLE DOMAINS USING VHOSTS
We could obviously go and find where the “Welcome to nginx!” index.html page is located on our Ubuntu server and try to place our domains there, but why go through the hassle? (It’s at /var/www/nginx-default/index.html if you are wondering.) Let’s just create our own place to put our multiple website applications on this server and tell Nginx to look there. Good idea!
Here’s an overview of what we need to do:
First, we need to create a “public_html” folder in the “super” user’s home directory. Then, we need to add folders to this “/home/super/public_html” folder that represent our (Rails, Django, Joomla, Python, PHP) web applications. Since our domain names ARE our web applications, these folders can simply be named “mydomain.com” and “myotherdomain.com”, etc. Next we’ll create a temporary index.html page in the “public” subfolder, which will act as our home page for now. Then we need to create a simple config file for each of our applications/domains for Nginx to use to point to our application domains on our multi-domain server. We’ll put these config files where Nginx expects to find them, which is “/etc/nginx/sites-available”. Then, to make enabling and disabling domains easy, we’ll need to create symlinks from the “sites-available” directory to the “sites-enabled” directory for the website applications we want enabled. And finally, we’ll need to restart Nginx so she knows to start serving these fine domains in the locations we’ve provided. Let’s get to work!
Create a public_html directory for our domains:
mkdir /home/super/public_html
Create multiple application domain folders, with “public” and “log” subfolders:
cd /home/super/public_html
mkdir -p mydomain.com/{public,log}
mkdir -p myotherdomain.com/{public,log}
mkdir -p mymomsdomain.com/{public,log}
Create a temporary home page in the public directory, and add some content:
nano mydomain.com/public/index.html
# type anything you like for now, then save and close
<div>My Nginx Vhost Site Is Running!</div>
Create and edit Nginx config files for each domain in “sites-available”:
sudo touch /etc/nginx/sites-available/mydomain.com
sudo touch /etc/nginx/sites-available/myotherdomain.com
sudo touch /etc/nginx/sites-available/mymomsdomain.com
Now that these config files are created, we need to put some content into them. We’ll just do the first domain for now, but you will want to repeat the next two steps for each website you want live on your server:
# open file with nano
sudo nano /etc/nginx/sites-available/mydomain.com# copy+paste+edit config file
server {
listen 80;
server_name www.mydomain.com;
rewrite ^/(.*) http://mydomain.com permanent;
}server {
listen 80;
server_name mydomain.com;
access_log /home/super/public_html/mydomain.com/log/access.log;
error_log /home/super/public_html/mydomain.com/log/error.log;
location / {
root /home/super/public_html/mydomain.com/public/;
index index.html;
}
}
Create a symlink for the domain to the Nginx “sites-enabled” directory:
sudo ln -s /etc/nginx/sites-available/mydomain.com /etc/nginx/sites-enabled/mydomain.com
If we want to config and enable other sites, we need to repeat the two steps above ^^. For more info on Nginx vhost configuration settings, check out this codemongers page.
Finally, to make our domains live, stop and start your Nginx server:
sudo /etc/init.d/nginx stop
sudo /etc/init.d/nginx start
Now visit your domain name(s) in a web browser:
—————————————————
MULTI-DOMAIN WEB SERVER RUNNING: DONE!
NOW LET’S ADD OUR WEB APPLICATIONS
AND THE SOFTWARE NEEDED TO RUN THEM…
I’m a Ruby on Rails fan myself, so let’s go through the steps needed to host a Ruby on Rails web site on our Ubuntu Hardy server.
First, we should run a quick “aptitude” update:
sudo aptitude update
Next, we need to install some essential Linux/Unix tools to help us build and install other applications from source code. Fortunately for us, Ubuntu has an aptitude package called “build-essential”, which we can install like this:
sudo aptitude install build-essential
This will install some lower level Linux packages like:
binutils dpkg-dev g++ g++-4.2 gcc gcc-4.2 libc6-dev libgomp1 libstdc++6-4.2-dev libtimedate-perl linux-libc-dev make patch perl perl-doc perl-modules
Again, we need to install these packages to be able to configure, make, and install other applications from source code, like RubyGems.
—————————————————
RUBY
Now we can go ahead an install Ruby using the aptitude package manager, then install RubyGems from source code, and in turn, install the Rails gem and a database application or two.
Install Ruby packages:
sudo aptitude install ruby1.8-dev ruby1.8 ri1.8 rdoc1.8 irb1.8 libreadline-ruby1.8 libruby1.8 libopenssl-ruby1.8
Now create some symlinks to point Ubuntu to the right Ruby locations:
sudo ln -s /usr/bin/ruby1.8 /usr/bin/ruby
sudo ln -s /usr/bin/ri1.8 /usr/bin/ri
sudo ln -s /usr/bin/rdoc1.8 /usr/bin/rdoc
sudo ln -s /usr/bin/irb1.8 /usr/bin/irb
Install RubyGems from source:
# create a directory to download source code
mkdir /home/super/src
cd /home/super/src# download and (un)tar the file
wget http://rubyforge.org/frs/download.php/38646/rubygems-1.2.0.tgz
tar -xzvf rubygems-1.2.0.tgz
cd rubygems-1.2.0# install rubygems
sudo ruby setup.rb# create a symlink to it for Linux
sudo ln -s /usr/bin/gem1.8 /usr/bin/gem# update rubygems whenever we want
sudo gem update –system# update all of our installed gems too
sudo gem update
—————————————————
MySQL and SQLite3
Okay, now we have Ruby 1.8 and RubyGems 1.2.0 installed. Now, before we install Rails, let’s install two database applications: MySQL (awesome) and SQLite3 (the Rails default, also awesome).
Install MySQL (with Ruby support):
sudo aptitude install mysql-server mysql-client libmysqlclient15-dev libmysql-ruby1.8
# during the installation of MySQL, a blue screen should come up asking for you to enter a “root” user password for MySQL
Make MySQL secure:
mysql_secure_installation
# Enter current password for root (enter for none): *****
# Change the root password? [Y/n] n
# Remove anonymous users? [Y/n] Y
# Disallow root login remotely? [Y/n] Y
# Remove test database and access to it? [Y/n] Y
# Reload privilege tables now? [Y/n] Y
# …All done! MySQL should now be secure.
Install SQLite3 (with Ruby support):
sudo aptitude install sqlite3 libsqlite3-ruby1.8
Alright! Let’s open the Ruby command line app called “irb” and test to make sure MySQL and SQLite3 are working with Ruby:
irb
> require ‘mysql’
=> true
> require ’sqlite3′
=> true
> exit
—————————————————
RAILS
Now we can install Rails 2.1.0 using RubyGems:
sudo gem install rails
Installing Rails will take a few minutes. Once it’s done, let’s check out where our applications are pointing on our Ubuntu server:
which ruby
> /usr/bin/ruby
which gem
> /usr/bin/gem
which mysql
> /usr/bin/mysql
which sqlite3
> /usr/bin/sqlite3
which rails
> /usr/bin/rails
—————————————————
THIN or MONGREL and MONGREL CLUSTERS?
Alright, Ruby on Rails is installed. But we need one last application to “serve” our Ruby application on the Rails framework. This can be confusing to newbees because we already installed a “web server” called Nginx, right? Well, yes, but Nginx is the Linux server that “points” to our web applications - we still need a language-specific backend server that does the actual work of serving Ruby code through the HTTP connection to the client browser. Nginx and Apache don’t have anything to do with the Ruby programming language, so like with most Web 2.0 applications, we need this “backend server” in between Nginx and Ruby on Rails. And, of course, there are a few good choices out there, but it really comes down to two: Mongrel and Thin.
I have been working with Mongrel and Mongrel Clusters for awhile and they can be a pain. Mongrels are cool, but now there is a simpler and faster alternative to Mongrels, called Thin. Thin is a pure-Ruby application server, just like Mongrel, but the speed of execution and the speed of setup puts it above. So let’s install Thin server for use with our Ruby on Rails website.
First, we need to get “thin” using the RubyGems package manager, then install it. Second, we need to configure a “thin” connection file for each of our Rails sites from their home directories. Third, we need to point Nginx to “thin” so “thin” can take over and serve our Ruby on Rails applications. And finally, we need to tell Ubuntu to start our configured “thin” instances on bootup. THIS IS ALL EASIER THAN IT SOUNDS :-)
Install thin gem and run thin installation script:
sudo gem install thin
sudo thin install
Create configuration file from our Rails app home directory:
cd /home/super/public_html/mydomain.com
sudo thin config -C /etc/thin/mydomain.com.yml -c /home/super/public_html/mydomain.com/ –servers 2 -e production
Edit our Nginx config file for our Rails app to handle thin. Changes from above are in bold:
upstream thinupstream {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
server {
listen 80;
server_name www.mydomain.com;
rewrite ^/(.*) http://mydomain.com permanent;
}server {
listen 80;
server_name mydomain.com;access_log /home/super/public_html/mydomain.com/log/access.log;
error_log /home/super/public_html/mydomain.com/log/error.log;
root /home/super/public_html/mydomain.com/public/;
#index index.html;location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect false;if (-f $request_filename/index.html) {
rewrite (.*) $1/index.html break;
}
if (-f $request_filename.html) {
rewrite (.*) $1.html break;
}
if (!-f $request_filename) {
proxy_pass http://thinupstream;
break;
}
}
}
Tell Ubuntu to start thin instances during boot/reboot:
sudo /usr/sbin/update-rc.d -f thin defaults
Now we should be golden. All that’s left is to upload our Rails application to the server, restart Nginx AND thin, and visit our hosted Rails website.
—————————————————























