feat: refactor, deploy and provisioning

This commit is contained in:
Tine 2024-11-04 21:32:57 +01:00
parent a9639a9340
commit 31bed3964b
Signed by: mentos1386
SSH key fingerprint: SHA256:MNtTsLbihYaWF8j1fkOHfkKNlnN1JQfxEU/rBU8nCGw
13 changed files with 404 additions and 205 deletions

View file

@ -1,6 +1,21 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
##
# Source Code
# We store all initial configs in the /srv location
cd /srv
# Clone if not yet cloned
if [ ! -d .git ]; then
git clone https://code.tjo.space/tjo-cloud/ingress.git .
else
git pull
fi
##
# Metadata
SERVICE_NAME="ingress.tjo.cloud" SERVICE_NAME="ingress.tjo.cloud"
SERVICE_VERSION="$(git describe --tags --always --dirty)" SERVICE_VERSION="$(git describe --tags --always --dirty)"
CLOUD_REGION="$(hostname -s)" CLOUD_REGION="$(hostname -s)"
@ -26,11 +41,11 @@ mkdir -p /etc/apt/keyrings/
wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor | tee /etc/apt/keyrings/grafana.gpg >/dev/null wget -q -O - https://apt.grafana.com/gpg.key | gpg --dearmor | tee /etc/apt/keyrings/grafana.gpg >/dev/null
echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" | tee /etc/apt/sources.list.d/grafana.list echo "deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main" | tee /etc/apt/sources.list.d/grafana.list
apt update -y apt update -y
apt get install -y alloy apt install -y alloy
## ##
# Copy Sysmtemd service/units # Copy Sysmtemd service/units
cp -r root/etc/systemd/* / #cp -r root/etc/systemd/* /
## ##
# Ensure services are enabled # Ensure services are enabled
@ -55,10 +70,10 @@ systemctl reload alloy
## ##
# Configure NGINX # Configure NGINX
cp -r root/etc/nginx/* /etc/nginx/ #cp -r root/etc/nginx/* /etc/nginx/
systemctl reload nginx systemctl reload nginx
## ##
# Configure Webhook # Configure Webhook
cp -r root/etc/webhook/* /etc/webhook/ #cp -r root/etc/webhook/* /etc/webhook/
systemctl reload webhook systemctl restart webhook

View file

@ -10,6 +10,10 @@ lint:
@tofu fmt -check -recursive . @tofu fmt -check -recursive .
@tflint --recursive @tflint --recursive
format:
@tofu fmt -recursive .
@tflint --recursive
deploy: deploy:
#!/usr/bin/env sh #!/usr/bin/env sh
cd {{justfile_directory()}}/terraform cd {{justfile_directory()}}/terraform
@ -20,3 +24,45 @@ destroy:
#!/usr/bin/env sh #!/usr/bin/env sh
cd {{justfile_directory()}}/terraform cd {{justfile_directory()}}/terraform
tofu destroy tofu destroy
# Create a list of blocked IP ranges. Traffic we don't want.
ingress-blocked-list:
#!/usr/bin/env bash
GOOGLE_BOT_IPV4=$(curl -s https://raw.githubusercontent.com/lord-alfred/ipranges/main/googlebot/ipv4_merged.txt)
GOOGLE_BOT_IPV6=$(curl -s https://raw.githubusercontent.com/lord-alfred/ipranges/main/googlebot/ipv6_merged.txt)
echo "# GOOGLE_BOT IPV4 $(echo $GOOGLE_BOT_IPV4 | wc -w)"
echo "# GOOGLE_BOT IPV6 $(echo $GOOGLE_BOT_IPV6 | wc -w)"
BING_BOT_IPV4=$(curl -s https://raw.githubusercontent.com/lord-alfred/ipranges/main/bing/ipv4_merged.txt)
echo "# BING_BOT IPV4 $(echo $BING_BOT_IPV4 | wc -w)"
OPENAI_IPV4=$(curl -s https://raw.githubusercontent.com/lord-alfred/ipranges/main/openai/ipv4_merged.txt)
echo "# OPENAI IPV4 $(echo $OPENAI_IPV4 | wc -w)"
FACEBOOK_IPV4=$(curl -s https://raw.githubusercontent.com/lord-alfred/ipranges/main/facebook/ipv4_merged.txt)
FACEBOOK_IPV6=$(curl -s https://raw.githubusercontent.com/lord-alfred/ipranges/main/facebook/ipv6_merged.txt)
echo "# FACEBOOK IPV4 $(echo $FACEBOOK_IPV4 | wc -w)"
echo "# FACEBOOK IPV6 $(echo $FACEBOOK_IPV6 | wc -w)"
IP_RANGES=$(echo -e "$GOOGLE_BOT_IPV4\n$GOOGLE_BOT_IPV6\n$BING_BOT_IPV4\n$BING_BOT_IPV6\n$OPENAI_IPV4\n$FACEBOOK_IPV4\n$FACEBOOK_IPV6" | sort | uniq)
echo "#!!DO NOT EDIT!! Generated by using just ingress-blocked-list command." > root/etc/nginx/partials/blocked.conf
for ip in $IP_RANGES; do
echo "deny $ip;" >> root/etc/nginx/partials/blocked.conf
done
provision:
#!/usr/bin/env sh
set -eou pipefail
pushd {{justfile_directory()}}/terraform > /dev/null
NODES=$(tofu output -json | jq -r '.nodes.value[]')
popd > /dev/null
for NODE in $NODES
do
echo "Provisioning node ${NODE}"
ssh ubuntu@${NODE} 'sudo rm -rf /srv && sudo mkdir /srv && sudo chown ubuntu:ubuntu /srv'
cat install.sh | ssh ubuntu@${NODE} 'sudo bash -s'
done

91
root/etc/nginx/nginx.conf Normal file
View file

@ -0,0 +1,91 @@
user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
}
error_log /dev/stderr error;
stream {
# Map of Host -> IP
# We will route the traffic to this endpoints.
map $ssl_preread_server_name $selected_upstream {
hostnames;
# if not knonw, use some non existing response thingy :shrug:
default 255.255.255.255:1;
## CLOUD
proxmox.tjo.cloud batuu.system.tjo.space:4443;
postgresql.tjo.cloud batuu.system.tjo.space:4443;
monitor.tjo.cloud hetzner.system.tjo.cloud:4443;
loki.monitor.tjo.cloud hetzner.system.tjo.cloud:4443;
prometheus.monitor.tjo.cloud hetzner.system.tjo.cloud:4443;
grpc.otel.monitor.tjo.cloud hetzner.system.tjo.cloud:4443;
http.otel.monitor.tjo.cloud hetzner.system.tjo.cloud:4443;
vault.tjo.cloud batuu.system.tjo.space:4443;
## HETZNER
tjo.space hetzner.system.tjo.cloud:4443;
chat.tjo.space hetzner.system.tjo.cloud:4443;
webhook.chat.tjo.space hetzner.system.tjo.cloud:4443;
matrix.chat.tjo.space hetzner.system.tjo.cloud:4443;
yt.tjo.space hetzner.system.tjo.cloud:4443;
search.tjo.space hetzner.system.tjo.cloud:4443;
send.tjo.space hetzner.system.tjo.cloud:4443;
## BATUU
cloud.tjo.space batuu.system.tjo.space:4443;
collabora.tjo.space batuu.system.tjo.space:4443;
code.tjo.space batuu.system.tjo.space:4443;
vault.tjo.space batuu.system.tjo.space:4443;
rss.tjo.space batuu.system.tjo.space:4443;
id.tjo.space batuu.system.tjo.space:4443;
ldap.id.tjo.space batuu.system.tjo.space:4443;
mnts.dev batuu.system.tjo.space:4443;
paperless.tjo.space batuu.system.tjo.space:4443;
penpot.tjo.space batuu.system.tjo.space:4443;
## JAKKU
books.tjo.space jakku.system.tjo.space:4443;
media.tjo.space jakku.system.tjo.space:4443;
next.media.tjo.space jakku.system.tjo.space:4443;
request.media.tjo.space jakku.system.tjo.space:4443;
tdarr.media.tjo.space jakku.system.tjo.space:4443;
stuff.tjo.space jakku.system.tjo.space:4443;
auth.media.tjo.space jakku.system.tjo.space:4443;
sonarr.media.tjo.space jakku.system.tjo.space:4443;
radarr.media.tjo.space jakku.system.tjo.space:4443;
lidarr.media.tjo.space jakku.system.tjo.space:4443;
prowlarr.media.tjo.space jakku.system.tjo.space:4443;
qbittorrent.media.tjo.space jakku.system.tjo.space:4443;
bazarr.media.tjo.space jakku.system.tjo.space:4443;
readarr.media.tjo.space jakku.system.tjo.space:4443;
}
geoip2 /var/geoip.mmdb {
$geoip2_data_country_iso_code country iso_code;
$geoip2_data_latitude location latitude;
$geoip2_data_longitude location longitude;
}
log_format basic 'country=$geoip2_data_country_iso_code lat=$geoip2_data_latitude long=$geoip2_data_longitude '
'ip=$remote_addr '
'protocol=$protocol server_name=$ssl_preread_server_name upstream=$selected_upstream status=$status bytes_sent=$bytes_sent bytes_received=$bytes_received '
'session_time=$session_time';
server {
listen 0.0.0.0:443 default_server;
listen [::]:443 default_server;
server_name _;
proxy_pass $selected_upstream;
proxy_protocol on;
include /etc/nginx/partials/server.conf;
include /etc/nginx/partials/blocked.conf;
}
}

View file

@ -0,0 +1,164 @@
#!!DO NOT EDIT!! Generated by using just ingress-blocked-list command.
deny 102.132.96.0/19;
deny 102.221.188.0/22;
deny 103.4.96.0/22;
deny 129.134.0.0/16;
deny 13.66.139.0/24;
deny 13.66.144.0/24;
deny 13.67.10.16/28;
deny 13.69.66.240/28;
deny 13.71.172.224/28;
deny 139.217.52.0/28;
deny 147.75.208.0/20;
deny 157.240.0.0/16;
deny 157.55.39.0/24;
deny 163.114.128.0/20;
deny 163.70.128.0/17;
deny 163.77.128.0/17;
deny 173.252.64.0/18;
deny 179.60.192.0/22;
deny 185.60.216.0/22;
deny 185.89.216.0/22;
deny 191.233.204.224/28;
deny 192.178.5.0/27;
deny 192.178.6.0/27;
deny 196.49.68.0/23;
deny 199.201.64.0/22;
deny 199.30.24.0/23;
deny 2001:4860:4801:10::/60;
deny 2001:4860:4801:20::/60;
deny 2001:4860:4801:2::/64;
deny 2001:4860:4801:31::/64;
deny 2001:4860:4801:32::/63;
deny 2001:4860:4801:34::/62;
deny 2001:4860:4801:38::/62;
deny 2001:4860:4801:3c::/63;
deny 2001:4860:4801:3e::/64;
deny 2001:4860:4801:40::/61;
deny 2001:4860:4801:48::/62;
deny 2001:4860:4801:50::/63;
deny 2001:4860:4801:53::/64;
deny 2001:4860:4801:54::/63;
deny 2001:4860:4801:60::/60;
deny 2001:4860:4801:70::/61;
deny 2001:4860:4801:78::/63;
deny 2001:4860:4801:80::/61;
deny 2001:4860:4801:88::/64;
deny 2001:4860:4801:90::/62;
deny 2001:4860:4801:94::/64;
deny 2001:4860:4801:c::/64;
deny 2001:4860:4801:f::/64;
deny 20.125.163.80/28;
deny 20.15.133.160/27;
deny 20.36.108.32/28;
deny 204.15.20.0/22;
deny 20.43.120.16/28;
deny 20.74.197.0/28;
deny 207.46.13.0/24;
deny 20.79.107.240/28;
deny 2401:db00::/32;
deny 2620:0:1c00::/40;
deny 2620:10d:c090::/44;
deny 2803:6080::/29;
deny 2a03:2880::/31;
deny 2a03:2887:ff02::/47;
deny 2a03:2887:ff04::/46;
deny 2a03:2887:ff08::/47;
deny 2a03:2887:ff18::/47;
deny 2a03:2887:ff1b::/48;
deny 2a03:2887:ff1c::/46;
deny 2a03:2887:ff21::/48;
deny 2a03:2887:ff23::/48;
deny 2a03:2887:ff24::/47;
deny 2a03:2887:ff27::/48;
deny 2a03:2887:ff28::/45;
deny 2a03:2887:ff30::/48;
deny 2a03:2887:ff33::/48;
deny 2a03:2887:ff35::/48;
deny 2a03:2887:ff36::/47;
deny 2a03:2887:ff38::/46;
deny 2a03:2887:ff3f::/48;
deny 2a03:2887:ff40::/46;
deny 2a03:2887:ff44::/47;
deny 2a03:2887:ff48::/46;
deny 2a03:2887:ff4d::/48;
deny 2a03:2887:ff4e::/47;
deny 2a03:2887:ff50::/45;
deny 2a03:2887:ff58::/47;
deny 2a03:2887:ff60::/45;
deny 2a03:2887:ff68::/47;
deny 2a03:83e0::/32;
deny 2a10:f781:10:cee0::/64;
deny 2c0f:ef78:10::/47;
deny 2c0f:ef78:12::/48;
deny 2c0f:ef78:3::/48;
deny 2c0f:ef78::/47;
deny 2c0f:ef78:5::/48;
deny 2c0f:ef78:6::/48;
deny 2c0f:ef78:9::/48;
deny 2c0f:ef78:c::/46;
deny 31.13.24.0/21;
deny 31.13.64.0/18;
deny 34.100.182.96/28;
deny 34.101.50.144/28;
deny 34.118.254.0/28;
deny 34.118.66.0/28;
deny 34.126.178.96/28;
deny 34.146.150.144/28;
deny 34.147.110.144/28;
deny 34.151.74.144/28;
deny 34.152.50.64/28;
deny 34.154.114.144/28;
deny 34.155.98.32/28;
deny 34.165.18.176/28;
deny 34.175.160.64/28;
deny 34.176.130.16/28;
deny 34.22.85.0/27;
deny 34.64.82.64/28;
deny 34.65.242.112/28;
deny 34.80.50.80/28;
deny 34.88.194.0/28;
deny 34.89.10.80/28;
deny 34.89.198.80/28;
deny 34.96.162.48/28;
deny 35.247.243.240/28;
deny 40.77.139.0/25;
deny 40.77.167.0/24;
deny 40.77.177.0/24;
deny 40.77.178.0/23;
deny 40.77.188.0/22;
deny 40.77.202.0/24;
deny 40.79.131.208/28;
deny 40.79.186.176/28;
deny 45.64.40.0/22;
deny 51.105.67.0/28;
deny 52.167.144.0/24;
deny 52.230.152.0/24;
deny 52.231.148.0/28;
deny 52.233.106.0/24;
deny 57.141.0.0/21;
deny 57.141.12.0/23;
deny 57.141.8.0/22;
deny 57.144.0.0/14;
deny 65.55.210.0/24;
deny 66.220.144.0/20;
deny 66.249.64.0/25;
deny 66.249.64.128/26;
deny 66.249.64.224/27;
deny 66.249.65.0/24;
deny 66.249.66.0/25;
deny 66.249.66.160/27;
deny 66.249.66.192/27;
deny 66.249.68.0/25;
deny 66.249.69.0/24;
deny 66.249.70.0/23;
deny 66.249.72.0/23;
deny 66.249.74.0/25;
deny 66.249.74.128/27;
deny 66.249.75.0/24;
deny 66.249.76.0/23;
deny 66.249.78.0/26;
deny 66.249.79.0/24;
deny 69.171.224.0/19;
deny 69.63.176.0/20;
deny 74.119.76.0/22;

View file

@ -0,0 +1,5 @@
# Default server configuration
access_log /dev/stdout basic;
resolver 9.9.9.9 1.1.1.1 8.8.8.8 8.8.4.4;
set_real_ip_from 0.0.0.0/0;
ssl_preread on;

View file

@ -24,30 +24,6 @@ provider "registry.opentofu.org/bpg/proxmox" {
] ]
} }
provider "registry.opentofu.org/digitalocean/digitalocean" {
version = "2.40.0"
constraints = "~> 2.0"
hashes = [
"h1:Y7VkuuqOBgv+1jgL/4Hi247K2BskXwXtR/Uk9ssK/e4=",
"zh:00235830abae70642ebefc4d9c00e5eb978e28b74abc6b34f16b078f242aa217",
"zh:09d77785f768bd568f85a121d3d79316083befe903ce4ccd5567689a23236fb0",
"zh:0c9c4e19b411702d316a6bd044903e2ec506a69d38495ed32cc31e3f3f26acae",
"zh:12b34c88faad5b6149e9a3ad1396680588e1bae263b20d6b19835460f111c190",
"zh:15f041fc57ea46673a828919efe2ef3f05f7c4b863b7d7881336b93e92bd1159",
"zh:45e01972de2fab1687a09ea8fb3e4519be11c93ef93a63f28665630850858a20",
"zh:4e18bf5c1d2ec1ec6b6a9f4b58045309006f510edf770168fc18e273e6a09289",
"zh:575528b7e36e3489d2309e0c6cb9bd9952595cac5459b914f2d2827de1a1e4fc",
"zh:67462192212f810875d556462c79f574a8f5713b7a869ba4fce25953bfcf2dd2",
"zh:7024637b31e8276b653265fdf3f479220182edde4b300b034562b4c287faefa5",
"zh:a7904721b2680be8330dde98dd826be15c67eb274da7876f042cbcd6592ac970",
"zh:b225d4b67037a19392b0ab00d1f5fc9e729db4dfc32d18d4b36225693270ef52",
"zh:bd1e8768819d6113b2ec16f939196a1f2ae6d2803824fde463a20d06e071b212",
"zh:c5da40dc0749548ee2e1943776fb41b952c994e50bbc404251df20a81f730242",
"zh:dabc3387392aaba297739e1e97fadf059258fc3efb4dff2f499dbc407b6e088d",
"zh:f42137cf424c3e7c9c935b3f73618e51096bd0367a8d364073e2d70588d2cbf2",
]
}
provider "registry.opentofu.org/goauthentik/authentik" { provider "registry.opentofu.org/goauthentik/authentik" {
version = "2024.8.3" version = "2024.8.3"
constraints = "2024.8.3" constraints = "2024.8.3"
@ -69,43 +45,3 @@ provider "registry.opentofu.org/goauthentik/authentik" {
"zh:f6af0fd2e89ea7b7e692ef893cf5fdcc6f53c37fc0c6e066a28d9c834226c539", "zh:f6af0fd2e89ea7b7e692ef893cf5fdcc6f53c37fc0c6e066a28d9c834226c539",
] ]
} }
provider "registry.opentofu.org/hashicorp/dns" {
version = "3.4.1"
constraints = "~> 3.4.1"
hashes = [
"h1:6Tb2wZRxfKunvjLw47ihfkaoARWIQrJd+WCOXkx0hg4=",
"zh:53f46f16fc3b25d9bdce61d7cc9a67cea9c67ea5347fcde35833451d5011f1c4",
"zh:662669802c99c7e698ca52e7a32afc19acf66d97bbc05f5effacdf0fe471848f",
"zh:8289aee49df8a0cb2fcdfded575e68df1721fc3e6ce75555def499d31b7e910d",
"zh:90aa9b049d19137ed15ea3f88238824e1fde7d1a806378cb97520391e169a96d",
"zh:9e5795e10d65927ba689cfabc06b72085055543342d1db26ef5963ae5ab5d64a",
"zh:9f39b6e0ca35cf2405fe4e13af8a405cf9b0b0c34b6b06ad36f319631405a3aa",
"zh:c37a2c140c95b3eaa9d0aeb79496b220ba1a0456d01a77fcc68bd565bee7edd5",
"zh:cd40d356c5382f0b868df2d8539f95031e3cf0c210053674ec4c7cd20c73b82a",
"zh:fde59f09e4e62fc84012fff8493bc64eac287a0245e1c8abf29b0138d3018dfb",
"zh:ffee5b54936bd43247e9316de0ecc0dde36556a2cd50f090d7f6443a3f689b95",
]
}
provider "registry.opentofu.org/tailscale/tailscale" {
version = "0.16.1"
constraints = "0.16.1"
hashes = [
"h1:NDIIkEo0G/leQSvGoh2Mk74ZE2xWrWgHX/S8ZVyBDYU=",
"zh:0a9d28e5195e0e29ebf9b12b345cafcb686125008151fa01677c399d8f8f1321",
"zh:249bce2fcfd3414211ae9e49e179e31b5d3c23dd9da24dc45acdea34ad308cb0",
"zh:3129fb52a2aaa0c8c30aff21e7d4c0601d80898b3ecb9d7604b5933c14f54924",
"zh:4ec3e255f34bb4f6362ab41aa9e05a3ce040a791bc07445dec86188dee867f85",
"zh:68d3995e5a1722e24f89a385899f56a63542159b884cac989196e9538b53c6ce",
"zh:799840b3bfbd14537397f157f4e6a5e54080cd4fee51521bac675aa188e0b33e",
"zh:99f1da9fdaddd8a1255dce56edf8eb3e235293c72738cf70f1fb9ee9631b40e6",
"zh:9b18fd51e260b2f3100937c34feae5f6fe3515df9b5e27ae23d00af75249a6d4",
"zh:a7154cdce28aeb80e822a97c6bc8b8acb7a074304fd198e265ac9cbcbda0ca06",
"zh:b0ce2ca42f018e5235a2171cdd8ba9829c90c54a6b2d602bd38e0e90c43d5d5d",
"zh:c67609f7018fc6e48b17befd6eeb21197e8f524496185c5e29707efa6967a0a5",
"zh:d4c9dc9d2a5a535851fc10049506bad1e7ab88193d5dcd371f91ac1b84f43a0a",
"zh:da27f2a9b9d5a4c02ec3893a763874513825c7c4dc2bb870ba741cf7725bcf9f",
"zh:e5bc1797b97607ff3d841c6c0d40da89c3843156ad43e15ded7d41fc0ac27717",
]
}

View file

@ -1,70 +0,0 @@
locals {
nodes_for_dns = { for k, v in var.nodes : k => v if v.public != null && v.internal != null }
}
data "digitalocean_domain" "ingress" {
name = "ingress.tjo.cloud"
}
resource "digitalocean_record" "public" {
for_each = merge(
{
for key, node in local.nodes_for_dns : key => {
node = node.name,
ip = node.public.ipv4,
type = "A",
}
},
{
for key, node in local.nodes_for_dns : key => {
node = node.name,
ip = node.public.ipv6,
type = "AAAA",
}
}
)
domain = data.digitalocean_domain.ingress.id
type = each.value.type
name = lower(each.value.node)
value = each.value.ip
ttl = 60
}
resource "digitalocean_record" "internal" {
for_each = merge(
{
for key, node in local.nodes_for_dns : key => {
node = node.name,
ip = node.internal.ipv4,
type = "A",
}
},
{
for key, node in local.nodes_for_dns : key => {
node = node.name,
ip = node.internal.ipv6,
type = "AAAA",
}
}
)
domain = data.digitalocean_domain.ingress.id
type = each.value.type
name = "internal.${lower(each.value.node)}"
value = each.value.ip
ttl = 60
}
resource "digitalocean_record" "srv" {
for_each = local.nodes_for_dns
domain = data.digitalocean_domain.ingress.id
type = "SRV"
name = digitalocean_record.internal[each.key].fqdn
value = "_nginx._tcp"
port = 9000
priority = 10
weight = 100
ttl = 60
}

View file

@ -3,12 +3,12 @@ locals {
nodes = { nodes = {
for k, v in var.nodes : k => merge(v, { for k, v in var.nodes : k => merge(v, {
id = 700 + index(keys(var.nodes), k) id = 800 + index(keys(var.nodes), k)
hash = sha1(v.name) hash = sha1(v.host)
mac_address = "AA:BB:07:00:${format("%v:%v", substr(sha1(v.name), 0, 2), substr(sha1(v.name), 2, 2))}" mac_address = "AA:BB:07:00:${format("%v:%v", substr(sha1(v.host), 0, 2), substr(sha1(v.host), 2, 2))}"
domain = local.domain domain = local.domain
meta = { meta = {
name = v.name name = v.host
domain = local.domain domain = local.domain
service_account = { service_account = {
username = authentik_user.service_account[k].username username = authentik_user.service_account[k].username
@ -17,12 +17,21 @@ locals {
} }
}) })
} }
ipv4_addresses = {
for key, node in local.nodes : key => {
for k, v in proxmox_virtual_environment_vm.nodes[key].ipv4_addresses :
proxmox_virtual_environment_vm.nodes[key].network_interface_names[k] => v
}
}
} }
resource "proxmox_virtual_environment_download_file" "ubuntu" { resource "proxmox_virtual_environment_download_file" "ubuntu" {
for_each = local.nodes
content_type = "iso" content_type = "iso"
datastore_id = var.common_storage datastore_id = each.value.iso_storage
node_name = var.nodes[keys(var.nodes)[0]].host node_name = each.value.host
url = "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img" url = "https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img"
overwrite = false overwrite = false
} }
@ -32,12 +41,12 @@ resource "proxmox_virtual_environment_file" "userdata" {
node_name = each.value.host node_name = each.value.host
content_type = "snippets" content_type = "snippets"
datastore_id = var.common_storage datastore_id = each.value.iso_storage
source_raw { source_raw {
data = <<-EOF data = <<-EOF
#cloud-config #cloud-config
hostname: ${each.value.name}.${each.value.domain} hostname: ${each.value.host}.${each.value.domain}
write_files: write_files:
- path: /etc/tjo.cloud/meta.json - path: /etc/tjo.cloud/meta.json
encoding: base64 encoding: base64
@ -48,7 +57,7 @@ resource "proxmox_virtual_environment_file" "userdata" {
power_state: power_state:
mode: reboot mode: reboot
EOF EOF
file_name = "${each.value.name}.ingress.tjo.cloud.userconfig.yaml" file_name = "${each.value.host}.ingress.tjo.cloud.userconfig.yaml"
} }
} }
@ -56,10 +65,15 @@ resource "proxmox_virtual_environment_vm" "nodes" {
for_each = local.nodes for_each = local.nodes
vm_id = each.value.id vm_id = each.value.id
name = "${each.value.name}.${each.value.domain}" name = "${each.value.host}.${each.value.domain}"
node_name = each.value.host node_name = each.value.host
description = "Node ${each.value.name} for ${each.value.domain}." description = <<EOT
An ingress.tjo.cloud instance for ${each.value.host}.
Repo: https://code.tjo.space/tjo-cloud/ingress
EOT
tags = [each.value.domain] tags = [each.value.domain]
stop_on_destroy = true stop_on_destroy = true
@ -79,7 +93,7 @@ resource "proxmox_virtual_environment_vm" "nodes" {
bios = "ovmf" bios = "ovmf"
efi_disk { efi_disk {
datastore_id = each.value.storage datastore_id = each.value.boot_storage
} }
operating_system { operating_system {
@ -91,15 +105,15 @@ resource "proxmox_virtual_environment_vm" "nodes" {
} }
network_device { network_device {
bridge = each.value.bridge bridge = "vmbr1"
mac_address = each.value.mac_address mac_address = each.value.mac_address
} }
scsi_hardware = "virtio-scsi-single" scsi_hardware = "virtio-scsi-single"
disk { disk {
file_id = proxmox_virtual_environment_download_file.ubuntu.id file_id = proxmox_virtual_environment_download_file.ubuntu[each.key].id
interface = "virtio0" interface = "virtio0"
datastore_id = each.value.storage datastore_id = each.value.boot_storage
size = each.value.boot_size size = each.value.boot_size
backup = true backup = true
cache = "none" cache = "none"
@ -108,7 +122,7 @@ resource "proxmox_virtual_environment_vm" "nodes" {
initialization { initialization {
interface = "scsi0" interface = "scsi0"
datastore_id = each.value.storage datastore_id = each.value.boot_storage
user_data_file_id = proxmox_virtual_environment_file.userdata[each.key].id user_data_file_id = proxmox_virtual_environment_file.userdata[each.key].id
ip_config { ip_config {

5
terraform/outputs.tf Normal file
View file

@ -0,0 +1,5 @@
output "nodes" {
value = [
for key, node in proxmox_virtual_environment_vm.nodes : local.ipv4_addresses[key]["eth0"][0]
]
}

View file

@ -6,12 +6,11 @@ data "authentik_group" "monitoring_publisher" {
resource "authentik_user" "service_account" { resource "authentik_user" "service_account" {
for_each = var.nodes for_each = var.nodes
username = "${each.value.name}.ingress@svc.tjo.cloud" username = "${each.value.host}.${local.domain}"
name = "${each.value.name}.ingress@svc.tjo.cloud" name = "${each.value.host}.${local.domain}"
email = "${each.value.name}.ingress@svc.tjo.cloud"
type = "service_account" type = "service_account"
path = "svc.tjo.cloud" path = "ingress.tjo.cloud"
groups = [ groups = [
data.authentik_group.monitoring_publisher.id, data.authentik_group.monitoring_publisher.id,
@ -21,9 +20,9 @@ resource "authentik_user" "service_account" {
resource "authentik_token" "service_account" { resource "authentik_token" "service_account" {
for_each = var.nodes for_each = var.nodes
identifier = "svc-tjo-cloud-service-account-${each.value.name}" identifier = replace("service-account-${each.value.host}-${local.domain}", ".", "-")
user = authentik_user.service_account[each.key].id user = authentik_user.service_account[each.key].id
description = "Service account for ${each.value.name} node" description = "Service account for ${each.value.host}.${local.domain} node."
expiring = false expiring = false
intent = "app_password" intent = "app_password"
retrieve_key = true retrieve_key = true

View file

@ -1,21 +1,9 @@
terraform { terraform {
required_providers { required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
dns = {
source = "hashicorp/dns"
version = "~> 3.4.1"
}
proxmox = { proxmox = {
source = "bpg/proxmox" source = "bpg/proxmox"
version = "0.61.1" version = "0.61.1"
} }
tailscale = {
source = "tailscale/tailscale"
version = "0.16.1"
}
authentik = { authentik = {
source = "goauthentik/authentik" source = "goauthentik/authentik"
version = "2024.8.3" version = "2024.8.3"
@ -30,21 +18,32 @@ provider "authentik" {
token = var.authentik_token token = var.authentik_token
} }
provider "digitalocean" {
token = var.digitalocean_token
}
provider "tailscale" {
api_key = var.tailscale_apikey
}
provider "proxmox" { provider "proxmox" {
# FIXME: Traefik/NGINX breaks this! 500 ERROR # FIXME: Traefik/NGINX breaks this! 500 ERROR
endpoint = "https://178.63.49.225:8006/api2/json" endpoint = "https://batuu.system.tjo.cloud:8006/api2/json"
insecure = true insecure = true
api_token = var.proxmox_token api_token = var.proxmox_token
ssh { ssh {
agent = true agent = true
username = "root" username = "root"
node {
name = "batuu"
address = "batuu.system.tjo.cloud"
port = 22
}
node {
name = "jakku"
address = "jakku.system.tjo.cloud"
port = 22
}
node {
name = "nevaroo"
address = "nevaroo.system.tjo.cloud"
port = 22
}
} }
} }

View file

@ -1,20 +1,21 @@
nodes = { nodes = {
de_01 = { batuu = {
name = "de-01" host = "batuu"
host = "hetzner" iso_storage = "local"
bridge = "vmprivate0" boot_storage = "local-nvme"
cloudinit = ""
} }
de_02 = { jakku = {
name = "de-02" host = "jakku"
host = "hetzner" iso_storage = "local"
bridge = "vmprivate0" boot_storage = "local-nvme"
cloudinit = "" }
nevaroo = {
host = "nevaroo"
iso_storage = "local"
boot_storage = "local"
} }
} }
ssh_keys = [ ssh_keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICXAlzwziqfUUb2qmFwNF/nrBYc5MNT1MMOx81ohBmB+ tine@little.sys.tjo.space" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICXAlzwziqfUUb2qmFwNF/nrBYc5MNT1MMOx81ohBmB+ tine@little.sys.tjo.space"
] ]
common_storage = "proxmox-backup-tjo-cloud"

View file

@ -1,6 +1,6 @@
variable "nodes" { variable "nodes" {
type = map(object({ type = map(object({
name = string host = string
internal = optional(object({ internal = optional(object({
ipv4 = string ipv4 = string
@ -11,16 +11,14 @@ variable "nodes" {
ipv6 = string ipv6 = string
})) }))
host = string
bridge = string
cores = optional(number, 1) cores = optional(number, 1)
memory = optional(number, 512) memory = optional(number, 512)
storage = optional(string, "main") iso_storage = string
boot_size = optional(number, 8)
cloudinit = string boot_storage = string
boot_size = optional(number, 8)
})) }))
} }
@ -28,10 +26,6 @@ variable "ssh_keys" {
type = list(string) type = list(string)
} }
variable "common_storage" {
type = string
}
variable "digitalocean_token" { variable "digitalocean_token" {
type = string type = string
sensitive = true sensitive = true