Het Security Office - a fully automated, secure and scaleable website

blog-image

As long as I can remember I’ve learned better by doing. Setting up Het Security Office is not an exception, and since I preach security as code I wanted not just to setup another WordPress blog.

My requirements were something:

  • that is fully automated. Note: some would consider this overkill!
  • that requires zero maintenance of infrastructure
  • that is secure by design
  • that scales forever
  • low cost at high traffic volumes

My requirements resulted in the following choices:

  • A static website - this will reduce the attack surface significantly.
  • Code will be versioned in a private GitHub repository.
  • Committed code changes needed to be automatically deployed.
  • Deployments need to be logged and notifications have to be send.
  • Content is hosted “serverless” - this will reduce the attack surface further.
  • Content is exposed though a Content Delivery Network, for security, scalability and performance.
  • HTTPS Only, TLS1.2 with no depreciated cipher suites.
  • Access to the website is being logged.
  • Infrastructure as code, everything needs to be setup automatically and have zero maintenance.
  • Authorisations needed to be role based.
  • Secrets, like API, HTTPS certificates and encryption keys need to be automatically managed.
  • Hugo CMS to create and manage content.

This resulted in the architecture below: Het Security Office architecture

Did I mention some would consider this overkill?

To setup I followed the steps following steps

Prequisites

  1. An AWS account with the possibility to pay for their services.

  2. A domain name registered in Route 53.

  3. aws command line interface installed. See AWS documentation

    aws --version
    aws-cli/1.16.150 Python/3.7.3 Darwin/19.6.0 botocore/1.12.140
    Your version output can/will differ!

  4. aws cli is configured with access credentials. See AWS documentation.
    To check:

    aws sts get-caller-identity
    {
        "UserId": "redacted",
        "Account": "redacted",
        "Arn": "redacted"
    }
    Output of aws sts command will be empty when not configured appropriately.

  5. A GitHub personal access token with scope (1) Repo and (2) admin:repo_hook that you will only use for AWS CodePipeline. To create one see GitHub documentation.

Let the fun begin!

  1. Lets store the GitHub personal access token in the AWS Parameter store. For securely managing this secret.
    github_token="your scoped GitHub Personal Access Token"
    github_token_key="GitHubCodePipelinePAT"
    region=us-east-1
    
    aws ssm put-parameter --name "$github_token_key" --type String \
    --value "$github_token" --type String --region=$region
    
    unset github_token
    I’ve chosen us-east-1 as my region, although living in Europe because AWS Certificate Manager, which we’ll use to create and manage HTTPS certificates, is only available in this region in combination with the Content Deliver Network (CloudFront).
    Note: for storing the personal access token I would have liked to use SecureString, but at the time of writing this is not supported by CloudFormation
  2. Now that we have stored the GitHub scope personal access token, it is time to spin up the infrastructure. Lets assign values to the used parameters in the CloudFormation template.
    region=us-east-1
    domain="your registered domain name"
    email="your email adres"
    template="the CloudFormationTemplate"
    stackname=${domain/./-}-$(date +%Y%m%d-%H%M)
    source_type=GitHub
    github_user="your GitHub username"
    github_repository="the name of your GitHub repository"
    github_token_key="GitHubCodePipelinePAT"
    And execute the CloudFormation template and get yourself a cup of coffee.
    aws cloudformation create-stack \
      --region "$region" \
      --stack-name "$stackname" \
      --capabilities CAPABILITY_IAM \
      --template-body "file://$template" \
      --tags "Key=Name,Value=$stackname" \
      --parameters \
        "ParameterKey=GeneratorLambdaFunctionS3Bucket,ParameterValue=run.alestic.com" \
        "ParameterKey=GeneratorLambdaFunctionS3Key,ParameterValue=lambda/aws-lambda-site-generator-hugo.zip" \
        "ParameterKey=DomainName,ParameterValue=$domain" \
        "ParameterKey=NotificationEmail,ParameterValue=$email" \
        "ParameterKey=SourceType,ParameterValue=$source_type" \
        "ParameterKey=GitHubRepository,ParameterValue=$github_repository" \
        "ParameterKey=GitHubUser,ParameterValue=$github_user" \
        "ParameterKey=GitHubToken,ParameterValue=$github_token_key"
    echo region=$region stackname=$stackname
    Note: I’ve chosen for e-mail validation of the AWS Certificate Manager certificate. Using DNS validation can take up to several hours which cause the creation of my CloudFormation stack to rollback
  3. The last step is to update the registrar with your hosted zone name servers. To get your hosted zone id do:
    hosted_zone_id=$(aws cloudformation describe-stacks \
      --region "$region" \
      --stack-name "$stackname" \
      --output text \
      --query 'Stacks[*].Outputs[?OutputKey==`HostedZoneId`].[OutputValue]')
    echo hosted_zone_id=$hosted_zone_id
    Now lets request the name servers for that specific hosted zone id:
    aws route53 get-hosted-zone \
      --id "$hosted_zone_id"    \
      --output text             \
      --query 'DelegationSet.NameServers'
    You can now go to your registered domains in AWS. Click on your domain and then click on ‘Add or edit name servers’. Add the name of your name serves (in order) and hit ‘Update’. Change the name servers screenshot

You can now commit code to your GitHub repo and it will be automatically deployed.