Skip to content

Commit 453b063

Browse files
committed
feat(oidc): add oidc support
\# Example 1. Create OIDC app just for the reference, gitlab_application is server wide and not ideal for use e.g. on gitlab.com, there's no support for the group applications ```tf resource "gitlab_application" "fridges_list" { name = "gitlab-app" redirect_url = "www.example.com" scopes = [ "openid", "read_user", "profile", "email", ] } ``` 2. use module ```tf module "static_site" { source = "cookielab/static-site/aws" version = "4.9.0" providers = { aws = aws aws.us_east_1 = aws.vir } domains = ["www.exmaple.com"] domain_zone_id = "aws_route53_zone.example_com.zone_id" s3_bucket_name = "example-com-web" gitlab_project_id = data.gitlab_project.app_fridges_list.id gitlab_environment = var.environment enable_deploy_role = true enable_deploy_user = false oidc = [ { # first oidc provider application_name = "gitlab" application_id = gitlab_application.gitlab_application.application_id client_secret = gitlab_application.gitlab_application.secret auth_url = "https://gitlab.com/oauth/authorize" token_url = "https://gitlab.com/oauth/token" }, { # second oidc provider application_name = "second" application_id = "second-oidc-app-id" client_secret = "second-oidc-client-secret" auth_url = "https://another-oidc-provicer.example.com/oauth/authorize" token_url = "https://another-oidc-provicer.example.com/oauth/token" } ] ``` Then https://www.example.com/?auth=APPLICATION_NAME forces auth with specified provider, e.g.: - https://www.example.com/?auth=gitlab - https://www.example.com/?auth=second This is messy. Perhaps we can get along with a single OIDC auth for the application or if no session cookie is present redirect to a url hosted on s3 bucket with constructed html from the oidc list. ``` <h1>Choose authentication method</h1> <ul> <li> <a href=/?auth=gitlab>gitlab</a> </li> <li> <a href=/?auth=second>gitlab</a> </li> </ul> ``` fmt cleanup review: update lambda engine version nodejs18.x -> nodejs22.x replace apigateway with lambda_function_url update docs + fix duplicate blocks of outdated documentation due to missing BEGIN_TF_DOCS comment review parametrize session_duration per provider, fix cookie domain comment sensitive log messages in lambdas
1 parent 04dbf9e commit 453b063

File tree

16 files changed

+732
-119
lines changed

16 files changed

+732
-119
lines changed

README.md

