This commit is contained in:
parent
339a72682a
commit
4a0b589859
10 changed files with 240 additions and 41 deletions
|
@ -1,3 +1,18 @@
|
|||
# ingress
|
||||
|
||||
Handling all Ingress traffic
|
||||
|
||||
## Rolling out changes
|
||||
|
||||
```sh
|
||||
# Apply code changes to single node.
|
||||
# Make sure to commit and push the changes first.
|
||||
just provision-only nevaroo
|
||||
|
||||
# Apply infrastructure changes to single node.
|
||||
just apply-only nevaroo
|
||||
|
||||
# Apply to all nodes
|
||||
just provision
|
||||
just apply
|
||||
```
|
|
@ -11,7 +11,7 @@ if [ ! -d .git ]; then
|
|||
--depth 1 \
|
||||
--no-checkout \
|
||||
--filter=tree:0 \
|
||||
https://code.tjo.space/tjo-cloud/infrastructure.git .
|
||||
https://github.com/tjo-space/tjo-cloud-infrastructure.git .
|
||||
git sparse-checkout set --no-cone /ingress.tjo.cloud
|
||||
git checkout
|
||||
else
|
||||
|
@ -31,6 +31,8 @@ SERVICE_ACCOUNT_PASSWORD=$(jq -r ".service_account.password" /etc/tjo.cloud/meta
|
|||
|
||||
TAILSCALE_AUTH_KEY=$(jq -r ".tailscale.auth_key" /etc/tjo.cloud/meta.json)
|
||||
|
||||
DIGITALOCEAN_TOKEN=$(jq -r ".digitalocean.token" /etc/tjo.cloud/meta.json)
|
||||
|
||||
##
|
||||
echo "== Install Dependencies"
|
||||
apt update -y
|
||||
|
@ -59,7 +61,7 @@ apt install -y tailscale
|
|||
|
||||
##
|
||||
echo "== Ensure services are enabled"
|
||||
systemctl enable --now nginx alloy tailscaled
|
||||
systemctl enable --now nginx alloy tailscaled dydns
|
||||
|
||||
##
|
||||
echo "== Configure Grafana Alloy"
|
||||
|
@ -71,13 +73,22 @@ ATTRIBUTES+="service.name=${SERVICE_NAME},"
|
|||
ATTRIBUTES+="service.version=${SERVICE_VERSION},"
|
||||
ATTRIBUTES+="cloud.region=${CLOUD_REGION}"
|
||||
echo "OTEL_RESOURCE_ATTRIBUTES=${ATTRIBUTES}" >>/etc/default/alloy
|
||||
# Set Credentials
|
||||
# set credentials
|
||||
{
|
||||
echo "ALLOY_USERNAME=${SERVICE_ACCOUNT_USERNAME}"
|
||||
echo "ALLOY_PASSWORD=${SERVICE_ACCOUNT_PASSWORD}"
|
||||
echo "alloy_username=${SERVICE_ACCOUNT_USERNAME}"
|
||||
echo "alloy_password=${SERVICE_ACCOUNT_PASSWORD}"
|
||||
} >>/etc/default/alloy
|
||||
systemctl restart alloy
|
||||
|
||||
##
|
||||
echo "== Configure Dydns"
|
||||
cp -r root/etc/default/dydns /etc/default/dydns
|
||||
{
|
||||
echo "DIGITALOCEAN_TOKEN=${DIGITALOCEAN_TOKEN}"
|
||||
echo "NAME=${CLOUD_REGION}"
|
||||
} >>/etc/default/dydns
|
||||
systemctl restart dydns
|
||||
|
||||
##
|
||||
echo "== Configure Tailscale"
|
||||
if tailscale status --json | jq -e -r '.BackendState != "Running"' >/dev/null; then
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
# Always use devbox environment to run commands.
|
||||
set shell := ["devbox", "run"]
|
||||
# Load dotenv
|
||||
set dotenv-load
|
||||
|
||||
default:
|
||||
@just --list
|
||||
|
||||
|
@ -14,17 +9,54 @@ format:
|
|||
@tofu fmt -recursive .
|
||||
@tflint --recursive
|
||||
|
||||
deploy:
|
||||
apply:
|
||||
#!/usr/bin/env sh
|
||||
cd {{justfile_directory()}}/terraform
|
||||
cd {{source_directory()}}/terraform
|
||||
tofu init
|
||||
tofu apply
|
||||
|
||||
apply-only node:
|
||||
#!/usr/bin/env sh
|
||||
cd {{source_directory()}}/terraform
|
||||
tofu init
|
||||
tofu apply --target 'proxmox_virtual_environment_vm.nodes["{{node}}"]'
|
||||
|
||||
|
||||
destroy:
|
||||
#!/usr/bin/env sh
|
||||
cd {{justfile_directory()}}/terraform
|
||||
cd {{source_directory()}}/terraform
|
||||
tofu destroy
|
||||
|
||||
provision:
|
||||
#!/usr/bin/env sh
|
||||
set -eou pipefail
|
||||
|
||||
pushd {{source_directory()}}/terraform > /dev/null
|
||||
NODES=$(tofu output -json | jq -r '.nodes.value[]')
|
||||
popd > /dev/null
|
||||
|
||||
for NODE in $NODES
|
||||
do
|
||||
echo "= Provisioning node ${NODE}"
|
||||
cat install.sh | tailscale ssh ubuntu@${NODE} 'sudo bash -s'
|
||||
done
|
||||
|
||||
provision-only node:
|
||||
#!/usr/bin/env sh
|
||||
set -eou pipefail
|
||||
|
||||
pushd {{source_directory()}}/terraform > /dev/null
|
||||
NODES=$(tofu output -json | jq -r '.nodes.value[]')
|
||||
popd > /dev/null
|
||||
|
||||
for NODE in $NODES
|
||||
do
|
||||
if [ "$NODE" -eq "{{node}}" ]
|
||||
echo "= Provisioning node ${NODE}"
|
||||
cat install.sh | tailscale ssh ubuntu@${NODE} 'sudo bash -s'
|
||||
fi
|
||||
done
|
||||
|
||||
# Create a list of blocked IP ranges. Traffic we don't want.
|
||||
update-blocked-list:
|
||||
#!/usr/bin/env bash
|
||||
|
@ -49,20 +81,3 @@ update-blocked-list:
|
|||
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}"
|
||||
cat install.sh | tailscale ssh ubuntu@${NODE} 'sudo bash -s'
|
||||
done
|
||||
|
||||
list-servers:
|
||||
@cd terraform && tofu output -json | jq -r '.nodes.value[]'
|
||||
|
|
17
ingress.tjo.cloud/root/etc/default/dyndns
Normal file
17
ingress.tjo.cloud/root/etc/default/dyndns
Normal file
|
@ -0,0 +1,17 @@
|
|||
## Path:
|
||||
## Description: DynDNS settings
|
||||
## Type: string
|
||||
## Default: ""
|
||||
## ServiceRestart: dydns
|
||||
|
||||
# If set to "true", removes extra DNS records if more than one A record is found on a subdomain.
|
||||
# Note that if this is not enabled, the script will NOT update subdomains with more than one A record
|
||||
# (default: false)
|
||||
REMOVE_DUPLICATES=true
|
||||
|
||||
# Polling time in seconds.
|
||||
# (default: 300)
|
||||
SLEEP_INTERVAL=600
|
||||
|
||||
# The domain your subdomain is registered at. (i.e. foo.com for home.foo.com)
|
||||
DOMAIN=ingress.tjo.cloud
|
8
ingress.tjo.cloud/root/systemd/system/dyndns.service
Normal file
8
ingress.tjo.cloud/root/systemd/system/dyndns.service
Normal file
|
@ -0,0 +1,8 @@
|
|||
[Unit]
|
||||
Description=Dynamic DNS Updater
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/local/bin/dyndns
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -1,8 +0,0 @@
|
|||
[Unit]
|
||||
Description=Webhooks
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/webhook -hooks /etc/webhook/hooks.json -port 7777 -verbose
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
132
ingress.tjo.cloud/root/usr/local/bin/dyndns
Executable file
132
ingress.tjo.cloud/root/usr/local/bin/dyndns
Executable file
|
@ -0,0 +1,132 @@
|
|||
#!/usr/bin/env bash
|
||||
# vim: set filetype=sh
|
||||
set -euo pipefail
|
||||
|
||||
api_host="https://api.digitalocean.com/v2"
|
||||
sleep_interval=${SLEEP_INTERVAL:-300}
|
||||
remove_duplicates=${REMOVE_DUPLICATES:-"false"}
|
||||
|
||||
# Only services with ipv6 supported are listed here.
|
||||
# And are not using cloudflare or similar services
|
||||
# that may block requests from this script.
|
||||
services=(
|
||||
"ifconfig.io"
|
||||
)
|
||||
|
||||
info() {
|
||||
echo "$1" >/dev/stdout
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo "$1" >/dev/stderr
|
||||
}
|
||||
|
||||
error() {
|
||||
warn "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
test -f "${DIGITALOCEAN_TOKEN_FILE:-}" && DIGITALOCEAN_TOKEN="$(cat "$DIGITALOCEAN_TOKEN_FILE")"
|
||||
test -z "$DIGITALOCEAN_TOKEN" && error "DIGITALOCEAN_TOKEN not set!"
|
||||
test -z "${DOMAIN}" && error "DOMAIN not set!"
|
||||
test -z "${NAME}" && error "NAME not set!"
|
||||
|
||||
dns_list="$api_host/domains/$DOMAIN/records"
|
||||
|
||||
configure_record() {
|
||||
# disable glob expansion
|
||||
set -f
|
||||
|
||||
ip=$1
|
||||
type=$2
|
||||
|
||||
for sub in ${NAME//;/ }; do
|
||||
record_id=$(echo "$domain_records" | jq ".domain_records[] | select(.type == \"$type\" and .name == \"$sub\") | .id")
|
||||
record_data=$(echo "$domain_records" | jq -r ".domain_records[] | select(.type == \"$type\" and .name == \"$sub\") | .data")
|
||||
|
||||
if [ "$(echo "$record_id" | wc -l)" -ge 2 ]; then
|
||||
if [[ "${remove_duplicates}" == "true" ]]; then
|
||||
echo "'$sub' domain name has duplicate DNS records, removing duplicates"
|
||||
record_id_to_delete=$(echo "$record_id" | tail -n +2)
|
||||
record_id=$(echo "$record_id" | head -1)
|
||||
record_data=$(echo "$record_data" | head -1)
|
||||
|
||||
while IFS= read -r line; do
|
||||
curl -s -X DELETE \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
|
||||
"$dns_list/$line" &>/dev/null
|
||||
done <<<"$record_id_to_delete"
|
||||
else
|
||||
warn "Unable to update '$sub' domain name as it has duplicate DNS records. Set REMOVE_DUPLICATES='true' to remove them."
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# re-enable glob expansion
|
||||
set +f
|
||||
|
||||
data="{\"type\": \"$type\", \"name\": \"$sub\", \"data\": \"$ip\"}"
|
||||
url="$dns_list/$record_id"
|
||||
|
||||
if [[ -z $record_id ]]; then
|
||||
info "No record found with '$sub' domain name. Creating record, sending data=$data to url=$url"
|
||||
|
||||
new_record=$(curl -s -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
|
||||
-d "$data" \
|
||||
"$url")
|
||||
|
||||
record_data=$(echo "$new_record" | jq -r ".data")
|
||||
fi
|
||||
|
||||
if [[ "$ip" != "$record_data" ]]; then
|
||||
info "existing DNS record address ($record_data) doesn't match current IP ($ip), sending data=$data to url=$url"
|
||||
|
||||
curl -s -X PUT \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
|
||||
-d "$data" \
|
||||
"$url" &>/dev/null
|
||||
else
|
||||
info "existing DNS record address ($record_data) did not need updating"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
while (true); do
|
||||
domain_records=$(curl -s -X GET \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \
|
||||
"$dns_list?per_page=200")
|
||||
|
||||
for service in "${services[@]}"; do
|
||||
info "Trying with $service..."
|
||||
|
||||
ipv4="$(curl -4 -s -f --connect-timeout 2 "$service" || echo "")"
|
||||
ipv6="$(curl -6 -s -f --connect-timeout 2 "$service" || echo "")"
|
||||
|
||||
if [[ -n "$ipv4$ipv6" ]]; then
|
||||
break
|
||||
else
|
||||
warn "Failed to retrieve IP from $service"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -z $ipv4 ]]; then
|
||||
warn "IPv4 wasn't retrieved within allowed interval. Will try $sleep_interval seconds later.."
|
||||
else
|
||||
info "Found IPv4 address $ipv4"
|
||||
configure_record "$ipv4" "A"
|
||||
fi
|
||||
|
||||
if [[ -z $ipv6 ]]; then
|
||||
warn "IPv6 wasn't retrieved within allowed interval. Will try $sleep_interval seconds later.."
|
||||
else
|
||||
info "Found IPv6 address $ipv6"
|
||||
configure_record "$ipv6" "AAAA"
|
||||
fi
|
||||
|
||||
sleep "$sleep_interval"
|
||||
done
|
|
@ -14,6 +14,9 @@ locals {
|
|||
tailscale = {
|
||||
auth_key = tailscale_tailnet_key.key.key
|
||||
}
|
||||
digitalocean = {
|
||||
token = var.digitalocean_token
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -60,8 +63,9 @@ resource "proxmox_virtual_environment_file" "userdata" {
|
|||
power_state:
|
||||
mode: reboot
|
||||
runcmd:
|
||||
- git clone https://code.tjo.space/tjo-cloud/ingress.git /srv
|
||||
- /srv/install.sh
|
||||
- git clone --depth 1 --no-checkout --filter=tree:0 https://github.com/tjo-space/tjo-cloud-infrastructure.git /srv
|
||||
- cd /srv && git sparse-checkout set --no-cone /ingress.tjo.cloud && git checkout
|
||||
- /srv/ingress.tjo.cloud/install.sh
|
||||
EOF
|
||||
file_name = "${each.value.host}.ingress.tjo.cloud.userconfig.yaml"
|
||||
}
|
||||
|
@ -87,7 +91,7 @@ Repo: https://code.tjo.space/tjo-cloud/ingress
|
|||
timeout_stop_vm = 60
|
||||
timeout_shutdown_vm = 60
|
||||
timeout_reboot = 60
|
||||
timeout_create = 600
|
||||
timeout_create = 60
|
||||
|
||||
cpu {
|
||||
cores = each.value.cores
|
||||
|
|
|
@ -37,3 +37,8 @@ variable "tailscale_apikey" {
|
|||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "digitalocean_token" {
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
Loading…
Reference in a new issue