13
votes

I am looking to add Basic User Authentication to a Static Site I will have up on AWS so that only those with the proper username + password which I will supply to those users have access to see the site. I found s3auth and it seems to be exactly what I am looking for, however, I am wondering if I will need to somehow set the authorization for pages besides the index.html. For example, I have 3 pages- index, about and contact.html, without authentication setup for about.html what is stopping an individual for directly accessing the site via www.mywebsite.com/about.html? I am more so looking for clarification or any resources anyone can provide to explain this!

Thank you for your help!

2
I haven't used s3auth, but it looks be a gateway that sits in front of an entire bucket. You don't need to worry about permissions for individual objects. - bwest
@bwest Thanks for the reply. In any case, do you have another solution that would accomplish the same thing? I read about setting a Cloudfront Distribution in front of the bucket (somehow? still new so not sure how) and then using a lambda function to simulate an http basic auth? More or less looking for something relatively secure for a test environment where I and others have access via username and password. - Gideon B

2 Answers

35
votes

This is the perfect use for Lambda@Edge.

Since you're hosing your static site on S3, you can easily and very economically (pennies) add some really great features to your site by using CloudFront, AWS's content distribution network, to serve your site to your site. You can learn how to setup host your site on S3 with CloudFront (including 100% free SSL) here.

While your CloudFront distribution is deploying, you'll have some time to go setup you Lambda, that you'll be using to do the basic user auth. If this is your first time creating a Lambda or creating a Lambda for use @Edge the process is going to feel really complex, but if you follow my step-by-step instructions below you'll be doing serverless basic-auth that is infinitely scaleable in less than 10 minutes. I'm going to use us-east-1 for this and it's important to know that if you're using Lambda@Edge you should author your functions in us-east-1, and when they're associated with your CloudFront distribution they'll automagically be replicated globally. Let's begin...

  1. Head over to Lambda in the AWS console, and click on "Create Function"
  2. Create your Lambda from scratch and give it a name
  3. Set your runtime as Node.js 8.10
  4. Give your Lambda some permissions by selecting "Choose or create an execution role"
  5. Give the role a name
  6. From Policy Templates select "Basic Lambda@Edge permissions (for CloudFront trigger)"
  7. Click "Create function"
  8. Once your Lambda is created take the following code and paste it in to the index.js file of the Function Code section - you can update the username and password you want to use by changing the authUser and authPass variables:
'use strict';
exports.handler = (event, context, callback) => {

    // Get request and request headers
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    // Configure authentication
    const authUser = 'user';
    const authPass = 'pass';

    // Construct the Basic Auth string
    const authString = 'Basic ' + new Buffer(authUser + ':' + authPass).toString('base64');

    // Require Basic authentication
    if (typeof headers.authorization == 'undefined' || headers.authorization[0].value != authString) {
        const body = 'Unauthorized';
        const response = {
            status: '401',
            statusDescription: 'Unauthorized',
            body: body,
            headers: {
                'www-authenticate': [{key: 'WWW-Authenticate', value:'Basic'}]
            },
        };
        callback(null, response);
    }

    // Continue request processing if authentication passed
    callback(null, request);
};

  1. Click "Save" in the upper right hand corner.
  2. Now that your Lambda is saved it's ready to attach to your CloudFront distribution. In the upper menu, select Actions -> Deploy to Lambda@Edge.
  3. In the modal that appears select the CloudFront distribution you created earlier from the drop down menu, leave the Cache Behavior as *, and for the CloudFront Event change it to "Viewer Request", and finally select/tick "Include Body". Select/tick the Confirm deploy to Lambda@Edge and click "Deploy".

And now you wait. It takes a few minutes (15-20) to replicate your Lambda@Edge across all regions and edge locations. Go to CloudFront to monitor the deployment of your function. When your CloudFront Distribution Status says "Deployed" your Lambda@Edge function is ready to use.

1
votes

Deploying Lambda@edge is quiet difficult to replicate via console. So I have created CDK Stack that you just add your own credentials and domain name and deploy.

https://github.com/apoorvmote/cdk-examples/tree/master/password-protect-s3-static-site

I have tested the following function with Node12.x

exports.handler = async (event, context, callback) => {

const request = event.Records[0].cf.request

const headers = request.headers

const user = 'my-username'

const password = 'my-password'

const authString = 'Basic ' + Buffer.from(user + ':' + password).toString('base64')

if (typeof headers.authorization === 'undefined' || headers.authorization[0].value !== authString) {

    const response = {
        status: '401',
        statusDescription: 'Unauthorized',
        body: 'Unauthorized',
        headers: {
            'www-authenticate': [{key: 'WWW-Authenticate', value:'Basic'}]
        }
    }

    callback(null, response)
}

callback(null, request)
}