SitesLinksOttawaLifePhotosTravelToolsJournalBlog
See More Stuff
Monday, October 10, 2011

Amazon Cloud Free Tier Micro Php MySQL

Rebuild your Lighttpd / PHP / MySQL box in AWS

Quick Reference

  • For most config, go to aws.amazon.com then "AWS Management Console" and login with email, then password, then token.
  • SSH is configured to a random port which you must know. Furthermore it will only allow connections from your IP which will change from time to time. When you can't connect via SSH, you must login to aws.amazon.com and update your firewall to instead allow your new IP. [EC2 > Security Groups > YOUR_SECURITY_GROUP > Inbound > Create a new rule: Custom TCP rule > Port range: YOUR_RANDOM_SSH_PORT > Source: YOUR_PUBLIC_IP/32 > Add Rule > Delete exiting (old) SSH rule > Apply Rule Changes]
  • It's a good idea to keep a test page at your root IP so you can diagnose problems: domain resolution, server config, php module, etc.
  • It's important to login from time-to-time to run security updates: sudo yum update
  • You don't have to reboot after updates unless they're kernel updates, but I always do. To reboot, login to aws.amazon.com. [EC2 > 1 Running Instance > YOUR_WEB_SERVER > Instance Actions > Reboot > Yes, Reboot]
  • Unlike a regular webhost, Amazon doesn't offer a Name Server service, so whoever you host your domain with needs to let you at least configure the A-record for your domains on their Name Servers. godaddy.com lets you do this.
  • I had a bunch of problems sending email from my webserver. Searching suggests that Amazon's IPs have been blacklisted for spam. Also, back when I was hosted on cpanel with iweb.ca, I could redirect whatever@mydomain.com to my gmail. For now, I'm achieving that via the free trial of google accounts.

The Long Story

This article explains how to get started with amazon's Free Usage Tier (aws micro instance) for a low traffic php/mysql website. Bold terms are values that may be different for you. It is written as I did it, so not all config to a particular component happens all at once.

Resources:
Really good advice
Good getting started guide

Amazon

For a low traffic website, it looks like the amazon micro instance is an affordable (~$10/month) alternative webhost. aws.amazon.com/ec2

It's available as "AWS's Free Usage Tier" for your first year, so there's not much risk in trying it. aws.amazon.com/free

To sign up, you just provide: email, password, billing info, enter a capcha, and they auto-dial your home phone and get you to enter a pin.

After you receive your confirmation email, you need to Sign in to the AWS Management Console

It started me on the S3 page and suggested I create a bucket. I didn't.

Click the EC2 tab and you'll see that you already have 1 security group. It looks open but is actually closed. You will eventually need to open http/https, but wait until later. Here's a bit about firewalls.

On the EC2 tab via the Region dropdown at top left, make sure you're in the region closest to your visitors.

On the main screen of the EC2 tab (click tab to get there) click the "launch instance" button:

- select "Basic 32-bit Amazon Linux" (my assumption is that 32bit will eat less resource than 64bit)
 - number of instances: 1
 - availability zone: no preference // I couldn't find any useful info on this
 - instance type: micro
 - launch instances
 # continue
 - // leave advanced options as default with moitoring off 
 # continue
 - Name = WebServer
 # continue
 - create a new key pair
 - name: WebServerKey
 # create and download your key pair // this is WebServerKey.pem that you will use later with putty
 - create a new security group
 - group name: WebServerSecurityGroup
 - group description: WebServerSecurityGroup
 - // it starts with SSH on port 22 open to all
 - create a new rule: HTTP
 - source: 0.0.0.0/0
 # add rule
 # continue
 - // note that in the summary your security group shows up by an internal id, not the name you gave it
 # launch
 # close

EC2 > EC2 Dashboard > click refresh button at top right to see that you now have:

- 1 running instance
- 1 ebs volume
- 1 key pair
- 2 security groups // one is default and not used

EC2 > INSTANCES > Instances > WebServer // shows details of this running instance

- note your "Private IP Address"
- note your "Public DNS"
(these change with every reboot of your instance)

SSH

If you're on windows, download putty.

The WebServer.pem key won't work with putty, you have to convert it. Download puttygen.

puttygen
 conversions > import key > WebServerKey.pem
 key passphrase: INVENT_A_STRONG_PASSWORD
 confirm passphrase: STRONG_PASSWORD_AGAIN
 save private key > WebServerKey.ppk
 exit

You now have an encrypted .ppk version of your plaintext .pem key. I'd suggest deleting, encrypting, or storing the .pem on a thumb drive.

Use putty to connect:

session.saved_sessions: amazon
session.host: YOUR_PUBLIC_DNS_FROM_ABOVE
session.port: 22
session.type: SSH
window.lines_of_scrollback: 2000
window.colours.use_system_colours: checked
connection.data.auto-login_username: ec2-user
connection.ssh.auth.private_key_file_for_authentication: WebServerKey.ppk

Session > Save
then double-click: amazon
- accept unknown thumbrint (only happens once)
- enter your .ppk passhprase

You're in! Now let's make it bullet proof.

# ssh is 22, but that's well known so we want to change it
# these commands show what's active
sudo lsof -i
sudo lsof -i -P

sudo vi /etc/ssh/sshd_config

All you really need to change is the port line. Here is the default file with just the port line changed.

#       $OpenBSD: sshd_config,v 1.80 2008/07/02 02:24:18 djm Exp $

# This is the sshd server system-wide configuration file.  See
# sshd_config(5) for more information.

# This sshd was compiled with PATH=/usr/local/bin:/bin:/usr/bin

# The strategy used for options in the default sshd_config shipped with
# OpenSSH is to specify options with their default value where
# possible, but leave them commented.  Uncommented options change a
# default value.

Port CHOOSE_A_FIVE_DIGIT_UNUSED_PORT
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::

# Disable legacy (protocol version 1) support in the server for new
# installations. In future the default will change to require explicit
# activation of protocol 1
Protocol 2

# HostKey for protocol version 1
#HostKey /etc/ssh/ssh_host_key
# HostKeys for protocol version 2
#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_dsa_key

# Lifetime and size of ephemeral version 1 server key
#KeyRegenerationInterval 1h
#ServerKeyBits 1024

# Logging
# obsoletes QuietMode and FascistLogging
#SyslogFacility AUTH
SyslogFacility AUTHPRIV
#LogLevel INFO

# Authentication:

#LoginGraceTime 2m
#PermitRootLogin yes
# Only allow root to run commands over ssh, no shell
PermitRootLogin forced-commands-only
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10

#RSAAuthentication yes
#PubkeyAuthentication yes
#AuthorizedKeysFile     .ssh/authorized_keys
#AuthorizedKeysCommand none
#AuthorizedKeysCommandRunAs nobody

# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
#RhostsRSAAuthentication no
# similar for protocol version 2
#HostbasedAuthentication no
# Change to yes if you don't trust ~/.ssh/known_hosts for
# RhostsRSAAuthentication and HostbasedAuthentication
#IgnoreUserKnownHosts no
# Don't read the user's ~/.rhosts and ~/.shosts files
#IgnoreRhosts yes

# To disable tunneled clear text passwords, change to no here!
#PasswordAuthentication yes
#PermitEmptyPasswords no
# EC2 uses keys for remote access
PasswordAuthentication no

# Change to no to disable s/key passwords
#ChallengeResponseAuthentication yes
ChallengeResponseAuthentication no

# Kerberos options
#KerberosAuthentication no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes
#KerberosGetAFSToken no
#KerberosUseKuserok yes

# GSSAPI options
#GSSAPIAuthentication no
#GSSAPIAuthentication yes
#GSSAPICleanupCredentials yes
#GSSAPICleanupCredentials yes
#GSSAPIStrictAcceptorCheck yes
#GSSAPIKeyExchange no

# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the ChallengeResponseAuthentication and
# PasswordAuthentication.  Depending on your PAM configuration,
# PAM authentication via ChallengeResponseAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and ChallengeResponseAuthentication to 'no'.
#UsePAM no
# Leaving enabled as described so that account and session checks are run
UsePAM yes

# Accept locale-related environment variables
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
AcceptEnv XMODIFIERS

#AllowAgentForwarding yes
#AllowTcpForwarding yes
#GatewayPorts no
#X11Forwarding no
X11Forwarding yes
#X11DisplayOffset 10
#X11UseLocalhost yes
#PrintMotd yes
#PrintLastLog yes
# Explicitly enable
PrintLastLog yes
#TCPKeepAlive yes
#UseLogin no
#UsePrivilegeSeparation yes
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
#ClientAliveCountMax 3
#ShowPatchLevel no
#UseDNS yes
#PidFile /var/run/sshd.pid
#MaxStartups 10
#PermitTunnel no
#ChrootDirectory none

# no default banner path
#Banner none

# override default of no subsystems
Subsystem       sftp    /usr/libexec/openssh/sftp-server

# Example of overriding settings on a per-user basis
#Match User anoncvs
#       X11Forwarding no
#       AllowTcpForwarding no
#       ForceCommand cvs server

But I decided to lock down the entire file. Here's my version:

# $OpenBSD: sshd_config,v 1.80 2008/07/02 02:24:18
# compiled with PATH=/usr/local/bin:/bin:/usr/bin

Port CHOOSE_A_FIVE_DIGIT_UNUSED_PORT
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::
Protocol 2
#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_dsa_key
#KeyRegenerationInterval 1h # applies only to Protocol 1
#ServerKeyBits 1024         # applies only to Protocol 1
SyslogFacility AUTHPRIV
#LogLevel INFO
#LoginGraceTime 2m
#-- not sure why amazon shipped with: PermitRootLogin forced-commands-only
PermitRootLogin no
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10
#RSAAuthentication yes
#PubkeyAuthentication yes
#AuthorizedKeysFile .ssh/authorized_keys
#AuthorizedKeysCommand none
#AuthorizedKeysCommandRunAs nobody
#-- explicitly disable weak authentication systems
RhostsRSAAuthentication no
HostbasedAuthentication no
IgnoreUserKnownHosts yes
IgnoreRhosts yes
PasswordAuthentication no
PermitEmptyPasswords no
ChallengeResponseAuthentication no
KerberosAuthentication no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes
#KerberosGetAFSToken no
#KerberosUseKuserok yes
GSSAPIAuthentication no
#GSSAPICleanupCredentials yes
#GSSAPICleanupCredentials yes
#GSSAPIStrictAcceptorCheck yes
#GSSAPIKeyExchange no
#-- not sure why amazon shipped with this on, but seems safe since pass/challenge are off
UsePAM yes
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
AcceptEnv XMODIFIERS
#AllowAgentForwarding yes
#AllowTcpForwarding yes
#GatewayPorts no
#-- not sure why amazon shipped with: X11Forwarding yes
X11Forwarding no
#X11DisplayOffset 10
#X11UseLocalhost yes
#PrintMotd yes
PrintLastLog yes
#TCPKeepAlive yes
UseLogin no
UsePrivilegeSeparation yes
PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
#ClientAliveCountMax 3
#ShowPatchLevel no
#UseDNS yes
#PidFile /var/run/sshd.pid
#MaxStartups 10
#PermitTunnel no
#ChrootDirectory none
#Banner none
Subsystem sftp /usr/libexec/openssh/sftp-server
# restart ssh to pickup the changes
sudo /etc/init.d/sshd restart

Close putty and attempt to reconnect. You should fail. Close the failed putty.

aws.amazon.com > EC2 > NEWWORK & SECURITY > Security Groups > WebServerSecurityGroup > Inbound > Create a new rule: Custom TCP rule > Port range: YOUR_SECRET_SSH_PORT > Source: YOUR_PUBLIC_IP/32 > Add Rule > Delete exiting (old) SSH rule > Apply Rule Changes

Open putty. Load the amazon config. Update the port. Save the config. Open the connection. Accept the server's host key (only happens once per config). Enter your password.

sudo lsof -i -P
# now nobody is listening on port 22

Great, but what if someone keylogs your AWS password, all is lost, and you're exposed to lots of billing-by-usage liability. Get multi factor authentication: aws.amazon.com/mfa > Purchase device from gemalto:

onlinenoram.gemalto.com
Price $12.99
With Standard International Shipping (averages 8-16 days)
Total $20.97

Web Server

Now, what webserver shall we install?
http://redmine.lighttpd.net/projects/lighttpd
http://www.linuxuser.co.uk/tutorials/master-lighttpd-the-lightweight-alternative-to-apache/
http://redmine.lighttpd.net/wiki/1/TutorialLighttpdAndPHP
http://www.cyberciti.biz/tips/ultimate-lighttpd-webserver-security.html
http://www.webrmedia.com/blog/apps/apache-httpd-and-lighttpd-on-an-amazon-aws-basic-ami
http://www.howtoforge.com/lighttpd_php5_mysql_fedora7
https://forums.aws.amazon.com/thread.jspa?threadID=64618
http://dev.mysql.com/doc/refman/5.1/en/adding-users.html
http://dev.mysql.com/doc/refman/5.1/en/create-database.html
http://dev.mysql.com/doc/refman/5.1/de/create-table.html

Here's what I chose. Note: sudo is necessary for almost every command because the lighttpd install will not be owned by ec2-user.

sudo yum -y install lighttpd
sudo yum -y install lighttpd-fastcgi
sudo yum -y install mysql
sudo yum -y install mysql-server
sudo yum -y install php-cli
sudo yum -y install php-mysql

Here's a summary of what happened:

================================================================================
 Package            Arch      Version                Repository       Size
================================================================================
Installing:
 lighttpd           i686      1.4.29-1.8.amzn1       amzn-main       335 k
 lighttpd-fastcgi   i686      1.4.29-1.8.amzn1       amzn-main        32 k
 spawn-fcgi         i686      1.6.3-1.2.amzn1        amzn-main        16 k
 mysql              i686      5.1.52-1.6.amzn1       amzn-main       1.0 M
 mysql-server       i686      5.1.52-1.6.amzn1       amzn-main        10 M
 mysql-libs         i686      5.1.52-1.6.amzn1       amzn-main       1.7 M
 perl-DBD-MySQL     i686      4.013-3.4.amzn1        amzn-main       144 k
 perl-DBI           i686      1.609-4.4.amzn1        amzn-main       742 k
 php-cli            i686      5.3.6-1.18.amzn1       amzn-main       2.4 M
 php-common         i686      5.3.6-1.18.amzn1       amzn-main       616 k
 php-mysql          i686      5.3.6-1.18.amzn1       amzn-main        88 k
 php-pdo            i686      5.3.6-1.18.amzn1       amzn-main        81 k

