postiz: add Temporal sidecar; lock both stacks behind Authentik
Postiz backend was crashlooping on connect ECONNREFUSED ::1:7233 — Postiz needs Temporal for cron/scheduled posts and the Helm chart doesn't bundle it. Added a single-replica temporalio/auto-setup:1.28.1 Deployment in the postiz namespace, backed by the bundled postiz-postgresql (separate `temporal` + `temporal_visibility` databases pre-created via init container), ENABLE_ES=false (Postiz only uses the workflow engine, not visibility search). Skips DYNAMIC_CONFIG_FILE_PATH because that file isn't bundled in auto-setup. Auth audit: - postiz: ingress now `protected = true` (Authentik forward-auth). Postiz also has its own login on top, but registration is no longer exposed to the open internet. - instagram-poster: split into two ingresses on the same host. `/image/*` stays public (Meta + Telegram fetch the 9:16 derivatives). Everything else (/healthz, /queue, /scan, /enqueue, /reject, /post-next) sits behind Authentik. The protected ingress sets dns_type=none — the public one already created the CF DNS record.
This commit is contained in:
parent
7f7698991e
commit
8c4a370a34
2 changed files with 176 additions and 7 deletions
|
|
@ -268,17 +268,35 @@ resource "kubernetes_service" "instagram_poster" {
|
|||
}
|
||||
}
|
||||
|
||||
# Public ingress. No UI — entire host is API-only and Meta needs to fetch
|
||||
# /image/<asset_id> unauthenticated to render preview cards. We therefore
|
||||
# leave `protected = false` so Authentik forward-auth doesn't run on any
|
||||
# path. Inbound auth is the API's own concern (Postiz webhook signature
|
||||
# / shared secret as configured by the parallel agent).
|
||||
module "ingress" {
|
||||
# Two ingresses on the same host — Traefik picks the longest path prefix.
|
||||
#
|
||||
# `/image/*` must be reachable WITHOUT auth so Meta's content fetcher (and
|
||||
# Telegram's photo preview) can render the 9:16 derivatives we produce.
|
||||
# Everything else (/queue, /scan, /enqueue, /post-next, /reject, /healthz)
|
||||
# sits behind Authentik forward-auth — same defense as every other UI on
|
||||
# the cluster, no random caller can pop items off the approval queue.
|
||||
module "ingress_image_public" {
|
||||
source = "../../../../modules/kubernetes/ingress_factory"
|
||||
dns_type = "proxied"
|
||||
namespace = kubernetes_namespace.instagram_poster.metadata[0].name
|
||||
name = "instagram-poster"
|
||||
name = "instagram-poster-image"
|
||||
host = "instagram-poster"
|
||||
tls_secret_name = var.tls_secret_name
|
||||
protected = false
|
||||
ingress_path = ["/image"]
|
||||
port = 80
|
||||
service_name = "instagram-poster"
|
||||
}
|
||||
|
||||
module "ingress_protected" {
|
||||
source = "../../../../modules/kubernetes/ingress_factory"
|
||||
dns_type = "none" # DNS record already created by the public ingress above
|
||||
namespace = kubernetes_namespace.instagram_poster.metadata[0].name
|
||||
name = "instagram-poster"
|
||||
host = "instagram-poster"
|
||||
tls_secret_name = var.tls_secret_name
|
||||
protected = true
|
||||
ingress_path = ["/"]
|
||||
port = 80
|
||||
service_name = "instagram-poster"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,6 +132,8 @@ resource "helm_release" "postiz" {
|
|||
DISABLE_REGISTRATION = "false"
|
||||
IS_GENERAL = "true"
|
||||
NX_ADD_PLUGINS = "false"
|
||||
# Postiz uses Temporal for cron/scheduling — bring our own; Helm chart doesn't.
|
||||
TEMPORAL_ADDRESS = "temporal:7233"
|
||||
}
|
||||
|
||||
# Postiz reads DATABASE_URL/REDIS_URL from this Secret. The chart does
|
||||
|
|
@ -212,6 +214,7 @@ module "ingress" {
|
|||
host = var.host
|
||||
service_name = "postiz" # chart Service name resolves to fullnameOverride
|
||||
port = 80
|
||||
protected = true # Authentik forward-auth — Postiz has its own login on top, but we don't expose registration to the open internet.
|
||||
tls_secret_name = var.tls_secret_name
|
||||
extra_annotations = {
|
||||
"gethomepage.dev/enabled" = "true"
|
||||
|
|
@ -222,3 +225,151 @@ module "ingress" {
|
|||
"gethomepage.dev/pod-selector" = ""
|
||||
}
|
||||
}
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Temporal — cron/workflow engine Postiz requires for scheduled posts.
|
||||
#
|
||||
# Lightweight single-replica deployment using temporalio/auto-setup, backed
|
||||
# by the bundled postiz-postgresql (separate `temporal` database). Visibility
|
||||
# search via Elasticsearch is disabled (ENABLE_ES=false) — Postiz only uses
|
||||
# the workflow engine, not visibility, so SQL is enough.
|
||||
#
|
||||
# Important: temporalio/auto-setup creates schemas in the `temporal` and
|
||||
# `temporal_visibility` databases on first boot. We pre-create them with an
|
||||
# init container running psql against postiz-postgresql.
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
resource "kubernetes_deployment" "temporal" {
|
||||
metadata {
|
||||
name = "temporal"
|
||||
namespace = kubernetes_namespace.postiz.metadata[0].name
|
||||
labels = {
|
||||
app = "temporal"
|
||||
}
|
||||
}
|
||||
spec {
|
||||
replicas = 1
|
||||
strategy {
|
||||
type = "Recreate"
|
||||
}
|
||||
selector {
|
||||
match_labels = { app = "temporal" }
|
||||
}
|
||||
template {
|
||||
metadata {
|
||||
labels = { app = "temporal" }
|
||||
}
|
||||
spec {
|
||||
# Pre-create the two databases Temporal expects on the bundled PG.
|
||||
init_container {
|
||||
name = "create-temporal-dbs"
|
||||
image = "docker.io/bitnamilegacy/postgresql:16.4.0-debian-12-r7"
|
||||
env {
|
||||
name = "PGPASSWORD"
|
||||
value = "postiz-password"
|
||||
}
|
||||
command = ["/bin/bash", "-c"]
|
||||
args = [
|
||||
<<-EOT
|
||||
set -e
|
||||
for db in temporal temporal_visibility; do
|
||||
psql -h postiz-postgresql -U postiz -d postgres -tc "SELECT 1 FROM pg_database WHERE datname='$db'" | grep -q 1 \
|
||||
|| psql -h postiz-postgresql -U postiz -d postgres -c "CREATE DATABASE \"$db\""
|
||||
done
|
||||
EOT
|
||||
]
|
||||
}
|
||||
container {
|
||||
name = "temporal"
|
||||
image = "temporalio/auto-setup:1.28.1"
|
||||
port {
|
||||
container_port = 7233
|
||||
name = "grpc"
|
||||
}
|
||||
env {
|
||||
name = "DB"
|
||||
value = "postgres12"
|
||||
}
|
||||
env {
|
||||
name = "DB_PORT"
|
||||
value = "5432"
|
||||
}
|
||||
env {
|
||||
name = "POSTGRES_USER"
|
||||
value = "postiz"
|
||||
}
|
||||
env {
|
||||
name = "POSTGRES_PWD"
|
||||
value = "postiz-password"
|
||||
}
|
||||
env {
|
||||
name = "POSTGRES_SEEDS"
|
||||
value = "postiz-postgresql"
|
||||
}
|
||||
env {
|
||||
name = "DBNAME"
|
||||
value = "temporal"
|
||||
}
|
||||
env {
|
||||
name = "VISIBILITY_DBNAME"
|
||||
value = "temporal_visibility"
|
||||
}
|
||||
env {
|
||||
name = "ENABLE_ES"
|
||||
value = "false"
|
||||
}
|
||||
env {
|
||||
name = "TEMPORAL_NAMESPACE"
|
||||
value = "default"
|
||||
}
|
||||
# NOTE: not setting DYNAMIC_CONFIG_FILE_PATH — that file isn't
|
||||
# bundled in temporalio/auto-setup. Defaults are fine for our
|
||||
# use (Postiz only needs the workflow engine, not dynamic config).
|
||||
resources {
|
||||
requests = {
|
||||
cpu = "50m"
|
||||
memory = "256Mi"
|
||||
}
|
||||
limits = {
|
||||
memory = "1Gi"
|
||||
}
|
||||
}
|
||||
# Auto-setup runs schema migrations on first boot — give it time.
|
||||
startup_probe {
|
||||
tcp_socket {
|
||||
port = 7233
|
||||
}
|
||||
failure_threshold = 30
|
||||
period_seconds = 5
|
||||
initial_delay_seconds = 10
|
||||
}
|
||||
liveness_probe {
|
||||
tcp_socket {
|
||||
port = 7233
|
||||
}
|
||||
period_seconds = 30
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycle {
|
||||
ignore_changes = [spec[0].template[0].spec[0].dns_config] # KYVERNO_LIFECYCLE_V1
|
||||
}
|
||||
depends_on = [helm_release.postiz]
|
||||
}
|
||||
|
||||
resource "kubernetes_service" "temporal" {
|
||||
metadata {
|
||||
name = "temporal"
|
||||
namespace = kubernetes_namespace.postiz.metadata[0].name
|
||||
}
|
||||
spec {
|
||||
selector = { app = "temporal" }
|
||||
port {
|
||||
name = "grpc"
|
||||
port = 7233
|
||||
target_port = 7233
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue