diff --git a/.github/workflows/aws_deploy_hydroserver.yml b/.github/workflows/aws_deploy_hydroserver.yml new file mode 100644 index 0000000..e69de29 diff --git a/.github/workflows/aws_deploy_infrastructure.yml b/.github/workflows/aws_deploy_infrastructure.yml new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore index 7e35246..81aae61 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ docs/.vitepress/cache/ docs/.vitepress/dist/ .idea* -.DS_Store \ No newline at end of file +.DS_Store +.terraform* +terraform.tfstate* \ No newline at end of file diff --git a/terraform/aws/cloudfront.tf b/terraform/aws/cloudfront.tf new file mode 100644 index 0000000..a09646a --- /dev/null +++ b/terraform/aws/cloudfront.tf @@ -0,0 +1,185 @@ +# ------------------------------------------------ # +# HydroServer CloudFront Distribution # +# ------------------------------------------------ # + +resource "aws_cloudfront_distribution" "hydroserver_distribution" { + origin { + domain_name = aws_s3_bucket.hydroserver_web_bucket.bucket_regional_domain_name + origin_access_control_id = aws_cloudfront_origin_access_control.hydroserver_oac.id + origin_id = "hydroserver-web" + } + + origin { + domain_name = aws_s3_bucket.hydroserver_static_bucket.bucket_regional_domain_name + origin_access_control_id = aws_cloudfront_origin_access_control.hydroserver_oac.id + origin_id = "hydroserver-static" + } + + origin { + domain_name = aws_s3_bucket.hydroserver_storage_bucket.bucket_regional_domain_name + origin_access_control_id = aws_cloudfront_origin_access_control.hydroserver_oac.id + origin_id = "hydroserver-storage" + } + + origin { + domain_name = aws_elastic_beanstalk_environment.hydroserver_django_env.endpoint_url + origin_id = "hydroserver-django" + + custom_origin_config { + http_port = "80" + https_port = "443" + origin_protocol_policy = "http-only" + origin_ssl_protocols = ["TLSv1", "TLSv1.1", "TLSv1.2"] + } + } + + default_cache_behavior { + target_origin_id = "hydroserver-web" + viewer_protocol_policy = "redirect-to-https" + + allowed_methods = ["GET", "HEAD", "OPTIONS"] + cached_methods = ["GET", "HEAD", "OPTIONS"] + + forwarded_values { + query_string = false + cookies { + forward = "none" + } + } + + function_association { + event_type = "viewer-request" + function_arn = aws_cloudfront_function.hydroserver_frontend_routing.arn + } + } + + ordered_cache_behavior { + path_pattern = "/api/sensorthings/*" + allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"] + cached_methods = ["GET", "HEAD"] + target_origin_id = "hydroserver-django" + viewer_protocol_policy = "allow-all" + forwarded_values { + query_string = false + cookies { + forward = "none" + } + } + } + + ordered_cache_behavior { + path_pattern = "/api/*" + allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"] + cached_methods = ["GET", "HEAD"] + target_origin_id = "hydroserver-django" + viewer_protocol_policy = "redirect-to-https" + forwarded_values { + query_string = false + cookies { + forward = "none" + } + } + } + + ordered_cache_behavior { + path_pattern = "/admin/*" + allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"] + cached_methods = ["GET", "HEAD"] + target_origin_id = "hydroserver-django" + viewer_protocol_policy = "redirect-to-https" + forwarded_values { + query_string = false + cookies { + forward = "none" + } + } + } + + restrictions { + geo_restriction { + restriction_type = "whitelist" + locations = ["US", "CA"] + } + } + + viewer_certificate { + cloudfront_default_certificate = true + minimum_protocol_version = "TLSv1.2_2021" + } + + enabled = true + is_ipv6_enabled = true + web_acl_id = aws_wafv2_web_acl.hydroserver_core_rules.arn +} + +# ------------------------------------------------ # +# HydroServer CloudFront Access Controls # +# ------------------------------------------------ # + +resource "aws_cloudfront_function" "hydroserver_frontend_routing" { + name = "frontend-routing" + runtime = "cloudfront-js-1.0" + comment = "Preserve Vue client-side routing." + code = file("${path.module}/frontend-routing.js") + publish = true +} + +resource "aws_cloudfront_origin_access_identity" "hydroserver_oai" {} + +resource "aws_cloudfront_origin_access_control" "hydroserver_oac" { + name = "hydroserver-${var.instance}-oac" + description = "" + origin_access_control_origin_type = "s3" + signing_behavior = "always" + signing_protocol = "sigv4" +} + +resource "aws_wafv2_web_acl" "hydroserver_core_rules" { + name = "CoreRulesWebACL" + scope = "CLOUDFRONT" + description = "WAF web ACL with Core Rules" + + default_action { + allow {} + } + + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "WAF_Common_Protections" + sampled_requests_enabled = true + } + + rule { + name = "AWS-AWSManagedRulesCommonRuleSet" + priority = 0 + override_action { + none { + } + } + statement { + managed_rule_group_statement { + name = "AWSManagedRulesCommonRuleSet" + vendor_name = "AWS" + rule_action_override { + action_to_use { + allow {} + } + name = "SizeRestrictions_BODY" + } + rule_action_override { + action_to_use { + allow {} + } + name = "NoUserAgent_HEADER" + } + } + } + visibility_config { + cloudwatch_metrics_enabled = true + metric_name = "AWS-AWSManagedRulesCommonRuleSet" + sampled_requests_enabled = true + } + } +} + + diff --git a/terraform/aws/elasticbeanstalk.tf b/terraform/aws/elasticbeanstalk.tf new file mode 100644 index 0000000..a871df1 --- /dev/null +++ b/terraform/aws/elasticbeanstalk.tf @@ -0,0 +1,132 @@ +# ------------------------------------------------ # +# HydroServer Elastic Beanstalk Application # +# ------------------------------------------------ # + +resource "aws_elastic_beanstalk_application" "hydroserver_django_app" { + name = "hydroserver-${var.instance}" + description = "HydroServer Django Application on Elastic Beanstalk" +} + +# ------------------------------------------------ # +# HydroServer Elastic Beanstalk Environment # +# ------------------------------------------------ # + +resource "aws_elastic_beanstalk_environment" "hydroserver_django_env" { + name = "hydroserver-${var.instance}-env" + application = aws_elastic_beanstalk_application.hydroserver_django_app.name + solution_stack_name = "64bit Amazon Linux 2 v3.5.11 running Python 3.8" + + setting { + namespace = "aws:elasticbeanstalk:environment" + name = "EnvironmentType" + value = "LoadBalanced" + } + + setting { + namespace = "aws:autoscaling:asg" + name = "MaxSize" + value = "1" + } + + setting { + namespace = "aws:autoscaling:launchconfiguration" + name = "IamInstanceProfile" + value = "aws-elasticbeanstalk-ec2-role" + } + + setting { + namespace = "aws:elasticbeanstalk:application:environment" + name = "ADMIN_EMAIL" + value = "" + } + + setting { + namespace = "aws:elasticbeanstalk:application:environment" + name = "ALLOWED_HOSTS" + value = "" + } + + setting { + namespace = "aws:elasticbeanstalk:application:environment" + name = "AWS_ACCESS_KEY_ID" + value = "" + } + + setting { + namespace = "aws:elasticbeanstalk:application:environment" + name = "AWS_SECRET_ACCESS_KEY" + value = "" + } + + setting { + namespace = "aws:elasticbeanstalk:application:environment" + name = "AWS_STORAGE_BUCKET_NAME" + value = "" + } + + setting { + namespace = "aws:elasticbeanstalk:application:environment" + name = "DATABASE_URL" + value = "" + } + + setting { + namespace = "aws:elasticbeanstalk:application:environment" + name = "DEBUG" + value = "True" + } + + setting { + namespace = "aws:elasticbeanstalk:application:environment" + name = "DEPLOYED" + value = "True" + } + + setting { + namespace = "aws:elasticbeanstalk:application:environment" + name = "OAUTH_GOOGLE_CLIENT" + value = "" + } + + setting { + namespace = "aws:elasticbeanstalk:application:environment" + name = "OAUTH_GOOGLE_SECRET" + value = "" + } + + setting { + namespace = "aws:elasticbeanstalk:application:environment" + name = "OAUTH_HYDROSHARE_CLIENT" + value = "" + } + + setting { + namespace = "aws:elasticbeanstalk:application:environment" + name = "OAUTH_HYDROSHARE_SECRET" + value = "" + } + + setting { + namespace = "aws:elasticbeanstalk:application:environment" + name = "OAUTH_ORCID_CLIENT" + value = "" + } + + setting { + namespace = "aws:elasticbeanstalk:application:environment" + name = "OAUTH_ORCID_SECRET" + value = "" + } + + setting { + namespace = "aws:elasticbeanstalk:application:environment" + name = "PROXY_BASE_URL" + value = "" + } + + setting { + namespace = "aws:elasticbeanstalk:application:environment" + name = "SECRET_KEY" + value = "" + } +} \ No newline at end of file diff --git a/terraform/aws/frontend-routing.js b/terraform/aws/frontend-routing.js new file mode 100644 index 0000000..9a2966a --- /dev/null +++ b/terraform/aws/frontend-routing.js @@ -0,0 +1,9 @@ +function handler(event) { + var request = event.request; + var uri = request.uri; + // If the URI ends with a slash or doesn't have a dot, return the main index.html + if (uri.endsWith("/") || !uri.includes(".")) { + request.uri = "/index.html"; + } + return request; +} \ No newline at end of file diff --git a/terraform/aws/frontend/main.tf b/terraform/aws/frontend/main.tf deleted file mode 100644 index 3c8f17d..0000000 --- a/terraform/aws/frontend/main.tf +++ /dev/null @@ -1,15 +0,0 @@ -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 4.16" - } - } - required_version = ">= 1.2.0" -} - -provider "aws" { - region = "us-east-1" -} - -variable "instance" {} diff --git a/terraform/aws/index.html b/terraform/aws/index.html new file mode 100644 index 0000000..006bd83 --- /dev/null +++ b/terraform/aws/index.html @@ -0,0 +1,45 @@ + + +
+ + +Congratulations! You've successfully set up the web infrastructure for HydroServer.
+To complete the setup, deploy the HydroServer application to this server.
+Refer to the HydroServer Documentation for deployment instructions.
+If you need assistance, contact support for help.
+