Installed:
  lighttpd.i686 0:1.4.29-1.8.amzn1
  lighttpd-fastcgi.i686 0:1.4.29-1.8.amzn1
  spawn-fcgi.i686 0:1.6.3-1.2.amzn1
  mysql.i686 0:5.1.52-1.6.amzn1
  mysql-server.i686 0:5.1.52-1.6.amzn1
  mysql-libs.i686 0:5.1.52-1.6.amzn1
  perl-DBD-MySQL.i686 0:4.013-3.4.amzn1
  perl-DBI.i686 0:1.609-4.4.amzn1
  php-cli.i686 0:5.3.6-1.18.amzn1
  php-common.i686 0:5.3.6-1.18.amzn1
  php-mysql.i686 0:5.3.6-1.18.amzn1
  php-pdo.i686 0:5.3.6-1.18.amzn1

Of course it's not obvious where they're installed or what to do next. First note that the above amazon installs have good defaults including creating lighttpd/mysql users and groups.

# observe that yum install created lighttpd/mysql users and groups
sudo cat /etc/passwd

PHP

Now we will configure the web server and php:

sudo vi /etc/lighttpd/lighttpd.conf
# startup complained so I commented out this line
server.use-ipv6 = "enable"
sudo vi /etc/lighttpd/modules.conf
# tell lighty that we'll be using fastcgi, you just uncomment it
include "conf.d/fastcgi.conf"

# same for mod_rewrite (near the top), which is necessary for url rewrites in .htaccess
  "mod_rewrite",
sudo vi /etc/lighttpd/conf.d/fastcgi.conf
# tell fastcgi that we'll be using php
# leave the example commented blocks untouched and just add this new one
fastcgi.server = ( ".php" =>
                   ( "php-local" =>
                     (
                       "socket" => socket_dir + "/php-fastcgi-1.socket",
                       "bin-path" => "/usr/bin/php-cgi",
                       "max-procs" => 1,
                       "broken-scriptfilename" => "enable",
                     )
                   )
                 )
sudo vi /etc/php.ini
# documentation says this is requied, uncomment it
cgi.fix_pathinfo = 1

# short tags on, that's not necessary, just my style
short_open_tag = On
sudo vi /etc/lighttpd/lighttpd.conf
# it seems that the default install has an invalid entry for the home dir
# since we want to eventually move all this to jail, define it there
var.home_dir    = "/jail/lightyhome"
sudo mkdir /jail
sudo mkdir /jail/lightyhome
sudo chown lighttpd:lighttpd /jail/lightyhome
sudo chmod 0700 /jail/lightyhome
sudo mkdir /jail/lightyhome/sockets
sudo chown lighttpd:lighttpd /jail/lightyhome/sockets
sudo chmod 0700 /jail/lightyhome/sockets

# start the webserver
sudo /etc/init.d/lighttpd start

If you now visit http://YOUR_PUBLIC_DNS, you should see the lighttpd default page.

# create a test php page
sudo vi /var/www/lighttpd/test.php
hello <? echo "world"; ?> <?php echo " again"; ?>

If you now visit http://YOUR_PUBLIC_DNS/test.php, you should see the hello page.

# stop the server for now
sudo /etc/init.d/lighttpd stop

# if you had problems, look here
sudo cat /var/log/lighttpd/error.log

MySQL

Now we will configure the database:

# start the db
sudo /etc/init.d/mysqld start

# it gives a long prompt warning about security. take it's advice. run this.
sudo /usr/bin/mysql_secure_installation
 # enter for blank root password
 # Y, set root password (for mysql, not this box)
 # YOUR_NEW_MYSQL_ROOT_PASSWORD
 # NEW_PASSWORD_AGAIN
 # Y, remove ananymous users
 # Y, disallow root login remotely
 # Y, remove test db and access to it
 # Y, reload privilege tables now

# what's it doing?
sudo netstat -tap | grep mysql
# tcp        0      0 *:mysql *:*                     LISTEN     ####/mysqld

The above command shows that the db is listening on all ports. Since only local lighttpd will use it, that's an unnecessary security risk. Also some posts say "you should set a MySQL password for your hostname, too, because otherwise anybody can access your database and modify data".

# note the contents of these files
cat /etc/my.cnf
cat /usr/share/doc/mysql-server-5.1.52/my-small.cnf

# it didn't seem safe to simply overwrite the one with the other, so I did this
sudo cp /etc/my.cnf /etc/my.cnf.org
sudo vi /etc/my.cnf
[mysqld]
datadir=/var/lib/mysql
port=WHATEVER_WAS_ALREADY_THERE
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
# skip-locking is deprecated, use skip-external-locking
skip-external-locking
key_buffer_size = 16K
max_allowed_packet = 1M
table_open_cache = 4
sort_buffer_size = 64K
read_buffer_size = 256K
read_rnd_buffer_size = 256K
net_buffer_length = 2K
thread_stack = 128K
# Don't listen on a TCP/IP port at all. This is a security enhancement,
skip-networking
server-id = 1
# wikipedia says InnoDB is the default storage engine for MySQL as of MySQL 5.5.
# and /var/log/mysqld.log shows it starting, but I left out its settings because I didn't understand them
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
[mysqldump]
quick
max_allowed_packet = 16M
[mysql]
no-auto-rehash
[myisamchk]
key_buffer_size = 8M
sort_buffer_size = 8M
[mysqlhotcopy]
interactive-timeout
# bounce the db
sudo /etc/init.d/mysqld restart

# note that it no longer appears on netstat
sudo netstat -tap | grep mysql

# mod the test php page
sudo vi /var/www/lighttpd/test.php
<? phpinfo(); ?>
# restart the webserver, vist the test page and note that the mysql module is present
sudo /etc/init.d/lighttpd start

# create a dbuser, db, and table
mysql -u root -p 
 # at the prompt: YOUR_MYSQL_ROOT_PASSWORD

mysql> SELECT User, Host, Password FROM mysql.user;
# I have two rows. No passwords should be blank.

mysql> SELECT * FROM mysql.db;
# Mine is empty. This means the anonymous access to test tables has been deleted.

mysql> CREATE DATABASE db1 CHARACTER SET utf8;
mysql> CREATE USER 'user1'@'localhost' IDENTIFIED BY 'NEW_DB_PASSWORD';
mysql> GRANT ALL PRIVILEGES ON db1.* TO 'user1'@'localhost';
mysql> exit

mysql -D db1 -u user1 -p
 # at the prompt: NEW_DB_PASSWORD

mysql> CREATE TABLE table1 (col1 INT, col2 INT);
mysql> INSERT INTO table1 (col1, col2) VALUES (1, 2);
mysql> INSERT INTO table1 (col1, col2) VALUES (3, 4);
mysql> exit

# mod the test php page so see that the db is working
sudo vi /var/www/lighttpd/test.php
<?
  try
  {
    // despite saying localhost, this will actually use sockets, not TCP/IP
    //  to use TCP/IP on the localhost, use 127.0.0.1 instead
    // connections to mysql don't use odbc and therefore benefit from making 
    //  persistent connections that are not closed at the end of the script
    //  but are cached and re-used thus avoiding overhead.
    $dbh = new PDO('mysql:host=localhost;dbname=db1','user1','NEW_DB_PASSWORD',array(PDO::ATTR_PERSISTENT=>true));

    // define the character set. not sure if this is necessary. defaults may be correct.
    $dbh->exec("SET CHARACTER SET utf8");

    foreach($dbh->query('SELECT * from table1') as $row)
    { print_r($row); }

    // to close the connection, all references must be deleted. do this by assigning NULL.
    // note that PHP will automatically close the connection when your script ends. 
    // but consider the not above about persistent connections
    $dbh = null;
  }
  catch (PDOException $e)
  {
    // only print this exception when debugging because it will display a back trace
    // that will likely reveal the full database username and password
    // print "Error!: " . $e->getMessage() . "<br/>";
    die();
  }
