Initial setup of the EC2 instance
So the first thing you'll want to do is sign up for your AWS account and create an EC2 instance. I'm not going to cover many details here, since the Web based GUI makes this pretty damn easy and so if you can't even handle those steps then you should probably look into managed hosting! Before you go any further, it's important to note that I used the Amazon EBS-Backed AMI (32-bit) that was last updated March 15, 2011. If you're using a different AMI (or even a newer version of the same AMI), these steps may not work for you.
The only other thing to note is that I placed my instance in a new Security Group called WebServer, which I'll make mention of in the future. Inbound TCP connections are allowed to port 80 from 0.0.0.0/0, and to port 22 from 1.2.3.4/32 (where 1.2.3.4 is my IP address). Later on we'll be setting up a cron job to handle making sure the SSH rule stays up to date (since if you have a dynamic IP address, as many people do, your IP may change at any time).
Return to Top
Connecting via SSH for the first time
OK, so now the instance is launched and you want to connect via SSH for the first time. When you setup your instance you should have created a Key Pair, and saved the .PEM file to your computer somewhere. So first we'll convert this .PEM file to a .PPK file that PuTTY can use. So fire up PuTTYgen, import your .PEM, enter a passphrase (optional, but strongly recommended), and click Save private key.
Now in PuTTY enter your instance's IP address in the Host Name field, enter ec2-user in the Connection -> Data -> Auto-login username field, and browse for your .PPK file on the Connection -> SSH -> Auth tab. Then hit Open, and you should be connected to your instance (since this is your first connection, you'll be warned that the server's host key is not cached in the registry. Hit yes to add it to the cache and avoid the warning in the future -- feel free to search elsewhere to find out what this message means, and what steps you should really be taking before blindly clicking Yes).
If all went well, you should now be connected to your instance, so we'll continue on with updating your system and installing the software we'll need.
Return to Top
Updating your system and installing new software
Now that you're connected, you'll first want to make sure your system is up to date, and then you'll want to install the software we'll be setting up later. We'll also set the timezone, since it doesn't really fit in with any of the later steps!
- sudo yum update
- sudo yum install screen sendmail-cf lighttpd lighttpd-fastcgi php-cgi php-pdo
- sudo mv /etc/localtime /etc/localtime.old
- sudo ln -s /usr/share/zoneinfo/America/Toronto /etc/localtime
-
sudo vi /etc/sysconfig/clock
- Change to ZONE="America/Toronto"
Return to Top
Creating your own user account (and ditching ec2-user)
I don't like being known as ec2-user, so next we'll create our own user account, which I'll call McLovin for demonstration purposes. We'll also add McLovin to the sudoers list, so he can administer the system, and copy the SSH keys from ec2-user, so we can still SSH in with our existing .PPK file.
- sudo groupadd McLovin
- sudo useradd -g McLovin -m McLovin
- sudo usermod -a -G wheel McLovin
-
sudo visudo
- Change last line (should reference ec2-user) to McLovin ALL = NOPASSWD: ALL
- sudo -s
- cp /home/ec2-user/.ssh/authorized_keys /home/McLovin
- chown McLovin:McLovin authorized_keys
- su McLovin
- mkdir -m 700 /home/McLovin/.ssh
- mv /home/McLovin/authorized_keys /home/McLovin/.ssh/
OK, now we should be able to logout, and SSH back in using McLovin instead of ec2-user as our Auto-login username, so do that now. Confirm that you're able to sudo as McLovin, and we'll continue on with the removal of ec2-user. With luck only the first command below will be required, but if you get an error saying ec2-user is logged in then run commands 2-4 before running the first command again. NB: If there are multiple processes running for ec2-user, you may have to run command 4 multiple times
- sudo userdel -r ec2-user
- sudo skill -KILL -u ec2-user
- ps aux | grep ec2-user
- sudo kill [ENTER_PID_HERE]
ec2-user is no more! Since logging in as root is disabled, we might as well get rid of its SSH key: sudo rm -rf /root/.ssh
Return to Top
Giving McLovin access to /var/www
Being a webserver, you'll probably be spending a lot of time in /var/www, and it REALLY sucks having to sudo all the time to edit plain old HTML files. It's also impossible to SFTP files in, so next we'll make some changes to make /var/www easier to deal with. After much searching on Google, this seemed to be the best answer to the problem (source).
- sudo groupadd www-data
- sudo usermod -a -G www-data McLovin
- sudo chgrp -R www-data /var/www/
- sudo chmod -R g+w /var/www/
- sudo find /var/www -type d -exec chmod 2775 {} \;
- sudo find /var/www -type f -exec chmod ug+rw {} \;
IIRC I had to log off and back on in order for these changes to take effect. There's probably a command you can use, but I didn't bother searching since logging off and on isn't too big of a deal. Feel free to let me know what the command is if you know it.
Return to Top
Setting up lighttpd
I'm just testing AWS using the free usage tier, so decided to go with lighttpd since it's supposed to perform better than Apache when resources are scarce. Unfortunately there are a few problems with the lighttpd setup out of the box (files/directories not existing), so there's a bunch of steps to follow here. NB: I chose to honour the lighttpd configuration in most cases, and created the missing files/directories. The other option would have been to update the configuration to match the filesystem, so you could choose to do that if you want.
Also, I host multiple domains on my server, which is why I have the vhosts.conf and vhosts-docroot.conf -- it makes it VERY simple to add a new vhost with very little effort. My initial setup was to use the simple_vhost module, but I couldn't find a way to have it use separate logfiles for each vhost, which is why I rolled my own (well not really, since the include method came from here).
sudo chmod 640 /etc/lighttpd/vhosts.conf
sudo vi /etc/lighttpd/vhosts-docroot.conf
## set the docroot based on basedir and servername
## both have to be defined before
server.document-root = vhosts_dir + "/" + servername + "/htdocs"
accesslog.filename = log_root + "/access-" + servername + ".log"
server.errorlog = log_root + "/error-" + servername + ".log"
sudo chmod 640 /etc/lighttpd/vhosts-docroot.conf
Return to Top
Setting up PHP
Now that we have a functioning webserver (well not quite since we haven't restarted it yet, but we'll do that in a second), it's time to setup PHP. Again there are directories missing (maybe the full-blown php package would have created them, but php-cgi didn't), but there's way fewer steps with PHP than there were with lighttpd! Speaking of which, the last step here is to restart lighttpd, after which you should be able to connect to your vhosts that you previously setup.
The php.ini settings are your personal preference. I disable a few of the error types in php.ini, but all my php programs use a common include file that enables all errors when it detects my IP address. This way users are not bugged with warnings/notices, but I am, so I can make sure my code is the best it can be. More on this later.
- sudo mkdir /var/lib/php/session
- sudo chown -R lighttpd:lighttpd /var/lib/php
-
sudo vi /etc/php.ini
- Change error_reporting = E_ALL & ~E_WARNING & ~E_NOTICE & ~E_DEPRECATED
- Uncomment error_log = php_errors.log
- Uncomment cgi.fix_pathinfo=1
- sudo /etc/init.d/lighttpd restart
OK, I guess you can't quite connect to your vhosts yet, because we haven't put any content there yet! So here's how to setup the default vhost directory, I'll leave it up to you to figure out how to setup the rest
- mkdir /var/www/vhosts
- mkdir /var/www/vhosts/default.vhost
- mkdir /var/www/vhosts/default.vhost/htdocs
-
vi /var/www/vhosts/default.vhost/htdocs/index.html
- Add content for the "vhost not found" message (since this is the site that will be displayed if the vhost wasn't found in vhosts.conf
Now do the same with mydomain.ca by creating the same directory structure as above, but in /var/www/vhosts/mydomain.ca/, and you should be good to go!
Return to Top
Setting up Sendmail to deliver mail via a smart host
The AWS IP range has been flagged as a source of spam by Spamhaus, which means any mail you send out from your EC2 instance has a pretty good chance of being labeled as spam, or possibly even being dropped altogether. So to avoid this problem, we'll setup Sendmail to deliver via a smart host, and assuming your smart host isn't also labeled by anybody as being a source of spam, you shouldn't have any troubles getting mail delivered. There are lots of variables in the following steps, so take not of anything in []. Also take note that Sendmail is picky about the way things are quoted, so when you see something in single quotes it's actuall a leading backtick ` and trailing single quote '.
-
sudo vi /etc/mail/sendmail.mc
- Change to define(`SMART_HOST', `[SMTP_HOSTNAME]')dnl
- Add MASQUERADE_AS(mydomain.ca)dnl
- Add FEATURE(masquerade_envelope)dnl
- Add FEATURE(masquerade_entire_domain)dnl
- Add MASQUERADE_DOMAIN(mydomain.ca)dnl
- Change to dnl EXPOSED_USER(`root')dnl
-
sudo vi /etc/mail/access
- Add AuthInfo:[SMTP_HOSTNAME] "U:[SMTP_USERNAME]" "P:[SMTP_PASSWORD]" "M:PLAIN"
- sudo chmod 600 /etc/mail/access
- sudo -s
- m4 /etc/mail/sendmail.mc > /etc/mail/sendmail.cf
- makemap hash /etc/mail/access < /etc/mail/access
- exit
- sudo /etc/init.d/sendmail restart
Return to Top
Change the LogWatch schedule and recipient
By default, LogWatch runs once per day and emails the root account. I'd rather it ran when I told it to (as part of a script that runs nightly, so I only get one cron job report email), and didn't email the root account, so here's how I changed that:
-
sudo vi /etc/logwatch/conf/logwatch.conf
- Add MailTo = my.email.address@gmail.com
- sudo rm /etc/cron.daily/0logwatch
Now, to run LogWatch as part of a nightly script, just use /usr/sbin/logwatch --print (this assumes your cron job isn't redirecting standard output to /dev/null, and you've set the MAILTO line in your crontab)
Return to Top
Setting up s3backer
As part of the free usage tier Amazon gives you 10GB of EBS storage, and since the EC2 instance only takes 8GB, that leaves you 2GB to create a second EBS volume for backup purposes. But EBS volumes don't have the same reliability as S3 storage (as evidenced by the fact that some users recently (April 29 2011) lost data in the US East region
, so we're going to use the 5GB of free S3 storage for backups.
There are many ways to copy your data to S3, but one of the coolest I came across is s3backer. So we'll install and configure that next. First the install:
- sudo yum install gcc gcc-c++ make libcurl-devel fuse-devel openssl-devel zlib-devel expat-devel
- cd /usr/local/src
- sudo wget [LATEST_S3BACKER_RELEASE]
- sudo tar zxvf [LATEST_S3BACKER_RELEATE]
- cd [LATEST_S3BACKER_RELEASE]
- ./configure
- make
- sudo make install
And now we'll configure it to mount a 5GB S3 bucket called MYBUCKET to a mount point in /mnt/MYBUCKET. Obviously you'll be using your actual accessID and accessKey, not the junk I included in the block below.
sudo chmod 600 /root/.s3backer_passwd
sudo s3backer --blockCacheSize=25 --blockSize=1m --size=5g --listBlocks MYBUCKET /mnt/s3backer/
sudo mkfs.ext3 -F -b 4096 /mnt/s3backer/file
sudo tune2fs -c 0 -i 0 /mnt/s3backer/file
sudo mkdir /mnt/MYBUCKET
sudo mount -o loop /mnt/s3backer/file /mnt/MYBUCKET
At some point I should update the fstab so this mount happens upon reboot, but for now I just do it manually.
Return to Top
Miscellaneous stuff
In the future I'll be adding a script sample that you can schedule to run regularly, to make sure your Security Group always contains your current IP address. If you want to figure it out for yourself, the steps are:
- Sign up with a DynDNS provider
- Create a script that resolves your DynDNS address to an IP address
- If the address has changed, ec2revoke the old address, and ec2auth the new address
I'll also be adding my nightly scheduled task, which basically consists of:
- Checking for updates via yum
- Using tar to backup some important files to my 2GB EBS mount, and my 5GB S3 mount
- du'ing the backup directory, to check for an unexpected jump in backup size
- logwatch'ing to see what happened on the server the previous day
There's probably lots of other stuff the nightly script should be doing, but that's it for now.
Return to Top