All public-facing services in the CRC CyberLab are routed through a centralized Nginx reverse proxy running on VM 101 (crc-proxy-gateway-01) at 192.168.1.55. This proxy handles TLS termination via Let's Encrypt certificates and forwards traffic to the appropriate internal backend VMs.
Internet -> Dreamwall (108.31.169.90) -> VM 101 (192.168.1.55:80/443) -> Internal Service VMs
*.tcecure.com subdomains point to 108.31.169.90 (public IP)192.168.1.55 (VM 101)server_name| Property | Value |
|---|---|
| VM ID | 101 (on PVE1) |
| Hostname | crc-proxy-gateway-01 |
| Internal IP | 192.168.1.55 |
| OS | Ubuntu (Debian-based) |
| Nginx Version | 1.18.0 |
| Certbot | Installed at /usr/bin/certbot |
| Admin User | crc-adm |
| Public IP | 108.31.169.90 (via Dreamwall NAT) |
| Path | Purpose |
|---|---|
/etc/nginx/sites-available/ |
All site configuration files |
/etc/nginx/sites-enabled/ |
Symlinks to active configs |
/etc/letsencrypt/live/ |
SSL certificates (per domain) |
/etc/letsencrypt/options-ssl-nginx.conf |
Shared SSL options |
/etc/letsencrypt/ssl-dhparams.pem |
DH parameters |
crc.awx.tcecure.com| Property | Value |
|---|---|
| Config File | /etc/nginx/sites-available/crc-awx |
| Backend | http://192.168.1.60:30080 |
| Backend VM | VM 103 (crc-awx-01) at 192.168.1.60 |
| Service | AWX (Ansible Tower) |
| Cert Expiry | 2026-05-14 |
server {
server_name crc.awx.tcecure.com;
location / {
proxy_pass http://192.168.1.60:30080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/crc.awx.tcecure.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/crc.awx.tcecure.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
server {
if ($host = crc.awx.tcecure.com) { return 301 https://$host$request_uri; }
listen 80;
server_name crc.awx.tcecure.com;
return 404;
}
crc.ai.tcecure.com| Property | Value |
|---|---|
| Config File | /etc/nginx/sites-available/crc.ai.tcecure.com |
| Backend | http://192.168.1.61:4000 |
| Backend VM | VM 105 (AI IDE) at 192.168.1.61 |
| Service | CyberLab Portal (Next.js) |
| Cert Expiry | 2026-06-30 |
server {
server_name crc.ai.tcecure.com;
location / {
proxy_pass http://192.168.1.61:4000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/crc.ai.tcecure.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/crc.ai.tcecure.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
server {
if ($host = crc.ai.tcecure.com) { return 301 https://$host$request_uri; }
listen 80;
server_name crc.ai.tcecure.com;
return 404;
}
crc.guac.01.tcecure.com| Property | Value |
|---|---|
| Config File | /etc/nginx/sites-available/crc.guac.01.tcecure.com |
| Backend | http://192.168.1.51:8080/guacamole/ |
| Backend VM | VM at 192.168.1.51 |
| Service | Apache Guacamole (remote desktop gateway) |
| Cert Expiry | 2026-05-12 |
| Special | sub_filter injects custom JS for progress links |
server {
server_name crc.guac.01.tcecure.com;
location /guac-assets/ {
alias /var/www/guac-assets/;
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header Pragma "no-cache";
}
location / {
proxy_pass http://192.168.1.51:8080/guacamole/;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Accept-Encoding "";
proxy_read_timeout 86400;
proxy_connect_timeout 300;
sub_filter '</body>' '<script src="/guac-assets/progress-link.js?v=2"></script></body>';
sub_filter_once on;
sub_filter_types text/html;
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/crc.guac.01.tcecure.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/crc.guac.01.tcecure.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
server {
if ($host = crc.guac.01.tcecure.com) { return 301 https://$host$request_uri; }
listen 80;
server_name crc.guac.01.tcecure.com;
return 404;
}
crc.ide.tcecure.com| Property | Value |
|---|---|
| Config File | /etc/nginx/sites-available/crc.ide.tcecure.com |
| Backend | http://192.168.1.61:3000 |
| Backend VM | VM 105 (AI IDE) at 192.168.1.61 |
| Service | OpenHands AI IDE |
| Cert Expiry | 2026-07-06 |
server {
server_name crc.ide.tcecure.com;
location / {
proxy_pass http://192.168.1.61:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/crc.ide.tcecure.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/crc.ide.tcecure.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
server {
if ($host = crc.ide.tcecure.com) { return 301 https://$host$request_uri; }
listen 80;
server_name crc.ide.tcecure.com;
return 404;
}
crc.lms.tcecure.com| Property | Value |
|---|---|
| Config File | /etc/nginx/sites-available/crc.lms.tcecure.com |
| Backend | https://192.168.1.169 (HTTPS backend) |
| Backend VM | VM at 192.168.1.169 |
| Service | Canvas LMS |
| Cert Expiry | 2026-05-12 |
| Special | proxy_ssl_verify off - backend uses self-signed cert |
server {
server_name crc.lms.tcecure.com;
location / {
proxy_pass https://192.168.1.169;
proxy_ssl_verify off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300;
proxy_connect_timeout 300;
client_max_body_size 256M;
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/crc.lms.tcecure.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/crc.lms.tcecure.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
server {
if ($host = crc.lms.tcecure.com) { return 301 https://$host$request_uri; }
listen 80;
server_name crc.lms.tcecure.com;
return 404;
}
training.status.tcecure.com| Property | Value |
|---|---|
| Config File | /etc/nginx/sites-available/training.status.tcecure.com |
| Backend | http://192.168.1.61:4000 |
| Backend VM | VM 105 (AI IDE) at 192.168.1.61 |
| Service | CyberLab Portal - Training Status pages |
| Cert Expiry | 2026-05-15 |
| Special | URL redirects: / to /training/status, /pod/XX to /training/status/pod/XX |
server {
server_name training.status.tcecure.com;
set $portal_backend http://192.168.1.61:4000;
location = / { return 302 /training/status; }
location ~ ^/pod/(.+)$ { return 302 /training/status/pod/$1; }
location / {
proxy_pass $portal_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/training.status.tcecure.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/training.status.tcecure.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
server {
if ($host = training.status.tcecure.com) { return 301 https://$host$request_uri; }
listen 80;
server_name training.status.tcecure.com;
return 404;
}
crc.wiki.tcecure.com| Property | Value |
|---|---|
| Config File | /etc/nginx/sites-available/crc.wiki.tcecure.com |
| Backend | http://192.168.1.42:80 |
| Backend VM | VM (crc-wiki-01) at 192.168.1.42 |
| Service | Wiki.js (this wiki) |
| Cert Expiry | 2026-07-07 |
| Special | client_max_body_size 50M for file uploads |
Proxy chain: VM 101 -> Wiki VM Nginx (:80) -> Wiki.js Docker (:3000)
The Wiki VM has its own Nginx at /etc/nginx/sites-available/crc-wiki that proxies port 80 to localhost:3000 where the Wiki.js Docker container runs. The Docker container is bound to 127.0.0.1:3000 to prevent direct external access.
server {
server_name crc.wiki.tcecure.com;
client_max_body_size 50M;
location / {
proxy_pass http://192.168.1.42:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/crc.wiki.tcecure.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/crc.wiki.tcecure.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
server {
if ($host = crc.wiki.tcecure.com) { return 301 https://$host$request_uri; }
listen 80;
server_name crc.wiki.tcecure.com;
return 404;
}
| Domain | Expiry | Days Left |
|---|---|---|
| crc.awx.tcecure.com | 2026-05-14 | ~35 |
| crc.guac.01.tcecure.com | 2026-05-12 | ~34 |
| crc.lms.tcecure.com | 2026-05-12 | ~34 |
| training.status.tcecure.com | 2026-05-15 | ~36 |
| crc.ai.tcecure.com | 2026-06-30 | ~82 |
| crc.ide.tcecure.com | 2026-07-06 | ~88 |
| crc.wiki.tcecure.com | 2026-07-07 | ~89 |
Certbot is configured to auto-renew certificates. Verify with:
sudo certbot renew --dry-run
Create the Nginx config on VM 101:
sudo nano /etc/nginx/sites-available/crc.NEWSITE.tcecure.com
Use this template:
server {
listen 80;
server_name crc.NEWSITE.tcecure.com;
location / {
proxy_pass http://BACKEND_IP:BACKEND_PORT;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}
Enable the site:
sudo ln -s /etc/nginx/sites-available/crc.NEWSITE.tcecure.com /etc/nginx/sites-enabled/
Test and reload Nginx:
sudo nginx -t
sudo systemctl reload nginx
Add DNS record: Create an A record for crc.NEWSITE.tcecure.com pointing to 108.31.169.90
Obtain SSL certificate (after DNS propagates):
sudo certbot --nginx -d crc.NEWSITE.tcecure.com --non-interactive --agree-tos --email admin@tcecure.com
Certbot will automatically modify the Nginx config to add SSL directives and HTTP-to-HTTPS redirect.
Verify:
curl -I https://crc.NEWSITE.tcecure.com
Upgrade and Connection headers are included in the template for WebSocket support.proxy_pass https://... and add proxy_ssl_verify off if the backend uses a self-signed certificate.client_max_body_size if the service allows large file uploads.VM 101 is accessible via:
ssh devin-adm@108.31.169.90 -p 2225 then ssh crc-adm@192.168.1.55sudo qm guest exec 101 -- <command> (from PVE1)# Test nginx configuration
sudo nginx -t
# Reload nginx (apply config changes)
sudo systemctl reload nginx
# List all certificates
sudo certbot certificates
# Test certificate renewal
sudo certbot renew --dry-run
# View active sites
ls -la /etc/nginx/sites-enabled/
| Problem | Solution |
|---|---|
| 502 Bad Gateway | Backend service is down. Check the internal VM and its service. |
| 504 Gateway Timeout | Backend is slow. Increase proxy_read_timeout. |
| Certificate expired | Run sudo certbot renew on VM 101. |
| New site not working | Verify: config symlink exists, nginx -t passes, nginx reloaded, DNS resolves. |
| WebSocket not connecting | Ensure Upgrade and Connection headers are set in proxy config. |
| Large upload fails | Add/increase client_max_body_size in the server block. |