Skip to content
This repository was archived by the owner on Oct 10, 2025. It is now read-only.

Commit 9af19e6

Browse files
committed
feat: [#21] implement complete HTTPS automation with self-signed certificates
- Enable HTTPS by default in deployment (ENABLE_HTTPS=true) - Fix Docker Compose SSL certificate volume mappings: - Mount /var/lib/torrust/proxy/certs to /etc/ssl/certs (was incorrectly mapped to dhparam) - Mount /var/lib/torrust/proxy/private to /etc/ssl/private - Move dhparam to /etc/ssl/dhparam to avoid conflicts - Fix nginx HTTPS template variable escaping for envsubst: - Replace all nginx variables () with ${DOLLAR}var to prevent envsubst processing - Ensures proper nginx variable substitution in generated config - Update deployment script for streamlined SSL certificate generation: - Generate 10-year self-signed certificates directly on VM using openssl - Switch domain from tracker-demo.local to test.local for consistency - Remove complex container-based certificate generation (chicken-egg problem) - Add comprehensive HTTPS connection info with /etc/hosts instructions - Implement complete twelve-factor deployment workflow: - Infrastructure provisioning (make infra-apply) - Application deployment with HTTPS (make app-deploy) - Health validation and connection info display Deployment now provides both HTTP and HTTPS endpoints: - HTTP: tracker.test.local, grafana.test.local (via nginx proxy) - HTTPS: Same domains with 10-year self-signed certificates - Direct access: VM IP for debugging/monitoring Resolves certificate chicken-egg problem by generating certificates on host before container startup, enabling automated HTTPS deployment without manual steps.
1 parent 331ba20 commit 9af19e6

File tree

3 files changed

+70
-32
lines changed

3 files changed

+70
-32
lines changed

application/compose.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ services:
2727
volumes:
2828
- /var/lib/torrust/proxy/webroot:/var/www/html
2929
- /var/lib/torrust/proxy/etc/nginx-conf:/etc/nginx/conf.d
30+
- /var/lib/torrust/proxy/certs:/etc/ssl/certs
31+
- /var/lib/torrust/proxy/private:/etc/ssl/private
3032
- /var/lib/torrust/certbot/etc:/etc/letsencrypt
3133
- /var/lib/torrust/certbot/webroot:/var/lib/torrust/certbot/webroot
3234
- /var/lib/torrust/certbot/lib:/var/lib/letsencrypt
33-
- /var/lib/torrust/dhparam:/etc/ssl/certs
35+
- /var/lib/torrust/dhparam:/etc/ssl/dhparam
3436
logging:
3537
options:
3638
max-size: "10m"

infrastructure/config/templates/nginx-https-selfsigned.conf.tpl

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# It is intended for development and testing environments
44

55
# WebSocket connection upgrade mapping for Grafana
6-
map $http_upgrade $connection_upgrade {
6+
map ${DOLLAR}http_upgrade ${DOLLAR}connection_upgrade {
77
default upgrade;
88
'' close;
99
}
@@ -44,23 +44,23 @@ server {
4444
# Tracker API endpoints
4545
location /api/ {
4646
proxy_pass http://tracker:1212/api/;
47-
proxy_set_header Host $host;
48-
proxy_set_header X-Real-IP $remote_addr;
49-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
50-
proxy_set_header X-Forwarded-Proto $scheme;
51-
proxy_set_header X-Forwarded-Host $host;
52-
proxy_set_header X-Forwarded-Server $host;
47+
proxy_set_header Host ${DOLLAR}host;
48+
proxy_set_header X-Real-IP ${DOLLAR}remote_addr;
49+
proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for;
50+
proxy_set_header X-Forwarded-Proto ${DOLLAR}scheme;
51+
proxy_set_header X-Forwarded-Host ${DOLLAR}host;
52+
proxy_set_header X-Forwarded-Server ${DOLLAR}host;
5353
}
5454

5555
# Tracker HTTP endpoints
5656
location / {
5757
proxy_pass http://tracker:7070;
58-
proxy_set_header Host $host;
59-
proxy_set_header X-Real-IP $remote_addr;
60-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
61-
proxy_set_header X-Forwarded-Proto $scheme;
62-
proxy_set_header X-Forwarded-Host $host;
63-
proxy_set_header X-Forwarded-Server $host;
58+
proxy_set_header Host ${DOLLAR}host;
59+
proxy_set_header X-Real-IP ${DOLLAR}remote_addr;
60+
proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for;
61+
proxy_set_header X-Forwarded-Proto ${DOLLAR}scheme;
62+
proxy_set_header X-Forwarded-Host ${DOLLAR}host;
63+
proxy_set_header X-Forwarded-Server ${DOLLAR}host;
6464
}
6565

6666
# Health check endpoint (accessible via HTTPS)
@@ -102,17 +102,17 @@ server {
102102
# Grafana web interface
103103
location / {
104104
proxy_pass http://grafana;
105-
proxy_set_header Host $host;
106-
proxy_set_header X-Real-IP $remote_addr;
107-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
108-
proxy_set_header X-Forwarded-Proto $scheme;
109-
proxy_set_header X-Forwarded-Host $host;
110-
proxy_set_header X-Forwarded-Server $host;
105+
proxy_set_header Host ${DOLLAR}host;
106+
proxy_set_header X-Real-IP ${DOLLAR}remote_addr;
107+
proxy_set_header X-Forwarded-For ${DOLLAR}proxy_add_x_forwarded_for;
108+
proxy_set_header X-Forwarded-Proto ${DOLLAR}scheme;
109+
proxy_set_header X-Forwarded-Host ${DOLLAR}host;
110+
proxy_set_header X-Forwarded-Server ${DOLLAR}host;
111111

112112
# WebSocket support for Grafana live features
113113
proxy_http_version 1.1;
114-
proxy_set_header Upgrade $http_upgrade;
115-
proxy_set_header Connection $connection_upgrade;
114+
proxy_set_header Upgrade ${DOLLAR}http_upgrade;
115+
proxy_set_header Connection ${DOLLAR}connection_upgrade;
116116
proxy_read_timeout 86400;
117117
proxy_buffering off;
118118
}
@@ -139,7 +139,7 @@ server {
139139

140140
# Redirect all other HTTP traffic to HTTPS
141141
location / {
142-
return 301 https://$server_name$request_uri;
142+
return 301 https://${DOLLAR}server_name${DOLLAR}request_uri;
143143
}
144144
}
145145

@@ -157,6 +157,6 @@ server {
157157

158158
# Redirect all other HTTP traffic to HTTPS
159159
location / {
160-
return 301 https://$server_name$request_uri;
160+
return 301 https://${DOLLAR}server_name${DOLLAR}request_uri;
161161
}
162162
}

infrastructure/scripts/deploy-app.sh

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ TERRAFORM_DIR="${PROJECT_ROOT}/infrastructure/terraform"
1414
ENVIRONMENT="${1:-local}"
1515
VM_IP="${2:-}"
1616
SKIP_HEALTH_CHECK="${SKIP_HEALTH_CHECK:-false}"
17-
ENABLE_HTTPS="${ENABLE_SSL:-false}" # Enable HTTPS with self-signed certificates by default
17+
ENABLE_HTTPS="${ENABLE_SSL:-true}" # Enable HTTPS with self-signed certificates by default
1818

1919
# Source shared shell utilities
2020
# shellcheck source=../../scripts/shell-utils.sh
@@ -344,7 +344,7 @@ generate_nginx_http_config() {
344344
# Generate and deploy nginx HTTPS configuration with self-signed certificates from template
345345
generate_nginx_https_selfsigned_config() {
346346
local vm_ip="$1"
347-
local domain_name="${DOMAIN_NAME:-tracker-demo.local}"
347+
local domain_name="${DOMAIN_NAME:-test.local}"
348348

349349
log_info "Generating nginx HTTPS configuration with self-signed certificates from template..."
350350

@@ -362,7 +362,7 @@ generate_nginx_https_selfsigned_config() {
362362
# Check if domain name is set
363363
if [[ -z "${domain_name}" ]]; then
364364
log_error "Domain name is required for HTTPS configuration"
365-
log_error "Set DOMAIN_NAME environment variable (e.g., DOMAIN_NAME=tracker-demo.local)"
365+
log_error "Set DOMAIN_NAME environment variable (e.g., DOMAIN_NAME=test.local)"
366366
exit 1
367367
fi
368368

@@ -402,7 +402,7 @@ generate_nginx_https_selfsigned_config() {
402402
# Generate self-signed SSL certificates on the VM
403403
generate_selfsigned_certificates() {
404404
local vm_ip="$1"
405-
local domain_name="${DOMAIN_NAME:-tracker-demo.local}"
405+
local domain_name="${DOMAIN_NAME:-test.local}"
406406

407407
log_info "Generating self-signed SSL certificates on VM..."
408408

@@ -428,13 +428,15 @@ generate_selfsigned_certificates() {
428428
# Make script executable and run it
429429
vm_exec "${vm_ip}" "chmod +x /tmp/ssl-generate-test-certs.sh"
430430

431-
# Create temporary shell-utils in the expected location for the script
432-
vm_exec "${vm_ip}" "sudo mkdir -p /tmp/share/dev"
433-
vm_exec "${vm_ip}" "sudo cp /tmp/shell-utils.sh /tmp/share/dev/shell-utils.sh"
431+
# Create temporary directory structure for certificate generation script
432+
vm_exec "${vm_ip}" "sudo mkdir -p /tmp/share/bin /tmp/share/dev"
433+
vm_exec "${vm_ip}" "sudo cp /tmp/ssl-generate-test-certs.sh /tmp/share/bin/"
434+
vm_exec "${vm_ip}" "sudo cp /tmp/shell-utils.sh /tmp/share/dev/"
435+
vm_exec "${vm_ip}" "sudo chmod +x /tmp/share/bin/ssl-generate-test-certs.sh"
434436

435437
# Run certificate generation
436438
log_info "Running certificate generation for domain: ${domain_name}"
437-
vm_exec "${vm_ip}" "cd /var/lib/torrust/compose && SCRIPT_DIR=/tmp /tmp/ssl-generate-test-certs.sh '${domain_name}'"
439+
vm_exec "${vm_ip}" "cd /var/lib/torrust/compose && /tmp/share/bin/ssl-generate-test-certs.sh '${domain_name}'"
438440

439441
# Clean up temporary files
440442
vm_exec "${vm_ip}" "rm -f /tmp/ssl-generate-test-certs.sh /tmp/shell-utils.sh"
@@ -807,6 +809,23 @@ run_stage() {
807809
# Wait for services to initialize
808810
wait_for_services "${vm_ip}"
809811

812+
# Setup HTTPS with self-signed certificates (if enabled)
813+
if [[ "${ENABLE_HTTPS}" == "true" ]]; then
814+
log_info "Setting up HTTPS with self-signed certificates..."
815+
generate_selfsigned_certificates "${vm_ip}"
816+
generate_nginx_https_selfsigned_config "${vm_ip}"
817+
818+
# Restart proxy to apply HTTPS configuration
819+
vm_exec "${vm_ip}" "
820+
cd /home/torrust/github/torrust/torrust-tracker-demo/application
821+
docker compose --env-file /var/lib/torrust/compose/.env restart proxy
822+
" "Restarting proxy with HTTPS configuration"
823+
824+
# Wait a moment for proxy to restart
825+
sleep 5
826+
log_success "HTTPS setup completed"
827+
fi
828+
810829
# Setup database backup automation (if enabled)
811830
setup_backup_automation "${vm_ip}"
812831

@@ -911,6 +930,23 @@ show_connection_info() {
911930
echo "UDP Tracker: udp://${vm_ip}:6868, udp://${vm_ip}:6969"
912931
echo "Grafana: http://${vm_ip}:3100 (admin/admin)" # DevSkim: ignore DS137138
913932
echo
933+
echo "=== HTTPS ENDPOINTS (with self-signed certificates) ==="
934+
echo "Tracker API: https://tracker.test.local (add to /etc/hosts)"
935+
echo "Grafana: https://grafana.test.local (add to /etc/hosts)"
936+
echo
937+
echo "=== SETUP FOR HTTPS TESTING ==="
938+
echo "Add these lines to your /etc/hosts file:"
939+
echo "${vm_ip} tracker.test.local"
940+
echo "${vm_ip} grafana.test.local"
941+
echo
942+
echo "Then access:"
943+
echo "• Tracker API: https://tracker.test.local/health_check"
944+
echo "• Tracker Stats: https://tracker.test.local/api/v1/stats?token=MyAccessToken"
945+
echo "• Grafana Login: https://grafana.test.local (admin/admin)"
946+
echo
947+
echo "Note: Your browser will show a security warning for self-signed certificates."
948+
echo " Click 'Advanced' -> 'Proceed to site' to continue."
949+
echo
914950
echo "=== NEXT STEPS ==="
915951
echo "Health Check: make app-health-check ENVIRONMENT=${ENVIRONMENT}"
916952
echo "View Logs: ssh torrust@${vm_ip} 'cd torrust-tracker-demo/application && docker compose --env-file /var/lib/torrust/compose/.env logs'"

0 commit comments

Comments
 (0)