diff --git a/.gitignore b/.gitignore index 0d77d87..2ae0a64 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,9 @@ -# Encrypted environment variables -env -*.env -!root/**/*.env +# ---> Dot ENV +.env -# Encrypted ssh keys -ssh -*.ssh +# ---> Secrets +**/secrets.env +!**/secrets.env.encrypted # ---> Terraform # Local .terraform directories diff --git a/example.env b/example.env new file mode 100644 index 0000000..c7c59e2 --- /dev/null +++ b/example.env @@ -0,0 +1,4 @@ +TF_VAR_hcloud_token=your_hcloud_token + +TF_VAR_dnsimple_token=your_dnsimple_token +TF_VAR_dnsimple_account_id=your_dnsimple_account_id diff --git a/id.tjo.space/configs/authentik/authentik.env b/id.tjo.space/configs/authentik/authentik.env new file mode 100644 index 0000000..d9a9d84 --- /dev/null +++ b/id.tjo.space/configs/authentik/authentik.env @@ -0,0 +1,23 @@ +AUTHENTIK_DISABLE_UPDATE_CHECK: false +AUTHENTIK_ERROR_REPORTING__ENABLED: false +AUTHENTIK_DISABLE_STARTUP_ANALYTICS: true + +AUTHENTIK_AVATARS: initials + +# AUTHENTIK_SECRET_KEY: "via secrets.env file" + +AUTHENTIK_IMPERSONATION: "false" + +AUTHENTIK_REDIS__HOST: valkey + +AUTHENTIK_POSTGRESQL__HOST: postgresql +AUTHENTIK_POSTGRESQL__USER: id.tjo.space +AUTHENTIK_POSTGRESQL__NAME: id.tjo.space +# AUTHENTIK_POSTGRESQL__PASSWORD: "via secrets.env file" + +AUTHENTIK_EMAIL__HOST: mail.tjo.space +AUTHENTIK_EMAIL__PORT: "587" +AUTHENTIK_EMAIL__USE_TLS: "true" +AUTHENTIK_EMAIL__FROM: id@tjo.space +AUTHENTIK_EMAIL__USERNAME: id@tjo.space +# AUTHENTIK_EMAIL__PASSWORD: "via secrets.env file" diff --git a/id.tjo.space/configs/caddy/Caddyfile b/id.tjo.space/configs/caddy/Caddyfile index 6fbe323..b5ea00d 100644 --- a/id.tjo.space/configs/caddy/Caddyfile +++ b/id.tjo.space/configs/caddy/Caddyfile @@ -2,7 +2,7 @@ acme_ca https://acme-staging-v02.api.letsencrypt.org/directory } -{$DOMAIN_NAME} +next.id.tjo.space respond /tjo-space/status "OK" diff --git a/id.tjo.space/configure.sh b/id.tjo.space/configure.sh new file mode 100755 index 0000000..85efc61 --- /dev/null +++ b/id.tjo.space/configure.sh @@ -0,0 +1,72 @@ +#!/bin/bash +set -euo pipefail + +SERVICE_DIR="/root/service" +mkdir -p ${SERVICE_DIR} +cd ${SERVICE_DIR} + +echo "== Fetch Source Code (from git)" +# Clone if not yet cloned +if [ ! -d .git ]; then + git clone \ + --depth 1 \ + --no-checkout \ + --filter=tree:0 \ + https://github.com/tjo-space/tjo-space-infrastructure.git . + git sparse-checkout set --no-cone /id.tjo.space + git checkout +else + git fetch --depth=1 + git reset --hard origin/main +fi + +echo "=== Configure Firewall" +ufw --force reset + +ufw default deny incoming +ufw default allow outgoing + +ufw allow 22/tcp # SSH +ufw allow 80/tcp # HTTP +ufw allow 443/tcp # HTTPS +ufw allow 636/tcp # LDAPS + +ufw --force enable + +echo "=== Copy Configuration Files" +rsync -a id.tjo.space/containers/ /etc/containers/systemd/ +rsync -a id.tjo.space/configs/ /etc/ +systemctl daemon-reload + +echo "=== Read Secrets" +age -d -i /etc/age/key.txt id.tjo.space/secrets.env.encrypted >id.tjo.space/secrets.env +set -a && source id.tjo.space/secrets.env && set +a + +echo "=== Setup Caddy" +systemctl start caddy + +echo "=== Setup Postgresql" +cat <<EOF >/etc/postgresql/secrets.env +POSTGRES_PASSWORD=${POSTGRESQL_PASSWORD} +EOF +systemctl start postgresql + +echo "=== Setup Redis" +systemctl start redis + +echo "=== Setup Authentik Server" +cat <<EOF >/etc/authentik/secrets.env +AUTHENTIK_SECRET_KEY=${AUTHENTIK_SECRET_KEY} +AUTHENTIK_EMAIL__PASSWORD=${AUTHENTIK_EMAIL__PASSWORD} +AUTHENTIK_POSTGRESQL__PASSWORD=${POSTGRESQL_PASSWORD} +EOF +systemctl start authentik-server + +echo "=== Setup Authentik Worker" +systemctl start authentik-worker + +echo "=== Setup Authentik LDAP" +cat <<EOF >/etc/authentik/ldap.secrets.env +AUTHENTIK_TOKEN=${AUTHENTIK_LDAP_TOKEN} +EOF +systemctl start authentik-ldap diff --git a/id.tjo.space/containers/authentik-ldap.container b/id.tjo.space/containers/authentik-ldap.container index bad2054..622db3d 100644 --- a/id.tjo.space/containers/authentik-ldap.container +++ b/id.tjo.space/containers/authentik-ldap.container @@ -3,6 +3,9 @@ Description=An Authentik LDAP Server [Container] Image=ghcr.io/goauthentik/ldap:2025.2.1 +Environment=AUTHENTIK_HOST=https://next.id.tjo.space +Environment=AUTHENTIK_INSECURE=false +EnvironmentFile=/etc/authentik/ldap.secrets.env [Service] Restart=always diff --git a/id.tjo.space/containers/authentik-server.container b/id.tjo.space/containers/authentik-server.container index 3b36969..f68891d 100644 --- a/id.tjo.space/containers/authentik-server.container +++ b/id.tjo.space/containers/authentik-server.container @@ -3,6 +3,9 @@ Description=An Authentik Server [Container] Image=ghcr.io/goauthentik/authentik:2025.2.1 +EnvironmentFile=/etc/authentik/authentik.env +EnvironmentFile=/etc/authentik/secrets.env +Volume=/media:/srv/authentik/media [Service] Restart=always diff --git a/id.tjo.space/containers/authentik-worker.container b/id.tjo.space/containers/authentik-worker.container index db8643c..98a2b1f 100644 --- a/id.tjo.space/containers/authentik-worker.container +++ b/id.tjo.space/containers/authentik-worker.container @@ -4,6 +4,8 @@ Description=An Authentik Worker [Container] Image=ghcr.io/goauthentik/authentik:2025.2.1 Exec=worker +EnvironmentFile=/etc/authentik/authentik.env +EnvironmentFile=/etc/authentik/secrets.env [Service] Restart=always diff --git a/id.tjo.space/containers/caddy.container b/id.tjo.space/containers/caddy.container index e7c86d3..11b65d4 100644 --- a/id.tjo.space/containers/caddy.container +++ b/id.tjo.space/containers/caddy.container @@ -5,7 +5,7 @@ Description=A Caddy Container Image=docker.io/caddy:2.9 PublishPort=443:443 PublishPort=80:80 -Volume=/etc/caddy:/etc/caddy +Volume=/etc/caddy:/etc/caddy:ro EnvironmentFile=/etc/caddy/env [Service] diff --git a/id.tjo.space/containers/postgresql.container b/id.tjo.space/containers/postgresql.container index e189655..acda9d7 100644 --- a/id.tjo.space/containers/postgresql.container +++ b/id.tjo.space/containers/postgresql.container @@ -3,7 +3,10 @@ Description=A Postgresql Container [Container] Image=docker.io/postgresql:17.4 -Volime=/var/lib/postgresql/data:/srv/postgresql/data +Volume=/var/lib/postgresql/data:/srv/postgresql/data +EnvironmentFile=/etc/postgresql/secrets.env +Environment=POSTGRES_USER=id.tjo.space +Environment=POSTGRES_DB=id.tjo.space [Service] Restart=always diff --git a/id.tjo.space/install.sh b/id.tjo.space/install.sh deleted file mode 100755 index a535441..0000000 --- a/id.tjo.space/install.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -set -euo pipefail - -SERVICE_DIR="/root/service" -mkdir -p ${SERVICE_DIR} -cd ${SERVICE_DIR} - -echo "== Fetch Source Code (from git)" -# Clone if not yet cloned -if [ ! -d .git ]; then - git clone \ - --depth 1 \ - --no-checkout \ - --filter=tree:0 \ - https://github.com/tjo-space/tjo-space-infrastructure.git . - git sparse-checkout set --no-cone /id.tjo.space - git checkout -else - git fetch --depth=1 - git reset --hard origin/main -fi - -echo "=== Installing Dependencies" -DEBIAN_FRONTEND=noninteractive apt update -y -DEBIAN_FRONTEND=noninteractive apt install -y \ - rsync \ - jq \ - podman - -echo "=== Configure Firewall" -ufw allow 22/tcp # SSH -ufw allow 80/tcp # HTTP -ufw allow 443/tcp # HTTPS -ufw allow 636/tcp # LDAPS -ufw --force enable - -echo "== Configure Metadata" -DOMAIN_NAME=$(jq -r ".domain" /etc/tjo.space/meta.json) - -echo "=== Copy Configuration Files" -rsync -av id.tjo.space/containers/ /etc/containers/systemd/ -rsync -av id.tjo.space/configs/ /etc/ - -systemctl daemon-reload - -echo "=== Setup Caddy" -cat <<EOF >/etc/caddy/env -DOMAIN_NAME=${DOMAIN_NAME} -EOF - -systemctl start caddy diff --git a/id.tjo.space/justfile b/id.tjo.space/justfile index db76c49..42dd647 100644 --- a/id.tjo.space/justfile +++ b/id.tjo.space/justfile @@ -1,6 +1,19 @@ default: @just --list +secrets-encrypt: + #!/usr/bin/env sh + age --encrypt \ + -r age1cl3d4wtrrqrgldmrzpu53q2mk60r7hrhrymsrwss8s57z4mdv9fst4a55h \ + -r age1a4t9l73au9fqfk0x5kw6v06ewwpl0lurw73f3m60c5ly58r3hp5q6shdfr \ + secrets.env > secrets.env.encrypted + +secrets-decrypt: + #!/usr/bin/env sh + age --decrypt \ + -i ${HOME}/.config/sops/age/keys.txt \ + secrets.env.encrypted > secrets.env + apply: #!/usr/bin/env sh cd {{source_directory()}}/terraform @@ -17,20 +30,24 @@ outputs: cd {{source_directory()}}/terraform tofu output -ssh node: +provision node: #!/usr/bin/env sh + set -eou pipefail + pushd {{source_directory()}}/terraform > /dev/null - IPV4=$(tofu output -json | jq -r '.ipv4[{{node}}]') + IPV4=$(tofu output -json | jq -r '.ipv4.value["{{node}}"]') popd > /dev/null - ssh root@${IPV4} + + echo "= Provision node: {{node}} (${IPV4})" + cat provision.sh | ssh -o StrictHostKeyChecking=no root@${IPV4} 'sudo bash -s' configure node: #!/usr/bin/env sh set -eou pipefail pushd {{source_directory()}}/terraform > /dev/null - IPV4=$(tofu output -json | jq -r '.ipv4[{{node}}]') + IPV4=$(tofu output -json | jq -r '.ipv4.value["{{node}}"]') popd > /dev/null - echo "= Provisioning id.tjo.space" - cat install.sh | ssh root@${IPV4} 'sudo bash -s' + echo "= Configuring node: {{node}} (${IPV4})" + cat configure.sh | ssh -o StrictHostKeyChecking=no root@${IPV4} 'sudo bash -s' diff --git a/id.tjo.space/provision.sh b/id.tjo.space/provision.sh new file mode 100755 index 0000000..da9184b --- /dev/null +++ b/id.tjo.space/provision.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -euo pipefail + +pushd "$(mktemp -d)" + +echo "=== Installing Dependencies" +DEBIAN_FRONTEND=noninteractive apt update -y +DEBIAN_FRONTEND=noninteractive apt install -y \ + rsync \ + jq \ + podman \ + age + +echo "=== Generating Age Key" +mkdir -p /etc/age +age-keygen -o /etc/age/key.txt diff --git a/id.tjo.space/secrets.env.encrypted b/id.tjo.space/secrets.env.encrypted new file mode 100644 index 0000000..83a3456 Binary files /dev/null and b/id.tjo.space/secrets.env.encrypted differ diff --git a/id.tjo.space/terraform/main.tf b/id.tjo.space/terraform/main.tf index 4438af4..92ef5fb 100644 --- a/id.tjo.space/terraform/main.tf +++ b/id.tjo.space/terraform/main.tf @@ -40,6 +40,9 @@ resource "hcloud_server" "main" { - path: /etc/tjo.space/meta.json encoding: base64 content: ${base64encode(jsonencode(each.value.meta))} + - path: /tmp/provision.sh + encoding: base64 + content: ${base64encode(file("${path.module}/../provision.sh"))} packages: - git - curl @@ -51,7 +54,9 @@ resource "hcloud_server" "main" { filename: /swapfile size: 512M runcmd: - - "curl -sL https://raw.githubusercontent.com/tjo-space/tjo-space-infrastructure/refs/heads/main/id.tjo.space/install.sh | bash" + - "chmod +x /tmp/provision.sh" + - "/tmp/provision.sh" + - "rm /tmp/provision.sh" EOF } @@ -65,13 +70,12 @@ resource "dnsimple_zone_record" "a" { ttl = 300 } -# Podman is PITA! -#resource "dnsimple_zone_record" "aaaa" { -# for_each = local.nodes -# -# zone_name = "tjo.space" -# name = trimsuffix(each.value.meta.domain, ".tjo.space") -# value = hcloud_server.main[each.key].ipv6_address -# type = "AAAA" -# ttl = 300 -#} +resource "dnsimple_zone_record" "aaaa" { + for_each = local.nodes + + zone_name = "tjo.space" + name = trimsuffix(each.value.meta.domain, ".tjo.space") + value = hcloud_server.main[each.key].ipv6_address + type = "AAAA" + ttl = 300 +}