Hosting a Static Pelican Site in S3 with GitHub Actions

With the static site already created you could manually copy these files to S3 via the AWS console, or via the S3 CLI. However, this adds friction to the writing process. When the files are sent to GitHub via the git push command they can be automatically be sent to Amazon S3 using GitHub Actions for continuous Deployment/ Continuous Integrations (CI/CD).

When working with any type of system like this ensure you review the security and pricing. I got a good start using GitHub Actions based on a great post by johnkevinlosito@.

Setting up AWS Assets

If you do not already have an S3 Bucket configured for static website hosting complete these steps first. Please note that S3 is a paid service. There is a free tier but by using AWS assets you can incur charges.

  1. In the Amazon S3 console locate the ARN of the bucket where you will store the files. It will look something like arn:aws:s3:::www.z1g1.net
  2. In the AWS IAM Console Create a new IAM policy which only has the PutObject permission to the specific bucket ARN you captured above. Sample policy below.
    1. Screenshot of json file with bucket policy
    2. Note that the Resource entry on line 8 ends with a *. This is key because if you don’t inclue this your user will not be able to actually overwrite any existing objects
  3. To allow Github Action to make authenticated calls to Amazon S3 an AWS IAM user with limited permissions will be created.
    1. In the AWS IAM console create a new user, select the “Access Key - Programatic Access” option.
    2. Attach the policy that you created above to this user.
    3. Once you complete this wizard you will be prompted to download credetnaisl for the user. The Access Key ID (AKID) is not sensitive alone. However, the “Secret Access Key” needs to be kept secret. Capture this secret for future steps but do not put it in a source code file, or any file which might get uploaded to GitHub. Once you leave this page you will not be able to retreive this information again.

At this point you will have a bucket which can serve static HTML content. A future hardening step for this infrastrcuture would be to move away from IAM users to IAM roles.

Configuring GitHub

  1. Following the GitHub Creating encrypted secrets for a repository documentation store the access key’s secret in the Github Repository for your static site.
  2. In the root of the static site project create a folder called .github which will store the .yml file which defines our GitHub Action mkdir -p .github/workflows. Within this folder create a file called main.yml
    1. Screenshot of main.yml file
    2. Lines 3-6 are the trigger for this GitHub Action. Reads as “When there’s a push to the master branch”
    3. Lines 8-9 starts the deployment job. If there were other actions defined in this file they would be at the same level of indention as line 9
    4. Line 10 declares what OS will run our deployment action. This files uses the latest Ubuntu image but there are other options
    5. Line 11 starts the steps which will be run on the OS image selected above. GitHub action’s purpose is to reduce repettive deployment actions.
    6. Lines 12 - 13 says to use the second version of the checkout action. This action checks-out your repository under $GITHUB_WORKSPACE, so your workflow can access it. If you want to learn what an action/* does you can look it up under the https://github.com/ACTION_NAME path.
    7. Lines 15-20 setup the use of the AWS CLI for use by our worker. These are setup via configure-aws-credentials
    8. Line 18 includes the AKID to be used for the upload process. An AKID alone should not be considered a secret. However, this does introduces an Avaliblity issue as this workflow is now tied directly to this AKID. If it’s ever disabled/loses permissions the job will fail.
    9. Line 19 makes use of the GitHub Actions Secrets API to get the value of the Z1G1_NEW_WRITE_ONLY secret created above. You never want to include the Secret Key for an AWS IAM user within a GitHub repo, or any source code
    10. Lines 22-23 declare that we will run the S3 Sync command to upload the output directory to the s3 bucket used to host the site. The --delete at the end of the command will remove files that exist in the destination but not in the source are deleted during sync
  3. When you run a git push to this repository your output folder will be written to the specified S3 bucket.

Debugging Deployment

There are a lot of moving parts to the setup which is created here. Capturing some debugging info if you run into errors.

  1. You need to ensure that your IAM User has permssions to:
    1. The PutObject and ListObject permissions. Both of these are required to run the S3 Sync command. In addtion as you want to be able to remove posts from the site, the GitHub action used in this example includes the --delete option. To acomplish this your IAM user needs to have the s3:DeleteObject permission as well.
    2. Has access to both the bucket "arn:aws:s3:::www.z1g1.net", and the objects inside it "arn:aws:s3:::www.z1g1.net/*",
  2. You might not be able to run git push after createing the workflow
    1. Ensure that your GitHub user / personal access token has permissions to create Workflows.
  3. You might get an error saying “actions/checkout@v2 and aws-actions/configure-aws-credentials@v1 are not allowed to be used in …”
    1. Items listed is Steps of a Github Actions (lines 13, and 16) are code that your runner is executing. It’s convenient to use code that other wrote so you don’t have to rewrite it. However, this does introduce some level of risk as you’re running code others wrote on a system you own. GitHub Actions has 4 levels of permissions to control what can run. These are defined in the repo settings and range (from most to least restrctive): no actions at all, actions owned by the current org/user, actions written by GitHub or orgs they have verified, any action.
    2. Screenshot of github actions permissions
    3. My repo is set to use GitHub and their verified plugins. I am trusting GitHub to write files to my S3 bucket so if they wanted to modify them they could, so this is an acceptable level of risk for me.

Example files

Sample IAM policy for GitHub Actions

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:ListBucket",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::www.z1g1.net/*",
                "arn:aws:s3:::www.z1g1.net"
            ]
        }
    ]
}

Sample main.yml for GitHub Actions

name: Upload Website via GitHub Actions

on:
  push:
    branches:
    - master

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v2

    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: AKIAR2JKVZGO665RVFMT
        aws-secret-access-key: $
        aws-region: us-east-1

    - name: Deploy static site to S3 bucket
      run: aws s3 sync ./output/ s3://www.z1g1.net --delete

2024

Back to Top ↑

2023

Back to Top ↑

2022

Setting up a site with Pelican

The site here is built using Pelican which is a static website generator. It allowed you to write webpages in markdown, then build them along with a config f...

Setting up WSL and Ubuntu on Windows

I wanted to get started with writing more online in 2022. I decided to setup a static site hosted out of S3 which I will cover in a future post. However, bef...

Back to Top ↑