diff --git a/README.md b/README.md index 3408936..40278eb 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,9 @@ -## AWS S3 + Terraform Setup (TODO - do first) -- [ ] Create new S3 bucket for Terraform state (e.g. `juntekim-terraform-state`) -- [ ] Enable versioning on the bucket -- [ ] Set up Terraform with S3 backend pointing to new bucket -- [ ] Use Terraform to define new infra (start with what exists, then expand) -- [ ] Store all future infra changes via version-controlled Terraform in Forgejo - ## Forgejo Backup (TODO) -- [ ] Set up restic CronJob to back up forgejo-pvc (/data) to S3 +- [ ] Set up restic CronJob to back up forgejo-pvc (/data) to S3 weekly - Mount forgejo-pvc read-only in CronJob - - Use restic to snapshot to S3 bucket (need: S3_BUCKET, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, RESTIC_PASSWORD) + - Use restic to snapshot to S3 bucket and put it under repos (need: S3_BUCKET, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, RESTIC_PASSWORD) - Schedule: daily -- [ ] Forgejo postgres backup via databasus (separate) +- [ ] Forgejo postgres backup via databasus same bucket but key would be postgres - [ ] Test restore from restic snapshot up # Lets deploy databasus first since i'm using it to sort my back up strategy first diff --git a/aws_infra/.gitignore b/aws_infra/.gitignore new file mode 100644 index 0000000..86c6442 --- /dev/null +++ b/aws_infra/.gitignore @@ -0,0 +1,4 @@ +.terraform/ +.terraform.lock.hcl +*.tfstate +*.tfstate.backup diff --git a/aws_infra/main.tf b/aws_infra/main.tf new file mode 100644 index 0000000..5dea194 --- /dev/null +++ b/aws_infra/main.tf @@ -0,0 +1,7 @@ +# aws_infra — central infrastructure managed via juntekim-infra-state + +module "forgejo_backup" { + source = "./modules/forgejo_backup" + + bucket_name = "juntekim-forgejo-backup" +} diff --git a/aws_infra/modules/forgejo_backup/main.tf b/aws_infra/modules/forgejo_backup/main.tf new file mode 100644 index 0000000..64dc77d --- /dev/null +++ b/aws_infra/modules/forgejo_backup/main.tf @@ -0,0 +1,39 @@ +module "bucket" { + source = "../s3_bucket" + + bucket_name = var.bucket_name + versioning_enabled = true + retention_days = 90 +} + +resource "aws_iam_user" "forgejo_backup" { + name = "forgejo-backup" +} + +resource "aws_iam_access_key" "forgejo_backup" { + user = aws_iam_user.forgejo_backup.name +} + +resource "aws_iam_user_policy" "forgejo_backup" { + name = "forgejo-backup-s3" + user = aws_iam_user.forgejo_backup.name + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "s3:PutObject", + "s3:GetObject", + "s3:DeleteObject", + "s3:ListBucket" + ] + Resource = [ + module.bucket.bucket_arn, + "${module.bucket.bucket_arn}/*" + ] + } + ] + }) +} diff --git a/aws_infra/modules/forgejo_backup/outputs.tf b/aws_infra/modules/forgejo_backup/outputs.tf new file mode 100644 index 0000000..cfe0166 --- /dev/null +++ b/aws_infra/modules/forgejo_backup/outputs.tf @@ -0,0 +1,16 @@ +output "bucket_name" { + value = module.bucket.bucket_name +} + +output "bucket_arn" { + value = module.bucket.bucket_arn +} + +output "iam_access_key_id" { + value = aws_iam_access_key.forgejo_backup.id +} + +output "iam_secret_access_key" { + value = aws_iam_access_key.forgejo_backup.secret + sensitive = true +} diff --git a/aws_infra/modules/forgejo_backup/variables.tf b/aws_infra/modules/forgejo_backup/variables.tf new file mode 100644 index 0000000..cb83042 --- /dev/null +++ b/aws_infra/modules/forgejo_backup/variables.tf @@ -0,0 +1,5 @@ +variable "bucket_name" { + description = "Name of the S3 bucket for Forgejo backups" + type = string + default = "juntekim-forgejo-backup" +} diff --git a/aws_infra/modules/s3_bucket/main.tf b/aws_infra/modules/s3_bucket/main.tf new file mode 100644 index 0000000..0edda21 --- /dev/null +++ b/aws_infra/modules/s3_bucket/main.tf @@ -0,0 +1,51 @@ +resource "aws_s3_bucket" "this" { + bucket = var.bucket_name +} + +resource "aws_s3_bucket_versioning" "this" { + bucket = aws_s3_bucket.this.id + + versioning_configuration { + status = var.versioning_enabled ? "Enabled" : "Disabled" + } +} + +resource "aws_s3_bucket_server_side_encryption_configuration" "this" { + bucket = aws_s3_bucket.this.id + + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } +} + +resource "aws_s3_bucket_public_access_block" "this" { + bucket = aws_s3_bucket.this.id + + block_public_acls = true + block_public_policy = true + ignore_public_acls = true + restrict_public_buckets = true +} + +resource "aws_s3_bucket_lifecycle_configuration" "this" { + count = var.retention_days > 0 ? 1 : 0 + bucket = aws_s3_bucket.this.id + + rule { + id = "expire-objects" + status = "Enabled" + + filter {} + + transition { + days = 30 + storage_class = "STANDARD_IA" + } + + expiration { + days = var.retention_days + } + } +} diff --git a/aws_infra/modules/s3_bucket/outputs.tf b/aws_infra/modules/s3_bucket/outputs.tf new file mode 100644 index 0000000..e8ceffd --- /dev/null +++ b/aws_infra/modules/s3_bucket/outputs.tf @@ -0,0 +1,7 @@ +output "bucket_name" { + value = aws_s3_bucket.this.bucket +} + +output "bucket_arn" { + value = aws_s3_bucket.this.arn +} diff --git a/aws_infra/modules/s3_bucket/variables.tf b/aws_infra/modules/s3_bucket/variables.tf new file mode 100644 index 0000000..9214402 --- /dev/null +++ b/aws_infra/modules/s3_bucket/variables.tf @@ -0,0 +1,16 @@ +variable "bucket_name" { + description = "Name of the S3 bucket" + type = string +} + +variable "versioning_enabled" { + description = "Enable versioning on the bucket" + type = bool + default = true +} + +variable "retention_days" { + description = "Number of days before objects expire. 0 disables expiry." + type = number + default = 0 +} diff --git a/aws_infra/provider.tf b/aws_infra/provider.tf new file mode 100644 index 0000000..888cc06 --- /dev/null +++ b/aws_infra/provider.tf @@ -0,0 +1,21 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } + + backend "s3" { + bucket = "juntekim-infra-state" + key = "aws_infra/terraform.tfstate" + region = "eu-west-2" + profile = "personal" + } + + required_version = ">= 1.5.0" +} + +provider "aws" { + region = var.aws_region +} diff --git a/aws_infra/variables.tf b/aws_infra/variables.tf new file mode 100644 index 0000000..bbacc64 --- /dev/null +++ b/aws_infra/variables.tf @@ -0,0 +1,5 @@ +variable "aws_region" { + description = "AWS region" + type = string + default = "eu-west-2" +}