Add automatic SSL certificate generation
All checks were successful
Build and Push / build-all (push) Successful in 15s

This commit is contained in:
doomtube 2026-01-06 03:46:28 -05:00
parent 1220c5101d
commit 38ecb718e7
8 changed files with 92 additions and 31 deletions

View file

@ -432,9 +432,9 @@ write_files:
deploy: deploy:
resources: resources:
limits: limits:
memory: 512M memory: 3G
reservations: reservations:
memory: 256M memory: 1G
networks: networks:
forgejo-internal: forgejo-internal:
@ -471,11 +471,11 @@ write_files:
} }
${domain} { ${domain} {
# Rate limiting - 100 requests per minute per IP # Rate limiting - 500 requests per minute per IP
rate_limit { rate_limit {
zone forgejo_zone { zone forgejo_zone {
key {remote_host} key {remote_host}
events 100 events 500
window 1m window 1m
} }
} }

View file

@ -182,7 +182,7 @@ services:
JWT_SECRET: ${JWT_SECRET} JWT_SECRET: ${JWT_SECRET}
volumes: volumes:
- uploads:/app/uploads:ro - uploads:/app/uploads:ro
- letsencrypt_data:/etc/letsencrypt:ro - /etc/letsencrypt:/etc/letsencrypt:ro
- certbot_webroot:/var/www/certbot:ro - certbot_webroot:/var/www/certbot:ro
networks: networks:
- frontend - frontend
@ -203,7 +203,7 @@ services:
image: certbot/certbot:latest image: certbot/certbot:latest
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- letsencrypt_data:/etc/letsencrypt - /etc/letsencrypt:/etc/letsencrypt
- certbot_webroot:/var/www/certbot - certbot_webroot:/var/www/certbot
entrypoint: ["/bin/sh", "-c"] entrypoint: ["/bin/sh", "-c"]
command: command:
@ -227,5 +227,4 @@ volumes:
redis_data: redis_data:
ome_logs: ome_logs:
uploads: uploads:
letsencrypt_data:
certbot_webroot: certbot_webroot:

View file

@ -91,17 +91,43 @@ http {
# Default for most uploads (images, stickers) # Default for most uploads (images, stickers)
client_max_body_size 5m; client_max_body_size 5m;
# ==========================================================================
# HTTP Server - Redirect to HTTPS (except ACME challenges)
# ==========================================================================
server { server {
listen 80; listen 80;
server_name localhost; server_name _;
# ACME challenge endpoint for Let's Encrypt certificate validation # ACME challenge endpoint for Let's Encrypt certificate validation
# This must come before any access control blocks
location /.well-known/acme-challenge/ { location /.well-known/acme-challenge/ {
root /var/www/certbot; root /var/www/certbot;
try_files $uri =404; try_files $uri =404;
} }
# Redirect all other HTTP traffic to HTTPS
location / {
return 301 https://$host$request_uri;
}
}
# ==========================================================================
# HTTPS Server - Main application server
# ==========================================================================
server {
listen 443 ssl http2;
server_name beeta.realms.pub;
# SSL certificates (obtained by certbot on host, mounted via docker-compose)
ssl_certificate /etc/letsencrypt/live/beeta.realms.pub/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/beeta.realms.pub/privkey.pem;
# Modern SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# Site-wide uberban check - blocks banned fingerprints from all endpoints # Site-wide uberban check - blocks banned fingerprints from all endpoints
access_by_lua_block { access_by_lua_block {
-- Skip OPTIONS requests (CORS preflight) -- Skip OPTIONS requests (CORS preflight)

View file

@ -38,20 +38,21 @@ locals {
module "app_server" { module "app_server" {
source = "./modules/app_server" source = "./modules/app_server"
project_name = var.project_name project_name = var.project_name
environment = var.environment environment = var.environment
region = var.region region = var.region
vpc_uuid = var.vpc_uuid vpc_uuid = var.vpc_uuid
vpc_ip_range = var.vpc_ip_range vpc_ip_range = var.vpc_ip_range
ssh_keys = local.all_ssh_key_ids ssh_keys = local.all_ssh_key_ids
droplet_size = var.app_droplet_size droplet_size = var.app_droplet_size
droplet_image = var.app_droplet_image droplet_image = var.app_droplet_image
ssh_port = var.app_ssh_port ssh_port = var.app_ssh_port
domain = var.app_domain domain = var.app_domain
enable_backups = var.enable_droplet_backups enable_backups = var.enable_droplet_backups
tags = local.common_tags tags = local.common_tags
manage_dns = var.manage_dns manage_dns = var.manage_dns
dns_zone = var.dns_zone dns_zone = var.dns_zone
dns_record_name = var.dns_record_name dns_record_name = var.dns_record_name
forgejo_registry = var.forgejo_registry forgejo_registry = var.forgejo_registry
letsencrypt_email = var.letsencrypt_email
} }

View file

@ -229,4 +229,20 @@ runcmd:
- systemctl enable unattended-upgrades - systemctl enable unattended-upgrades
- systemctl start unattended-upgrades - systemctl start unattended-upgrades
final_message: "Realms app server ready after $UPTIME seconds. Deploy via Forgejo CI/CD." # Install certbot for SSL certificates
- DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=60 install -y certbot
# Create directories for certbot webroot
- mkdir -p /opt/realms/certbot_webroot
# Obtain initial SSL certificate (standalone mode - no webserver running yet)
# This runs before Docker services start, so port 80 is free
- |
certbot certonly --standalone \
--non-interactive \
--agree-tos \
--email ${letsencrypt_email} \
-d ${domain} \
|| echo "Certbot failed - certificate may need to be obtained manually after DNS propagates"
final_message: "Realms app server ready after $UPTIME seconds. SSL cert obtained for ${domain}. Deploy via Forgejo CI/CD."

View file

@ -15,10 +15,11 @@ resource "digitalocean_droplet" "app" {
ipv6 = true ipv6 = true
user_data = templatefile("${path.module}/cloud-init.yaml.tpl", { user_data = templatefile("${path.module}/cloud-init.yaml.tpl", {
ssh_port = var.ssh_port ssh_port = var.ssh_port
vpc_ip_range = var.vpc_ip_range vpc_ip_range = var.vpc_ip_range
domain = var.domain domain = var.domain
forgejo_registry = var.forgejo_registry forgejo_registry = var.forgejo_registry
letsencrypt_email = var.letsencrypt_email
}) })
tags = var.tags tags = var.tags

View file

@ -99,3 +99,12 @@ variable "forgejo_registry" {
type = string type = string
default = "qbit.realms.pub" default = "qbit.realms.pub"
} }
# =============================================================================
# SSL Certificate Configuration
# =============================================================================
variable "letsencrypt_email" {
description = "Email for Let's Encrypt certificate notifications"
type = string
}

View file

@ -139,3 +139,12 @@ variable "forgejo_registry" {
type = string type = string
default = "qbit.realms.pub" default = "qbit.realms.pub"
} }
# =============================================================================
# SSL Certificate Configuration
# =============================================================================
variable "letsencrypt_email" {
description = "Email for Let's Encrypt certificate notifications"
type = string
}