Establish clean homelab infrastructure baseline
Reorganize the brownfield repository, remove retired and generated artifacts, harden ignore rules, and record the GitOps/IaC redesign.
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
.terraform/
|
||||
terraform.tfvars
|
||||
crash.log
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/hashicorp/azuread" {
|
||||
version = "3.9.0"
|
||||
constraints = "~> 3.0"
|
||||
hashes = [
|
||||
"h1:+ZknnMPMLJ1dIVqxto9ZWoakX4ljsek5cmajhUfEwN4=",
|
||||
"zh:1c3e89cf19118fc07d7b04257251fc9897e722c16e0a0df7b07fcd261f8c12e7",
|
||||
"zh:39b11a075e4baa4f6ed5c72a8427013d50f43eecc1a7603b73bccf80f952f758",
|
||||
"zh:41484c196c943b39411f561e70a308bd2a71da18155bfec7381ba0bd61361d34",
|
||||
"zh:42068e5da223494beea5f7fcb9057c308cbfa92f96e53c50083e2639216479d8",
|
||||
"zh:464d7da44682443a4b64bfdaf3d0eb53011c6e1471f244f6354c4d5bca18edce",
|
||||
"zh:49f597ea3fac39931ff91e55afd5b5cc91e449920a03716f82509d588aaab708",
|
||||
"zh:6092c376accfc50b555b7a0cd56b76c09abc3d65ac9dd5069063d6f9f1e76d3b",
|
||||
"zh:65326a9f3ac0783c16e05c16422d191f0a926b8d021fd5303c1fdf8dc42f16e9",
|
||||
"zh:784214ed809347d74562bb38194c0cef57831eaa621ba3b7cdd3fe7a7a76d844",
|
||||
"zh:b4233f9bc791adc7d6643507fa5b47360a21125763a072d953586151cacb65f9",
|
||||
"zh:c4ecdd995ff99b7e362e087c45f080816bcf097da5be257c87b912210e45dd3e",
|
||||
"zh:f0122771f71cb98248e70cdd6c2ccd3bffb34e79d19897fa28b785c86b2312ed",
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# Remote state in SeaweedFS S3, on the LAN.
|
||||
#
|
||||
# WHY remote at all: local state means the only copy lives on this laptop, which is
|
||||
# also the k3s node, the NFS server and the libvirt host — i.e. the single point of
|
||||
# failure. It also cannot be locked, so two concurrent applies silently corrupt it.
|
||||
#
|
||||
# WHY s3.ad.ddupan.top and NOT obj.ddupan.top: the public name resolves to
|
||||
# Cloudflare and hairpins through the WAN. On 2026-07-28 that path was blackholed
|
||||
# for hours by a dead VPN tunnel. State must be reachable when the WAN is not —
|
||||
# it is what you need DURING an incident. See ../../seaweedfs/httproute-s3.yaml.
|
||||
#
|
||||
# CREDENTIALS are not in this file. Export them before running terraform:
|
||||
# export AWS_ACCESS_KEY_ID=$(bao kv get -field=... kv/k8s/seaweedfs-s3) # see README
|
||||
# export AWS_SECRET_ACCESS_KEY=...
|
||||
# The `terraform` S3 identity is scoped to this bucket only — it deliberately
|
||||
# cannot create buckets or read anything else in the store.
|
||||
terraform {
|
||||
backend "s3" {
|
||||
bucket = "tfstate"
|
||||
key = "smtp-relay/terraform.tfstate"
|
||||
|
||||
endpoints = {
|
||||
s3 = "https://s3.ad.ddupan.top"
|
||||
}
|
||||
|
||||
# SeaweedFS is not AWS: it has no regions, no IAM, no metadata service and no
|
||||
# account IDs, so every AWS-specific validation has to be skipped or the
|
||||
# provider fails before it ever talks to the endpoint.
|
||||
region = "us-east-1"
|
||||
use_path_style = true
|
||||
skip_credentials_validation = true
|
||||
skip_metadata_api_check = true
|
||||
skip_region_validation = true
|
||||
skip_requesting_account_id = true
|
||||
|
||||
# Native S3 locking (Terraform >= 1.10; this repo runs 1.15). Writes a
|
||||
# .tflock object alongside the state — no DynamoDB table needed.
|
||||
use_lockfile = true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
# Entra app registration for the Postfix sasl-xoauth2 relay.
|
||||
# Delegated Microsoft Graph SMTP.Send + admin consent + a client secret. The relay
|
||||
# still needs a one-time device-code login to mint the refresh token (see ../README.md).
|
||||
|
||||
data "azuread_client_config" "current" {}
|
||||
|
||||
# Microsoft Graph well-known IDs, so we don't hardcode the SMTP.Send permission UUID.
|
||||
data "azuread_application_published_app_ids" "well_known" {}
|
||||
|
||||
resource "azuread_service_principal" "msgraph" {
|
||||
client_id = data.azuread_application_published_app_ids.well_known.result["MicrosoftGraph"]
|
||||
use_existing = true
|
||||
}
|
||||
|
||||
resource "azuread_application" "smtp_relay" {
|
||||
display_name = var.app_display_name
|
||||
sign_in_audience = "AzureADMyOrg"
|
||||
|
||||
# Enables "Allow public client flows" so the device-code flow works, while we still
|
||||
# keep a client secret for confidential refresh.
|
||||
fallback_public_client_enabled = true
|
||||
|
||||
public_client {
|
||||
redirect_uris = ["https://login.microsoftonline.com/common/oauth2/nativeclient"]
|
||||
}
|
||||
|
||||
required_resource_access {
|
||||
resource_app_id = data.azuread_application_published_app_ids.well_known.result["MicrosoftGraph"]
|
||||
|
||||
resource_access {
|
||||
id = azuread_service_principal.msgraph.oauth2_permission_scope_ids["SMTP.Send"]
|
||||
type = "Scope" # delegated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "azuread_service_principal" "smtp_relay" {
|
||||
client_id = azuread_application.smtp_relay.client_id
|
||||
}
|
||||
|
||||
# NOTE: no client secret. This is a PUBLIC client (device-code delegated flow); the
|
||||
# refresh token is the credential. Presenting a secret makes Entra reject the refresh
|
||||
# with AADSTS700025 ("Client is public..."). CLIENT_SECRET in the k8s secret is empty.
|
||||
|
||||
# Org-wide admin consent for the delegated SMTP.Send scope (no per-user consent prompt).
|
||||
resource "azuread_service_principal_delegated_permission_grant" "smtp_send" {
|
||||
service_principal_object_id = azuread_service_principal.smtp_relay.object_id
|
||||
resource_service_principal_object_id = azuread_service_principal.msgraph.object_id
|
||||
claim_values = ["SMTP.Send"]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# Feed these into smtp-relay/secret.yaml (CLIENT_ID / CLIENT_SECRET / TENANT_ID).
|
||||
# terraform output -raw client_id
|
||||
# terraform output -raw tenant_id
|
||||
# terraform output -raw client_secret
|
||||
|
||||
output "client_id" {
|
||||
value = azuread_application.smtp_relay.client_id
|
||||
description = "CLIENT_ID for the relay secret."
|
||||
}
|
||||
|
||||
output "tenant_id" {
|
||||
value = data.azuread_client_config.current.tenant_id
|
||||
description = "TENANT_ID for the relay secret."
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
variable "app_display_name" {
|
||||
type = string
|
||||
default = "smtp-relay-sasl-xoauth2"
|
||||
description = "Display name of the Entra app registration for the SMTP relay."
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
terraform {
|
||||
required_version = ">= 1.5"
|
||||
required_providers {
|
||||
azuread = {
|
||||
source = "hashicorp/azuread"
|
||||
version = "~> 3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Authenticates via Azure CLI by default: run `az login` as an account that can
|
||||
# create app registrations AND grant admin consent (Global Admin / Privileged Role
|
||||
# Admin) before `terraform apply`.
|
||||
provider "azuread" {}
|
||||
Reference in New Issue
Block a user