Lines changed: 11 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -24,93 +24,40 @@ module "static-site" {
2424
}
2525
```
2626

27-
## Requirements
28-
29-
| Name | Version |
30-
| ------------------------------------------------------------------------ | ------------- |
31-
| <a name="requirement_terraform"></a> [terraform](#requirement_terraform) | >= 1.1, < 2.0 |
32-
| <a name="requirement_aws"></a> [aws](#requirement_aws) | ~> 4.32 |
33-
34-
## Providers
35-
36-
| Name | Version |
37-
| ------------------------------------------------ | ------- |
38-
| <a name="provider_aws"></a> [aws](#provider_aws) | ~> 4.32 |
39-
40-
## Modules
41-
42-
| Name | Source | Version |
43-
| -------------------------------------------------------------------- | ----------------------------------- | ------- |
44-
| <a name="module_certificate"></a> [certificate](#module_certificate) | terraform-aws-modules/acm/aws | 4.3.1 |
45-
| <a name="module_gitlab"></a> [gitlab](#module_gitlab) | ./modules/gitlab | n/a |
46-
| <a name="module_s3_bucket"></a> [s3_bucket](#module_s3_bucket) | terraform-aws-modules/s3-bucket/aws | 3.6.0 |
47-
48-
## Resources
49-
50-
| Name | Type |
51-
| ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
52-
| [aws_cloudfront_distribution.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_distribution) | resource |
53-
| [aws_cloudfront_origin_access_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_access_identity) | resource |
54-
| [aws_iam_access_key.deploy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_access_key) | resource |
55-
| [aws_iam_user.deploy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user) | resource |
56-
| [aws_iam_user_policy.deploy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_user_policy) | resource |
57-
| [aws_route53_record.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
58-
| [aws_iam_policy_document.bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
59-
| [aws_iam_policy_document.deploy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
60-
61-
## Inputs
62-
63-
| Name | Description | Type | Default | Required |
64-
| --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | -------------- | ------------------ | :------: |
65-
| <a name="input_cloudfront_price_class"></a> [cloudfront_price_class](#input_cloudfront_price_class) | n/a | `string` | `"PriceClass_100"` | no |
66-
| <a name="input_domain_zone_id"></a> [domain_zone_id](#input_domain_zone_id) | The ID of the hosted zone for domain | `string` | n/a | yes |
67-
| <a name="input_domains"></a> [domains](#input_domains) | List of domain aliases. You can also specify wildcard eg.: `*.example.com` | `list(string)` | n/a | yes |
68-
| <a name="input_gitlab_environment"></a> [gitlab_environment](#input_gitlab_environment) | n/a | `string` | `"*"` | no |
69-
| <a name="input_gitlab_project_id"></a> [gitlab_project_id](#input_gitlab_project_id) | n/a | `string` | `null` | no |
70-
| <a name="input_logs_bucket"></a> [logs_bucket](#input_logs_bucket) | n/a | `string` | `null` | no |
71-
| <a name="input_s3_bucket_name"></a> [s3_bucket_name](#input_s3_bucket_name) | n/a | `string` | n/a | yes |
72-
| <a name="input_tags"></a> [tags](#input_tags) | n/a | `map(string)` | `{}` | no |
73-
74-
## Outputs
75-
76-
| Name | Description |
77-
| ----------------------------------------------------------------------------------------------------------------------------- | ----------- |
78-
| <a name="output_aws_access_key_id"></a> [aws_access_key_id](#output_aws_access_key_id) | n/a |
79-
| <a name="output_aws_cloudfront_distribution_id"></a> [aws_cloudfront_distribution_id](#output_aws_cloudfront_distribution_id) | n/a |
80-
| <a name="output_aws_s3_bucket_name"></a> [aws_s3_bucket_name](#output_aws_s3_bucket_name) | n/a |
81-
| <a name="output_aws_secret_access_key"></a> [aws_secret_access_key](#output_aws_secret_access_key) | n/a |
82-
8327
<!-- BEGIN_TF_DOCS -->
8428
## Requirements
8529

8630
| Name | Version |
8731
|------|---------|
8832
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.5, < 2.0 |
8933
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 5.27 |
90-
| <a name="requirement_gitlab"></a> [gitlab](#requirement\_gitlab) | >= 15.7, < 18.0 |
34+
| <a name="requirement_gitlab"></a> [gitlab](#requirement\_gitlab) | >= 15.7, < 19.0 |
9135

9236
## Providers
9337

9438
| Name | Version |
9539
|------|---------|
96-
| <a name="provider_aws"></a> [aws](#provider\_aws) | ~> 5.27 |
97-
| <a name="provider_gitlab"></a> [gitlab](#provider\_gitlab) | >= 15.7, < 18.0 |
40+
| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.61.0 |
41+
| <a name="provider_gitlab"></a> [gitlab](#provider\_gitlab) | 17.2.0 |
9842

9943
## Modules
10044

10145
| Name | Source | Version |
10246
|------|--------|---------|
103-
| <a name="module_certificate"></a> [certificate](#module\_certificate) | terraform-aws-modules/acm/aws | 5.1.1 |
47+
| <a name="module_certificate"></a> [certificate](#module\_certificate) | terraform-aws-modules/acm/aws | 5.2.0 |
10448
| <a name="module_gitlab"></a> [gitlab](#module\_gitlab) | ./modules/gitlab | n/a |
105-
| <a name="module_s3_bucket"></a> [s3\_bucket](#module\_s3\_bucket) | terraform-aws-modules/s3-bucket/aws | 4.8.0 |
49+
| <a name="module_oidc"></a> [oidc](#module\_oidc) | ./modules/oidc | n/a |
50+
| <a name="module_s3_bucket"></a> [s3\_bucket](#module\_s3\_bucket) | terraform-aws-modules/s3-bucket/aws | 4.11.0 |
10651

10752
## Resources
10853

10954
| Name | Type |
11055
|------|------|
56+
| [aws_cloudfront_cache_policy.oidc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_cache_policy) | resource |
11157
| [aws_cloudfront_distribution.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_distribution) | resource |
11258
| [aws_cloudfront_origin_access_control.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_access_control) | resource |
11359
| [aws_cloudfront_origin_access_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_access_identity) | resource |
60+
| [aws_cloudfront_origin_request_policy.oidc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_request_policy) | resource |
11461
| [aws_cloudfront_response_headers_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_response_headers_policy) | resource |
11562
| [aws_iam_access_key.deploy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_access_key) | resource |
11663
| [aws_iam_role.deploy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
@@ -158,6 +105,7 @@ module "static-site" {
158105
| <a name="input_logs_bucket_domain_name"></a> [logs\_bucket\_domain\_name](#input\_logs\_bucket\_domain\_name) | n/a | `string` | `null` | no |
159106
| <a name="input_max_ttl"></a> [max\_ttl](#input\_max\_ttl) | Maximum amount of time that you want objects to stay in a CloudFront cache | `number` | `86400` | no |
160107
| <a name="input_min_ttl"></a> [min\_ttl](#input\_min\_ttl) | Minimum amount of time that you want objects to stay in a CloudFront cache | `number` | `0` | no |
108+
| <a name="input_oidc"></a> [oidc](#input\_oidc) | List of OIDC providers | <pre>list(object({<br/> application_name = string<br/> application_id = string<br/> client_secret = string<br/> auth_url = string<br/> token_url = string<br/> session_druation = optional(number, 12 * 3600)<br/> }))</pre> | `[]` | no |
161109
| <a name="input_origin_path"></a> [origin\_path](#input\_origin\_path) | Cloudfront origin path | `string` | `""` | no |
162110
| <a name="input_override_status_code_403"></a> [override\_status\_code\_403](#input\_override\_status\_code\_403) | Override status code for 403 error | `number` | `403` | no |
163111
| <a name="input_override_status_code_404"></a> [override\_status\_code\_404](#input\_override\_status\_code\_404) | Override status code for 404 error | `number` | `200` | no |
@@ -182,5 +130,6 @@ module "static-site" {
182130
| <a name="output_aws_s3_bucket_name"></a> [aws\_s3\_bucket\_name](#output\_aws\_s3\_bucket\_name) | n/a |
183131
| <a name="output_aws_s3_bucket_regional_domain_name"></a> [aws\_s3\_bucket\_regional\_domain\_name](#output\_aws\_s3\_bucket\_regional\_domain\_name) | n/a |
184132
| <a name="output_aws_secret_access_key"></a> [aws\_secret\_access\_key](#output\_aws\_secret\_access\_key) | n/a |
133+
| <a name="output_oidc_callback_url"></a> [oidc\_callback\_url](#output\_oidc\_callback\_url) | n/a |
185134
| <a name="output_s3_kms_key_arn"></a> [s3\_kms\_key\_arn](#output\_s3\_kms\_key\_arn) | n/a |
186-
<!-- END_TF_DOCS -->
135+
<!-- END_TF_DOCS -->

main.tf

Lines changed: 109 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,51 @@ data "aws_cloudfront_cache_policy" "managed_caching_disabled" {
232232
name = "Managed-CachingDisabled"
233233
}
234234

235+
resource "aws_cloudfront_cache_policy" "oidc" {
236+
count = length(var.oidc) == 0 ? 0 : 1
237+
238+
name = "no-cache-oidc-policy"
239+
comment = "Disable caching for OIDC"
240+
default_ttl = 0
241+
min_ttl = 0
242+
max_ttl = 0
243+
244+
parameters_in_cache_key_and_forwarded_to_origin {
245+
cookies_config {
246+
cookie_behavior = "none"
247+
}
248+
249+
headers_config {
250+
header_behavior = "none"
251+
}
252+
253+
query_strings_config {
254+
query_string_behavior = "none"
255+
}
256+
257+
#enable_accept_encoding_gzip = true
258+
}
259+
}
260+
261+
resource "aws_cloudfront_origin_request_policy" "oidc" {
262+
count = length(var.oidc) == 0 ? 0 : 1
263+
264+
name = "oidc-origin-policy"
265+
comment = "Forward all cookies and query strings for OIDC"
266+
267+
cookies_config {
268+
cookie_behavior = "all"
269+
}
270+
271+
headers_config {
272+
header_behavior = "none"
273+
}
274+
275+
query_strings_config {
276+
query_string_behavior = "all"
277+
}
278+
}
279+
235280
resource "aws_cloudfront_distribution" "this" {
236281
comment = local.main_domain
237282

@@ -243,6 +288,22 @@ resource "aws_cloudfront_distribution" "this" {
243288
origin_path = var.origin_path
244289
}
245290

291+
dynamic "origin" {
292+
for_each = length(var.oidc) == 0 ? [] : [1]
293+
294+
content {
295+
domain_name = split("/", module.oidc.oidc_callback_url_base)[2]
296+
origin_id = "api-gateway-origin"
297+
298+
custom_origin_config {
299+
http_port = 80
300+
https_port = 443
301+
origin_protocol_policy = "https-only"
302+
origin_ssl_protocols = ["TLSv1.2"]
303+
}
304+
}
305+
}
306+
246307
dynamic "origin" {
247308
for_each = var.proxy_paths
248309

@@ -265,18 +326,26 @@ resource "aws_cloudfront_distribution" "this" {
265326
is_ipv6_enabled = true
266327
default_root_object = "index.html"
267328

268-
custom_error_response {
269-
error_caching_min_ttl = 3000
270-
error_code = 404
271-
response_code = var.override_status_code_404
272-
response_page_path = "/index.html"
273-
}
329+
dynamic "custom_error_response" {
330+
for_each = length(var.oidc) > 0 ? [] : [
331+
{
332+
error_code = 404
333+
response_code = var.override_status_code_404
334+
response_page_path = "/index.html"
335+
},
336+
{
337+
error_code = 403
338+
response_code = var.override_status_code_403
339+
response_page_path = "/index.html"
340+
}
341+
]
274342

275-
custom_error_response {
276-
error_caching_min_ttl = 3000
277-
error_code = 403
278-
response_code = var.override_status_code_403
279-
response_page_path = "/index.html"
343+
content {
344+
error_caching_min_ttl = 3000
345+
error_code = custom_error_response.value.error_code
346+
response_code = custom_error_response.value.response_code
347+
response_page_path = custom_error_response.value.response_page_path
348+
}
280349
}
281350

282351
default_cache_behavior {
@@ -289,7 +358,7 @@ resource "aws_cloudfront_distribution" "this" {
289358
query_string = false
290359

291360
cookies {
292-
forward = "none"
361+
forward = length(var.oidc) == 0 ? "none" : "all"
293362
}
294363
}
295364

@@ -298,6 +367,15 @@ resource "aws_cloudfront_distribution" "this" {
298367
default_ttl = var.default_ttl
299368
max_ttl = var.max_ttl
300369

370+
dynamic "lambda_function_association" {
371+
for_each = module.oidc.lambda_edge_function_arn != null ? [module.oidc.lambda_edge_function_arn] : []
372+
content {
373+
event_type = "viewer-request"
374+
lambda_arn = lambda_function_association.value
375+
include_body = false
376+
}
377+
}
378+
301379
dynamic "function_association" {
302380
for_each = concat(
303381
var.functions.viewer_request == null ? [] : [
@@ -321,6 +399,25 @@ resource "aws_cloudfront_distribution" "this" {
321399
}
322400
}
323401

402+
dynamic "ordered_cache_behavior" {
403+
for_each = length(var.oidc) == 0 ? [] : [1]
404+
405+
content {
406+
path_pattern = "/callback*"
407+
target_origin_id = "api-gateway-origin"
408+
409+
allowed_methods = ["GET", "HEAD", "OPTIONS"]
410+
cached_methods = ["GET", "HEAD"]
411+
412+
viewer_protocol_policy = "redirect-to-https"
413+
414+
compress = true
415+
416+
cache_policy_id = aws_cloudfront_cache_policy.oidc[0].id
417+
origin_request_policy_id = aws_cloudfront_origin_request_policy.oidc[0].id
418+
}
419+
}
420+
324421
dynamic "ordered_cache_behavior" {
325422
for_each = var.proxy_paths
326423

modules/gitlab/README.md

Lines changed: 3 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,61 +2,20 @@
22

33
This module will setup GitLab CI variables for static website deployment.
44

5-
## Requirements
6-
7-
| Name | Version |
8-
| ------------------------------------------------------------------------ | ------------- |
9-
| <a name="requirement_terraform"></a> [terraform](#requirement_terraform) | >= 1.1, < 2.0 |
10-
| <a name="requirement_gitlab"></a> [gitlab](#requirement_gitlab) | >= 15.7, < 18.0 |
11-
12-
## Providers
13-
14-
| Name | Version |
15-
| --------------------------------------------------------- | ------- |
16-
| <a name="provider_gitlab"></a> [gitlab](#provider_gitlab) | >= 15.7, < 18.0 |
17-
18-
## Modules
19-
20-
No modules.
21-
22-
## Resources
23-
24-
| Name | Type |
25-
| ---------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
26-
| [gitlab_project_variable.cloudfront_distribution_id](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs/resources/project_variable) | resource |
27-
| [gitlab_project_variable.s3_bucket](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs/resources/project_variable) | resource |
28-
| [gitlab_project_variable.site_aws_access_key_id](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs/resources/project_variable) | resource |
29-
| [gitlab_project_variable.site_aws_secret_access_key](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs/resources/project_variable) | resource |
30-
| [gitlab_project.this](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs/data-sources/project) | data source |
31-
32-
## Inputs
33-
34-
| Name | Description | Type | Default | Required |
35-
| --------------------------------------------------------------------------------------------------------------------------- | ----------- | -------- | ------- | :------: |
36-
| <a name="input_aws_access_key_id"></a> [aws_access_key_id](#input_aws_access_key_id) | n/a | `string` | n/a | yes |
37-
| <a name="input_aws_cloudfront_distribution_id"></a> [aws_cloudfront_distribution_id](#input_aws_cloudfront_distribution_id) | n/a | `string` | n/a | yes |
38-
| <a name="input_aws_s3_bucket_name"></a> [aws_s3_bucket_name](#input_aws_s3_bucket_name) | n/a | `string` | n/a | yes |
39-
| <a name="input_aws_secret_access_key"></a> [aws_secret_access_key](#input_aws_secret_access_key) | n/a | `string` | n/a | yes |
40-
| <a name="input_gitlab_environment"></a> [gitlab_environment](#input_gitlab_environment) | n/a | `string` | `"*"` | no |
41-
| <a name="input_gitlab_project_id"></a> [gitlab_project_id](#input_gitlab_project_id) | n/a | `string` | n/a | yes |
42-
43-
## Outputs
44-
45-
No outputs.
465

476
<!-- BEGIN_TF_DOCS -->
487
## Requirements
498

509
| Name | Version |
5110
|------|---------|
5211
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.5, < 2.0 |
53-
| <a name="requirement_gitlab"></a> [gitlab](#requirement\_gitlab) | >= 15.7, < 18.0 |
12+
| <a name="requirement_gitlab"></a> [gitlab](#requirement\_gitlab) | >= 15.7, < 19.0 |
5413

5514
## Providers
5615

5716
| Name | Version |
5817
|------|---------|
59-
| <a name="provider_gitlab"></a> [gitlab](#provider\_gitlab) | >= 15.7, < 18.0 |
18+
| <a name="provider_gitlab"></a> [gitlab](#provider\_gitlab) | >= 15.7, < 19.0 |
6019

6120
## Modules
6221

@@ -95,4 +54,4 @@ No modules.
9554
## Outputs
9655

9756
No outputs.
98-
<!-- END_TF_DOCS -->
57+
<!-- END_TF_DOCS -->

modules/gitlab/main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ locals {
22
cicd_variable_flat_list = flatten([
33
for project_id in var.gitlab_project_ids : [
44
for variable in var.extra_gitlab_cicd_variables : {
5-
id = "${project_id}-${variable.key}"
5+
id = "${project_id}-${variable.key}"
66
project_id = project_id
77
variable = variable
88
}

0 commit comments

Comments
 (0)