Security of WHM backups to an AWS S3 bucket

When you give your Web Hosting Manager (WHM) the ability to send a copy of your backup to Amazon’s AWS S3 service, you have to hand it a Key and Secret that give it that permission.

The worst option is to hand over a Secret and Key related to your own log in.

The best is to:

  1. Create a policy which
    • Only gives access to a single bucket
    • Only allows the least access required for the task
    • Only allows connections from trusted IP addresses
  2. Create a Group to connect the policy to
  3. Create a user (for the WHM) to connect to the group

For validation of connection to S3 WHM currently requires the ability to write objects, list all objects in a bucket, delete objects. Now for some this is frustratingly more than you want to hand over. In theory WHM could make do with just the ability to write to the bucket, but at the moment we have to make all those abilities available.

Luckily, the fact we’re also limiting by IP and then Secret and Key—which WHM encrypts when you submit them—should make it highly unlikely anybody else will be able to abuse the ability to delete objects.

Here is the example policy that I have in place. Feel free to copy, personalise and use:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "WHMBackupAccess",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:ListBucket",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::YOUR_BUCKET_NAME",
                "arn:aws:s3:::YOUR_BUCKET_NAME/*"
            ],
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": "YOUR_SERVER_IP/32"
                }
            }
        }    
    ]
}

If you’re in need of help putting this all together, let me know and I might expand this post to include how to do the setup inside WHM and the AWS console.

.htaccess redirects based on date and time

A useful trick for implementing maintenance windows and redirects without having to use a php or similar script is to check date and time in .htaccess files or use it to build a redirect url.

Date and time values in .htaccess come in the form %{TIME_XXXX} where XXXX is the type of date or time you want.

So if you want to redirect a generic url to one which contains today’s date, you might use:

RewriteRule ^posts/today$ /posts/%{TIME_YEAR}-%{TIME_MON}-%{TIME_DAY}

That would result in /posts/today being redirected to something like /posts/2015-08-27

If you wanted redirect a page after a date (and time) is passed you could use something like the following, where if the date and time is passed 9am on 27th August 2015 the redirect will happen. We use a simple number comparison of turning the date into an integer and then comparing it.

RewriteCond %{TIME_YEAR}%{TIME_MON}%{TIME_DAY}%{TIME_HOUR} >2015082709
RewriteRule ^$ /destination/url.html [R=301,L]

The following would only redirect until a specific time (10.22am on 27th August 2015)

RewriteCond %{TIME_YEAR}%{TIME_MON}%{TIME_DAY}%{TIME_HOUR}%{TIME_MIN} <201508271022
RewriteRule ^$ /destination/url.html [R=301,L]

The following would only redirect between two specific dates (20th July 2015 and 27th August 2015)

RewriteCond %{TIME_YEAR}%{TIME_MON}%{TIME_DAY} <20150828
RewriteCond %{TIME_YEAR}%{TIME_MON}%{TIME_DAY} >20150719
RewriteRule ^$ /destination/url.html [R=301,L]

The options you have for %{TIME_XXXX} values are:

TIME_YEAR // current four-digit year
TIME_MON // current month
TIME_DAY // current day of month
TIME_HOUR // current hour (24 hour clock) of day
TIME_MIN // current minute of hour
TIME_SEC // current second of minute
TIME_WDAY // current week-day
TIME // a formatted string representing the date and time down to seconds. e.g. 20150827112234

MySQL roughly random string generation for inserting or updating rows

Ever wanted to inject hashes into new or existing rows of a MySQL database?

Two slightly different methods, but the same result…

Insert

The code below allows you to generate a different hash for each row you’re inserting. You can tweak to choose the string’s length. No unique checks are done.

INSERT INTO table_name (
column_name
) VALUES (
    (SUBSTRING(MD5(RAND()) FROM 1 FOR 20))
)

Update columns and values to suit your needs

Update

The code below allows you to generate a different string for each row affected by the update and choose the random string’s length from 1 to 32 character.

I’m aware it’s not the most random of generators but for url hashes etc, it works well. Be sure to then check for duplicates, which are possible!

Change 20 to a length between 1 and 32 that suits your needs.

Update the WHERE condition to suit your needs

UPDATE table_name
SET column_name = (
    SELECT substring(MD5(RAND()), -20)
)
WHERE condition_column = 1;

Work in a software agency where you struggle to delivery consistently to your unique clients and unique projects? I specialise in that challenge!

Follow me on X (formerly Twitter), Threads for insights, on Medium for thought pieces, or read about my agency focused workshops (UK only) for more information.

Add global Twitter Bootstrap modal listeners

If you’re ever browsed the Twitter Bootstrap javascript documentation then you’ll know that the modal parts of the Bootstrap library require some javascript to show and hide them.

Part of the javascript is related to events which you can attach listeners to so you know when your modal shown and hidden.

What they don’t tell you is that you can add a single global (or semi global if you want) listener to all modal events which will fire when any modal is shown or hidden.

$('body').on('shown', '.modal', function () {

	console.log('we have shown a modal');

});

For example the above will console log “we have shown a modal” when any modal is shown. You can switch the shown event to be show, shown, hide or hidden.

Fix apache after upgrading to Mountain Lion

Every time I upgrade OS X something breaks with apache or php or both.

The steps for me to fix it this time was…

* Open your httpd.conf file
* uncomment the Load php5 module
* uncomment the load vhosts module

in etc/apache2/users

* duplicate Guest.conf to harrybailey.conf – replace harrybailey with your username obviously
* edit harrybailey.conf to read:

<Directory "/Users/harrybailey/Sites/">
    Options Indexes MultiViews
    AllowOverride All
    Order allow,deny
    Allow from all
</Directory>

Note: as Michal mentions below you may want to add +FollowSymLinks to your options too. This allows the system to follow any symbolic links you have set up.

AGAIN – replace harrybailey with your username.

* save the file

You may also get a php error which includes…

“It is not safe to rely on the system’s timezone settings”

The fix for that is
* navigate to /etc/ and rename php.ini.default to php.ini
* edit your new php.ini file – find “;date.timezone = ” (no brackets)
* update it to read “date.timezone = Europe/London” (no brackets) – if you aren’t on GMT then find your relevant timezone string and use that
* save the file

* open terminal
* submit the line:
sudo apachectl restart

That did it for me. Hopefully it will do it for you too.

I was first seeing the default html file in the /Library/Webserver/Documents folder
I was then seeing 403 errors for all vhosts that I has setup
Personally I saw these problems after upgrading from Lion (10.7) to Mountain Lion (10.8)