Jekyll, S3 & CloudFront


Introduction

I’ve been poking around with Jekyll in my free time and I have to say I really enjoy working with it. It, unlike other platforms, such as Ghost, can not only be deployed easily, but you can version control it with Git and automate a lot of the work.

I am planning, in future posts, to have a look at automating Jekyll deployment. A script triggered after a new push is something that I find really cool.

From what I’ve noticed a decent chunk of people host their blogs on GitHub Pages, which, although a decent service, still lacks SSL on own domains. I, myself, have the urge to always SSL web traffic, so I decided to have a look for a different hosting method.

Enter AWS

I work with AWS quite a lot, both for my work and school, and it is the platform I am most familiar with. Keeping in mind how cheap and scalable S3 is, especially for hosting static websites, I think it’s a pretty decent option for hosting a Jekyll blog.

So, this is basically how I deployed my blog:

Prerequisites

  1. Have a jekyll blog
  2. Have an AWS account

Provision a deployer account in IAM

It is a good idea to make an IAM account for s3_website with only the needed permissions. I would personally not like it if it had full permissions to my AWS account.

I will be talking about managing AWS with Terraform in future posts, so for now we’ll just use the AWS console.

Creating an IAM policy

So, let’s go ahead and create an IAM policy for our deployer.

In your AWS console hit the “Create Policy” button in IAM -> Policies and select the Policy Generator option. The Policy Generator is a super easy tool for making IAM policies by just selection the resources and actions that should be allowed to be used.

I did not refine the policies that much, and yes, I really should, but just for starters I came up with the following policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "<GENERATED_SID>",
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::vasko.io"
            ]
        },
        {
            "Sid": "<GENERATED_SID>",
            "Effect": "Allow",
            "Action": [
                "cloudfront:*"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

So, when you’re done, review your policy, test it out, give it a name and hit “Create Policy”.

Creating an IAM account

So, after we’re done with the policy we can proceed with creating the account. In IAM go to “Users” and hit “Add User”. Give it a recognisable name, such as s3_website_deployer or something of the sorts.

Please make sure to tick the “Programmatic access” checkbox at “Access type”.

Next give it your permission by selecting “Attach existing polices directly” and searching for your policy.

Now you can review and create your account. In the final screen you will be presented with the ID and secret key, which you need to copy to the s3_website.yml config file.

Creating a certificate in “Certificate Manager”

NOTE: Make sure you’re in the N. Virginia region for this.

We can provision a certificate for your CloudFront distribution for free from the AWS Certificate Manager.

Go to Certificate Manager from your dashboard and hit “Request a certificate”, type in your domain, review and request. AWS will send you a mail for approval.

After you’ve approved it you can go back to your preferred region.

Configuring s3_website

First have a look at the official repo on GitHub. You can find most of the information you need there, but just to sum up the deployment process, here is what you need to do.

  1. Install the gem by running gem install s3_website
  2. cd to the directory where your blog is
  3. Run s3_website cfg create to generate the configuration file
  4. Put your AWS credentials by either:
    • Copy-pasting your access credentials
    • Using ENV variables
  5. Put your S3 bucket name
  6. Configure which files should be excluded from download by listing them under exclude_from_upload
  7. Configure CloudFront

Configuring Credentials

I ended up with the following as my configuration:

s3_id: <%= ENV['AWS_ID'] %>
s3_secret: <%= ENV['AWS_SECRET'] %>

Configuring S3

For S3 make sure to put your

  • S3 bucket name (if it does not exist yet it will make it for out)
  • S3 endpoint(if you wish to use something else than the default one)

You should end up having something like this:

s3_bucket: my.bucket.name
s3_endpoint: eu-west-1

Configuring files to be excluded from upload

You probably don’t want anything extra to be uploaded than just your blog, so we can just remove some files from the upload list. For example:

exclude_from_upload:
  - Rakefile

Configuring CloudFront

You do not have to add a CloudFront distribution ID yet, since s3_website will make a new one for you. You should, however, configure the CloudFront default cache behaviour, aliases and invalidations. I have the following configuration:

cloudfront_distribution_config:
  default_cache_behavior:
    min_ttl: <%= 60 * 60 * 24 %>
  aliases:
    quantity: 1
    items:
      - vasko.io

cloudfront_invalidate_root: true
cloudfront_wildcard_invalidation: true

After applying the configuration, s3_website will present you with a prompt asking whether you want to use CloudFront or not. You can type in Y and you will be given your CloudFront distribution ID. Then you can add it to the config file.

CloudFront SSL

When all of that is done go to CloudFront in your AWS dashboard and locate your distribution. From there you can hit the “Edit” button and configure SSL by selecting “Custom SSL Certificate” and then picking the correct certificate for your domain.

Restrict S3 access

Also, you can block access to the S3 bucket from the outside, so that people can only access your blog via CloudFront. You can do that by going to your CloudFront distribution in your AWS console -> “Origins”. Select the default origin and edit it. You will see that you get the option to “Restrict Bucket Access”, so go ahead and choose “yes”. Then create a new identity and choose “Yes, Update Bucket Policy”. CloudFront will automatically update the S3 bucket’s policy.

If the option to restrict bucket access is not available click on “Origin Domain Name” and re-select the S3 bucket that you’re using to host the blog.

Now, after CloudFront has updated the S3 policy, you will find out that you can still browse to it. That’s because CloudFront added the restricting policy, but it never removed the allowing policy. To fix that go to your S3 bucket in the AWS dashboard -> Permissions -> Bucket Policy. Remove the block that allows public access and leave only the CloudFront Origin Access Identity and save it.

Your end file should look something like this:

{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "2",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity <CLOUDFRONT_ID>"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<BUCKET>/*"
        }
    ]
}

Deploying your blog

Now, after all of the configuring is done we can go ahead and deploy our blog.

We need to do the following:

  1. Generate our blog by running jekyll build or bundle exec jekyll build.
  2. Push the website to S3 by running s3_website push.

Give CloudFront some time to run the invalidation and then browse to your blog. 🎉

So, is it ready yet?

Yes, yes it is. 😃 Your blog should now be accessible via your own domain. Any time you add more content just follow the deployment steps from above and your blog will be updated.

In future posts I will also look into automating the process with deployment scripts and Terraform.

So, how do you deploy your blog?