Hearth is the infrastructure home for the letemcook ecosystem. Ported from coherence-mcp/infra: - Terraform modules (VPC, EKS, IAM, NLB, S3, storage) - Kubernetes manifests (Forgejo, ingress, cert-manager, karpenter) - Deployment scripts (phased rollout) Status: Not deployed. EKS cluster needs to be provisioned. Next steps: 1. Bootstrap terraform backend 2. Deploy phase 1 (foundation) 3. Deploy phase 2 (core services including Forgejo) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
246 lines
5.4 KiB
HCL
246 lines
5.4 KiB
HCL
# Storage Module - EBS, EFS, S3
|
|
# RFC 0039: ADR-Compliant Foundation Infrastructure
|
|
#
|
|
# Architecture:
|
|
# - EFS for shared persistent storage (git repos, files)
|
|
# - S3 for backups and blob storage
|
|
# - KMS for encryption (FIPS compliance)
|
|
|
|
# KMS Key for Storage Encryption
|
|
resource "aws_kms_key" "storage" {
|
|
description = "Storage encryption key"
|
|
deletion_window_in_days = 30
|
|
enable_key_rotation = true
|
|
|
|
tags = merge(var.tags, {
|
|
Name = "${var.name}-storage"
|
|
})
|
|
}
|
|
|
|
resource "aws_kms_alias" "storage" {
|
|
name = "alias/${var.name}-storage"
|
|
target_key_id = aws_kms_key.storage.key_id
|
|
}
|
|
|
|
# EFS File System
|
|
resource "aws_efs_file_system" "main" {
|
|
creation_token = var.name
|
|
encrypted = var.enable_encryption
|
|
kms_key_id = var.enable_encryption ? aws_kms_key.storage.arn : null
|
|
performance_mode = "generalPurpose"
|
|
throughput_mode = "bursting"
|
|
|
|
lifecycle_policy {
|
|
transition_to_ia = "AFTER_30_DAYS"
|
|
}
|
|
|
|
lifecycle_policy {
|
|
transition_to_primary_storage_class = "AFTER_1_ACCESS"
|
|
}
|
|
|
|
tags = merge(var.tags, {
|
|
Name = var.name
|
|
})
|
|
}
|
|
|
|
# EFS Mount Targets (one per AZ)
|
|
resource "aws_efs_mount_target" "main" {
|
|
count = length(var.private_subnet_ids)
|
|
|
|
file_system_id = aws_efs_file_system.main.id
|
|
subnet_id = var.private_subnet_ids[count.index]
|
|
security_groups = [aws_security_group.efs.id]
|
|
}
|
|
|
|
# EFS Security Group
|
|
resource "aws_security_group" "efs" {
|
|
name = "${var.name}-efs"
|
|
description = "EFS security group"
|
|
vpc_id = var.vpc_id
|
|
|
|
ingress {
|
|
description = "NFS from VPC"
|
|
from_port = 2049
|
|
to_port = 2049
|
|
protocol = "tcp"
|
|
cidr_blocks = [data.aws_vpc.main.cidr_block]
|
|
}
|
|
|
|
egress {
|
|
description = "All outbound"
|
|
from_port = 0
|
|
to_port = 0
|
|
protocol = "-1"
|
|
cidr_blocks = ["0.0.0.0/0"]
|
|
}
|
|
|
|
tags = merge(var.tags, {
|
|
Name = "${var.name}-efs"
|
|
})
|
|
}
|
|
|
|
# EFS Access Point for CSI driver
|
|
resource "aws_efs_access_point" "main" {
|
|
file_system_id = aws_efs_file_system.main.id
|
|
|
|
posix_user {
|
|
gid = 1000
|
|
uid = 1000
|
|
}
|
|
|
|
root_directory {
|
|
path = "/data"
|
|
creation_info {
|
|
owner_gid = 1000
|
|
owner_uid = 1000
|
|
permissions = "0755"
|
|
}
|
|
}
|
|
|
|
tags = merge(var.tags, {
|
|
Name = "${var.name}-data"
|
|
})
|
|
}
|
|
|
|
# S3 Bucket for Backups
|
|
resource "aws_s3_bucket" "backups" {
|
|
bucket = "${var.name}-backups-${data.aws_caller_identity.current.account_id}"
|
|
|
|
tags = merge(var.tags, {
|
|
Name = "${var.name}-backups"
|
|
Purpose = "CockroachDB and service backups"
|
|
})
|
|
}
|
|
|
|
resource "aws_s3_bucket_versioning" "backups" {
|
|
bucket = aws_s3_bucket.backups.id
|
|
|
|
versioning_configuration {
|
|
status = "Enabled"
|
|
}
|
|
}
|
|
|
|
resource "aws_s3_bucket_server_side_encryption_configuration" "backups" {
|
|
bucket = aws_s3_bucket.backups.id
|
|
|
|
rule {
|
|
apply_server_side_encryption_by_default {
|
|
sse_algorithm = var.enable_encryption ? "aws:kms" : "AES256"
|
|
kms_master_key_id = var.enable_encryption ? aws_kms_key.storage.arn : null
|
|
}
|
|
bucket_key_enabled = true
|
|
}
|
|
}
|
|
|
|
resource "aws_s3_bucket_public_access_block" "backups" {
|
|
bucket = aws_s3_bucket.backups.id
|
|
|
|
block_public_acls = true
|
|
block_public_policy = true
|
|
ignore_public_acls = true
|
|
restrict_public_buckets = true
|
|
}
|
|
|
|
resource "aws_s3_bucket_lifecycle_configuration" "backups" {
|
|
bucket = aws_s3_bucket.backups.id
|
|
|
|
rule {
|
|
id = "transition-to-ia"
|
|
status = "Enabled"
|
|
|
|
transition {
|
|
days = 30
|
|
storage_class = "STANDARD_IA"
|
|
}
|
|
|
|
transition {
|
|
days = 90
|
|
storage_class = "GLACIER"
|
|
}
|
|
|
|
noncurrent_version_transition {
|
|
noncurrent_days = 30
|
|
storage_class = "STANDARD_IA"
|
|
}
|
|
|
|
noncurrent_version_expiration {
|
|
noncurrent_days = 365
|
|
}
|
|
}
|
|
}
|
|
|
|
# S3 Bucket for Blob Storage
|
|
resource "aws_s3_bucket" "blobs" {
|
|
bucket = "${var.name}-blobs-${data.aws_caller_identity.current.account_id}"
|
|
|
|
tags = merge(var.tags, {
|
|
Name = "${var.name}-blobs"
|
|
Purpose = "Application blob storage"
|
|
})
|
|
}
|
|
|
|
resource "aws_s3_bucket_versioning" "blobs" {
|
|
bucket = aws_s3_bucket.blobs.id
|
|
|
|
versioning_configuration {
|
|
status = "Enabled"
|
|
}
|
|
}
|
|
|
|
resource "aws_s3_bucket_server_side_encryption_configuration" "blobs" {
|
|
bucket = aws_s3_bucket.blobs.id
|
|
|
|
rule {
|
|
apply_server_side_encryption_by_default {
|
|
sse_algorithm = var.enable_encryption ? "aws:kms" : "AES256"
|
|
kms_master_key_id = var.enable_encryption ? aws_kms_key.storage.arn : null
|
|
}
|
|
bucket_key_enabled = true
|
|
}
|
|
}
|
|
|
|
resource "aws_s3_bucket_public_access_block" "blobs" {
|
|
bucket = aws_s3_bucket.blobs.id
|
|
|
|
block_public_acls = true
|
|
block_public_policy = true
|
|
ignore_public_acls = true
|
|
restrict_public_buckets = true
|
|
}
|
|
|
|
resource "aws_s3_bucket_cors_configuration" "blobs" {
|
|
bucket = aws_s3_bucket.blobs.id
|
|
|
|
cors_rule {
|
|
allowed_headers = ["*"]
|
|
allowed_methods = ["GET", "PUT", "POST", "DELETE", "HEAD"]
|
|
allowed_origins = ["*"] # Restrict in production
|
|
expose_headers = ["ETag"]
|
|
max_age_seconds = 3000
|
|
}
|
|
}
|
|
|
|
resource "aws_s3_bucket_lifecycle_configuration" "blobs" {
|
|
bucket = aws_s3_bucket.blobs.id
|
|
|
|
rule {
|
|
id = "intelligent-tiering"
|
|
status = "Enabled"
|
|
|
|
transition {
|
|
days = 30
|
|
storage_class = "INTELLIGENT_TIERING"
|
|
}
|
|
|
|
noncurrent_version_expiration {
|
|
noncurrent_days = 90
|
|
}
|
|
}
|
|
}
|
|
|
|
# Data sources
|
|
data "aws_vpc" "main" {
|
|
id = var.vpc_id
|
|
}
|
|
|
|
data "aws_caller_identity" "current" {}
|