From 595df12b3ee2b3d814abfd03013b3f5ae63ddf26 Mon Sep 17 00:00:00 2001 From: MichaelFisher1997 Date: Sat, 28 Feb 2026 00:33:14 +0000 Subject: [PATCH 1/4] update: automate tailscale enrollment from Gitea secrets Add a first-boot tailscale enrollment service to the NixOS template and wire terraform-apply to inject TS auth key at runtime from secrets, so keys are not baked into templates or repo files. --- .gitea/workflows/terraform-apply.yml | 21 ++++++++++++++++++++ nixos/template-base/configuration.nix | 28 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/.gitea/workflows/terraform-apply.yml b/.gitea/workflows/terraform-apply.yml index 5a49a78..3aa01c6 100644 --- a/.gitea/workflows/terraform-apply.yml +++ b/.gitea/workflows/terraform-apply.yml @@ -37,3 +37,24 @@ jobs: - name: Terraform Apply working-directory: terraform run: terraform apply -auto-approve + + - name: Enroll VMs in Tailscale + env: + TS_AUTHKEY: ${{ secrets.TS_AUTHKEY }} + TAILSCALE_ENROLL_HOSTS: ${{ secrets.TAILSCALE_ENROLL_HOSTS }} + VM_SSH_PRIVATE_KEY: ${{ secrets.VM_SSH_PRIVATE_KEY }} + run: | + if [ -z "$TS_AUTHKEY" ] || [ -z "$TAILSCALE_ENROLL_HOSTS" ] || [ -z "$VM_SSH_PRIVATE_KEY" ]; then + echo "Skipping Tailscale enrollment (missing TS_AUTHKEY, TAILSCALE_ENROLL_HOSTS, or VM_SSH_PRIVATE_KEY)." + exit 0 + fi + + install -m 700 -d ~/.ssh + printf '%s\n' "$VM_SSH_PRIVATE_KEY" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + + for host in $(printf '%s' "$TAILSCALE_ENROLL_HOSTS" | tr ',' ' '); do + echo "Enrolling $host into Tailscale" + ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_rsa "micqdf@$host" \ + "echo '$TS_AUTHKEY' | sudo tee /etc/tailscale/authkey >/dev/null && sudo chmod 600 /etc/tailscale/authkey && sudo systemctl start tailscale-firstboot.service" + done diff --git a/nixos/template-base/configuration.nix b/nixos/template-base/configuration.nix index 867d6c0..88e37ea 100644 --- a/nixos/template-base/configuration.nix +++ b/nixos/template-base/configuration.nix @@ -39,6 +39,33 @@ security.sudo.wheelNeedsPassword = false; + systemd.services.tailscale-firstboot = { + description = "One-time Tailscale enrollment"; + after = [ "network-online.target" "tailscaled.service" ]; + wants = [ "network-online.target" "tailscaled.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + script = '' + if [ -f /var/lib/tailscale/.joined ]; then + exit 0 + fi + + if [ ! -s /etc/tailscale/authkey ]; then + exit 0 + fi + + key="$(cat /etc/tailscale/authkey)" + ${pkgs.tailscale}/bin/tailscale up --auth-key="$key" --hostname="$(hostname)" + + install -d -m 0700 /var/lib/tailscale + touch /var/lib/tailscale/.joined + rm -f /etc/tailscale/authkey + ''; + }; + environment.systemPackages = with pkgs; [ btop curl @@ -50,6 +77,7 @@ htop jq ripgrep + tailscale tree unzip vim -- 2.49.1 From c0dd091b5107d8dadca6238fa7ef60c2c07a3b3a Mon Sep 17 00:00:00 2001 From: MichaelFisher1997 Date: Sat, 28 Feb 2026 00:44:08 +0000 Subject: [PATCH 2/4] chore: align template base with live VM config Set NixOS stateVersion to 25.05 and include neovim in the default utility package set. --- nixos/template-base/configuration.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nixos/template-base/configuration.nix b/nixos/template-base/configuration.nix index 88e37ea..957346b 100644 --- a/nixos/template-base/configuration.nix +++ b/nixos/template-base/configuration.nix @@ -81,8 +81,9 @@ tree unzip vim + neovim wget ]; - system.stateVersion = "24.11"; + system.stateVersion = "25.05"; } -- 2.49.1 From b0768db7a7bbc97af6cd380bf00e851a6339ab71 Mon Sep 17 00:00:00 2001 From: MichaelFisher1997 Date: Sat, 28 Feb 2026 00:52:40 +0000 Subject: [PATCH 3/4] feat: store Terraform state in Backblaze B2 Configure an s3 backend and initialize Terraform in CI with backend config from Gitea secrets so state persists across runs and apply operations stay consistent. --- .gitea/workflows/terraform-apply.yml | 15 ++++++++++++++- .gitea/workflows/terraform-plan.yml | 15 ++++++++++++++- terraform/main.tf | 2 ++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/terraform-apply.yml b/.gitea/workflows/terraform-apply.yml index 3aa01c6..602b21b 100644 --- a/.gitea/workflows/terraform-apply.yml +++ b/.gitea/workflows/terraform-apply.yml @@ -20,6 +20,19 @@ jobs: cat > secrets.auto.tfvars << EOF pm_api_token_secret = "${{ secrets.PM_API_TOKEN_SECRET }}" EOF + cat > backend.hcl << EOF + bucket = "${{ secrets.B2_TF_BUCKET }}" + key = "terraform.tfstate" + region = "us-east-005" + endpoint = "${{ secrets.B2_TF_ENDPOINT }}" + access_key = "${{ secrets.B2_KEY_ID }}" + secret_key = "${{ secrets.B2_APPLICATION_KEY }}" + skip_credentials_validation = true + skip_metadata_api_check = true + skip_region_validation = true + skip_requesting_account_id = true + force_path_style = true + EOF - name: Set up Terraform uses: hashicorp/setup-terraform@v2 @@ -28,7 +41,7 @@ jobs: - name: Terraform Init working-directory: terraform - run: terraform init + run: terraform init -reconfigure -backend-config=backend.hcl - name: Terraform Plan working-directory: terraform diff --git a/.gitea/workflows/terraform-plan.yml b/.gitea/workflows/terraform-plan.yml index 35637d1..34fb360 100644 --- a/.gitea/workflows/terraform-plan.yml +++ b/.gitea/workflows/terraform-plan.yml @@ -22,6 +22,19 @@ jobs: cat > secrets.auto.tfvars << EOF pm_api_token_secret = "${{ secrets.PM_API_TOKEN_SECRET }}" EOF + cat > backend.hcl << EOF + bucket = "${{ secrets.B2_TF_BUCKET }}" + key = "terraform.tfstate" + region = "us-east-005" + endpoint = "${{ secrets.B2_TF_ENDPOINT }}" + access_key = "${{ secrets.B2_KEY_ID }}" + secret_key = "${{ secrets.B2_APPLICATION_KEY }}" + skip_credentials_validation = true + skip_metadata_api_check = true + skip_region_validation = true + skip_requesting_account_id = true + force_path_style = true + EOF echo "Created secrets.auto.tfvars:" cat secrets.auto.tfvars | sed 's/=.*/=***/' echo "Using token ID from terraform.tfvars:" @@ -34,7 +47,7 @@ jobs: - name: Terraform Init working-directory: terraform - run: terraform init + run: terraform init -reconfigure -backend-config=backend.hcl - name: Terraform Format Check working-directory: terraform diff --git a/terraform/main.tf b/terraform/main.tf index 55e812f..8631a17 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -1,4 +1,6 @@ terraform { + backend "s3" {} + required_providers { proxmox = { source = "Telmate/proxmox" -- 2.49.1 From 47f950d6678cc038daba37a14bd668890045e4ed Mon Sep 17 00:00:00 2001 From: MichaelFisher1997 Date: Sat, 28 Feb 2026 00:56:12 +0000 Subject: [PATCH 4/4] fix: update S3 backend config for Terraform init Use non-deprecated s3 endpoint settings, switch to use_path_style, and trim newline characters from B2 credentials when generating backend.hcl in CI. --- .gitea/workflows/terraform-apply.yml | 10 ++++++---- .gitea/workflows/terraform-plan.yml | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.gitea/workflows/terraform-apply.yml b/.gitea/workflows/terraform-apply.yml index 602b21b..a94816c 100644 --- a/.gitea/workflows/terraform-apply.yml +++ b/.gitea/workflows/terraform-apply.yml @@ -24,14 +24,16 @@ jobs: bucket = "${{ secrets.B2_TF_BUCKET }}" key = "terraform.tfstate" region = "us-east-005" - endpoint = "${{ secrets.B2_TF_ENDPOINT }}" - access_key = "${{ secrets.B2_KEY_ID }}" - secret_key = "${{ secrets.B2_APPLICATION_KEY }}" + endpoints = { + s3 = "${{ secrets.B2_TF_ENDPOINT }}" + } + access_key = "$(printf '%s' "${{ secrets.B2_KEY_ID }}" | tr -d '\r\n')" + secret_key = "$(printf '%s' "${{ secrets.B2_APPLICATION_KEY }}" | tr -d '\r\n')" skip_credentials_validation = true skip_metadata_api_check = true skip_region_validation = true skip_requesting_account_id = true - force_path_style = true + use_path_style = true EOF - name: Set up Terraform diff --git a/.gitea/workflows/terraform-plan.yml b/.gitea/workflows/terraform-plan.yml index 34fb360..7854dd6 100644 --- a/.gitea/workflows/terraform-plan.yml +++ b/.gitea/workflows/terraform-plan.yml @@ -26,14 +26,16 @@ jobs: bucket = "${{ secrets.B2_TF_BUCKET }}" key = "terraform.tfstate" region = "us-east-005" - endpoint = "${{ secrets.B2_TF_ENDPOINT }}" - access_key = "${{ secrets.B2_KEY_ID }}" - secret_key = "${{ secrets.B2_APPLICATION_KEY }}" + endpoints = { + s3 = "${{ secrets.B2_TF_ENDPOINT }}" + } + access_key = "$(printf '%s' "${{ secrets.B2_KEY_ID }}" | tr -d '\r\n')" + secret_key = "$(printf '%s' "${{ secrets.B2_APPLICATION_KEY }}" | tr -d '\r\n')" skip_credentials_validation = true skip_metadata_api_check = true skip_region_validation = true skip_requesting_account_id = true - force_path_style = true + use_path_style = true EOF echo "Created secrets.auto.tfvars:" cat secrets.auto.tfvars | sed 's/=.*/=***/' -- 2.49.1