Skip to content

Commit 3ea3b02

Browse files
committed
Update README
1 parent ab0f2e8 commit 3ea3b02

File tree

1 file changed

+276
-22
lines changed

1 file changed

+276
-22
lines changed

README.adoc

Lines changed: 276 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,296 @@
1-
= terraform-aws-lambda-edge-authentication
1+
= Terraform module for S3 website authentication via Lambda and CloudFront@Edge
22

3-
== Create the configuration file
3+
This module provides an AWS Lambda function that works together
4+
with CloudFront@Edge to authenticate S3 website resources (paths).
5+
6+
This module is developed for
7+
https://github.com/riboseinc/terraform-aws-s3-cloudfront-website[terraform-aws-s3-cloudfront-website],
8+
which helps set up a full static website using using S3, CloudFront and ACM.
9+
10+
NOTE: This module utilizes AWS Lambda -- a paid resource.
11+
Keep this in mind when adopting this solution.
12+
13+
NOTE: If you're using this module with https://github.com/riboseinc/terraform-aws-s3-cloudfront-website[terraform-aws-s3-cloudfront-website], please refer to its website for further instructions.
14+
Certain AWS quirks with regions are specifically explained there.
15+
16+
17+
== How it works
18+
19+
This module works through applying an AWS Lambda HTTP authentication function
20+
to the CloudFront@Edge distribution of the static website.
21+
22+
Specifically, this Lambda function is executed on every access to the site to check whether:
23+
24+
. the path being access should be protected
25+
. if so, authenticate the client:
26+
.. if the client was previously authentication (and therefore carries a cookie), allow
27+
.. with an HTTP authentication, if it matches the configuration, allow
28+
. if the client is allowed, place (or update) the cookie to allow for further access.
29+
30+
31+
== Setting up the module
32+
33+
Two things are required:
34+
35+
. A permission configuration file, used to configure the Lambda function for authentication.
36+
. Configuration in Terraform that deploys this module.
37+
38+
39+
== Configuration
40+
41+
To allow the Lambda function to be configurable dynamically
42+
(i.e. the configuration is not bound to Terraform), the configuration file
43+
(in JSON) is located in an S3 bucket that Terraform may or may not
44+
have access to.
45+
46+
NOTE: You could also manually create/update the configuration file.
47+
48+
You will need to create (or re-use) a S3 bucket to store the configuration
49+
file, and this configuration file must be readable for the Lambda function.
50+
51+
The configuration file does two things:
52+
53+
. Specifies a set of paths that are "`protected`" (i.e. authentication is required)
54+
. Specifies a set of usernames and passwords via `htpasswd`
55+
(the typical basic HTTP authentication method)
56+
57+
=== Authentication credentials
58+
59+
https://httpd.apache.org/docs/current/programs/htpasswd.html[`htpasswd` format]
60+
allows specification of usernames and password in a single file/string:
61+
62+
* each line (`\n`) contains one username and its corresponding password
63+
* within each line the username and password are separated by a colon (`:`)
64+
65+
For example:
66+
[source,htpasswd]
67+
----
68+
foobar:$2y$05$1h9cwwFusLcZCIUpdM7Gke.ei1E2QV6ORH/ZmvbR4h2tDGHb7q8lW
69+
zeebaa:$2y$05$aWBOi47GEOOoNB/ZUgdPY.NukDalyc.Bvn.S0aOlKDD9wp0R9mQHm
70+
----
71+
72+
Assume you want to create a user called `foobar` with a password `FooBar#PassW0RD`.
73+
74+
Run `htaccess` to generate access credentials to upload:
75+
76+
[source,sh]
77+
----
78+
$ htpasswd -nbB foobar FooBar#PassW0RD
79+
foobar:$2y$05$1h9cwwFusLcZCIUpdM7Gke.ei1E2QV6ORH/ZmvbR4h2tDGHb7q8lW
80+
----
81+
82+
83+
NOTE: This command uses `bcrypt` to store the password hash. While it is
84+
the best choice out of available `htpasswd` algorithms (MD5, SHA1, crypt),
85+
remember that by default there is no rate limiting on the Lambda function
86+
-- meaning that someone can brute force the passwords via the public interface.
87+
(You could use the `reserved_concurrent_executions` option to limit
88+
Lambda concurrency.)
89+
90+
=== Protected paths patterns
91+
92+
93+
The module uses
94+
https://github.com/micromatch/micromatch[micromatch] to implement
95+
wildcard and glob matching URIs, and all
96+
https://github.com/micromatch/micromatch#matching-features[Micromatch Features]
97+
are supported.
98+
99+
==== Blacklisting paths
100+
101+
These rules specify blacklisted paths.
102+
103+
[source,js]
104+
----
105+
/* protect particular file */
106+
"/sample.png",
107+
----
108+
109+
[source,js]
110+
----
111+
/* protects all files that end with `.png` inside a subdirectory */
112+
"/sample/*.png"
113+
----
114+
115+
==== Whitelisting paths
116+
117+
These rules whitelists otherwise publicly accessible files.
118+
119+
[source,js]
120+
----
121+
/* do not protect this particular file => all others are protected */
122+
"!/sample.png"
123+
----
124+
125+
==== Wildcards
126+
127+
Notice that a full wildcard require double asterisks.
128+
129+
[source,js]
130+
----
131+
/* all files (i.e. the whole site) */
132+
"**"
133+
----
134+
135+
[source,js]
136+
----
137+
/* all files that end in `.png` in the whole site */
138+
"**/*.png"
139+
----
140+
141+
[source,js]
142+
----
143+
/* all files inside the `/secret/` subdirectory */
144+
"/secret/**"
145+
----
146+
147+
=== Creating the configuration file
148+
149+
In the configuration file:
150+
151+
* the `htpassword` portion is serialized into a single string
152+
* the protected paths patterns are specified individually.
153+
154+
For example:
4155

