Phase 5 — CI pipelines: - default.yml: add SOPS decrypt in prepare step, change git add . to specific paths (stacks/ state/ .woodpecker/), cleanup on success+failure - renew-tls.yml: change git add . to git add secrets/ state/ Phase 6 — sensitive=true: - Add sensitive = true to 256 variable declarations across 149 stack files - Prevents secret values from appearing in terraform plan output - Does NOT modify shared modules (ingress_factory, nfs_volume) to avoid breaking module interface contracts Note: CI pipeline SOPS decryption requires sops_age_key Woodpecker secret to be created before the pipeline will work with SOPS. Until then, the old terraform.tfvars path continues to function.
288 lines
6.9 KiB
HCL
288 lines
6.9 KiB
HCL
variable "tls_secret_name" {
|
|
type = string
|
|
sensitive = true
|
|
}
|
|
variable "nfs_server" { type = string }
|
|
|
|
|
|
resource "kubernetes_namespace" "poison_fountain" {
|
|
metadata {
|
|
name = "poison-fountain"
|
|
labels = {
|
|
"istio-injection" = "disabled"
|
|
tier = local.tiers.cluster
|
|
}
|
|
}
|
|
}
|
|
|
|
module "tls_secret" {
|
|
source = "../../modules/kubernetes/setup_tls_secret"
|
|
namespace = kubernetes_namespace.poison_fountain.metadata[0].name
|
|
tls_secret_name = var.tls_secret_name
|
|
}
|
|
|
|
module "nfs_data" {
|
|
source = "../../modules/kubernetes/nfs_volume"
|
|
name = "poison-fountain-data"
|
|
namespace = kubernetes_namespace.poison_fountain.metadata[0].name
|
|
nfs_server = var.nfs_server
|
|
nfs_path = "/mnt/main/poison-fountain"
|
|
}
|
|
|
|
# ConfigMap for the Python service code
|
|
resource "kubernetes_config_map" "poison_fountain_code" {
|
|
metadata {
|
|
name = "poison-fountain-code"
|
|
namespace = kubernetes_namespace.poison_fountain.metadata[0].name
|
|
}
|
|
|
|
data = {
|
|
"server.py" = file("${path.module}/app/server.py")
|
|
}
|
|
}
|
|
|
|
# ConfigMap for the fetcher script
|
|
resource "kubernetes_config_map" "poison_fountain_fetcher" {
|
|
metadata {
|
|
name = "poison-fountain-fetcher"
|
|
namespace = kubernetes_namespace.poison_fountain.metadata[0].name
|
|
}
|
|
|
|
data = {
|
|
"fetch-poison.sh" = file("${path.module}/app/fetch-poison.sh")
|
|
}
|
|
}
|
|
|
|
# Main service deployment
|
|
resource "kubernetes_deployment" "poison_fountain" {
|
|
metadata {
|
|
name = "poison-fountain"
|
|
namespace = kubernetes_namespace.poison_fountain.metadata[0].name
|
|
labels = {
|
|
app = "poison-fountain"
|
|
tier = local.tiers.cluster
|
|
}
|
|
}
|
|
|
|
spec {
|
|
replicas = 2
|
|
strategy {
|
|
type = "RollingUpdate"
|
|
rolling_update {
|
|
max_unavailable = 0
|
|
max_surge = 1
|
|
}
|
|
}
|
|
selector {
|
|
match_labels = {
|
|
app = "poison-fountain"
|
|
}
|
|
}
|
|
template {
|
|
metadata {
|
|
labels = {
|
|
app = "poison-fountain"
|
|
}
|
|
}
|
|
spec {
|
|
topology_spread_constraint {
|
|
max_skew = 1
|
|
topology_key = "kubernetes.io/hostname"
|
|
when_unsatisfiable = "DoNotSchedule"
|
|
label_selector {
|
|
match_labels = {
|
|
app = "poison-fountain"
|
|
}
|
|
}
|
|
}
|
|
container {
|
|
name = "poison-fountain"
|
|
image = "python:3.12-slim"
|
|
command = ["python", "/app/server.py"]
|
|
|
|
port {
|
|
container_port = 8080
|
|
}
|
|
|
|
env {
|
|
name = "CACHE_DIR"
|
|
value = "/data/cache"
|
|
}
|
|
env {
|
|
name = "DRIP_BYTES"
|
|
value = "50"
|
|
}
|
|
env {
|
|
name = "DRIP_DELAY"
|
|
value = "0.5"
|
|
}
|
|
env {
|
|
name = "POISON_DOMAIN"
|
|
value = "poison.viktorbarzin.me"
|
|
}
|
|
|
|
volume_mount {
|
|
name = "code"
|
|
mount_path = "/app"
|
|
read_only = true
|
|
}
|
|
volume_mount {
|
|
name = "data"
|
|
mount_path = "/data"
|
|
}
|
|
|
|
liveness_probe {
|
|
http_get {
|
|
path = "/healthz"
|
|
port = 8080
|
|
}
|
|
initial_delay_seconds = 5
|
|
period_seconds = 30
|
|
}
|
|
readiness_probe {
|
|
http_get {
|
|
path = "/healthz"
|
|
port = 8080
|
|
}
|
|
initial_delay_seconds = 3
|
|
period_seconds = 10
|
|
}
|
|
|
|
resources {
|
|
requests = {
|
|
cpu = "10m"
|
|
memory = "32Mi"
|
|
}
|
|
limits = {
|
|
cpu = "100m"
|
|
memory = "128Mi"
|
|
}
|
|
}
|
|
}
|
|
|
|
volume {
|
|
name = "code"
|
|
config_map {
|
|
name = kubernetes_config_map.poison_fountain_code.metadata[0].name
|
|
}
|
|
}
|
|
volume {
|
|
name = "data"
|
|
persistent_volume_claim {
|
|
claim_name = module.nfs_data.claim_name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Internal service (for ForwardAuth from Traefik)
|
|
resource "kubernetes_service" "poison_fountain" {
|
|
metadata {
|
|
name = "poison-fountain"
|
|
namespace = kubernetes_namespace.poison_fountain.metadata[0].name
|
|
labels = {
|
|
app = "poison-fountain"
|
|
}
|
|
}
|
|
|
|
spec {
|
|
selector = {
|
|
app = "poison-fountain"
|
|
}
|
|
port {
|
|
name = "http"
|
|
port = 8080
|
|
target_port = 8080
|
|
}
|
|
}
|
|
}
|
|
|
|
# Public ingress for the poison trap subdomain
|
|
# Deliberately NO rate limiting, NO CrowdSec, NO anti-AI (we WANT scrapers here)
|
|
module "ingress" {
|
|
source = "../../modules/kubernetes/ingress_factory"
|
|
namespace = kubernetes_namespace.poison_fountain.metadata[0].name
|
|
name = "poison-fountain"
|
|
host = "poison"
|
|
port = 8080
|
|
tls_secret_name = var.tls_secret_name
|
|
skip_default_rate_limit = true
|
|
exclude_crowdsec = true
|
|
anti_ai_scraping = false
|
|
}
|
|
|
|
# CronJob to fetch and cache poisoned content from Poison Fountain
|
|
resource "kubernetes_cron_job_v1" "poison_fetcher" {
|
|
metadata {
|
|
name = "poison-fountain-fetcher"
|
|
namespace = kubernetes_namespace.poison_fountain.metadata[0].name
|
|
}
|
|
|
|
spec {
|
|
schedule = "0 */6 * * *"
|
|
successful_jobs_history_limit = 1
|
|
failed_jobs_history_limit = 1
|
|
concurrency_policy = "Forbid"
|
|
|
|
job_template {
|
|
metadata {
|
|
name = "poison-fountain-fetcher"
|
|
}
|
|
spec {
|
|
template {
|
|
metadata {
|
|
name = "poison-fountain-fetcher"
|
|
}
|
|
spec {
|
|
container {
|
|
name = "fetcher"
|
|
image = "curlimages/curl:latest"
|
|
command = ["sh", "/scripts/fetch-poison.sh"]
|
|
|
|
env {
|
|
name = "CACHE_DIR"
|
|
value = "/data/cache"
|
|
}
|
|
env {
|
|
name = "POISON_URL"
|
|
value = "https://rnsaffn.com/poison2/"
|
|
}
|
|
env {
|
|
name = "FETCH_COUNT"
|
|
value = "50"
|
|
}
|
|
|
|
volume_mount {
|
|
name = "scripts"
|
|
mount_path = "/scripts"
|
|
read_only = true
|
|
}
|
|
volume_mount {
|
|
name = "data"
|
|
mount_path = "/data"
|
|
}
|
|
}
|
|
|
|
volume {
|
|
name = "scripts"
|
|
config_map {
|
|
name = kubernetes_config_map.poison_fountain_fetcher.metadata[0].name
|
|
default_mode = "0755"
|
|
}
|
|
}
|
|
volume {
|
|
name = "data"
|
|
persistent_volume_claim {
|
|
claim_name = module.nfs_data.claim_name
|
|
}
|
|
}
|
|
|
|
restart_policy = "Never"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|