# S3 Module - Blob Storage Buckets # RFC 0039: ADR-Compliant Foundation Infrastructure # # Additional S3 buckets for: # - Email blob storage (Stalwart attachments) # - Loki log chunks # - Tempo trace storage # - Git LFS objects (Forgejo) # KMS Key for S3 Encryption resource "aws_kms_key" "s3" { description = "S3 bucket encryption key" deletion_window_in_days = 30 enable_key_rotation = true tags = merge(var.tags, { Name = "${var.name}-s3" }) } resource "aws_kms_alias" "s3" { name = "alias/${var.name}-s3" target_key_id = aws_kms_key.s3.key_id } # ============================================================================ # EMAIL BLOB STORAGE # ============================================================================ resource "aws_s3_bucket" "email_blobs" { bucket = "${var.name}-email-blobs-${data.aws_caller_identity.current.account_id}" tags = merge(var.tags, { Name = "${var.name}-email-blobs" Purpose = "Stalwart email attachments and large messages" Service = "stalwart" }) } resource "aws_s3_bucket_versioning" "email_blobs" { bucket = aws_s3_bucket.email_blobs.id versioning_configuration { status = "Enabled" } } resource "aws_s3_bucket_server_side_encryption_configuration" "email_blobs" { bucket = aws_s3_bucket.email_blobs.id rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" kms_master_key_id = aws_kms_key.s3.arn } bucket_key_enabled = true } } resource "aws_s3_bucket_public_access_block" "email_blobs" { bucket = aws_s3_bucket.email_blobs.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true } resource "aws_s3_bucket_lifecycle_configuration" "email_blobs" { bucket = aws_s3_bucket.email_blobs.id rule { id = "archive-old-attachments" status = "Enabled" transition { days = 90 storage_class = "STANDARD_IA" } transition { days = 365 storage_class = "GLACIER" } noncurrent_version_expiration { noncurrent_days = 30 } } } # ============================================================================ # LOKI LOG CHUNKS # ============================================================================ resource "aws_s3_bucket" "loki_chunks" { bucket = "${var.name}-loki-chunks-${data.aws_caller_identity.current.account_id}" tags = merge(var.tags, { Name = "${var.name}-loki-chunks" Purpose = "Loki log storage chunks" Service = "loki" }) } resource "aws_s3_bucket_versioning" "loki_chunks" { bucket = aws_s3_bucket.loki_chunks.id versioning_configuration { status = "Suspended" # Not needed for Loki chunks } } resource "aws_s3_bucket_server_side_encryption_configuration" "loki_chunks" { bucket = aws_s3_bucket.loki_chunks.id rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" kms_master_key_id = aws_kms_key.s3.arn } bucket_key_enabled = true } } resource "aws_s3_bucket_public_access_block" "loki_chunks" { bucket = aws_s3_bucket.loki_chunks.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true } resource "aws_s3_bucket_lifecycle_configuration" "loki_chunks" { bucket = aws_s3_bucket.loki_chunks.id rule { id = "expire-old-logs" status = "Enabled" # Delete logs older than retention period expiration { days = var.log_retention_days } # Move to cheaper storage after 30 days transition { days = 30 storage_class = "STANDARD_IA" } } } # ============================================================================ # TEMPO TRACE STORAGE # ============================================================================ resource "aws_s3_bucket" "tempo_traces" { bucket = "${var.name}-tempo-traces-${data.aws_caller_identity.current.account_id}" tags = merge(var.tags, { Name = "${var.name}-tempo-traces" Purpose = "Tempo distributed trace storage" Service = "tempo" }) } resource "aws_s3_bucket_versioning" "tempo_traces" { bucket = aws_s3_bucket.tempo_traces.id versioning_configuration { status = "Suspended" } } resource "aws_s3_bucket_server_side_encryption_configuration" "tempo_traces" { bucket = aws_s3_bucket.tempo_traces.id rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" kms_master_key_id = aws_kms_key.s3.arn } bucket_key_enabled = true } } resource "aws_s3_bucket_public_access_block" "tempo_traces" { bucket = aws_s3_bucket.tempo_traces.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true } resource "aws_s3_bucket_lifecycle_configuration" "tempo_traces" { bucket = aws_s3_bucket.tempo_traces.id rule { id = "expire-old-traces" status = "Enabled" expiration { days = var.trace_retention_days } transition { days = 7 storage_class = "STANDARD_IA" } } } # ============================================================================ # GIT LFS STORAGE # ============================================================================ resource "aws_s3_bucket" "git_lfs" { bucket = "${var.name}-git-lfs-${data.aws_caller_identity.current.account_id}" tags = merge(var.tags, { Name = "${var.name}-git-lfs" Purpose = "Git LFS object storage for Forgejo" Service = "forgejo" }) } resource "aws_s3_bucket_versioning" "git_lfs" { bucket = aws_s3_bucket.git_lfs.id versioning_configuration { status = "Enabled" } } resource "aws_s3_bucket_server_side_encryption_configuration" "git_lfs" { bucket = aws_s3_bucket.git_lfs.id rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" kms_master_key_id = aws_kms_key.s3.arn } bucket_key_enabled = true } } resource "aws_s3_bucket_public_access_block" "git_lfs" { bucket = aws_s3_bucket.git_lfs.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true } resource "aws_s3_bucket_lifecycle_configuration" "git_lfs" { bucket = aws_s3_bucket.git_lfs.id rule { id = "intelligent-tiering" status = "Enabled" transition { days = 30 storage_class = "INTELLIGENT_TIERING" } noncurrent_version_expiration { noncurrent_days = 90 } } } # ============================================================================ # IAM POLICIES FOR SERVICE ACCESS # ============================================================================ # Policy for Loki to access its bucket resource "aws_iam_policy" "loki_s3" { name = "${var.name}-loki-s3-access" description = "Allow Loki to access S3 bucket for log storage" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket" ] Resource = [ aws_s3_bucket.loki_chunks.arn, "${aws_s3_bucket.loki_chunks.arn}/*" ] }, { Effect = "Allow" Action = [ "kms:Encrypt", "kms:Decrypt", "kms:GenerateDataKey" ] Resource = [aws_kms_key.s3.arn] } ] }) tags = var.tags } # Policy for Tempo to access its bucket resource "aws_iam_policy" "tempo_s3" { name = "${var.name}-tempo-s3-access" description = "Allow Tempo to access S3 bucket for trace storage" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket" ] Resource = [ aws_s3_bucket.tempo_traces.arn, "${aws_s3_bucket.tempo_traces.arn}/*" ] }, { Effect = "Allow" Action = [ "kms:Encrypt", "kms:Decrypt", "kms:GenerateDataKey" ] Resource = [aws_kms_key.s3.arn] } ] }) tags = var.tags } # Policy for Stalwart to access email blobs resource "aws_iam_policy" "stalwart_s3" { name = "${var.name}-stalwart-s3-access" description = "Allow Stalwart to access S3 bucket for email blob storage" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket" ] Resource = [ aws_s3_bucket.email_blobs.arn, "${aws_s3_bucket.email_blobs.arn}/*" ] }, { Effect = "Allow" Action = [ "kms:Encrypt", "kms:Decrypt", "kms:GenerateDataKey" ] Resource = [aws_kms_key.s3.arn] } ] }) tags = var.tags } # Policy for Forgejo to access Git LFS bucket resource "aws_iam_policy" "forgejo_s3" { name = "${var.name}-forgejo-s3-access" description = "Allow Forgejo to access S3 bucket for Git LFS storage" policy = jsonencode({ Version = "2012-10-17" Statement = [ { Effect = "Allow" Action = [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket" ] Resource = [ aws_s3_bucket.git_lfs.arn, "${aws_s3_bucket.git_lfs.arn}/*" ] }, { Effect = "Allow" Action = [ "kms:Encrypt", "kms:Decrypt", "kms:GenerateDataKey" ] Resource = [aws_kms_key.s3.arn] } ] }) tags = var.tags } # ============================================================================ # DATA SOURCES # ============================================================================ data "aws_caller_identity" "current" {}