5-
Sample:
6156
[source,json]
7157
----
8158
{
9-
/* store usernames and password for basic authentication of HTTP users in "htpasswd" Format */
10-
"htpasswd": "test:$apr1$uRhlu4OE$c5XdFhXUjm9PJtyyhI8WN/",
159+
/* store usernames and password in "htpasswd" format */
160+
"htpasswd": "foobar:$2y$05$1h9cwwFusLcZCIUpdM7Gke.ei1E2QV6ORH/ZmvbR4h2tDGHb7q8lW\nzeebaa:$2y$05$aWBOi47GEOOoNB/ZUgdPY.NukDalyc.Bvn.S0aOlKDD9wp0R9mQHm",
11161
12-
/* wildcard matching uris */
162+
/* path patterns to protect in micromatch syntax */
13163
"uriPatterns": [
14-
"/*.{png,txt}"
164+
165+
/* all files that end with `.png` or `.sh` in the first level */
166+
"/*.{png,sh}",
167+
168+
/* all files regardless of depth */
169+
"**"
15170
]
16171
}
17172
----
18173

19-
The module use https://github.com/micromatch/micromatch[micromatch] to implement wildcard and glob matching URIs. +
20-
So all https://github.com/micromatch/micromatch#matching-features[Micromatch Features] is supported
21174

22-
Blacklisting Files in example bellow require Basic Authentication to view
23-
[source,json]
175+
== Deploying this module
176+
177+
Create an S3 bucket and upload the configuration JSON file.
178+
179+
[source,hcl]
24180
----
25-
[
26-
"/sample.png", // protect particular file
27-
"/sample/*.png" //Protect all files inside a subdirectory
28-
]
181+
provider "aws" {
182+
region = "us-east-1"
183+
#description = "AWS Region for Cloudfront (ACM certs only supports us-east-1)"
184+
alias = "cloudfront"
185+
}
186+
187+
resource "aws_s3_bucket" "permissions" {
188+
bucket = "my-site-permissions"
189+
acl = "private"
190+
provider = "aws.cloudfront"
191+
}
192+
193+
resource "aws_s3_bucket_object" "permissions" {
194+
bucket = "${aws_s3_bucket.permissions.bucket}"
195+
key = "config.json"
196+
197+
# Assume that your configuration JSON file is stored locally at `config.json`
198+
source = "./config.json"
199+
etag = "${filemd5("./config.json")}"
200+
201+
provider = "aws.cloudfront"
202+
}
29203
----
30204

