adding edge services
This commit is contained in:
parent
71fab52e96
commit
c7263f4673
30
README.md
30
README.md
|
|
@ -25,6 +25,12 @@ Argo CD.
|
|||
- default apps are `container-registry`, `gitea`, and
|
||||
`website-production`
|
||||
|
||||
4. `bootstrap/edge`
|
||||
- connects to the OCI jump box
|
||||
- uploads nginx, HAProxy, Varnish, and Squid configs
|
||||
- obtains and renews Let's Encrypt certificates for the configured hostname
|
||||
- runs the edge cache/proxy chain with Docker Compose
|
||||
|
||||
## Adding Nodes
|
||||
|
||||
Add entries to `bootstrap/cluster/variables.tf` or a `.tfvars` file:
|
||||
|
|
@ -70,6 +76,30 @@ single-node rebuild.
|
|||
|
||||
Add Helm releases through `bootstrap/platform`'s `extra_helm_releases` map.
|
||||
|
||||
## Edge Services
|
||||
|
||||
The OCI jump box runs the public edge path:
|
||||
|
||||
```text
|
||||
nginx -> HAProxy -> Varnish/Squid -> Raspberry Pi Tailscale NodePort
|
||||
```
|
||||
|
||||
The `bootstrap/edge` stack renders configs from `bootstrap/edge/templates` and
|
||||
deploys them to `/opt/homelab-edge` on the OCI host. Defaults are in
|
||||
`bootstrap/edge/variables.tf`; override them through `TF_VAR_*` or a `.tfvars`
|
||||
file when the public host, SSH key, server name, backend Tailscale IP, or
|
||||
NodePort changes.
|
||||
|
||||
Use the configured `server_name` in the browser, for example
|
||||
`https://lab2025.duckdns.org`. A raw OCI IP address will still show a browser
|
||||
certificate warning because the trusted certificate is issued for the hostname.
|
||||
|
||||
The edge stack uses HTTP-01 validation, so public DNS for `server_name` must
|
||||
point to the OCI public IP and inbound TCP 80 and 443 must be open before
|
||||
`./lab.sh deploy` runs. Set `TF_VAR_letsencrypt_email` to receive expiry notices,
|
||||
or leave it empty to register without an email. Set
|
||||
`TF_VAR_enable_letsencrypt=false` to keep using the temporary local certificate.
|
||||
|
||||
## Adding Apps
|
||||
|
||||
Add Kubernetes manifests under `apps/<name>` and register them in
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
# This file is maintained automatically by "tofu init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/hashicorp/null" {
|
||||
version = "3.3.0"
|
||||
constraints = "~> 3.2"
|
||||
hashes = [
|
||||
"h1:0r7+t8CqzjfBgHgEiJGBCw+McEUdRXliMdF+Hk29d8o=",
|
||||
"h1:EvvCOc4FJY3NitSm6BpzCcUPU53LayVCB/tPOxYmy7U=",
|
||||
"h1:IDVnZXNCh0u4LfeSazc9z1v/kNz+92Eej7ePWV6SbyE=",
|
||||
"h1:Iw2c0n9/4fS92N5WnJ3CCSwSUXZO953oHp9gj3pWCaM=",
|
||||
"h1:JofS1og3hPN0ANjH+gNjxrJyyk6znodpC/F0qhp4eEk=",
|
||||
"h1:QIBhsJ4+5+t0vFEgJwtezNLT31tsptFHOEyGAAhLR1o=",
|
||||
"h1:RjjoL9qRPwNTwLdtJsYUaFvunbPM2/oujf2DcUcitOE=",
|
||||
"h1:SSirA+z2VWTs1s+TCAx8vVKg9jh6cRjxqc8LYi2iQTI=",
|
||||
"h1:U2XZc7hxcpcWp/C2S9LtuGUimhMOD2UT5xAEJJQQQaU=",
|
||||
"h1:bPG+xE5UonkJv3y/Yn9Q7OfbP2qHU/QKiS31nwfe7S0=",
|
||||
"h1:eODLdk/pARc4yxChAFtwseVmBr+r5fF9yGOvUhwGEyM=",
|
||||
"h1:iFj1oM5ZPENspsPqK1kcvZzyP95jJE/CM0rlu0MfIss=",
|
||||
"h1:mdu+qpyVmjDDLMrcL1JFy+cSyF58I3TFJwB5NssCZ58=",
|
||||
"h1:tJmep6aoBeDH77XsYU65HAbi0RAjxtsmbCOXmnqT13U=",
|
||||
"h1:tdMTn1evBLd6KCeLqWdQXCpF07hBu3n5rY6N3rXw3Rc=",
|
||||
"zh:083dcc0bec53f8abfa3f2aa2ce9d732a9675338fd60ae7d61162e25db7cb08bf",
|
||||
"zh:19f7456b5a2ad16595860974714bfdb25b87bc16356ea9d5c7453892aaa27864",
|
||||
"zh:222c0ed1fed4e4c677ebe626104dbfdba66763e264de0d9c27c58ce60104ee69",
|
||||
"zh:271711d6caa7dd5a4e9b79fe8c679fab61a840bcf80040a0f5ebb425d1b27d97",
|
||||
"zh:5adcf35f30baaea13f80c2a2c774deb9369892719493049687e23476c9dff40f",
|
||||
"zh:5bcfd19df16e73d7f0ad75bd09e2b3b86cf6700d09822d585d68304b71de1d97",
|
||||
"zh:604edecf263e38674decb35bb4e0e048fdc951f26fa103c33065ff9728f0313b",
|
||||
"zh:782acbfb4fa4807e273e588fe45b4aaea9dd0fd1136f76ec3200f6f4db3af8d6",
|
||||
"zh:84411a596d528fe67294e5c1cfd0c2036b08802497bcc4215ce518924f3c9a4a",
|
||||
"zh:85e79eecf3f5348975cffec3016b0eba3baf605646102d4348796ccd2df2e5f6",
|
||||
"zh:95669535ca17aeefef307ebfd59ce6930953173baae5637e8cbbf0297ec7ad58",
|
||||
"zh:d04d9b177747bfd66b4a45b5d911a2a7822aa8451f5e35621971fb7a4206b530",
|
||||
"zh:e6d9c924475283e90833450a14a732f4deb6d9bb131db8f86ab856e894270836",
|
||||
"zh:ebcab0c8a1334c86ed7cfa53f571a17ad6d27e9901f27a8854ea622a74b54bb6",
|
||||
"zh:ef9c757bb2c83d2103811a3d86b6ec5be06b0ffc337b84db1582d023bce7cdcd",
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,250 @@
|
|||
terraform {
|
||||
required_version = ">= 1.0"
|
||||
required_providers {
|
||||
null = {
|
||||
source = "hashicorp/null"
|
||||
version = "~> 3.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
compose_file = templatefile("${path.module}/templates/docker-compose.yml.tftpl", {})
|
||||
default_conf = templatefile("${path.module}/templates/default.conf.tftpl", {
|
||||
server_name = var.server_name
|
||||
})
|
||||
default_vcl = templatefile("${path.module}/templates/default.vcl.tftpl", {
|
||||
backend_host = var.backend_host
|
||||
backend_port = tostring(var.backend_port)
|
||||
})
|
||||
haproxy_cfg = templatefile("${path.module}/templates/haproxy.cfg.tftpl", {
|
||||
stats_user = var.haproxy_stats_user
|
||||
stats_password = var.haproxy_stats_password
|
||||
})
|
||||
squid_conf = templatefile("${path.module}/templates/squid.conf.tftpl", {
|
||||
backend_host = var.backend_host
|
||||
backend_port = tostring(var.backend_port)
|
||||
})
|
||||
config_hash = sha256(join("\n---\n", [
|
||||
local.compose_file,
|
||||
local.default_conf,
|
||||
local.default_vcl,
|
||||
local.haproxy_cfg,
|
||||
local.squid_conf,
|
||||
]))
|
||||
}
|
||||
|
||||
resource "null_resource" "edge_services" {
|
||||
triggers = {
|
||||
host = var.edge_host
|
||||
user = var.edge_user
|
||||
install_dir = var.edge_install_dir
|
||||
server_name = var.server_name
|
||||
enable_letsencrypt = tostring(var.enable_letsencrypt)
|
||||
letsencrypt_email = var.letsencrypt_email
|
||||
letsencrypt_staging = tostring(var.letsencrypt_staging)
|
||||
certbot_version = "1"
|
||||
config_hash = local.config_hash
|
||||
}
|
||||
|
||||
connection {
|
||||
type = "ssh"
|
||||
user = self.triggers.user
|
||||
private_key = file(var.edge_ssh_key_path)
|
||||
host = self.triggers.host
|
||||
}
|
||||
|
||||
provisioner "remote-exec" {
|
||||
inline = [
|
||||
"rm -rf /tmp/homelab-edge",
|
||||
"mkdir -p /tmp/homelab-edge/config_files",
|
||||
]
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
content = local.compose_file
|
||||
destination = "/tmp/homelab-edge/docker-compose.yml"
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
content = local.default_conf
|
||||
destination = "/tmp/homelab-edge/config_files/default.conf"
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
content = local.default_vcl
|
||||
destination = "/tmp/homelab-edge/config_files/default.vcl"
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
content = local.haproxy_cfg
|
||||
destination = "/tmp/homelab-edge/config_files/haproxy.cfg"
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
content = local.squid_conf
|
||||
destination = "/tmp/homelab-edge/config_files/squid.conf"
|
||||
}
|
||||
|
||||
provisioner "remote-exec" {
|
||||
inline = [
|
||||
<<EOT
|
||||
set -eu
|
||||
|
||||
install_dir="${self.triggers.install_dir}"
|
||||
server_name="${self.triggers.server_name}"
|
||||
enable_letsencrypt="${self.triggers.enable_letsencrypt}"
|
||||
letsencrypt_email="${self.triggers.letsencrypt_email}"
|
||||
letsencrypt_staging="${self.triggers.letsencrypt_staging}"
|
||||
certbot_image="certbot/certbot:latest"
|
||||
|
||||
install_missing_packages() {
|
||||
missing_packages=""
|
||||
for package in "$@"; do
|
||||
if ! dpkg-query -W -f='$${Status}' "$package" 2>/dev/null | grep -q "install ok installed"; then
|
||||
missing_packages="$missing_packages $package"
|
||||
fi
|
||||
done
|
||||
if [ -n "$missing_packages" ]; then
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends $missing_packages
|
||||
fi
|
||||
}
|
||||
|
||||
install_missing_packages ca-certificates curl openssl
|
||||
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
curl -fsSL https://get.docker.com | sudo sh
|
||||
fi
|
||||
|
||||
if ! sudo docker compose version >/dev/null 2>&1; then
|
||||
install_missing_packages docker-compose-plugin
|
||||
fi
|
||||
|
||||
sudo mkdir -p \
|
||||
"$install_dir/config_files" \
|
||||
"$install_dir/certs" \
|
||||
"$install_dir/certbot/www" \
|
||||
"$install_dir/letsencrypt"
|
||||
sudo cp /tmp/homelab-edge/docker-compose.yml "$install_dir/docker-compose.yml"
|
||||
sudo cp /tmp/homelab-edge/config_files/default.conf "$install_dir/config_files/default.conf"
|
||||
sudo cp /tmp/homelab-edge/config_files/default.vcl "$install_dir/config_files/default.vcl"
|
||||
sudo cp /tmp/homelab-edge/config_files/haproxy.cfg "$install_dir/config_files/haproxy.cfg"
|
||||
sudo cp /tmp/homelab-edge/config_files/squid.conf "$install_dir/config_files/squid.conf"
|
||||
|
||||
if [ ! -s "$install_dir/certs/current.crt" ] || [ ! -s "$install_dir/certs/current.key" ]; then
|
||||
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
|
||||
-subj "/CN=$server_name" \
|
||||
-keyout "$install_dir/certs/current.key" \
|
||||
-out "$install_dir/certs/current.crt"
|
||||
fi
|
||||
|
||||
deploy_current_certificate() {
|
||||
if [ ! -s "$install_dir/letsencrypt/live/$server_name/fullchain.pem" ] ||
|
||||
[ ! -s "$install_dir/letsencrypt/live/$server_name/privkey.pem" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
sudo cp -L "$install_dir/letsencrypt/live/$server_name/fullchain.pem" "$install_dir/certs/current.crt"
|
||||
sudo cp -L "$install_dir/letsencrypt/live/$server_name/privkey.pem" "$install_dir/certs/current.key"
|
||||
sudo chmod 0644 "$install_dir/certs/current.crt"
|
||||
sudo chmod 0600 "$install_dir/certs/current.key"
|
||||
}
|
||||
|
||||
install_renewal_timer() {
|
||||
sudo tee /usr/local/sbin/homelab-edge-renew-certs.sh >/dev/null <<RENEW_EOT
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
docker run --rm -v "$install_dir/letsencrypt:/etc/letsencrypt" -v "$install_dir/certbot/www:/var/www/certbot" "$certbot_image" renew --webroot -w /var/www/certbot --quiet
|
||||
|
||||
if [ -s "$install_dir/letsencrypt/live/$server_name/fullchain.pem" ] &&
|
||||
[ -s "$install_dir/letsencrypt/live/$server_name/privkey.pem" ]; then
|
||||
cp -L "$install_dir/letsencrypt/live/$server_name/fullchain.pem" "$install_dir/certs/current.crt"
|
||||
cp -L "$install_dir/letsencrypt/live/$server_name/privkey.pem" "$install_dir/certs/current.key"
|
||||
chmod 0644 "$install_dir/certs/current.crt"
|
||||
chmod 0600 "$install_dir/certs/current.key"
|
||||
cd "$install_dir"
|
||||
docker compose exec -T nginx-dev nginx -s reload || docker compose restart nginx-dev
|
||||
fi
|
||||
RENEW_EOT
|
||||
sudo chmod 0755 /usr/local/sbin/homelab-edge-renew-certs.sh
|
||||
|
||||
sudo tee /etc/systemd/system/homelab-edge-renew-certs.service >/dev/null <<'SERVICE_EOT'
|
||||
[Unit]
|
||||
Description=Renew Homelab Edge TLS certificates
|
||||
After=docker.service network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/sbin/homelab-edge-renew-certs.sh
|
||||
SERVICE_EOT
|
||||
|
||||
sudo tee /etc/systemd/system/homelab-edge-renew-certs.timer >/dev/null <<'TIMER_EOT'
|
||||
[Unit]
|
||||
Description=Renew Homelab Edge TLS certificates twice daily
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 03,15:17:00
|
||||
RandomizedDelaySec=30m
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
TIMER_EOT
|
||||
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now homelab-edge-renew-certs.timer >/dev/null
|
||||
}
|
||||
|
||||
cd "$install_dir"
|
||||
sudo docker compose pull
|
||||
sudo docker compose up -d --remove-orphans
|
||||
sudo docker compose ps
|
||||
|
||||
if [ "$enable_letsencrypt" = "true" ]; then
|
||||
email_args="--register-unsafely-without-email"
|
||||
if [ -n "$letsencrypt_email" ]; then
|
||||
email_args="--email $letsencrypt_email"
|
||||
fi
|
||||
|
||||
staging_args=""
|
||||
if [ "$letsencrypt_staging" = "true" ]; then
|
||||
staging_args="--staging"
|
||||
fi
|
||||
|
||||
sudo docker run --rm \
|
||||
-v "$install_dir/letsencrypt:/etc/letsencrypt" \
|
||||
-v "$install_dir/certbot/www:/var/www/certbot" \
|
||||
"$certbot_image" certonly \
|
||||
--webroot \
|
||||
-w /var/www/certbot \
|
||||
-d "$server_name" \
|
||||
--preferred-challenges http \
|
||||
--agree-tos \
|
||||
--non-interactive \
|
||||
--keep-until-expiring \
|
||||
$email_args \
|
||||
$staging_args
|
||||
|
||||
deploy_current_certificate
|
||||
sudo docker compose exec -T nginx-dev nginx -s reload || sudo docker compose restart nginx-dev
|
||||
install_renewal_timer
|
||||
curl -fsS --connect-timeout 10 --resolve "$server_name:443:127.0.0.1" "https://$server_name/" >/dev/null
|
||||
else
|
||||
sudo systemctl disable --now homelab-edge-renew-certs.timer >/dev/null 2>&1 || true
|
||||
curl -kfsS --connect-timeout 10 https://127.0.0.1/ >/dev/null
|
||||
fi
|
||||
EOT
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
output "edge_host" {
|
||||
value = var.edge_host
|
||||
}
|
||||
|
||||
output "edge_install_dir" {
|
||||
value = var.edge_install_dir
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
# WAF-like rules
|
||||
map $request_uri $blocked_uris {
|
||||
default 0;
|
||||
~*(/\.env|/\.git|/\.aws|/wp-admin) 1;
|
||||
}
|
||||
|
||||
# Rate limiting zone
|
||||
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
|
||||
|
||||
# Cache zones
|
||||
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=static_assets:10m max_size=100m inactive=24h;
|
||||
proxy_cache_path /var/cache/nginx_dynamic levels=1:2 keys_zone=dynamic_content:5m max_size=50m inactive=1h;
|
||||
|
||||
upstream haproxy_backend {
|
||||
server haproxy-dev:9000;
|
||||
}
|
||||
|
||||
# Cloudflare IP ranges
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
set_real_ip_from 103.22.200.0/22;
|
||||
set_real_ip_from 103.31.4.0/22;
|
||||
set_real_ip_from 141.101.64.0/18;
|
||||
set_real_ip_from 108.162.192.0/18;
|
||||
set_real_ip_from 190.93.240.0/20;
|
||||
set_real_ip_from 188.114.96.0/20;
|
||||
set_real_ip_from 197.234.240.0/22;
|
||||
set_real_ip_from 198.41.128.0/17;
|
||||
set_real_ip_from 162.158.0.0/15;
|
||||
set_real_ip_from 104.16.0.0/13;
|
||||
set_real_ip_from 104.24.0.0/14;
|
||||
set_real_ip_from 172.64.0.0/13;
|
||||
set_real_ip_from 131.0.72.0/22;
|
||||
real_ip_header CF-Connecting-IP;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name ${server_name};
|
||||
|
||||
location ^~ /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
default_type "text/plain";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name ${server_name};
|
||||
|
||||
ssl_certificate /etc/nginx/certs/current.crt;
|
||||
ssl_certificate_key /etc/nginx/certs/current.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
|
||||
if ($blocked_uris) { return 403; }
|
||||
|
||||
add_header X-Frame-Options SAMEORIGIN;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin";
|
||||
add_header Permissions-Policy "geolocation=(), microphone=()";
|
||||
|
||||
gzip on;
|
||||
gzip_comp_level 2;
|
||||
gzip_min_length 1000;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
|
||||
location ~* \.(css|js)$ {
|
||||
proxy_pass http://haproxy_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_set_header CF-Connecting-IP $http_cf_connecting_ip;
|
||||
|
||||
proxy_cache static_assets;
|
||||
proxy_cache_valid 200 301 302 1h;
|
||||
proxy_cache_key "$scheme$request_method$host$request_uri";
|
||||
add_header X-Nginx-Cache "$upstream_cache_status";
|
||||
add_header Cache-Control "public, immutable, max-age=604800";
|
||||
}
|
||||
|
||||
location ~* \.(jpg|jpeg|png|gif|ico|webp|svg)$ {
|
||||
proxy_pass http://haproxy_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_set_header CF-Connecting-IP $http_cf_connecting_ip;
|
||||
|
||||
proxy_cache static_assets;
|
||||
proxy_cache_valid 200 301 302 1d;
|
||||
proxy_cache_key "$scheme$request_method$host$request_uri";
|
||||
add_header X-Nginx-Cache "$upstream_cache_status";
|
||||
add_header Cache-Control "public, immutable, max-age=2592000";
|
||||
}
|
||||
|
||||
location / {
|
||||
limit_req zone=one burst=20 nodelay;
|
||||
|
||||
proxy_pass http://haproxy_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 https;
|
||||
proxy_set_header CF-Connecting-IP $http_cf_connecting_ip;
|
||||
|
||||
proxy_cache dynamic_content;
|
||||
proxy_cache_valid 200 301 302 5m;
|
||||
proxy_cache_key "$scheme$request_method$host$request_uri";
|
||||
add_header X-Nginx-Cache "$upstream_cache_status";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
vcl 4.1;
|
||||
|
||||
backend default {
|
||||
.host = "${backend_host}";
|
||||
.port = "${backend_port}";
|
||||
}
|
||||
|
||||
sub vcl_recv {
|
||||
if (req.url == "/" || req.url == "/health") {
|
||||
return (pass);
|
||||
}
|
||||
|
||||
if (req.method != "GET" && req.method != "HEAD") {
|
||||
return (pass);
|
||||
}
|
||||
|
||||
return (hash);
|
||||
}
|
||||
|
||||
sub vcl_backend_response {
|
||||
set beresp.ttl = 1h;
|
||||
}
|
||||
|
||||
sub vcl_deliver {
|
||||
if (obj.hits > 0) {
|
||||
set resp.http.X-Cache = "HIT";
|
||||
} else {
|
||||
set resp.http.X-Cache = "MISS";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
services:
|
||||
nginx-dev:
|
||||
image: nginx:latest
|
||||
container_name: nginx-dev
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- haproxy-dev
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./config_files/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
- ./certs:/etc/nginx/certs:ro
|
||||
- ./certbot/www:/var/www/certbot:ro
|
||||
- nginx_cache:/var/cache/nginx
|
||||
- nginx_dynamic_cache:/var/cache/nginx_dynamic
|
||||
|
||||
haproxy-dev:
|
||||
image: haproxy:alpine
|
||||
container_name: haproxy-dev
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- varnish-dev
|
||||
- squid-dev
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "8404:8404"
|
||||
volumes:
|
||||
- ./config_files/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
|
||||
|
||||
varnish-dev:
|
||||
image: varnish:fresh-alpine
|
||||
container_name: varnish-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "6081:80"
|
||||
volumes:
|
||||
- ./config_files/default.vcl:/etc/varnish/default.vcl:ro
|
||||
|
||||
squid-dev:
|
||||
image: ubuntu/squid:latest
|
||||
container_name: squid-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3128:3128"
|
||||
volumes:
|
||||
- ./config_files/squid.conf:/etc/squid/squid.conf:ro
|
||||
- squid_cache:/var/spool/squid
|
||||
- squid_logs:/var/log/squid
|
||||
|
||||
volumes:
|
||||
nginx_cache:
|
||||
nginx_dynamic_cache:
|
||||
squid_cache:
|
||||
squid_logs:
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
global
|
||||
daemon
|
||||
maxconn 2000
|
||||
log stdout format raw local0 info
|
||||
|
||||
defaults
|
||||
mode http
|
||||
option httplog
|
||||
option dontlognull
|
||||
timeout connect 5s
|
||||
timeout client 30s
|
||||
timeout server 30s
|
||||
retries 2
|
||||
|
||||
frontend cache_in
|
||||
bind *:9000
|
||||
mode http
|
||||
default_backend cache_servers
|
||||
|
||||
backend cache_servers
|
||||
mode http
|
||||
balance roundrobin
|
||||
option tcp-check
|
||||
server varnish varnish-dev:80 check
|
||||
server squid squid-dev:3128 check
|
||||
|
||||
listen stats
|
||||
bind *:8404
|
||||
mode http
|
||||
stats enable
|
||||
stats uri /haproxy?stats
|
||||
stats refresh 10s
|
||||
stats realm Haproxy\ Statistics
|
||||
stats auth ${stats_user}:${stats_password}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
http_port 3128 accel vhost vport defaultsite=${backend_host}
|
||||
cache_peer ${backend_host} parent ${backend_port} 0 no-query originserver name=myBackend
|
||||
|
||||
acl docker_network src 172.16.0.0/12
|
||||
acl localhost src 127.0.0.1/32
|
||||
acl to_homelab dst ${backend_host}
|
||||
|
||||
http_access allow docker_network
|
||||
http_access allow localhost
|
||||
http_access allow to_homelab
|
||||
http_access deny all
|
||||
|
||||
cache_peer_access myBackend allow all
|
||||
|
||||
cache_mem 256 MB
|
||||
maximum_object_size_in_memory 512 KB
|
||||
maximum_object_size 50 MB
|
||||
minimum_object_size 0 KB
|
||||
cache_dir ufs /var/spool/squid 2000 16 256
|
||||
|
||||
refresh_pattern -i \.html?$ 60 20% 3600
|
||||
refresh_pattern -i \.(css|js)$ 1440 10% 10080
|
||||
refresh_pattern -i \.(jpg|jpeg|png|gif|ico|webp|svg)$ 10080 5% 43200
|
||||
refresh_pattern -i \.(woff|woff2|ttf|eot)$ 43200 1% 31536000
|
||||
refresh_pattern -i \.php$ 5 50% 30
|
||||
refresh_pattern -i /api/ 5 50% 30
|
||||
refresh_pattern . 1440 20% 4320
|
||||
|
||||
access_log /var/log/squid/access.log squid
|
||||
cache_log /var/log/squid/cache.log
|
||||
cache_store_log /var/log/squid/store.log
|
||||
|
||||
request_header_access Allow allow all
|
||||
request_header_access Authorization allow all
|
||||
request_header_access WWW-Authenticate allow all
|
||||
request_header_access Proxy-Authorization allow all
|
||||
request_header_access Proxy-Authenticate allow all
|
||||
request_header_access Cache-Control allow all
|
||||
request_header_access Content-Encoding allow all
|
||||
request_header_access Content-Length allow all
|
||||
request_header_access Content-Type allow all
|
||||
request_header_access Date allow all
|
||||
request_header_access Expires allow all
|
||||
request_header_access Host allow all
|
||||
request_header_access If-Modified-Since allow all
|
||||
request_header_access Last-Modified allow all
|
||||
request_header_access Location allow all
|
||||
request_header_access Pragma allow all
|
||||
request_header_access Accept allow all
|
||||
request_header_access Accept-Charset allow all
|
||||
request_header_access Accept-Encoding allow all
|
||||
request_header_access Accept-Language allow all
|
||||
request_header_access Content-Language allow all
|
||||
request_header_access Mime-Version allow all
|
||||
request_header_access Retry-After allow all
|
||||
request_header_access Title allow all
|
||||
request_header_access Connection allow all
|
||||
request_header_access Proxy-Connection allow all
|
||||
request_header_access User-Agent allow all
|
||||
request_header_access Cookie allow all
|
||||
|
||||
coredump_dir /var/spool/squid
|
||||
|
||||
forwarded_for off
|
||||
via off
|
||||
|
||||
negative_ttl 5 minutes
|
||||
positive_dns_ttl 6 hours
|
||||
read_timeout 15 minutes
|
||||
client_db off
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
variable "edge_host" {
|
||||
type = string
|
||||
default = "132.145.170.74"
|
||||
}
|
||||
|
||||
variable "edge_user" {
|
||||
type = string
|
||||
default = "ubuntu"
|
||||
}
|
||||
|
||||
variable "edge_ssh_key_path" {
|
||||
type = string
|
||||
default = "/home/jv/.ssh/id_ed25519"
|
||||
}
|
||||
|
||||
variable "edge_install_dir" {
|
||||
type = string
|
||||
default = "/opt/homelab-edge"
|
||||
}
|
||||
|
||||
variable "server_name" {
|
||||
type = string
|
||||
default = "lab2025.duckdns.org"
|
||||
}
|
||||
|
||||
variable "enable_letsencrypt" {
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "letsencrypt_email" {
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "letsencrypt_staging" {
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "backend_host" {
|
||||
type = string
|
||||
default = "100.77.80.72"
|
||||
}
|
||||
|
||||
variable "backend_port" {
|
||||
type = number
|
||||
default = 30080
|
||||
}
|
||||
|
||||
variable "haproxy_stats_user" {
|
||||
type = string
|
||||
default = "admin"
|
||||
}
|
||||
|
||||
variable "haproxy_stats_password" {
|
||||
type = string
|
||||
default = "adminpassword"
|
||||
}
|
||||
28
lab.sh
28
lab.sh
|
|
@ -7,6 +7,25 @@ KUBECONFIG_PATH="${KUBECONFIG_PATH:-${TF_VAR_kubeconfig_path:-/home/jv/.kube/con
|
|||
|
||||
trap 'rm -f "${BUILDX_CONFIG}"' EXIT
|
||||
|
||||
require_debian_server() {
|
||||
local command_name="$1"
|
||||
local os_id=""
|
||||
|
||||
if [[ "$(uname -s)" != "Linux" ]]; then
|
||||
echo "Refusing to run '${command_name}' from this machine. Run it on the Debian homelab server." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -r /etc/os-release ]]; then
|
||||
os_id="$(awk -F= '$1 == "ID" {gsub(/"/, "", $2); print $2; exit}' /etc/os-release)"
|
||||
fi
|
||||
|
||||
if [[ "${os_id}" != "debian" ]]; then
|
||||
echo "Refusing to run '${command_name}' on ${os_id:-unknown OS}. Run it on the Debian homelab server." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
run_tofu_stack() {
|
||||
local stack="$1"
|
||||
|
||||
|
|
@ -222,6 +241,8 @@ refresh_argocd_application() {
|
|||
up() {
|
||||
local registry_endpoint
|
||||
|
||||
require_debian_server "up"
|
||||
|
||||
registry_endpoint="$(website_registry_endpoint)"
|
||||
export TF_VAR_registry_endpoint="${TF_VAR_registry_endpoint:-${registry_endpoint}}"
|
||||
export TF_VAR_kubeconfig_path="${TF_VAR_kubeconfig_path:-${KUBECONFIG_PATH}}"
|
||||
|
|
@ -278,6 +299,8 @@ EOF
|
|||
recreate_pods_for_selector website-production app=php-website website-production
|
||||
wait_for_deployment_ready website-production php-website-deployment website-production 300
|
||||
|
||||
run_tofu_stack "bootstrap/edge"
|
||||
|
||||
echo "Deployment successfully completed."
|
||||
}
|
||||
|
||||
|
|
@ -286,6 +309,8 @@ nuke() {
|
|||
local worker_targets
|
||||
local target
|
||||
|
||||
require_debian_server "nuke"
|
||||
|
||||
echo "Brutally nuking the homelab infrastructure..."
|
||||
worker_ssh_targets="${WORKER_SSH_TARGETS-jv@192.168.100.89}"
|
||||
read -r -a worker_targets <<< "${worker_ssh_targets}"
|
||||
|
|
@ -413,6 +438,9 @@ EOF
|
|||
rm -rf "${REPO_ROOT}"/bootstrap/apps/terraform.tfstate*
|
||||
rm -f "${REPO_ROOT}"/bootstrap/apps/.terraform.tfstate.lock.info
|
||||
rm -rf "${REPO_ROOT}"/bootstrap/apps/.terraform/
|
||||
rm -rf "${REPO_ROOT}"/bootstrap/edge/terraform.tfstate*
|
||||
rm -f "${REPO_ROOT}"/bootstrap/edge/.terraform.tfstate.lock.info
|
||||
rm -rf "${REPO_ROOT}"/bootstrap/edge/.terraform/
|
||||
|
||||
echo "Destruction complete. Retained data under /var/openebs/local was left intact."
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue