diff --git a/.gitignore b/.gitignore index 6304eb3..c377a8b 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ override.tf.json # Ignore CLI configuration files .terraformrc terraform.rc + +.idea/ \ No newline at end of file diff --git a/README.md b/README.md index 94c68dc..b96ba14 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# dandi-infrastructure -Deployment infrastructure for the DANDI Archive. +# linc-archive-infrastructure +Deployment infrastructure for the LINC Brain Archive. diff --git a/terraform/.terraform.lock.hcl b/terraform/.terraform.lock.hcl index 66378d7..47cb290 100644 --- a/terraform/.terraform.lock.hcl +++ b/terraform/.terraform.lock.hcl @@ -3,9 +3,10 @@ provider "registry.terraform.io/hashicorp/aws" { version = "5.17.0" - constraints = ">= 4.9.0" + constraints = ">= 4.9.0, >= 4.30.0" hashes = [ "h1:U+EDfeUqefebA1h7KyBMD1xH0h311LMi7wijPDPkC/0=", + "h1:rplvK7UGP2FuzM44t2eRX+QYYPC0aUIoKdi5XayRI8M=", "zh:0087b9dd2c9c638fd63e527e5b9b70988008e263d480a199f180efe5a4f070f0", "zh:0fd532a4fd03ddef11f0502ff9fe4343443e1ae805cb088825a71d6d48906ec7", "zh:16411e731100cd15f7e165f53c23be784b2c86c2fcfd34781e0642d17090d342", @@ -28,6 +29,7 @@ provider "registry.terraform.io/hashicorp/local" { version = "2.4.0" hashes = [ "h1:R97FTYETo88sT2VHfMgkPU3lzCsZLunPftjSI5vfKe8=", + "h1:ZUEYUmm2t4vxwzxy1BvN1wL6SDWrDxfH7pxtzX8c6d0=", "zh:53604cd29cb92538668fe09565c739358dc53ca56f9f11312b9d7de81e48fab9", "zh:66a46e9c508716a1c98efbf793092f03d50049fa4a83cd6b2251e9a06aca2acf", "zh:70a6f6a852dd83768d0778ce9817d81d4b3f073fab8fa570bff92dcb0824f732", @@ -46,6 +48,7 @@ provider "registry.terraform.io/hashicorp/local" { provider "registry.terraform.io/hashicorp/random" { version = "3.5.1" hashes = [ + "h1:IL9mSatmwov+e0+++YX2V6uel+dV6bn+fC/cnGDK3Ck=", "h1:VSnd9ZIPyfKHOObuQCaKfnjIHRtR7qTw19Rz8tJxm+k=", "zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64", "zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d", @@ -66,6 +69,7 @@ provider "registry.terraform.io/heroku/heroku" { version = "5.2.6" constraints = ">= 5.0.0" hashes = [ + "h1:YQl8iAwo1sEzUd4zhLs87nuk3La/iNyLa4G0eLfvSvA=", "h1:a8S25Rq0oLoUeIjpNpEaeuTlw0vua4o1vTPbrjU4aSg=", "zh:1a50b1749f49377edf368695309a8945435e8c2803adb981a382fa87a4eda1f0", "zh:1d2c157bf1619acdc0f02f0f43c4852d38fce1e75b23a8c4f32a0751cc31fa7a", @@ -90,6 +94,7 @@ provider "registry.terraform.io/jianyuan/sentry" { version = "0.12.2" hashes = [ "h1:7VdazIutMx8oGuy8S6iC1rL2IaU8S6qFzZO0dVzta9A=", + "h1:e7ldfvSDL4VSTNGNvr8439/ttvY9KpVzAcg4tDKk304=", "zh:0dde99e7b343fa01f8eefc378171fb8621bedb20f59157d6cc8e3d46c738105f", "zh:1b0d79eb5343187724c85996b3972d00daf242569395f6ca2e1a88286146223e", "zh:22b6766f1fae35823b3881c198e3965c690cb87309af1527df9cc5781f7633d4", diff --git a/terraform/api.tf b/terraform/api.tf index a8ea0a0..a1d8e71 100644 --- a/terraform/api.tf +++ b/terraform/api.tf @@ -1,14 +1,14 @@ -data "heroku_team" "dandi" { - name = "dandi" +data "heroku_team" "linc-brain-mit" { + name = "linc-brain-mit" } module "api" { source = "girder/girder4/heroku" version = "0.13.0" - project_slug = "dandi-api" - heroku_team_name = data.heroku_team.dandi.name - route53_zone_id = aws_route53_zone.dandi.zone_id + project_slug = "linc-brain-prod" + heroku_team_name = data.heroku_team.linc-brain-mit.name + route53_zone_id = aws_route53_zone.linc-brain-mit.zone_id subdomain_name = "api" heroku_web_dyno_size = "standard-2x" @@ -20,32 +20,39 @@ module "api" { heroku_web_dyno_quantity = 1 heroku_worker_dyno_quantity = 1 - django_default_from_email = "admin@api.dandiarchive.org" - django_cors_origin_whitelist = ["https://dandiarchive.org"] - django_cors_origin_regex_whitelist = ["^https:\\/\\/[0-9a-z\\-]+--gui-dandiarchive-org\\.netlify\\.app$"] + django_default_from_email = "admin@api.lincbrain.org" + django_cors_origin_whitelist = ["https://lincbrain.org"] + django_cors_origin_regex_whitelist = ["^https:\\/\\/[0-9a-z\\-]+\\.netlify\\.app$"] - additional_django_vars = { - DJANGO_CONFIGURATION = "HerokuProductionConfiguration" - DJANGO_DANDI_DANDISETS_BUCKET_NAME = module.sponsored_dandiset_bucket.bucket_name - DJANGO_DANDI_DANDISETS_BUCKET_PREFIX = "" - DJANGO_DANDI_DANDISETS_EMBARGO_BUCKET_NAME = module.sponsored_embargo_bucket.bucket_name - DJANGO_DANDI_DANDISETS_EMBARGO_BUCKET_PREFIX = "" - DJANGO_DANDI_DANDISETS_LOG_BUCKET_NAME = module.sponsored_dandiset_bucket.log_bucket_name - DJANGO_DANDI_DANDISETS_EMBARGO_LOG_BUCKET_NAME = module.sponsored_embargo_bucket.log_bucket_name - DJANGO_DANDI_DOI_API_URL = "https://api.datacite.org/dois" - DJANGO_DANDI_DOI_API_USER = "dartlib.dandi" - DJANGO_DANDI_DOI_API_PREFIX = "10.48324" - DJANGO_DANDI_DOI_PUBLISH = "true" - DJANGO_SENTRY_DSN = data.sentry_key.this.dsn_public - DJANGO_SENTRY_ENVIRONMENT = "production" - DJANGO_CELERY_WORKER_CONCURRENCY = "4" - DJANGO_DANDI_WEB_APP_URL = "https://dandiarchive.org" - DJANGO_DANDI_API_URL = "https://api.dandiarchive.org" - DJANGO_DANDI_JUPYTERHUB_URL = "https://hub.dandiarchive.org/" - } - additional_sensitive_django_vars = { - DJANGO_DANDI_DOI_API_PASSWORD = var.doi_api_password - } + additional_django_vars = { + CLOUDFRONT_BASE_URL = "lincbrain.org" + CLOUDFRONT_NEUROGLANCER_URL = "https://neuroglancer.lincbrain.org" + CLOUDFRONT_PEM_KEY_ID = "K3OG4MF62CGEDN" + CLOUDFRONT_PRIVATE_PEM_S3_LOCATION = "cloudfront/private_key_prod_new.pem" + DJANGO_CONFIGURATION = "HerokuProductionConfiguration" + DJANGO_DANDI_DANDISETS_BUCKET_NAME = module.sponsored_lincset_bucket_us_east_2.bucket_name + DJANGO_DANDI_DANDISETS_BUCKET_PREFIX = "" + DJANGO_DANDI_DANDISETS_EMBARGO_BUCKET_NAME = module.sponsored_embargo_bucket_us_east_2.bucket_name + DJANGO_DANDI_DANDISETS_EMBARGO_BUCKET_PREFIX = "" + DJANGO_DANDI_DANDISETS_LOG_BUCKET_NAME = module.sponsored_lincset_bucket_us_east_2.log_bucket_name + DJANGO_DANDI_DANDISETS_EMBARGO_LOG_BUCKET_NAME = module.sponsored_embargo_bucket_us_east_2.log_bucket_name + DJANGO_DANDI_DOI_API_URL = "https://api.datacite.org/dois" + DJANGO_DANDI_DOI_API_USER = "temp.dandi" + DJANGO_DANDI_DOI_API_PREFIX = "temp" + DJANGO_DANDI_DOI_PUBLISH = "true" + DJANGO_SENTRY_DSN = "https://833c159dc622528b21b4ce4adef6dbf8@o4506237212033024.ingest.sentry.io/4506237213212672" + DJANGO_SENTRY_ENVIRONMENT = "production" + DJANGO_CELERY_WORKER_CONCURRENCY = "4" + DJANGO_DANDI_WEB_APP_URL = "https://lincbrain.org" + DJANGO_DANDI_API_URL = "https://api.lincbrain.org" + DJANGO_DANDI_JUPYTERHUB_URL = "https://hub.lincbrain.org" + WEBKNOSSOS_API_URL = "https://webknossos.lincbrain.org" + WEBKNOSSOS_ORGANIZATION_DISPLAY_NAME = "LINC" + WEBKNOSSOS_ORGANIZATION_NAME = "LINC" + } + additional_sensitive_django_vars = { + DJANGO_DANDI_DOI_API_PASSWORD = "temp" + } } resource "heroku_formation" "api_checksum_worker" { @@ -64,4 +71,4 @@ resource "heroku_formation" "api_analytics_worker" { data "aws_iam_user" "api" { user_name = module.api.heroku_iam_user_id -} +} \ No newline at end of file diff --git a/terraform/domain.tf b/terraform/domain.tf index 525ccd5..8aedaca 100644 --- a/terraform/domain.tf +++ b/terraform/domain.tf @@ -1,41 +1,61 @@ -resource "aws_route53_zone" "dandi" { - name = "dandiarchive.org" -} - -resource "aws_route53_record" "acm_validation" { - zone_id = aws_route53_zone.dandi.zone_id - name = "_cbe41dfe1888c2bb5c157cacc35e1722" - type = "CNAME" - ttl = "300" - records = ["_46df7ee9a9c17698aedbb737f220c63a.mzlfeqexyx.acm-validations.aws"] +resource "aws_route53_zone" "linc-brain-mit" { + name = "lincbrain.org" } resource "aws_route53_record" "gui" { - zone_id = aws_route53_zone.dandi.zone_id + zone_id = aws_route53_zone.linc-brain-mit.zone_id name = "" # apex type = "A" ttl = "300" - records = ["75.2.60.5"] # Netlify's load balancer, which will proxy to our app + records = ["75.2.60.5"] # Netlify's load balancer, which will proxy to our app -- https://docs.netlify.com/domains-https/custom-domains/configure-external-dns/#configure-an-apex-domain } -resource "aws_route53_record" "gui-staging" { - zone_id = aws_route53_zone.dandi.zone_id - name = "gui-staging" - type = "CNAME" - ttl = "300" - records = ["gui-staging-dandiarchive-org.netlify.com"] +# resource "aws_route53_record" "gui-staging" { +# zone_id = aws_route53_zone.linc-brain-mit.zone_id +# name = "gui-staging" +# type = "CNAME" +# ttl = "300" +# records = ["staging--gui-staging-lincbrain-org.netlify.app"] +# } + +resource "aws_acm_certificate" "cert" { + domain_name = "lincbrain.org" + validation_method = "DNS" + + subject_alternative_names = [ + "*.lincbrain.org" + ] } -resource "aws_route53_record" "www" { - zone_id = aws_route53_zone.dandi.zone_id - name = "www" - type = "CNAME" + +resource "aws_route53_record" "validation" { + for_each = { + for domain_validation_option in aws_acm_certificate.cert.domain_validation_options : domain_validation_option.domain_name => { + name = domain_validation_option.resource_record_name + record = domain_validation_option.resource_record_value + type = domain_validation_option.resource_record_type + } + } + + zone_id = aws_route53_zone.linc-brain-mit.zone_id + name = each.value.name + type = each.value.type + records = [each.value.record] ttl = "300" - records = ["dandi.github.io"] + + lifecycle { + create_before_destroy = true + ignore_changes = [records, name, type] + } +} + +resource "aws_acm_certificate_validation" "cert" { + certificate_arn = aws_acm_certificate.cert.arn + validation_record_fqdns = [for record in aws_route53_record.validation : record.fqdn] } resource "aws_route53_record" "email" { - zone_id = aws_route53_zone.dandi.zone_id + zone_id = aws_route53_zone.linc-brain-mit.zone_id name = "" # apex type = "MX" ttl = "300" @@ -46,7 +66,7 @@ resource "aws_route53_record" "email" { } resource "aws_route53_record" "email-spf" { - zone_id = aws_route53_zone.dandi.zone_id + zone_id = aws_route53_zone.linc-brain-mit.zone_id name = "" # apex type = "TXT" ttl = "300" diff --git a/terraform/main.tf b/terraform/main.tf index 577673e..4a3cc71 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -1,37 +1,49 @@ terraform { backend "remote" { - organization = "dandi" + organization = "linc-brain-mit" workspaces { - name = "dandi-prod" + name = "linc-archive-terraform" } } } -// This is the "project" account, the primary account with most resources provider "aws" { - region = "us-east-2" - allowed_account_ids = ["278212569472"] - # Must set AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY envvars + region = "us-east-1" + allowed_account_ids = ["151312473579"] + + assume_role { + role_arn = "arn:aws:iam::151312473579:role/linc-infrastructure" + } +} + +provider "aws" { + region = "us-east-2" + alias = "target" + allowed_account_ids = ["151312473579"] + + assume_role { + role_arn = "arn:aws:iam::151312473579:role/linc-infrastructure" + } } // The "sponsored" account, the Amazon-sponsored account with the public bucket provider "aws" { alias = "sponsored" - region = "us-east-2" - allowed_account_ids = ["769362853226"] + region = "us-east-1" + allowed_account_ids = ["151312473579"] # TODO: Aaron make new ID // This will authenticate using credentials from the project account, then assume the - // "dandi-infrastructure" role from the sponsored account to manage resources there + // "linc-infrastructure" role from the sponsored account to manage resources there assume_role { - role_arn = "arn:aws:iam::769362853226:role/dandi-infrastructure" + role_arn = "arn:aws:iam::151312473579:role/linc-infrastructure" } # Must set AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY envvars for project account } provider "heroku" { - # Must set HEROKU_EMAIL, HEROKU_API_KEY envvars + } provider "sentry" { diff --git a/terraform/modules/dandiset_bucket/log_bucket.tf b/terraform/modules/dandiset_bucket/log_bucket.tf deleted file mode 100644 index 431a30b..0000000 --- a/terraform/modules/dandiset_bucket/log_bucket.tf +++ /dev/null @@ -1,98 +0,0 @@ -data "aws_canonical_user_id" "log_bucket_owner_account" {} - -resource "aws_s3_bucket" "log_bucket" { - bucket = var.log_bucket_name - - lifecycle { - prevent_destroy = true - } -} - -resource "aws_s3_bucket_server_side_encryption_configuration" "log_bucket" { - bucket = aws_s3_bucket.log_bucket.id - - rule { - apply_server_side_encryption_by_default { - sse_algorithm = "AES256" - } - } -} - -data "aws_iam_policy_document" "dandiset_log_bucket_policy" { - statement { - resources = [ - "${aws_s3_bucket.log_bucket.arn}", - "${aws_s3_bucket.log_bucket.arn}/*", - ] - - actions = [ - # Needed for the app to process logs for collecting download analytics - "s3:GetObject", - "s3:ListBucket", - ] - - principals { - type = "AWS" - identifiers = [var.heroku_user.arn] - } - } - - statement { - sid = "S3ServerAccessLogsPolicy" - effect = "Allow" - resources = ["${aws_s3_bucket.log_bucket.arn}/*"] - actions = ["s3:PutObject"] - - condition { - test = "StringEquals" - variable = "aws:SourceAccount" - values = [data.aws_caller_identity.current.account_id] - } - - condition { - test = "ArnLike" - variable = "aws:SourceArn" - values = [aws_s3_bucket.dandiset_bucket.arn] - } - - principals { - type = "Service" - identifiers = ["logging.s3.amazonaws.com"] - } - } -} - -resource "aws_s3_bucket_policy" "dandiset_log_bucket_policy" { - provider = aws - - bucket = aws_s3_bucket.log_bucket.id - policy = data.aws_iam_policy_document.dandiset_log_bucket_policy.json -} - -data "aws_iam_policy_document" "dandiset_log_bucket_owner" { - version = "2008-10-17" - - // TODO: gate behind a "cross account" flag, since this is technically only - // needed for sponsored log bucket. - statement { - resources = [ - "${aws_s3_bucket.log_bucket.arn}", - "${aws_s3_bucket.log_bucket.arn}/*", - ] - - actions = [ - "s3:GetObject", - "s3:ListBucket", - ] - } -} - -resource "aws_iam_user_policy" "dandiset_log_bucket_owner" { - // The Heroku IAM user will always be in the project account - provider = aws.project - - name = "${var.log_bucket_name}-ownership-policy" - user = var.heroku_user.user_name - - policy = data.aws_iam_policy_document.dandiset_log_bucket_owner.json -} diff --git a/terraform/modules/dandiset_bucket/main.tf b/terraform/modules/dandiset_bucket/main.tf deleted file mode 100644 index c3948ff..0000000 --- a/terraform/modules/dandiset_bucket/main.tf +++ /dev/null @@ -1,292 +0,0 @@ -data "aws_caller_identity" "sponsored_account" { - provider = aws -} - -data "aws_caller_identity" "current" {} - -resource "aws_s3_bucket" "dandiset_bucket" { - - bucket = var.bucket_name - - lifecycle { - prevent_destroy = true - } -} - -resource "aws_s3_bucket_server_side_encryption_configuration" "dandiset_bucket" { - bucket = aws_s3_bucket.dandiset_bucket.id - - rule { - apply_server_side_encryption_by_default { - sse_algorithm = "AES256" - } - } -} - -resource "aws_s3_bucket_cors_configuration" "dandiset_bucket" { - bucket = aws_s3_bucket.dandiset_bucket.id - - cors_rule { - allowed_origins = [ - "*", - ] - allowed_methods = [ - "PUT", - "POST", - "GET", - "DELETE", - ] - allowed_headers = [ - "*" - ] - expose_headers = [ - "ETag", - ] - max_age_seconds = 3000 - } -} - -resource "aws_s3_bucket_logging" "dandiset_bucket" { - bucket = aws_s3_bucket.dandiset_bucket.id - - target_bucket = aws_s3_bucket.log_bucket.id - target_prefix = "" -} - -resource "aws_s3_bucket_versioning" "dandiset_bucket" { - count = var.versioning ? 1 : 0 - - bucket = aws_s3_bucket.dandiset_bucket.id - - versioning_configuration { - status = "Enabled" - } -} - -resource "aws_s3_bucket_ownership_controls" "dandiset_bucket" { - bucket = aws_s3_bucket.dandiset_bucket.id - - rule { - object_ownership = "BucketOwnerPreferred" - } -} - -resource "aws_s3_bucket_acl" "dandiset_bucket" { - depends_on = [aws_s3_bucket_ownership_controls.dandiset_bucket] - - bucket = aws_s3_bucket.dandiset_bucket.id - - // Public access is granted via a bucket policy, not a canned ACL - acl = "private" -} - -resource "aws_iam_user_policy" "dandiset_bucket_owner" { - // The Heroku IAM user will always be in the project account - provider = aws.project - - name = "${var.bucket_name}-ownership-policy" - user = var.heroku_user.user_name - - policy = data.aws_iam_policy_document.dandiset_bucket_owner.json -} - -data "aws_iam_policy_document" "dandiset_bucket_owner" { - version = "2008-10-17" - - statement { - resources = [ - "${aws_s3_bucket.dandiset_bucket.arn}", - "${aws_s3_bucket.dandiset_bucket.arn}/*", - ] - - actions = [ - "s3:Get*", - "s3:List*", - "s3:Delete*", - ] - } - - dynamic "statement" { - for_each = var.allow_heroku_put_object ? [1] : [] - content { - - resources = [ - "${aws_s3_bucket.dandiset_bucket.arn}", - "${aws_s3_bucket.dandiset_bucket.arn}/*", - ] - - actions = ["s3:PutObject"] - } - } - - statement { - resources = [ - "${aws_s3_bucket.dandiset_bucket.arn}", - "${aws_s3_bucket.dandiset_bucket.arn}/*", - ] - - actions = ["s3:*"] - - condition { - test = "StringEquals" - variable = "s3:x-amz-acl" - values = ["bucket-owner-full-control"] - } - } -} - -resource "aws_s3_bucket_policy" "dandiset_bucket_policy" { - provider = aws - - bucket = aws_s3_bucket.dandiset_bucket.id - policy = data.aws_iam_policy_document.dandiset_bucket_policy.json -} - -data "aws_iam_policy_document" "dandiset_bucket_policy" { - version = "2008-10-17" - - dynamic "statement" { - for_each = var.public ? [1] : [] - - content { - resources = [ - "${aws_s3_bucket.dandiset_bucket.arn}", - "${aws_s3_bucket.dandiset_bucket.arn}/*", - ] - - actions = [ - "s3:Get*", - "s3:List*", - ] - - principals { - identifiers = ["*"] - type = "*" - } - } - } - - dynamic "statement" { - for_each = var.allow_cross_account_heroku_put_object ? [1] : [] - - content { - sid = "S3PolicyStmt-DO-NOT-MODIFY-1569973164923" - principals { - identifiers = ["s3.amazonaws.com"] - type = "Service" - } - actions = [ - "s3:PutObject", - ] - resources = [ - "${aws_s3_bucket.dandiset_bucket.arn}/*", - ] - condition { - test = "StringEquals" - variable = "aws:SourceAccount" - values = [data.aws_caller_identity.sponsored_account.account_id] - } - condition { - test = "StringEquals" - variable = "s3:x-amz-acl" - values = ["bucket-owner-full-control"] - } - condition { - test = "ArnLike" - variable = "aws:SourceArn" - values = [aws_s3_bucket.dandiset_bucket.arn] - } - } - } - - statement { - resources = [ - "${aws_s3_bucket.dandiset_bucket.arn}", - "${aws_s3_bucket.dandiset_bucket.arn}/*", - ] - - actions = [ - "s3:Get*", - "s3:List*", - "s3:Delete*", - ] - - principals { - type = "AWS" - identifiers = [var.heroku_user.arn] - } - } - - statement { - resources = [ - "${aws_s3_bucket.dandiset_bucket.arn}", - "${aws_s3_bucket.dandiset_bucket.arn}/*", - ] - - actions = ["s3:*"] - - condition { - test = "StringEquals" - variable = "s3:x-amz-acl" - values = ["bucket-owner-full-control"] - } - - principals { - type = "AWS" - identifiers = [var.heroku_user.arn] - } - } - - dynamic "statement" { - for_each = var.trailing_delete ? [1] : [] - - content { - sid = "PreventDeletionOfObjectVersions" - - resources = [ - "${aws_s3_bucket.dandiset_bucket.arn}/*" - ] - - actions = [ - "s3:DeleteObjectVersion", - ] - - effect = "Deny" - - principals { - identifiers = ["*"] - type = "*" - } - } - } -} - - -# S3 lifecycle policy that permanently deletes objects with delete markers -# after 30 days. -resource "aws_s3_bucket_lifecycle_configuration" "expire_deleted_objects" { - # Must have bucket versioning enabled first - depends_on = [aws_s3_bucket_versioning.dandiset_bucket] - - count = var.trailing_delete ? 1 : 0 - - bucket = aws_s3_bucket.dandiset_bucket.id - - # Based on https://docs.aws.amazon.com/AmazonS3/latest/userguide/lifecycle-configuration-examples.html#lifecycle-config-conceptual-ex7 - rule { - id = "ExpireOldDeleteMarkers" - filter {} - - # Expire objects with delete markers after 30 days - noncurrent_version_expiration { - noncurrent_days = 30 - } - - # Also delete any delete markers associated with the expired object - expiration { - expired_object_delete_marker = true - } - - status = "Enabled" - } -} diff --git a/terraform/modules/dandiset_bucket/outputs.tf b/terraform/modules/dandiset_bucket/outputs.tf deleted file mode 100644 index 53a0f8a..0000000 --- a/terraform/modules/dandiset_bucket/outputs.tf +++ /dev/null @@ -1,14 +0,0 @@ -output "bucket_name" { - value = aws_s3_bucket.dandiset_bucket.id - description = "The S3 bucket name." -} - -output "log_bucket_name" { - value = aws_s3_bucket.log_bucket.id - description = "The S3 log bucket name." -} - -output "bucket_arn" { - value = aws_s3_bucket.dandiset_bucket.arn - description = "The S3 bucket ARN." -} diff --git a/terraform/modules/lincset_bucket/log_bucket.tf b/terraform/modules/lincset_bucket/log_bucket.tf new file mode 100644 index 0000000..ba241ee --- /dev/null +++ b/terraform/modules/lincset_bucket/log_bucket.tf @@ -0,0 +1,198 @@ +data "aws_canonical_user_id" "log_bucket_owner_account" {} + +resource "aws_s3_bucket" "log_bucket" { + bucket = var.log_bucket_name + + lifecycle { + prevent_destroy = true + } +} + +resource "aws_s3_bucket_server_side_encryption_configuration" "log_bucket" { + bucket = aws_s3_bucket.log_bucket.id + + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } +} + +data "aws_iam_policy_document" "lincset_log_bucket_policy" { + statement { + resources = [ + "${aws_s3_bucket.log_bucket.arn}", + "${aws_s3_bucket.log_bucket.arn}/*", + ] + + actions = [ + # Needed for the app to process logs for collecting download analytics + "s3:GetObject", + "s3:ListBucket", + ] + + principals { + type = "AWS" + identifiers = [var.heroku_user.arn] + } + } + + statement { + sid = "S3ServerAccessLogsPolicy" + effect = "Allow" + resources = ["${aws_s3_bucket.log_bucket.arn}/*"] + actions = ["s3:PutObject"] + + condition { + test = "StringEquals" + variable = "aws:SourceAccount" + values = [data.aws_caller_identity.current.account_id] + } + + condition { + test = "ArnLike" + variable = "aws:SourceArn" + values = [aws_s3_bucket.lincset_bucket.arn] + } + + principals { + type = "Service" + identifiers = ["logging.s3.amazonaws.com"] + } + } +} + +resource "aws_s3_bucket_policy" "lincset_log_bucket_policy" { + provider = aws + + bucket = aws_s3_bucket.log_bucket.id + policy = data.aws_iam_policy_document.lincset_log_bucket_policy.json +} + +data "aws_iam_policy_document" "lincset_log_bucket_owner" { + version = "2008-10-17" + + // TODO: gate behind a "cross account" flag, since this is technically only + // needed for sponsored log bucket. + statement { + resources = [ + "${aws_s3_bucket.log_bucket.arn}", + "${aws_s3_bucket.log_bucket.arn}/*", + ] + + actions = [ + "s3:GetObject", + "s3:ListBucket", + ] + } +} + +resource "aws_iam_user_policy" "lincset_log_bucket_owner" { + // The Heroku IAM user will always be in the project account + provider = aws.project + + name = "${var.log_bucket_name}-ownership-policy" + user = var.heroku_user.user_name + + policy = data.aws_iam_policy_document.lincset_log_bucket_owner.json +} + +# data "aws_canonical_user_id" "log_bucket_owner_account" {} + +resource "aws_s3_bucket" "log_bucket_us_east_2" { + bucket = var.log_bucket_name + + lifecycle { + prevent_destroy = true + } +} + +resource "aws_s3_bucket_server_side_encryption_configuration" "log_bucket_us_east_2" { + bucket = aws_s3_bucket.log_bucket_us_east_2.id + + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } +} + +data "aws_iam_policy_document" "lincset_log_bucket_policy_us_east_2" { + statement { + resources = [ + "${aws_s3_bucket.log_bucket_us_east_2.arn}", + "${aws_s3_bucket.log_bucket_us_east_2.arn}/*", + ] + + actions = [ + # Needed for the app to process logs for collecting download analytics + "s3:GetObject", + "s3:ListBucket", + ] + + principals { + type = "AWS" + identifiers = [var.heroku_user.arn] + } + } + + statement { + sid = "S3ServerAccessLogsPolicy" + effect = "Allow" + resources = ["${aws_s3_bucket.log_bucket_us_east_2.arn}/*"] + actions = ["s3:PutObject"] + + condition { + test = "StringEquals" + variable = "aws:SourceAccount" + values = [data.aws_caller_identity.current.account_id] + } + + condition { + test = "ArnLike" + variable = "aws:SourceArn" + values = [aws_s3_bucket.lincset_bucket_us_east_2.arn] + } + + principals { + type = "Service" + identifiers = ["logging.s3.amazonaws.com"] + } + } +} + +resource "aws_s3_bucket_policy" "lincset_log_bucket_policy_us_east_2" { + provider = aws + + bucket = aws_s3_bucket.log_bucket.id + policy = data.aws_iam_policy_document.lincset_log_bucket_policy_us_east_2.json +} + +data "aws_iam_policy_document" "lincset_log_bucket_owner_us_east_2" { + version = "2008-10-17" + + // TODO: gate behind a "cross account" flag, since this is technically only + // needed for sponsored log bucket. + statement { + resources = [ + "${aws_s3_bucket.log_bucket_us_east_2.arn}", + "${aws_s3_bucket.log_bucket_us_east_2.arn}/*", + ] + + actions = [ + "s3:GetObject", + "s3:ListBucket", + ] + } +} + +resource "aws_iam_user_policy" "lincset_log_bucket_owner_us_east_2" { + // The Heroku IAM user will always be in the project account + provider = aws.project + + name = "${var.log_bucket_name}-ownership-policy" + user = var.heroku_user.user_name + + policy = data.aws_iam_policy_document.lincset_log_bucket_owner_us_east_2.json +} + diff --git a/terraform/modules/lincset_bucket/main.tf b/terraform/modules/lincset_bucket/main.tf new file mode 100644 index 0000000..6382b4c --- /dev/null +++ b/terraform/modules/lincset_bucket/main.tf @@ -0,0 +1,587 @@ +data "aws_caller_identity" "sponsored_account" { + provider = aws +} + +data "aws_caller_identity" "current" {} + +resource "aws_s3_bucket" "lincset_bucket" { + bucket = var.bucket_name + + lifecycle { + prevent_destroy = true + } + +} + +resource "aws_s3_bucket_server_side_encryption_configuration" "lincset_bucket" { + bucket = aws_s3_bucket.lincset_bucket.id + + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } +} + +resource "aws_s3_bucket_cors_configuration" "lincset_bucket" { + bucket = aws_s3_bucket.lincset_bucket.id + + cors_rule { + allowed_origins = [ + "*", + ] + allowed_methods = [ + "PUT", + "POST", + "GET", + "DELETE", + ] + allowed_headers = [ + "*" + ] + expose_headers = [ + "ETag", + ] + max_age_seconds = 3000 + } +} + +resource "aws_s3_bucket_logging" "lincset_bucket" { + bucket = aws_s3_bucket.lincset_bucket.id + + target_bucket = aws_s3_bucket.log_bucket.id + target_prefix = "" +} + +resource "aws_s3_bucket_versioning" "lincset_bucket" { + count = var.versioning ? 1 : 0 + + bucket = aws_s3_bucket.lincset_bucket.id + + versioning_configuration { + status = "Enabled" + } +} + +resource "aws_s3_bucket_ownership_controls" "lincset_bucket" { + bucket = aws_s3_bucket.lincset_bucket.id + + rule { + object_ownership = "BucketOwnerPreferred" + } +} + +resource "aws_s3_bucket_acl" "lincset_bucket" { + depends_on = [aws_s3_bucket_ownership_controls.lincset_bucket] + + bucket = aws_s3_bucket.lincset_bucket.id + + // Public access is granted via a bucket policy, not a canned ACL + acl = "private" +} + +resource "aws_iam_user_policy" "lincset_bucket_owner" { + // The Heroku IAM user will always be in the project account + provider = aws.project + + name = "${var.bucket_name}-ownership-policy" + user = var.heroku_user.user_name + + policy = data.aws_iam_policy_document.lincset_bucket_owner.json +} + +data "aws_iam_policy_document" "lincset_bucket_owner" { + version = "2008-10-17" + + statement { + resources = [ + "${aws_s3_bucket.lincset_bucket.arn}", + "${aws_s3_bucket.lincset_bucket.arn}/*", + ] + + actions = [ + "s3:Get*", + "s3:List*", + "s3:Delete*", + ] + } + + dynamic "statement" { + for_each = var.allow_heroku_put_object ? [1] : [] + content { + + resources = [ + "${aws_s3_bucket.lincset_bucket.arn}", + "${aws_s3_bucket.lincset_bucket.arn}/*", + ] + + actions = ["s3:PutObject"] + } + } + + statement { + resources = [ + "${aws_s3_bucket.lincset_bucket.arn}", + "${aws_s3_bucket.lincset_bucket.arn}/*", + ] + + actions = ["s3:*"] + + condition { + test = "StringEquals" + variable = "s3:x-amz-acl" + values = ["bucket-owner-full-control"] + } + } +} + +resource "aws_s3_bucket_policy" "lincset_bucket_policy" { + provider = aws + + bucket = aws_s3_bucket.lincset_bucket.id + policy = data.aws_iam_policy_document.lincset_bucket_policy.json +} + +data "aws_iam_policy_document" "lincset_bucket_policy" { + version = "2008-10-17" + + dynamic "statement" { + for_each = var.public ? [1] : [] + + content { + resources = [ + "${aws_s3_bucket.lincset_bucket.arn}", + "${aws_s3_bucket.lincset_bucket.arn}/*", + ] + + actions = [ + "s3:Get*", + "s3:List*", + ] + + principals { + identifiers = ["*"] + type = "*" + } + } + } + + dynamic "statement" { + for_each = var.allow_cross_account_heroku_put_object ? [1] : [] + + content { + sid = "S3PolicyStmt-DO-NOT-MODIFY-1569973164923" + principals { + identifiers = ["s3.amazonaws.com"] + type = "Service" + } + actions = [ + "s3:PutObject", + ] + resources = [ + "${aws_s3_bucket.lincset_bucket.arn}/*", + ] + condition { + test = "StringEquals" + variable = "aws:SourceAccount" + values = [data.aws_caller_identity.sponsored_account.account_id] + } + condition { + test = "StringEquals" + variable = "s3:x-amz-acl" + values = ["bucket-owner-full-control"] + } + condition { + test = "ArnLike" + variable = "aws:SourceArn" + values = [aws_s3_bucket.lincset_bucket.arn] + } + } + } + + statement { + resources = [ + "${aws_s3_bucket.lincset_bucket.arn}", + "${aws_s3_bucket.lincset_bucket.arn}/*", + ] + + actions = [ + "s3:Get*", + "s3:List*", + "s3:Delete*", + ] + + principals { + type = "AWS" + identifiers = [var.heroku_user.arn] + } + } + + statement { + resources = [ + "${aws_s3_bucket.lincset_bucket.arn}", + "${aws_s3_bucket.lincset_bucket.arn}/*", + ] + + actions = ["s3:*"] + + condition { + test = "StringEquals" + variable = "s3:x-amz-acl" + values = ["bucket-owner-full-control"] + } + + principals { + type = "AWS" + identifiers = [var.heroku_user.arn] + } + } + + dynamic "statement" { + for_each = var.trailing_delete ? [1] : [] + + content { + sid = "PreventDeletionOfObjectVersions" + + resources = [ + "${aws_s3_bucket.lincset_bucket.arn}/*" + ] + + actions = [ + "s3:DeleteObjectVersion", + ] + + effect = "Deny" + + principals { + identifiers = ["*"] + type = "*" + } + } + } +} + + +# S3 lifecycle policy that permanently deletes objects with delete markers +# after 30 days. +resource "aws_s3_bucket_lifecycle_configuration" "expire_deleted_objects" { + # Must have bucket versioning enabled first + depends_on = [aws_s3_bucket_versioning.lincset_bucket] + + count = var.trailing_delete ? 1 : 0 + + bucket = aws_s3_bucket.lincset_bucket.id + + # Based on https://docs.aws.amazon.com/AmazonS3/latest/userguide/lifecycle-configuration-examples.html#lifecycle-config-conceptual-ex7 + rule { + id = "ExpireOldDeleteMarkers" + filter {} + + # Expire objects with delete markers after 30 days + noncurrent_version_expiration { + noncurrent_days = 30 + } + + # Also delete any delete markers associated with the expired object + expiration { + expired_object_delete_marker = true + } + + status = "Enabled" + } +} + + +data "aws_caller_identity" "sponsored_account_us_east_2" { + provider = aws +} + +# data "aws_caller_identity" "current" {} + +resource "aws_s3_bucket" "lincset_bucket_us_east_2" { + bucket = var.bucket_name + + lifecycle { + prevent_destroy = true + } + +} + +resource "aws_s3_bucket_server_side_encryption_configuration" "lincset_bucket_us_east_2" { + bucket = aws_s3_bucket.lincset_bucket.id + + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } +} + +resource "aws_s3_bucket_cors_configuration" "lincset_bucket_us_east_2" { + bucket = aws_s3_bucket.lincset_bucket_us_east_2.id + + cors_rule { + allowed_origins = [ + "*", + ] + allowed_methods = [ + "PUT", + "POST", + "GET", + "DELETE", + ] + allowed_headers = [ + "*" + ] + expose_headers = [ + "ETag", + ] + max_age_seconds = 3000 + } +} + +resource "aws_s3_bucket_logging" "lincset_bucket_us_east_2" { + bucket = aws_s3_bucket.lincset_bucket_us_east_2.id + + target_bucket = aws_s3_bucket.log_bucket.id + target_prefix = "" +} + +resource "aws_s3_bucket_versioning" "lincset_bucket_us_east_2" { + count = var.versioning ? 1 : 0 + + bucket = aws_s3_bucket.lincset_bucket_us_east_2.id + + versioning_configuration { + status = "Enabled" + } +} + +resource "aws_s3_bucket_ownership_controls" "lincset_bucket_us_east_2" { + bucket = aws_s3_bucket.lincset_bucket_us_east_2.id + + rule { + object_ownership = "BucketOwnerPreferred" + } +} + +resource "aws_s3_bucket_acl" "lincset_bucket_us_east_2" { + depends_on = [aws_s3_bucket_ownership_controls.lincset_bucket_us_east_2] + + bucket = aws_s3_bucket.lincset_bucket_us_east_2.id + + // Public access is granted via a bucket policy, not a canned ACL + acl = "private" +} + +resource "aws_iam_user_policy" "lincset_bucket_owner_us_east_2" { + // The Heroku IAM user will always be in the project account + provider = aws.project + + name = "${var.bucket_name}-ownership-policy" + user = var.heroku_user.user_name + + policy = data.aws_iam_policy_document.lincset_bucket_owner_us_east_2.json +} + +data "aws_iam_policy_document" "lincset_bucket_owner_us_east_2" { + version = "2008-10-17" + + statement { + resources = [ + "${aws_s3_bucket.lincset_bucket_us_east_2.arn}", + "${aws_s3_bucket.lincset_bucket_us_east_2.arn}/*", + ] + + actions = [ + "s3:Get*", + "s3:List*", + "s3:Delete*", + ] + } + + dynamic "statement" { + for_each = var.allow_heroku_put_object ? [1] : [] + content { + + resources = [ + "${aws_s3_bucket.lincset_bucket_us_east_2.arn}", + "${aws_s3_bucket.lincset_bucket_us_east_2.arn}/*", + ] + + actions = ["s3:PutObject"] + } + } + + statement { + resources = [ + "${aws_s3_bucket.lincset_bucket_us_east_2.arn}", + "${aws_s3_bucket.lincset_bucket_us_east_2.arn}/*", + ] + + actions = ["s3:*"] + + condition { + test = "StringEquals" + variable = "s3:x-amz-acl" + values = ["bucket-owner-full-control"] + } + } +} + +resource "aws_s3_bucket_policy" "lincset_bucket_policy_us_east_2" { + provider = aws + + bucket = aws_s3_bucket.lincset_bucket_us_east_2.id + policy = data.aws_iam_policy_document.lincset_bucket_policy_us_east_2.json +} + +data "aws_iam_policy_document" "lincset_bucket_policy_us_east_2" { + version = "2008-10-17" + + dynamic "statement" { + for_each = var.public ? [1] : [] + + content { + resources = [ + "${aws_s3_bucket.lincset_bucket_us_east_2.arn}", + "${aws_s3_bucket.lincset_bucket_us_east_2.arn}/*", + ] + + actions = [ + "s3:Get*", + "s3:List*", + ] + + principals { + identifiers = ["*"] + type = "*" + } + } + } + + dynamic "statement" { + for_each = var.allow_cross_account_heroku_put_object ? [1] : [] + + content { + sid = "S3PolicyStmt-DO-NOT-MODIFY-1569973164923" + principals { + identifiers = ["s3.amazonaws.com"] + type = "Service" + } + actions = [ + "s3:PutObject", + ] + resources = [ + "${aws_s3_bucket.lincset_bucket_us_east_2.arn}/*", + ] + condition { + test = "StringEquals" + variable = "aws:SourceAccount" + values = [data.aws_caller_identity.sponsored_account.account_id] + } + condition { + test = "StringEquals" + variable = "s3:x-amz-acl" + values = ["bucket-owner-full-control"] + } + condition { + test = "ArnLike" + variable = "aws:SourceArn" + values = [aws_s3_bucket.lincset_bucket_us_east_2.arn] + } + } + } + + statement { + resources = [ + "${aws_s3_bucket.lincset_bucket_us_east_2.arn}", + "${aws_s3_bucket.lincset_bucket_us_east_2.arn}/*", + ] + + actions = [ + "s3:Get*", + "s3:List*", + "s3:Delete*", + ] + + principals { + type = "AWS" + identifiers = [var.heroku_user.arn] + } + } + + statement { + resources = [ + "${aws_s3_bucket.lincset_bucket_us_east_2.arn}", + "${aws_s3_bucket.lincset_bucket_us_east_2.arn}/*", + ] + + actions = ["s3:*"] + + condition { + test = "StringEquals" + variable = "s3:x-amz-acl" + values = ["bucket-owner-full-control"] + } + + principals { + type = "AWS" + identifiers = [var.heroku_user.arn] + } + } + + dynamic "statement" { + for_each = var.trailing_delete ? [1] : [] + + content { + sid = "PreventDeletionOfObjectVersions" + + resources = [ + "${aws_s3_bucket.lincset_bucket_us_east_2.arn}/*" + ] + + actions = [ + "s3:DeleteObjectVersion", + ] + + effect = "Deny" + + principals { + identifiers = ["*"] + type = "*" + } + } + } +} + + +# S3 lifecycle policy that permanently deletes objects with delete markers +# after 30 days. +resource "aws_s3_bucket_lifecycle_configuration" "expire_deleted_objects_us_east_2" { + # Must have bucket versioning enabled first + depends_on = [aws_s3_bucket_versioning.lincset_bucket_us_east_2] + + count = var.trailing_delete ? 1 : 0 + + bucket = aws_s3_bucket.lincset_bucket_us_east_2.id + + # Based on https://docs.aws.amazon.com/AmazonS3/latest/userguide/lifecycle-configuration-examples.html#lifecycle-config-conceptual-ex7 + rule { + id = "ExpireOldDeleteMarkers" + filter {} + + # Expire objects with delete markers after 30 days + noncurrent_version_expiration { + noncurrent_days = 30 + } + + # Also delete any delete markers associated with the expired object + expiration { + expired_object_delete_marker = true + } + + status = "Enabled" + } +} + diff --git a/terraform/modules/lincset_bucket/outputs.tf b/terraform/modules/lincset_bucket/outputs.tf new file mode 100644 index 0000000..e7f9cf3 --- /dev/null +++ b/terraform/modules/lincset_bucket/outputs.tf @@ -0,0 +1,29 @@ +output "bucket_name" { + value = aws_s3_bucket.lincset_bucket.id + description = "The S3 bucket name." +} + +output "log_bucket_name" { + value = aws_s3_bucket.log_bucket.id + description = "The S3 log bucket name." +} + +output "bucket_arn" { + value = aws_s3_bucket.lincset_bucket.arn + description = "The S3 bucket ARN." +} + +output "bucket_name_us_east_2" { + value = aws_s3_bucket.lincset_bucket_us_east_2.id + description = "The S3 bucket name." +} + +output "log_bucket_name_us_east_2" { + value = aws_s3_bucket.log_bucket_us_east_2.id + description = "The S3 log bucket name." +} + +output "bucket_arn_us_east_2" { + value = aws_s3_bucket.lincset_bucket_us_east_2.arn + description = "The S3 bucket ARN." +} diff --git a/terraform/modules/dandiset_bucket/variables.tf b/terraform/modules/lincset_bucket/variables.tf similarity index 100% rename from terraform/modules/dandiset_bucket/variables.tf rename to terraform/modules/lincset_bucket/variables.tf diff --git a/terraform/modules/dandiset_bucket/versions.tf b/terraform/modules/lincset_bucket/versions.tf similarity index 100% rename from terraform/modules/dandiset_bucket/versions.tf rename to terraform/modules/lincset_bucket/versions.tf diff --git a/terraform/redirector.tf b/terraform/redirector.tf index 09bb45f..bd2f078 100644 --- a/terraform/redirector.tf +++ b/terraform/redirector.tf @@ -1,8 +1,8 @@ -# Record to point gui.dandiarchive.org to the Netlify hosted redirector +# Record to point gui.lincbrain.org to the Netlify hosted redirector resource "aws_route53_record" "redirector" { - zone_id = aws_route53_zone.dandi.zone_id + zone_id = aws_route53_zone.linc-brain-mit.zone_id name = "gui" type = "CNAME" ttl = "300" - records = ["redirect-dandiarchive-org.netlify.com"] + records = ["redirect-lincbrain-org.netlify.com"] } diff --git a/terraform/sentry.tf b/terraform/sentry.tf index dbbb251..d621e5d 100644 --- a/terraform/sentry.tf +++ b/terraform/sentry.tf @@ -1,15 +1,15 @@ data "sentry_organization" "this" { - slug = "dandiarchive" + slug = "mit-m3" } data "sentry_team" "this" { organization = data.sentry_organization.this.id - slug = "dandidevs" + slug = "mit" } data "sentry_project" "this" { organization = data.sentry_organization.this.id - slug = "dandi-api" + slug = "linc-api" } data "sentry_key" "this" { diff --git a/terraform/sponsored_bucket.tf b/terraform/sponsored_bucket.tf index a1d32ce..3a5ff00 100644 --- a/terraform/sponsored_bucket.tf +++ b/terraform/sponsored_bucket.tf @@ -1,12 +1,12 @@ -module "sponsored_dandiset_bucket" { - source = "./modules/dandiset_bucket" - bucket_name = "dandiarchive" - public = true +module "sponsored_lincset_bucket" { + source = "./modules/lincset_bucket" + bucket_name = "linc-brain-mit-prod" + public = false versioning = true trailing_delete = false allow_cross_account_heroku_put_object = true heroku_user = data.aws_iam_user.api - log_bucket_name = "dandiarchive-logs" + log_bucket_name = "linc-brain-mit-logs" providers = { aws = aws.sponsored aws.project = aws @@ -14,14 +14,42 @@ module "sponsored_dandiset_bucket" { } module "sponsored_embargo_bucket" { - source = "./modules/dandiset_bucket" - bucket_name = "dandiarchive-embargo" + source = "./modules/lincset_bucket" + bucket_name = "linc-brain-mit-embargo-prod" versioning = false trailing_delete = false heroku_user = data.aws_iam_user.api - log_bucket_name = "dandiarchive-embargo-logs" + log_bucket_name = "linc-brain-mit-embargo-logs" providers = { aws = aws.sponsored aws.project = aws } } + +module "sponsored_lincset_bucket_us_east_2" { + source = "./modules/lincset_bucket" + bucket_name = "linc-brain-mit-prod-us-east-2" + public = false + versioning = true + trailing_delete = false + allow_cross_account_heroku_put_object = true + heroku_user = data.aws_iam_user.api + log_bucket_name = "linc-brain-mit-logs-us-east-2" + providers = { + aws = aws.target + aws.project = aws + } +} + +module "sponsored_embargo_bucket_us_east_2" { + source = "./modules/lincset_bucket" + bucket_name = "linc-brain-mit-embargo-prod-us-east-2" + versioning = false + trailing_delete = false + heroku_user = data.aws_iam_user.api + log_bucket_name = "linc-brain-mit-embargo-logs-us-east-2" + providers = { + aws = aws.target + aws.project = aws + } +} diff --git a/terraform/sponsored_iam.tf b/terraform/sponsored_iam.tf index 1339be1..7cfa3f1 100644 --- a/terraform/sponsored_iam.tf +++ b/terraform/sponsored_iam.tf @@ -37,8 +37,56 @@ data "aws_iam_policy_document" "sponsored_writers" { "s3:GetObjectVersion", ] resources = [ - "${module.sponsored_dandiset_bucket.bucket_arn}/*", - module.sponsored_dandiset_bucket.bucket_arn, + "${module.sponsored_lincset_bucket.bucket_arn}/*", + module.sponsored_lincset_bucket.bucket_arn, ] } } + +resource "aws_iam_group" "sponsored_writers_us_east_2" { + provider = aws.target + + name = "writers" +} + +resource "aws_iam_group_policy" "sponsored_writers_us_east_2" { + provider = aws.target + + name = "bucket-write" + group = aws_iam_group.sponsored_writers_us_east_2.name + policy = data.aws_iam_policy_document.sponsored_writers_us_east_2.json +} + +data "aws_iam_policy_document" "sponsored_writers_us_east_2" { + version = "2012-10-17" + statement { + sid = "VisualEditor0" + actions = [ + "s3:DeleteObjectTagging", + "s3:ListBucketByTags", + "s3:ListBucketMultipartUploads", + "s3:GetBucketTagging", + "s3:ListBucketVersions", + "s3:PutObjectVersionTagging", + "s3:ListBucket", + "s3:DeleteObjectVersionTagging", + "s3:GetBucketVersioning", + "s3:GetObjectVersionTorrent", + "s3:PutObject", + "s3:GetObject", + "s3:PutBucketTagging", + "s3:GetObjectTagging", + "s3:PutObjectTagging", + "s3:DeleteObject", + "s3:GetBucketLocation", + "s3:GetObjectVersion", + ] + resources = [ + "${module.sponsored_lincset_bucket_us_east_2.bucket_arn}/*", + module.sponsored_lincset_bucket_us_east_2.bucket_arn, + ] + } +} + + + diff --git a/terraform/staging_bucket.tf b/terraform/staging_bucket.tf index b6ceeea..3efb64c 100644 --- a/terraform/staging_bucket.tf +++ b/terraform/staging_bucket.tf @@ -1,12 +1,12 @@ -module "staging_dandiset_bucket" { - source = "./modules/dandiset_bucket" - bucket_name = "dandi-api-staging-dandisets" - public = true +module "staging_lincset_bucket" { + source = "./modules/lincset_bucket" + bucket_name = "linc-brain-mit-staging" + public = false versioning = true trailing_delete = true allow_heroku_put_object = true heroku_user = data.aws_iam_user.api_staging - log_bucket_name = "dandi-api-staging-dandiset-logs" + log_bucket_name = "linc-brain-mit-staging-logs" providers = { aws = aws aws.project = aws @@ -14,14 +14,42 @@ module "staging_dandiset_bucket" { } module "staging_embargo_bucket" { - source = "./modules/dandiset_bucket" - bucket_name = "dandi-api-staging-embargo-dandisets" + source = "./modules/lincset_bucket" + bucket_name = "linc-brain-mit-embargo-staging" versioning = false trailing_delete = false heroku_user = data.aws_iam_user.api_staging - log_bucket_name = "dandi-api-staging-embargo-dandisets-logs" + log_bucket_name = "linc-brain-mit-staging-embargo-logs" providers = { aws = aws aws.project = aws } } + +module "staging_lincset_bucket_us_east_2" { + source = "./modules/lincset_bucket" + bucket_name = "linc-brain-mit-staging-us-east-2" + public = false + versioning = true + trailing_delete = true + allow_heroku_put_object = true + heroku_user = data.aws_iam_user.api_staging + log_bucket_name = "linc-brain-mit-staging-logs-us-east-2" + providers = { + aws = aws.target + aws.project = aws + } +} + +module "staging_embargo_bucket_us_east_2" { + source = "./modules/lincset_bucket" + bucket_name = "linc-brain-mit-embargo-staging-us-east-2" + versioning = false + trailing_delete = false + heroku_user = data.aws_iam_user.api_staging + log_bucket_name = "linc-brain-mit-staging-embargo-logs-us-east-2" + providers = { + aws = aws.target + aws.project = aws + } +} \ No newline at end of file diff --git a/terraform/staging_pipeline.tf b/terraform/staging_pipeline.tf index fdd3b0a..00461cd 100644 --- a/terraform/staging_pipeline.tf +++ b/terraform/staging_pipeline.tf @@ -5,10 +5,10 @@ module "api_staging" { source = "girder/girder4/heroku" version = "0.13.0" - project_slug = "dandi-api-staging" - heroku_team_name = data.heroku_team.dandi.name - route53_zone_id = aws_route53_zone.dandi.zone_id - subdomain_name = "api-staging" + project_slug = "linc-brain-staging" + heroku_team_name = data.heroku_team.linc-brain-mit.name + route53_zone_id = aws_route53_zone.linc-brain-mit.zone_id + subdomain_name = "staging-api" heroku_web_dyno_size = "basic" heroku_worker_dyno_size = "basic" @@ -19,31 +19,38 @@ module "api_staging" { heroku_web_dyno_quantity = 1 heroku_worker_dyno_quantity = 1 - django_default_from_email = "admin@api-staging.dandiarchive.org" - django_cors_origin_whitelist = ["https://gui-staging.dandiarchive.org"] - django_cors_origin_regex_whitelist = ["^https:\\/\\/[0-9a-z\\-]+--gui-staging-dandiarchive-org\\.netlify\\.app$"] + django_default_from_email = "admin@staging-api.lincbrain.org" + django_cors_origin_whitelist = ["https://gui-staging.lincbrain.org", "https://staging--lincbrain-org.netlify.app"] + django_cors_origin_regex_whitelist = ["https://staging--gui-staging-lincbrain-org.netlify.app"] additional_django_vars = { + CLOUDFRONT_BASE_URL = "lincbrain.org" + CLOUDFRONT_NEUROGLANCER_URL = "https://neuroglancer-staging.lincbrain.org" + CLOUDFRONT_PEM_KEY_ID = "KZQ92MU8PCLJ8" + CLOUDFRONT_PRIVATE_PEM_S3_LOCATION = "cloudfront/private_key_staging_new.pem" DJANGO_CONFIGURATION = "HerokuStagingConfiguration" - DJANGO_DANDI_DANDISETS_BUCKET_NAME = module.staging_dandiset_bucket.bucket_name + DJANGO_DANDI_DANDISETS_BUCKET_NAME = module.staging_lincset_bucket_us_east_2.bucket_name DJANGO_DANDI_DANDISETS_BUCKET_PREFIX = "" - DJANGO_DANDI_DANDISETS_EMBARGO_BUCKET_NAME = module.staging_embargo_bucket.bucket_name + DJANGO_DANDI_DANDISETS_EMBARGO_BUCKET_NAME = module.staging_embargo_bucket_us_east_2.bucket_name DJANGO_DANDI_DANDISETS_EMBARGO_BUCKET_PREFIX = "" - DJANGO_DANDI_DANDISETS_LOG_BUCKET_NAME = module.staging_dandiset_bucket.log_bucket_name - DJANGO_DANDI_DANDISETS_EMBARGO_LOG_BUCKET_NAME = module.staging_embargo_bucket.log_bucket_name + DJANGO_DANDI_DANDISETS_LOG_BUCKET_NAME = module.staging_lincset_bucket_us_east_2.log_bucket_name + DJANGO_DANDI_DANDISETS_EMBARGO_LOG_BUCKET_NAME = module.staging_embargo_bucket_us_east_2.log_bucket_name DJANGO_DANDI_DOI_API_URL = "https://api.test.datacite.org/dois" DJANGO_DANDI_DOI_API_USER = "dartlib.dandi" DJANGO_DANDI_DOI_API_PREFIX = "10.80507" DJANGO_DANDI_DOI_PUBLISH = "false" - DJANGO_SENTRY_DSN = data.sentry_key.this.dsn_public + DJANGO_SENTRY_DSN = "https://833c159dc622528b21b4ce4adef6dbf8@o4506237212033024.ingest.sentry.io/4506237213212672" DJANGO_SENTRY_ENVIRONMENT = "staging" DJANGO_CELERY_WORKER_CONCURRENCY = "2" - DJANGO_DANDI_WEB_APP_URL = "https://gui-staging.dandiarchive.org" - DJANGO_DANDI_API_URL = "https://api-staging.dandiarchive.org" - DJANGO_DANDI_JUPYTERHUB_URL = "https://hub.dandiarchive.org/" + DJANGO_DANDI_WEB_APP_URL = "https://staging--lincbrain-org.netlify.app" + DJANGO_DANDI_API_URL = "https://staging-api.lincbrain.org" + DJANGO_DANDI_JUPYTERHUB_URL = "https://hub.lincbrain.org/" + WEBKNOSSOS_API_URL = "https://webknossos-staging.lincbrain.org" + WEBKNOSSOS_ORGANIZATION_DISPLAY_NAME = "LINC Staging" + WEBKNOSSOS_ORGANIZATION_NAME = "LINC_Staging" } additional_sensitive_django_vars = { - DJANGO_DANDI_DOI_API_PASSWORD = var.test_doi_api_password + DJANGO_DANDI_DOI_API_PASSWORD = "temp" } } @@ -65,23 +72,23 @@ data "aws_iam_user" "api_staging" { user_name = module.api_staging.heroku_iam_user_id } -resource "heroku_pipeline" "dandi_pipeline" { - name = "dandi-pipeline" +resource "heroku_pipeline" "linc_pipeline" { + name = "linc-pipeline" owner { - id = data.heroku_team.dandi.id + id = data.heroku_team.linc-brain-mit.id type = "team" } } resource "heroku_pipeline_coupling" "staging" { app_id = module.api_staging.heroku_app_id - pipeline = heroku_pipeline.dandi_pipeline.id + pipeline = heroku_pipeline.linc_pipeline.id stage = "staging" } resource "heroku_pipeline_coupling" "production" { app_id = module.api.heroku_app_id - pipeline = heroku_pipeline.dandi_pipeline.id + pipeline = heroku_pipeline.linc_pipeline.id stage = "production" -} +} \ No newline at end of file diff --git a/terraform/variables.tf b/terraform/variables.tf index 9d26cb7..4cdb22e 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -1,9 +1,11 @@ variable "doi_api_password" { type = string description = "The password for the Datacite API, used to mint new DOIs during publish." + default = "yourdefaultpassword" } variable "test_doi_api_password" { type = string description = "The password for the Datacite Test API, used to mint new DOIs on staging during publish." + default = "yourtestdefaultpassword" }