31-
Whitelisting publicly accessible files
32-
[source,json]
205+
Create the authentication Lambda function. Remember that it must
206+
use the same provider (same region) as the S3 bucket did.
207+
208+
[source,hcl]
209+
----
210+
module "staging-lambda" {
211+
source = "github.com/riboseinc/terraform-aws-lambda-edge-authentication"
212+
213+
/* S3 bucket that stores configuration JSON file. */
214+
bucketName = "${aws_s3_bucket.permissions.bucket}"
215+
216+
/* S3 object name of the configuration JSON file in the above bucket. */
217+
bucketKey = "${aws_s3_bucket_object.permissions.key}"
218+
219+
/* the domain scope of cookie to be set */
220+
cookieDomain = "my-s3-website-domain-name.com"
221+
222+
providers {
223+
"aws" = "aws.cloudfront"
224+
}
225+
}
33226
----
34-
[
35-
"!/sample.png" // not protect particular file => others are protected
36-
]
227+
228+
Then you have to associate the Lambda function with your CloudFront distribution
229+
using CloudFront@Edge.
230+
231+
[source,hcl]
232+
----
233+
resource "aws_cloudfront_distribution" "main-lambda-edge" {
234+
235+
provider = "aws.cloudfront"
236+
enabled = true
237+
http_version = "http2"
238+
aliases = "..."
239+
240+
origin {
241+
# ...
242+
243+
# Use a secret to authenticate CloudFront requests to origin
244+
custom_header {
245+
name = "User-Agent"
246+
value = "${var.refer_secret}"
247+
}
248+
}
249+
250+
default_cache_behavior {
251+
# ...
252+
253+
# Link the Lambda function to CloudFront request
254+
# for authenticating
255+
lambda_function_association {
256+
event_type = "viewer-request"
257+
lambda_arn = "${var.lambda_edge_arn_version}"
258+
}
259+
260+
# Link the Lambda function to CloudFront response
261+
# for setting the authenticated cookie
262+
lambda_function_association {
263+
event_type = "viewer-response"
264+
lambda_arn = "${var.lambda_edge_arn_version}"
265+
}
266+
}
267+
}
37268
----
38269

39270

271+
Now run `terraform apply` and see everything being setup.
272+
273+
274+
== Confirming functionality
275+
276+
To confirm this works:
277+
278+
. Visit a protected path in the browser and confirm that HTTP authentication
279+
is required. (You'll be prompted to log in.)
280+
281+
. Visit a protected path again in a browser, but this time with caches disabled.
282+
Check whether a cookie has been set in your request -- it should have been
283+
set in the previous successful authentication. It's working properly if you
284+
see it.
285+
286+
How awesome is this!
287+
288+
NOTE: If you're using this module with https://github.com/riboseinc/terraform-aws-s3-cloudfront-website[terraform-aws-s3-cloudfront-website], please refer to its website for further instructions.
289+
Certain AWS quirks with regions are specifically explained there.
290+
291+
292+
40293
== Development
41-
1. Run `npm run build` to build the lambda typescript code => `main.js` is generated
42-
2. Rerun `terraform apply` to upload new `main.js` (webpack compiled entry)
294+
295+
. Run `npm run build` to build the lambda typescript code => `main.js` is generated
296+
. Rerun `terraform apply` to upload new `main.js` (webpack compiled entry)

0 commit comments

Comments
 (0)