?>

Next we will configure services to auto-start:

# show web server config
chkconfig --list lighttpd
 # lighttpd        0:off   1:off   2:off   3:off   4:off   5:off   6:off

# show db config
chkconfig --list mysqld
 # mysqld          0:off   1:off   2:off   3:off   4:off   5:off   6:off

# configure to autostart
sudo chkconfig --levels 235 lighttpd on
sudo chkconfig --levels 235 mysqld   on

Now let's confirm some security settings:

# from this we learn that a lighttpd user was created for us
sudo cat /etc/passwd

# from this we learn that only the ec2-user has sudo permission
sudo cat /etc/sudoers

# from this we learn that lighttpd runs as the lighttpd user on port 80 
# and won't serve .php files without processing them
sudo cat /etc/lighttpd/lighttpd.conf
 # server.port = 80
 # server.username  = "lighttpd"
 # server.groupname = "lighttpd"
 # static-file.exclude-extensions = ( ".php", ".pl", ".fcgi", ".scgi" )
 # server.upload-dirs = ( "/var/tmp" )

From lighttpd: "...will switch to the user lighttpd and the group lighttpd. The server has to be started as root to take control of port 80, but it's not necessary or a good idea to continue running as root after port acquisition."

Amazon

Here I just do a bit of poking around:

EC2 Dashboard
 My Resources

  1 Running Instance
   AMI:                amzn-ami-####.##.#.i386-ebs (ami-########) // amazon machine image
   Status:             running
   Root Device Type:   ebs
   Block Devices:      sda1
    EBS ID:                vol-########
    Root device type:      ebs
    Block device status:   attached
    Delete on termination: Yes
    Snapshot ID:           snap-########
   Public DNS:         ec2-##-##-###-###.compute-#.amazonaws.com
   Private DNS:        ip-##-###-###-##.ec2.internal
   Private IP Address: ##.###.###.##
   Root Device:        sda1
   Termination Protection: Disabled

  1 EBS Volume (elastic block store) 
   Volume ID:  vol-########
   Snapshot:   snap-########
   Attachment: i-######## (WebServer):/dev/sda1 (attached)
   Capacity:   8 GiB
   Status:     in-use

aws.amazon.com/ebs" Amazon EBS volumes are off-instance storage that persists independently from the life of an instance. ... can be attached to a running Amazon EC2 instance and exposed as a device within the instance.

