From b3682b1936714674c4c0cd37a0dfee1b24c8d200 Mon Sep 17 00:00:00 2001 From: doomtube Date: Tue, 6 Jan 2026 00:26:54 -0500 Subject: [PATCH] Initial commit - realms platform --- .forgejo/workflows/build.yml | 8 +- .forgejo/workflows/deploy.yml | 2 +- .gitignore | 5 ++ devops/forgejo-server/docker-compose.yml | 30 ++++++++ .../modules/forgejo/cloud-init.yaml.tpl | 72 +++++++++++++++++- devops/terraform/terraform.tfvars.example | 76 ------------------- devops/terraform/variables.tf | 4 +- terraform/main.tf | 19 +++-- terraform/modules/app_server/main.tf | 12 +-- terraform/modules/app_server/variables.tf | 6 ++ terraform/modules/app_server/versions.tf | 8 ++ terraform/variables.tf | 6 ++ 12 files changed, 148 insertions(+), 100 deletions(-) delete mode 100644 devops/terraform/terraform.tfvars.example create mode 100644 terraform/modules/app_server/versions.tf diff --git a/.forgejo/workflows/build.yml b/.forgejo/workflows/build.yml index 8437d69..f7b55cf 100644 --- a/.forgejo/workflows/build.yml +++ b/.forgejo/workflows/build.yml @@ -23,7 +23,7 @@ jobs: # Build Backend (C++/Drogon) # =========================================================================== build-backend: - runs-on: docker + runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 @@ -50,7 +50,7 @@ jobs: # Build Frontend (SvelteKit) # =========================================================================== build-frontend: - runs-on: docker + runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 @@ -77,7 +77,7 @@ jobs: # Build Chat Service # =========================================================================== build-chat: - runs-on: docker + runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 @@ -104,7 +104,7 @@ jobs: # Build OpenResty (Nginx + Lua) # =========================================================================== build-openresty: - runs-on: docker + runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index 6bdb46c..2f510f2 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -22,7 +22,7 @@ jobs: deploy: # Only deploy if build succeeded OR manual trigger if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }} - runs-on: docker + runs-on: ubuntu-latest steps: - name: Checkout code diff --git a/.gitignore b/.gitignore index 3897b19..af870ca 100644 --- a/.gitignore +++ b/.gitignore @@ -122,6 +122,11 @@ secrets.json .gnupg/ backend/config.json !backend/config.json.example +terraform.tfvars +terraform.tfstate +terraform.tfstate.backup +.terraform/ +.terraform.lock.hcl # Temporary Files # ====================================================== diff --git a/devops/forgejo-server/docker-compose.yml b/devops/forgejo-server/docker-compose.yml index bc7fe24..7610ef4 100644 --- a/devops/forgejo-server/docker-compose.yml +++ b/devops/forgejo-server/docker-compose.yml @@ -24,6 +24,12 @@ services: interval: 10s timeout: 5s retries: 5 + deploy: + resources: + limits: + memory: 256M + reservations: + memory: 128M # --------------------------------------------------------------------------- # Forgejo Git Server @@ -105,6 +111,14 @@ services: timeout: 10s retries: 3 start_period: 60s + security_opt: + - no-new-privileges:true + deploy: + resources: + limits: + memory: 384M + reservations: + memory: 192M # --------------------------------------------------------------------------- # Caddy Reverse Proxy @@ -128,6 +142,14 @@ services: - forgejo-public environment: FORGEJO_DOMAIN: ${FORGEJO_DOMAIN} + security_opt: + - no-new-privileges:true + deploy: + resources: + limits: + memory: 64M + reservations: + memory: 32M # --------------------------------------------------------------------------- # Forgejo Actions Runner @@ -161,6 +183,12 @@ services: fi forgejo-runner daemon --config /data/config.yaml ' + deploy: + resources: + limits: + memory: 256M + reservations: + memory: 128M # --------------------------------------------------------------------------- # Docker-in-Docker for Runner @@ -184,6 +212,8 @@ services: resources: limits: memory: 512M + reservations: + memory: 256M # ============================================================================= # Networks diff --git a/devops/terraform/modules/forgejo/cloud-init.yaml.tpl b/devops/terraform/modules/forgejo/cloud-init.yaml.tpl index 2ffee24..858fa0d 100644 --- a/devops/terraform/modules/forgejo/cloud-init.yaml.tpl +++ b/devops/terraform/modules/forgejo/cloud-init.yaml.tpl @@ -379,16 +379,77 @@ write_files: reservations: memory: 32M + # Forgejo Actions Runner (CI/CD) + forgejo-runner: + image: code.forgejo.org/forgejo/runner:6.3.1 + container_name: forgejo-runner + restart: unless-stopped + depends_on: + forgejo: + condition: service_healthy + docker-dind: + condition: service_started + environment: + DOCKER_HOST: tcp://docker-dind:2376 + DOCKER_TLS_VERIFY: "1" + DOCKER_CERT_PATH: /certs/client + volumes: + - /mnt/forgejo/runner-data:/data + - dind-certs-client:/certs/client:ro + networks: + - forgejo-internal + - dind-network + command: > + sh -c ' + if [ ! -f /data/.runner ]; then + echo "Runner not registered. Run: docker compose exec forgejo-runner forgejo-runner register" + sleep infinity + fi + forgejo-runner daemon --config /data/config.yaml + ' + deploy: + resources: + limits: + memory: 256M + reservations: + memory: 128M + + # Docker-in-Docker for Runner (builds images in CI/CD) + docker-dind: + image: docker:27-dind + container_name: forgejo-dind + restart: unless-stopped + privileged: true + environment: + DOCKER_TLS_CERTDIR: /certs + volumes: + - dind-certs-ca:/certs/ca + - dind-certs-client:/certs/client + - dind-storage:/var/lib/docker + networks: + - dind-network + deploy: + resources: + limits: + memory: 512M + reservations: + memory: 256M + networks: forgejo-internal: driver: bridge internal: true forgejo-public: driver: bridge + dind-network: + driver: bridge volumes: caddy_data: caddy_config: + dind-certs-ca: + dind-certs-client: + dind-storage: permissions: '0644' # Caddy Dockerfile with rate-limit plugin @@ -516,6 +577,15 @@ write_files: | cd /opt/forgejo && docker compose logs -f | | cd /opt/forgejo && docker compose restart | | | + | Runner Registration: | + | 1. Get token from Forgejo: Site Admin > Actions > Runners | + | 2. Register runner: | + | cd /opt/forgejo && docker compose exec forgejo-runner \ | + | forgejo-runner register --instance https://${domain} | + | --token YOUR_TOKEN --name realms-runner | + | --labels ubuntu-latest,docker | + | 3. Restart: docker compose restart forgejo-runner | + | | +---------------------------------------------------------------+ permissions: '0644' @@ -580,7 +650,7 @@ runcmd: # Start Forgejo stack (build Caddy with rate-limit plugin, pull others) - cd /opt/forgejo && docker compose build caddy - - cd /opt/forgejo && docker compose pull forgejo forgejo-db + - cd /opt/forgejo && docker compose pull forgejo forgejo-db forgejo-runner docker-dind - cd /opt/forgejo && docker compose up -d # Enable unattended upgrades diff --git a/devops/terraform/terraform.tfvars.example b/devops/terraform/terraform.tfvars.example deleted file mode 100644 index d89b739..0000000 --- a/devops/terraform/terraform.tfvars.example +++ /dev/null @@ -1,76 +0,0 @@ -# ============================================================================= -# DigitalOcean Terraform Configuration -# ============================================================================= -# Copy this file to terraform.tfvars and fill in your values -# NEVER commit terraform.tfvars to version control! -# -# Set the DO token via environment variable: -# export TF_VAR_do_token="dop_v1_your_token_here" -# ============================================================================= - -# ============================================================================= -# Project Configuration -# ============================================================================= - -project_name = "realms" -environment = "production" -region = "nyc3" - -# ============================================================================= -# VPC Configuration -# ============================================================================= - -vpc_ip_range = "10.10.0.0/16" - -# ============================================================================= -# SSH Configuration -# ============================================================================= - -# Add your admin SSH public key(s) here -# Generate with: ssh-keygen -t ed25519 -C "your_email@example.com" -admin_ssh_public_keys = { - # "admin-name" = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINlczdk2KKjY2CyYV1Ql8enjRn8gpBBgSLmbbCUyG5Qs admin@doom.tube" -} - -# SSH ports (non-standard for security) -jump_host_ssh_port = 49822 # Jump host public SSH -forgejo_ssh_port = 52913 # Forgejo system SSH (VPC only) -forgejo_git_ssh_port = 2222 # Forgejo Git SSH (public) - -# ============================================================================= -# Jump Host Configuration -# ============================================================================= - -jump_host_size = "s-1vcpu-512mb-10gb" # $4/mo -jump_host_image = "debian-12-x64" - -# ============================================================================= -# Forgejo Configuration -# ============================================================================= - -forgejo_droplet_size = "s-1vcpu-1gb-intel" # $7/mo - 1GB RAM, 1 Intel vCPU -forgejo_droplet_image = "debian-12-x64" -forgejo_volume_size = 50 # GB for repositories and LFS -forgejo_domain = "qbit.realms.pub" - -# ============================================================================= -# DNS Configuration (requires domain to be managed by DigitalOcean) -# ============================================================================= - -# Set to true to automatically create/update A record for Forgejo -manage_dns = true - -# Base domain managed by DigitalOcean DNS -dns_zone = "realms.pub" - -# ============================================================================= -# Backup Configuration -# ============================================================================= - -enable_droplet_backups = true - -# ============================================================================= -# Additional Tags -# ============================================================================= - -tags = [] diff --git a/devops/terraform/variables.tf b/devops/terraform/variables.tf index bb239ad..67b9884 100644 --- a/devops/terraform/variables.tf +++ b/devops/terraform/variables.tf @@ -94,9 +94,9 @@ variable "jump_host_image" { # ============================================================================= variable "forgejo_droplet_size" { - description = "Size slug for the Forgejo droplet" + description = "Size slug for the Forgejo droplet (2GB+ recommended for Actions Runner)" type = string - default = "s-1vcpu-1gb-intel" + default = "s-2vcpu-2gb-intel" } variable "forgejo_droplet_image" { diff --git a/terraform/main.tf b/terraform/main.tf index a3809f2..2f2c612 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -16,11 +16,19 @@ locals { # SSH Keys # ============================================================================= -resource "digitalocean_ssh_key" "admin" { - for_each = var.admin_ssh_public_keys +# Get all SSH keys on account +data "digitalocean_ssh_keys" "all" {} - name = "${var.project_name}-app-${var.environment}-${each.key}" - public_key = each.value +# Find the internal VPC key (created by devops/terraform for jump host access) +locals { + internal_key_name = "${var.project_name}-${var.environment}-internal-key" + internal_key_ids = [for k in data.digitalocean_ssh_keys.all.ssh_keys : k.id if k.name == local.internal_key_name] + + # Combine: internal key (for jump host) + all admin keys + all_ssh_key_ids = distinct(concat( + local.internal_key_ids, + [for k in data.digitalocean_ssh_keys.all.ssh_keys : k.id] + )) } # ============================================================================= @@ -35,7 +43,7 @@ module "app_server" { region = var.region vpc_uuid = var.vpc_uuid vpc_ip_range = var.vpc_ip_range - ssh_keys = [for key in digitalocean_ssh_key.admin : key.id] + ssh_keys = local.all_ssh_key_ids droplet_size = var.app_droplet_size droplet_image = var.app_droplet_image ssh_port = var.app_ssh_port @@ -44,5 +52,6 @@ module "app_server" { tags = local.common_tags manage_dns = var.manage_dns dns_zone = var.dns_zone + dns_record_name = var.dns_record_name forgejo_registry = var.forgejo_registry } diff --git a/terraform/modules/app_server/main.tf b/terraform/modules/app_server/main.tf index 0bb20ed..fa0b3c4 100644 --- a/terraform/modules/app_server/main.tf +++ b/terraform/modules/app_server/main.tf @@ -180,17 +180,7 @@ resource "digitalocean_record" "app" { domain = var.dns_zone type = "A" - name = "@" - value = digitalocean_droplet.app.ipv4_address - ttl = 600 -} - -resource "digitalocean_record" "app_www" { - count = var.manage_dns ? 1 : 0 - - domain = var.dns_zone - type = "A" - name = "www" + name = var.dns_record_name value = digitalocean_droplet.app.ipv4_address ttl = 600 } diff --git a/terraform/modules/app_server/variables.tf b/terraform/modules/app_server/variables.tf index d65985b..04cad88 100644 --- a/terraform/modules/app_server/variables.tf +++ b/terraform/modules/app_server/variables.tf @@ -84,6 +84,12 @@ variable "dns_zone" { default = "realms.pub" } +variable "dns_record_name" { + description = "DNS record name (subdomain). Use '@' for root or 'beeta' for beeta.realms.pub" + type = string + default = "beeta" +} + # ============================================================================= # Forgejo Registry # ============================================================================= diff --git a/terraform/modules/app_server/versions.tf b/terraform/modules/app_server/versions.tf new file mode 100644 index 0000000..801275e --- /dev/null +++ b/terraform/modules/app_server/versions.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + digitalocean = { + source = "digitalocean/digitalocean" + version = "~> 2.34" + } + } +} diff --git a/terraform/variables.tf b/terraform/variables.tf index bf6270e..7b38b09 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -104,6 +104,12 @@ variable "dns_zone" { default = "realms.pub" } +variable "dns_record_name" { + description = "DNS record name (subdomain). Use '@' for root or 'beeta' for beeta.realms.pub" + type = string + default = "beeta" +} + # ============================================================================= # Backup Configuration # =============================================================================