EC2 Dashboard
 My Resources
  1 Running Instance
   Root Device Type:
    The root volume will either be an 'ebs' volume, or 'instance-store' 
    (local storage that's not persistant beyond instance termination).
    The Create Image, Start and Stop actions only apply to instances with 
    an 'ebs' root device type.
   Block Devices:
    Comma seperated list of Amazon EBS volumes associated with this AMI.
   Root Device:
    System device name that contains the boot volume (e.g. /dev/sda1)
   Termination Protection:
    If enabled, you will not be able to terminate this instance until 
    termination proteciton has been disabled
cd /
df -h
  Filesystem            Size  Used Avail Use% Mounted on
  /dev/xvda1            7.9G  951M  6.9G  12% /
  tmpfs                 302M     0  302M   0% /dev/shm

ls -la /dev/s*
  lrwxrwxrwx 1 root root  5 Oct  2 17:55 /dev/sda1 -> xvda1
  lrwxrwxrwx 1 root root 15 Oct  2 17:55 /dev/stderr -> /proc/self/fd/2
  lrwxrwxrwx 1 root root 15 Oct  2 17:55 /dev/stdin -> /proc/self/fd/0
  lrwxrwxrwx 1 root root 15 Oct  2 17:55 /dev/stdout -> /proc/self/fd/1

So, my entire server is the 8gb /dev/sda1 device, and it is configured to Delete on termination: Yes

EC2 Dashboard
 My Resources
  1 Running Instance
   Instance Actions
    Terminate          // I guess this nukes the EBS
    Reboot             // or this?
    Stop               // but will this?
    Start

Let's make some changes:

EC2 Dashboard
 My Resources
  1 Running Instance
   Instance Actions
    Change Termination Protection
     Yes, Enable
EC2 Dashboard
 My Resources
  1 Running Instance
   Instance Actions
    Stop
     Warning: Please note that any data on the ephemeral storage of your instance will be lost when it is stopped.
     Yes, Stop
     (takes about 30s)

At this stage, both putty and browser fail to connect.

EC2 Dashboard
 My Resources
  1 Running Instance
   Instance Actions
    Start
     Yes, Start
     (takes about 30s)

At this stage, both putty and browser still fail to connect. It's because the PUBLIC_DNS has changed. We'll fix that with an elastic IP.

aws.amazon.com/ec2: An Elastic IP address is associated with your account not a particular instance, and you control that address until you choose to explicitly release it. Unlike traditional static IP addresses, however, Elastic IP addresses allow you to mask instance or Availability Zone failures by programmatically remapping your public IP addresses to any instance in your account.

aws.amazon.com/ec2/pricing
No cost for Elastic IP addresses while in use
$0.01 per non-attached Elastic IP address per complete hour
$0.00 per Elastic IP address remap – first 100 remaps / month

EC2 Dashboard
 My Resources
  0 Elastic IPs
   Allocate New Address
    Are you sure you want to allocate a new IP address?
    EIP used in: EC2
    Yes, Allocate
   Associate Address
    Instance: i-######## - WebServer
    Yes, Associate

 Address: ###.##.###.### ----------- this is your new fixed ip
 Instance ID: i-######## (WebServer)
 Scope: standard
 Public DNS: ec2-###-##-###-##.compute-#.amazonaws.com

Ok, that works great, but I need a nameserver I can edit. My current domain registrar tera-byte.com doesn't provide that. I could use bind9, but that seems like a lot of work. godaddy.com does support nameserver editing, but it'll take 5-7 days for the transfer to complete. In the mean time, I'll just fake it with my hosts record. (Note: on windows you must be logged in as admin to edit the hosts file).

C:\WINDOWS\system32\drivers\etc
hosts
MY_ELASTIC_IP MY_DOMAIN.com
MY_ELASTIC_IP www.MY_DOMAIN.com

Now in my browser MY_DOMAIN.com shows the lighty homepage.

Note: you'll also have to update your putty config to point to the elastic IP for SSH login.

File Transfer GUI

Although I think you can transfer files via putty, it's nice to have a file transfer GUI. But it has to go over SSH because we haven't enabled FTP (which isn't secure anyway). I use WinSCP.

WinSCP 4.3.5
Installation package
winscp435setup.exe > agree with defaults

Host: YOUR_ELASTIC_IP
Port: YOUR_SECRET_SSH_PORT
User: ec2-user
Password: [blank]
Private key file: WebServerKey.ppk
File protocol: SFTP (don't allow scp fallback)
> save > login > [enter password when pompted]

Multiple Domains

This bit looks confusing, because at first I tried to setup .htaccess, but that's an apache feature and doesn't exist in lighttpd.
redmine.lighttpd.net
howto.berbahaya.com

# what do we have?
sudo ls -la /var/www/lighttpd/
-rw-r--r-- 1 root root  3638 Jul 16 04:53 favicon.ico
-rw-r--r-- 1 root root   844 Jul 16 04:53 index.html
-rw-r--r-- 1 root root  2072 Jul 16 04:53 light_button.png
-rw-r--r-- 1 root root 35431 Jul 16 04:53 light_logo.png
-rw-r--r-- 1 root root   103 Jul 16 04:53 poweredby.png
-rw-r--r-- 1 root root  1204 Oct  3 05:03 test.php

# create folders as roots for two unrelated websites
sudo mkdir /var/www/lighttpd/DOMAIN1
sudo mkdir /var/www/lighttpd/DOMAIN2

# chown them as the ec2 user so we don't have to sudo all the time.
sudo chown ec2-user:ec2-user /var/www/lighttpd/DOMAIN1
sudo chown ec2-user:ec2-user /var/www/lighttpd/DOMAIN2

# create pages for each (no longer need to sudo)

vi /var/www/lighttpd/DOMAIN1/index.html
 hello DOMAIN1
 :wq

vi /var/www/lighttpd/DOMAIN2/index.html
 hello DOMAIN2
 :wq

sudo vi /etc/lighttpd/lighttpd.conf
 ## url handling modules (rewrite, redirect)
 include "/etc/lighttpd/rewrites.conf"
 :wq

sudo vi /etc/lighttpd/rewrites.conf

Here's some notes I'm not sure are correct. I ended up doing what I show in the last block below.

# this                http://www.DOMAIN1.com/url/
# would be                 /var/www/lighttpd/url/
# becomes  /var/www/lighttpd/www.DOMAIN1.com/url/

$HTTP["host"] =~ "^.*\.([^.]+\.com)$" {
  url.rewrite-once = ( "^/(.*)" => "/%0/$1" )
}

# this                http://www.DOMAIN1.com/url/
# would be                 /var/www/lighttpd/url/
# becomes          /var/www/lighttpd/DOMAIN1/url/

$HTTP["host"] =~ "^.*DOMAIN1\.com$" {
  url.rewrite-once = ( "^/(.*)" => "/DOMAIN1/$1" )
}

# this works
$HTTP["host"] =~ "^.*DOMAIN1\.com$" { url.rewrite-once = ( "^/(.*)" => "/DOMAIN1/$1" ) }
$HTTP["host"] =~ "^.*DOMAIN2\.com$" { url.rewrite-once = ( "^/(.*)" => "/DOMAIN2/$1" ) }

# this is better
$HTTP["host"] =~ "^.*DOMAIN1\.com$" {
  server.error-handler-404 = "/DOMAIN1/error.php"
  url.rewrite-once = ( "^/(.*)" => "/DOMAIN1/$1" )
}
else $HTTP["host"] =~ "^.*DOMAIN2\.com$" {
  server.error-handler-404 = "/DOMAIN2/error.php"
  url.rewrite-once = ( "^/(.*)" => "/DOMAIN2/$1" )
}

# this is better, with the above you lose the mapping ...blah/ to ...blah/index.php
$HTTP["host"] =~ "^.*DOMAIN1\.com$" {
  server.document-root = "/var/www/lighttpd/DOMAIN1"
}
else $HTTP["host"] =~ "^.*DOMAIN2\.com$" {
  server.document-root = "/var/www/lighttpd/DOMAIN2"
}
server.error-handler-404 = "/_error.php"

Note that some of the above require the existance of error.php like the following:

<?
  header('Location: http://www.DOMAIN1.com');
  exit;
?>
sudo chmod 640 /etc/lighttpd/rewrites.conf

sudo /etc/init.d/lighttpd restart
This works, but in the future, I'll want:
 -- no indexes
 -- no hotlinking

Security

Okay, initially I was going to put stuff in a chrooted jail, but it seems like regular security updates to the web server (and os/php/db/anything) are more important.

aws.amazon.com/amazon-linux-ami: Security updates are automatically applied on the initial boot of the AMI. Upon login, the Message of the Day (/etc/motd) indicates whether or not any additional updates are available.

[ec2-user@ip-##-###-##-### ~]$ cat /etc/motd

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

See /usr/share/doc/system-release/ for latest release notes.
No packages needed for security; 20 packages available

aws.amazon.com/amazon-linux-ami: The Amazon Linux AMI is designed to be used in conjunction with online package repositories hosted in each Amazon EC2 region. These repositories provide ongoing updates to packages in the Amazon Linux AMI as well as access to hundreds of additional common open source server applications. Security updates are provided via the Amazon Linux AMI yum repositories as well as via updated Amazon Linux AMIs. Security alerts are published in the Amazon Linux AMI Security Center.

So you have to update stuff manually, but they make it easy for you to know what to do.

aws.amazon.com/amazon-linux-ami/security-bulletins
  October 11, 2011  Important Important          ALAS-2011-2: cyrus-imapd Security Update - Buffer Overflow Vulnerability
  October 11, 2011  Important Important          ALAS-2011-3: ca-certificates Security Update - Fradulent HTTPS Certificates
  October 11, 2011  Important Important          ALAS-2011-4: openssl Security Update - Remote Vulnerability
  October 11, 2011  Important Important          ALAS-2011-5: perl-FCGI Security Update - Remote Vulnerability
  October 11, 2011  Important Important          ALAS-2011-6: openswan Security Update - Denial of Service
  October 11, 2011  Important Important          ALAS-2011-7: php Security Update - Multiple Vulnerabilities
September 28, 2011  Important Important          ALAS-2011-1: httpd Security Update - Range Header Vulnerability
 November 09, 2010  Informational Informational  Linux kernel vulnerability in certain EC2 AMIs
 November 09, 2010  Important Important          Linux kernel IA32 System Call Emulation Vulnerability

aws.amazon.com/amazon-linux-ami/security-bulletins/ALAS-2011-3

Issue Overview:
This update includes the latest updates to the root Certificate Authority list from Mozilla. It was found 
that a Certificate Authority (CA) issued fraudulent HTTPS certificates. This update removes that CA's root 
certificate from the ca-certificates package, rendering any HTTPS certificates signed by that CA as untrusted.

New Packages:
noarch:
    ca-certificates-2010.63-3.7.amzn1.noarch

But it's even easier than that. Just do this:

$ yum list updates
Loaded plugins: fastestmirror, priorities, security, update-motd
Determining fastest mirrors
 * amzn-main: packages.us-east-1.amazonaws.com
 * amzn-updates: packages.us-east-1.amazonaws.com
amzn-main                                                                   | 2.1 kB     00:00
amzn-updates                                                                | 2.1 kB     00:00
amzn-updates/primary_db                                                     | 119 kB     00:00
Updated Packages
aws-cfn-bootstrap.noarch                     1.0-5.amzn1                               amzn-updates
aws-scripts-ses.noarch                       2011.10.12-1.5.amzn1                      amzn-updates
ca-certificates.noarch                       2010.63-3.7.amzn1                         amzn-updates
dracut.noarch                                004-53.11.amzn1                           amzn-updates
get_reference_source.noarch                  1.1-1.13.amzn1                            amzn-updates
java-1.6.0-openjdk.i686                      1:1.6.0.0-52.1.9.10.40.amzn1              amzn-updates
kernel.i686                                  2.6.35.14-97.44.amzn1                     amzn-updates
krb5-libs.i686                               1.9-9.19.amzn1                            amzn-updates
krb5-workstation.i686                        1.9-9.19.amzn1                            amzn-updates
openssl.i686                                 1.0.0e-2.16.amzn1                         amzn-updates
perl-libwww-perl.noarch                      5.837-4.1.amzn1                           amzn-updates
php-cli.i686                                 5.3.8-3.20.amzn1                          amzn-updates
php-common.i686                              5.3.8-3.20.amzn1                          amzn-updates
php-mysql.i686                               5.3.8-3.20.amzn1                          amzn-updates
php-pdo.i686                                 5.3.8-3.20.amzn1                          amzn-updates
system-release.noarch                        2011.09-1.34                              amzn-updates
yum-plugin-fastestmirror.noarch              1.1.30-6.11.amzn1                         amzn-updates
yum-plugin-priorities.noarch                 1.1.30-6.11.amzn1                         amzn-updates
yum-plugin-security.noarch                   1.1.30-6.11.amzn1                         amzn-updates
yum-utils.noarch                             1.1.30-6.11.amzn1                         amzn-updates

These are the 20 updates mentioned by the motd. One of them is the security update from above:
ca-certificates-2010.63-3.7.amzn1.noarch

So, a good rule is probably to always apply all updates. It's easy.

$ sudo yum update
...
Complete!

Will it ever need a reboot? Sounds like no, but it doesn't hurt. I'll always reboot after updates.

With symlinks it might be possible to have the server in jail. The db is probably less critical.

GoDaddy

See Using GoDaddy with AWS

Gemalto

My gemalto token has arrived:

http://aws.amazon.com/mfa/
2. Enable AWS MFA
-- prompts for login
-- you just enter the serial, and two codes
works great

SSL

This was a bit of a fail. I wanted EC, but this version of lighttpd doesn't support it. Also note that self-signed SSL is only useful for your personal stuff, since the browser will give an untrusted warning and other visitors aren't likely to accept it.

yum list installed

lighttpd.i686            1.4.29-1.8.amzn1   @amzn-main
lighttpd-fastcgi.i686    1.4.29-1.8.amzn1   @amzn-main
openssl.i686             1.0.0e-2.16.amzn1  @amzn-updates

aws.amazon.com/account > EC2 > NEWWORK & SECURITY > Security Groups > WebServerSecurityGroup > Inbound > Create a new rule: HTTPS (implicitly 443) > Source: 0.0.0.0/0 > Add Rule > Apply Rule Changes

http://redmine.lighttpd.net/wiki/1/Docs:SSL
Elliptic-Curve supported in lighttpd 1.4.29 using prime256v1
Elliptic-Curve is supported in OpenSSL 0.9.8f onwards

It is possible to put multiple names in a single certificate. These certificates use the SAN property to store multiple domain names in a single certificate. This allows lighttpd to always use the same certificate, which is valid for all the domains it serves.

one can get a certificate for the *.example.org domain to support all of the subdomains of the example.org domain.

A self-signed SSL certificate can be generated like this:

$ openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes

If you have a .crt and a .key file, cat them together into a single PEM file (the order isn't important):

$ cat host.key host.crt > host.pem

To enable SSL in addition to normal HTTP, put the ssl.engine configuration in a socket conditional block:

$SERVER["socket"] == ":443" {
ssl.engine = "enable"
ssl.pemfile = "/etc/lighttpd/ssl/www.example.org.pem"
}

Be careful to keep your .pem file private! Lighttpd reads all pemfiles at startup, before dropping privileges. It is therefore best to make the pem file owned by root and readable by root only:

$ chown root:root /etc/lighttpd/ssl/example.org.pem
$ chmod 400 /etc/lighttpd/ssl/example.org.pem

Some PHP scripts try to detect HTTPS by checking if $_SERVER['HTTPS'] equals 'on'. To allow that, you can try this:

server.modules = (
"mod_setenv",
)
$SERVER["socket"] == "0.0.0.0:443" {
ssl.engine = "enable"
ssl.pemfile = "/etc/lighttpd/server.pem"
ssl.use-sslv2 = "disable"
setenv.add-environment = (
"HTTPS" => "on"
)
}
---- this step wasn't necessary for me, $_SERVER['HTTPS']=on by itself

http://www.madboa.com/geek/openssl/#key-ec

openssl ecparam -out key.pem -name prime256v1 -genkey
 # fail

openssl version
 # OpenSSL 1.0.0e-fips 6 Sep 2011

http://opensource.apple.com/source/OpenSSL098/OpenSSL098-27/src/demos/ssltest-ecc/ECCcertgen.sh

# Generate a new certificate request in $TEST_CA_FILE.req.pem. A 
# new ecdsa (actually ECC) key pair is generated on the parameters in
# $TEST_CA_CURVE.pem and the private key is saved in $TEST_CA_FILE.key.pem
# WARNING: By using the -nodes option, we force the private key to be 
# stored in the clear (rather than encrypted with a password).
$OPENSSL_CMD req $OPENSSL_CNF -nodes -subj "$TEST_CA_DN" \
    -keyout $KEYS_DIR/$TEST_CA_FILE.key.pem \
    -newkey ec:$TEST_CA_CURVE.pem -new \
    -out $CERTS_DIR/$TEST_CA_FILE.req.pem
openssl -?
openssl req -?
openssl x509 -?
openssl genpkey -?

OpenSSL> ecparam -name secp384r1 -out nist384param.pem
OpenSSL> req -x509 -nodes -newkey ec:nist384param.pem -sha384 -keyout ectest.key -out ectest.pem
http://www.openssl.org/docs/apps/x509v3_config.html

-extfile        - configuration file with X509V3 extensions to add
-extensions     - section from config file with X509V3 extensions to add

It looks like my version of openssl doesn't support EC.

sudo mkdir /etc/lighttpd/ssl
cd /etc/lighttpd/ssl

sudo openssl req -nodes -subj "/DC=MY_DOMAIN1/DC=com" -keyout key.pem -newkey rsa:2048 -new -out req.pem 

sudo vi extn.txt
keyUsage=digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth
subjectAltName=DNS:*.MY_DOMAIN1.com,DNS:*.MY_DOMAIN2.com
sudo openssl x509 -req -days 730 -set_serial SOME_LAREGE_INTEGER -sha256 -in req.pem -extfile extn.txt -signkey key.pem -out cert.pem

sudo openssl x509 -in cert.pem -text

sudo rm extn.txt
sudo rm req.pem

sudo cat key.pem cert.pem > /tmp/ssl.pem
sudo mv /tmp/ssl.pem /etc/lighttpd/ssl

sudo chmod 400 key.pem
sudo chmod 400 cert.pem
sudo chmod 400 ssl.pem
sudo chown root:root ssl.pem

ls -la
 # -r-------- 1 root root 1257 Nov  5 15:42 cert.pem
 # -r-------- 1 root root 1704 Nov  5 15:25 key.pem
 # -r-------- 1 root root 2961 Nov  5 15:45 ssl.pem

sudo vi /etc/lighttpd/lighttpd.conf
$SERVER["socket"] == ":443" {
  ssl.engine  = "enable"
  ssl.pemfile = "/etc/lighttpd/ssl/ssl.pem"
}
sudo /etc/init.d/lighttpd restart

SendMail

sudo vi /etc/php.ini
; For Unix only.  You may supply arguments as well (default: "sendmail -t -i").
; http://www.php.net/manual/en/mail.configuration.php#ini.sendmail-path
sendmail_path = /usr/sbin/sendmail -t -i

My mail isn't sending. Stackoverflow says it's because of too much spam from amazon. Info from amazon.

sudo vi /var/log/maillog
Nov  5 17:00:35 ip-10-245-82-134 sendmail[7736]: pA5H0ZVx007736: from=lighttpd, size=135, class=0, nrcpts=1, msgid=<201111051700.pA5H0ZVx007736@ip-10-245-82-134.ec2.internal>, relay=lighttpd@localhost
Nov  5 17:00:35 ip-10-245-82-134 sendmail[7738]: pA5H0ZJj007738: from=<lighttpd@ip-10-245-82-134.ec2.internal>, size=384, class=0, nrcpts=1, msgid=<201111051700.pA5H0ZVx007736@ip-10-245-82-134.ec2.internal>, proto=ESMTP, daemon=MTA, relay=localhost [127.0.0.1]
Nov  5 17:00:35 ip-10-245-82-134 sendmail[7736]: pA5H0ZVx007736: to=MYTEST@MYTEST.com, ctladdr=lighttpd (220/220), delay=00:00:00, xdelay=00:00:00, mailer=relay, pri=30135, relay=[127.0.0.1] [127.0.0.1], dsn=2.0.0, stat=Sent (pA5H0ZJj007738 Message accepted for delivery)
Nov  5 17:00:37 ip-10-245-82-134 sendmail[7740]: STARTTLS=client, relay=MYTEST.com., version=TLSv1/SSLv3, verify=FAIL, cipher=AES256-SHA, bits=256/256
Nov  5 17:00:37 ip-10-245-82-134 sendmail[7740]: pA5H0ZJj007738: to=<MYTEST@MYTEST.com>, ctladdr=<lighttpd@ip-10-245-82-134.ec2.internal> (220/220), delay=00:00:02, xdelay=00:00:02, mailer=esmtp, pri=120384, relay=MYTEST.com. [72.55.186.59], dsn=5.1.1, stat=User unknown
Nov  5 17:00:37 ip-10-245-82-134 sendmail[7740]: pA5H0ZJj007738: pA5H0bJj007740: DSN: User unknown
Nov  5 17:00:37 ip-10-245-82-134 sendmail[7740]: pA5H0bJj007740: to=<lighttpd@ip-10-245-82-134.ec2.internal>, delay=00:00:00, xdelay=00:00:00, mailer=local, pri=31653, dsn=2.0.0, stat=Sent

This is too complicated for me to solve right now.

Jail

I didn't get this working. I think it's pretty close. But I gave up in the end.

http://www.unixwiz.net/techtips/chroot-practices.html
http://www.cyberciti.biz/tips/howto-setup-lighttpd-php-mysql-chrooted-jail.html

sudo /etc/init.d/lighttpd stop
sudo /etc/init.d/mysqld stop

# recall we already created /jail above
ls -la /
 drwxr-xr-x  3 root root  4096 Oct  2 21:29 jail

# we'll need copies of these in our jail
ls -la /
 drwxrwxrwt  3 root root  4096 Oct 22 15:09 tmp
 drwxr-xr-x 69 root root  4096 Oct 22 15:09 etc
 drwxr-xr-x 20 root root  4096 Oct  2 18:57 var
 drwxr-xr-x  3 root root  4096 Sep 22 01:29 home

ls -la /var
 drwxr-xr-x  8 root root 4096 Oct 22 15:09 log

ls -la /var/log
 drwxr-x---  2 lighttpd lighttpd    4096 Oct 16 03:49 lighttpd
 drwxr-xr-x  2 root     root        4096 Sep 22 01:29 mail

ls -la /etc
 drwxr-xr-x  3 root root    4096 Oct  3 04:27 lighttpd
 drwxr-xr-x  2 root root    4096 Sep 22 01:29 mail
 drwxr-xr-x  2 root root    4096 Oct  3 03:14 php.d
 -rw-r--r--  1 root root   69095 Oct  2 21:46 php.ini

ls -la /var/run
 drwxr-x---  2 lighttpd lighttpd 4096 Jul 16 04:55 lighttpd

ls -la /var/www
 drwxr-xr-x  5 ec2-user ec2-user 4096 Oct 29 14:08 lighttpd

-rw-r--r--  1 root     root       69097 Nov  5 16:46 /etc/php.ini
-rw-r--r--  1 root     root          44 Sep 22 01:30 hosts
-rw-r--r--  1 root     root        1688 May  4  2010 nsswitch.conf
-rw-r--r--  1 root     root          80 Oct 29 00:21 resolv.conf
-rw-r--r--  1 root     root      640999 Jan 12  2010 services
-rw-r--r--  1 root     root         118 Sep 22 01:30 localtime

# we want to move the actual files into the jail and create links from
# the normal locations to the jail locations so updates still work

lrwxrwxrwx  1 root root 22 Nov  5 17:43 lighttpd -> /jail/var/log/lighttpd
lrwxrwxrwx  1 root root 22 Nov  5 19:12 lighttpd -> /jail/var/www/lighttpd
lrwxrwxrwx  1 root root 22 Nov  5 19:12 lighttpd -> /jail/var/run/lighttpd

# note: I already performed these steps above

sudo mkdir /jail
sudo mkdir /jail/lightyhome
sudo chown lighttpd:lighttpd /jail/lightyhome
sudo chmod 0700 /jail/lightyhome
sudo mkdir /jail/lightyhome/sockets
sudo chown lighttpd:lighttpd /jail/lightyhome/sockets
sudo chmod 0700 /jail/lightyhome/sockets

# now additionally do this

sudo /etc/init.d/lighttpd stop
sudo /etc/init.d/mysqld stop

sudo mkdir /jail/tmp
sudo chmod 1777 /jail/tmp

sudo mkdir -p /jail/var/log
sudo mv /var/log/lighttpd /jail/var/log
sudo ln -s /jail/var/log/lighttpd /var/log/lighttpd

sudo mkdir -p /jail/var/run
sudo mv /var/run/lighttpd /jail/var/run
sudo ln -s /jail/var/run/lighttpd /var/run/lighttpd

sudo mkdir -p /jail/var/www
sudo mv /var/www/lighttpd /jail/var/www
sudo ln -s /jail/var/www/lighttpd /var/www/lighttpd

sudo mkdir -p /jail/etc
sudo mv /etc/php.ini /jail/etc
sudo ln -s /jail/etc/php.ini /etc/php.ini

sudo vi /bin/copy_shared_lib_dependancies_to_jail.sh
#!/bin/bash
BASE="/jail"
if [ $# -eq 0 ]; then
  echo "Syntax : $0 /path/to/executable"
  echo "Example: $0 /jail/usr/bin/php"
  exit 1
fi
[ ! $BASE ] && mkdir -p $BASE || :
# iggy ld-linux* file as it is not shared one
FILES="$(ldd $1 | awk '{ print $3 }' |egrep -v ^'\(')"
for i in $FILES
do
  d="$(dirname $i)"
  [ ! -d $BASE$d ] && mkdir -p $BASE$d || :
  echo "Copying $i $BASE$d"
  /bin/cp $i $BASE$d
done
# copy /lib/ld-linux* or /lib64/ld-linux* to $BASE/$sldlsubdir
# get ld-linux full file location
sldl="$(ldd $1 | grep 'ld-linux' | awk '{ print $1}')"
# now get sub-dir
sldlsubdir="$(dirname $sldl)"
if [ ! -f $BASE$sldl ];
then
  echo "Copying $sldl $BASE$sldlsubdir"
  /bin/cp $sldl $BASE$sldlsubdir
else
  :
fi
sudo vi /bin/refresh_chroot_jail.sh
#!/bin/bash

echo "Cleaning: lib, usr, etc"
rm -rf /jail/lib
rm -rf /jail/usr
rm -r  /jail/etc/hosts
rm -r  /jail/etc/nsswitch.conf
rm -r  /jail/etc/resolv.conf
rm -r  /jail/etc/services
rm -r  /jail/etc/localtime

echo "Fetching: etc"
cp /etc/hosts         /jail/etc/
cp /etc/nsswitch.conf /jail/etc/
cp /etc/resolv.conf   /jail/etc/
cp /etc/services      /jail/etc/
cp /etc/localtime     /jail/etc/

echo "Fetching: php"
mkdir -p /jail/usr/bin
cp /usr/bin/php     /jail/usr/bin
cp /usr/bin/php-cgi /jail/usr/bin
/bin/copy_shared_lib_dependancies_to_jail.sh /jail/usr/bin/php
/bin/copy_shared_lib_dependancies_to_jail.sh /jail/usr/bin/php-cgi

echo "Fetching: php mysql modules"
mkdir -p /jail/usr/lib/php/modules
cp /usr/lib/php/modules/mysql.so /jail/usr/lib/php/modules
/bin/copy_shared_lib_dependancies_to_jail.sh /jail/usr/lib/php/modules/mysql.so
sudo chmod 700 /bin/copy_shared_lib_dependancies_to_jail.sh
sudo chmod 700 /bin/refresh_chroot_jail.sh

sudo /bin/refresh_chroot_jail.sh

sudo vi /etc/lighttpd/lighttpd.conf
var.log_root    = "/var/log/lighttpd"
var.server_root = "/var/www"
var.state_dir   = "/var/run"
var.home_dir    = "/lightyhome"
var.conf_dir    = "/etc/lighttpd"
server.chroot   = "/jail"
sudo /etc/init.d/mysqld start
sudo /etc/init.d/lighttpd start

sudo vi /jail/var/log/lighttpd/error.log
2011-11-05 19:17:53: (mod_fastcgi.c.2699) FastCGI-stderr: PHP Fatal error:  Class 'PDO' not found in /var/www/lighttpd/index.php on line 9
sudo /etc/init.d/lighttpd stop

sudo cp /usr/lib/php/modules/pdo.so        /jail/usr/lib/php/modules
sudo cp /usr/lib/php/modules/pdo_mysql.so  /jail/usr/lib/php/modules
sudo cp /usr/lib/php/modules/mysqli.so     /jail/usr/lib/php/modules
sudo cp /usr/lib/php/modules/pdo_sqlite.so /jail/usr/lib/php/modules
sudo cp /usr/lib/php/modules/sqlite3.so    /jail/usr/lib/php/modules

sudo /bin/copy_shared_lib_dependancies_to_jail.sh /jail/usr/lib/php/modules/pdo.so
sudo /bin/copy_shared_lib_dependancies_to_jail.sh /jail/usr/lib/php/modules/pdo_mysql.so
sudo /bin/copy_shared_lib_dependancies_to_jail.sh /jail/usr/lib/php/modules/mysqli.so
sudo /bin/copy_shared_lib_dependancies_to_jail.sh /jail/usr/lib/php/modules/pdo_sqlite.so
sudo /bin/copy_shared_lib_dependancies_to_jail.sh /jail/usr/lib/php/modules/sqlite3.so

sudo /etc/init.d/lighttpd start
2011-11-05 19:25:22: (mod_fastcgi.c.2699) FastCGI-stderr: PHP Fatal error:  Class 'PDO' not found in /var/www/lighttpd/index.php on line 9
sudo /etc/init.d/lighttpd stop

sudo ln -s /jail/lightyhome /lightyhome

sudo vi /etc/lighttpd/lighttpd.conf
#server.chroot   = "/jail"

sudo /etc/init.d/lighttpd start
# works fine when not in jail

http://forum.lighttpd.net/topic/650 --- looking at phpinfo() jailed/unjailed, when jailed, none of the modules load, curl.so for example. wait, I never added curl

sudo cp /usr/lib/php/modules/curl.so     /jail/usr/lib/php/modules
sudo cp /usr/lib/php/modules/fileinfo.so /jail/usr/lib/php/modules
sudo cp /usr/lib/php/modules/json.so     /jail/usr/lib/php/modules
sudo cp /usr/lib/php/modules/phar.so     /jail/usr/lib/php/modules
sudo cp /usr/lib/php/modules/zip.so      /jail/usr/lib/php/modules

sudo /bin/copy_shared_lib_dependancies_to_jail.sh /jail/usr/lib/php/modules/curl.so
sudo /bin/copy_shared_lib_dependancies_to_jail.sh /jail/usr/lib/php/modules/fileinfo.so
sudo /bin/copy_shared_lib_dependancies_to_jail.sh /jail/usr/lib/php/modules/json.so
sudo /bin/copy_shared_lib_dependancies_to_jail.sh /jail/usr/lib/php/modules/phar.so
sudo /bin/copy_shared_lib_dependancies_to_jail.sh /jail/usr/lib/php/modules/zip.so

sudo /etc/init.d/lighttpd stop
sudo /etc/init.d/lighttpd start

Yeah, no modules are loading. It's just too hard. We'll just have to live without the jail for now.

PHP Config

For simpler logs
change /etc/php.ini
from
error_reporting = E_ALL & ~E_DEPRECATED
to
error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED

MySQL Config

mysql -u root -p 
 # at prompt enter MYSQL_ROOT_PASSWORD

CREATE DATABASE yourdb CHARACTER SET utf8;
CREATE USER 'yourdb'@'localhost' IDENTIFIED BY 'yournewpassword';
GRANT ALL PRIVILEGES ON yourdb.* TO 'yourdb'@'localhost';

exit

mysql -D yourdb -u yourdb -p
 # at prompt enter yournewpassword

source /jail/tmp/yourscript.sql

exit

Problems

Problem: all web is owned by ec2, so lighty can't write symlinks.
Solution: make lighty the group for DOMAIN1

sudo chown ec2-user:lighttpd tribalworker

Problem: php wants to write to /var/lib/php/session, wich doensn't exist and is owned by root.
Solution:

sudo mkdir -p /jail/var/lib/php/session
sudo ln -s /jail/var/lib/php/session /var/lib/php/session
sudo chown lighttpd:lighttpd /jail/var/lib/php/session

sudo /etc/init.d/lighttpd restart

Much later additions

sudo yum install php-mbstring
sudo service lighttpd restart

I wanted the default character set to be utf8

// I tried this
sudo vi /etc/my.cnf
[mysqld]
character-set-server=utf8
collation-server=utf8_general_ci

// Bounce
sudo /etc/init.d/mysqld restart

// Then I tried this
sudo vi /etc/php.ini
default_charset = "utf8"

// Bounce
sudo /etc/init.d/lighttpd restart

// But still, I got back "latin1" from this
$db->character_set_name()

// So, I have to explicitly set this after the mysqli constructor
$db->set_charset('utf8');

// Note this:
mysql> show variables like '%character%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | latin1                     |
| character_set_connection | latin1                     |
| character_set_database   | utf8                       |
| character_set_filesystem | binary                     |
| character_set_results    | latin1                     |
| character_set_server     | utf8                       |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+

// Also did this
sudo vi /etc/php.ini
[mbstring]
mbstring.language = Neutral
mbstring.internal_encoding = UTF-8
mbstring.encoding_translation = Off
mbstring.http_input = auto
mbstring.http_output = UTF-8
mbstring.detect_order = auto
mbstring.substitute_character = none
default_charset = UTF-8

// Bounce
sudo /etc/init.d/lighttpd restart

// Now this works
if (mb_internal_encoding() != "UTF-8")
{ die; }

// See this
http://tympanus.net/codrops/2009/08/31/solving-php-mysql-utf-8-issues/

// Also, to support:
$dom = new DOMDocument;

// you need:
sudo yum -y install php-xml
sudo /etc/init.d/lighttpd restart