publish kms.lux.cloud at the KMS that is actually running

The host resolved and answered 404 on every path, so Lux had a secret store and
no way to reach it by name. Three separate reasons, each silent:

  DNS pointed kms.lux.cloud at 129.212.164.5 — hanzo-k8s — which has no ingress
  for that host. The Lux KMS is on lux-k8s (134.199.138.27). Repointed.

  The Ingress named namespace lux-kms, the Node-era KMS. Its Service still
  exists and has ZERO endpoints, so even a matching route would have found an
  empty backend. The live store is the Go one in lux-kms-go.

  cert-manager.io/cluster-issuer said agnost-dns01, which is not a ClusterIssuer
  on this cluster — only letsencrypt-prod is. No certificate was ever going to
  be issued.

Now serving with a real certificate (CN=kms.lux.cloud, Let's Encrypt, to
2026-11-03): /v1/kms/health 200, /v1/kms/secrets 401 missing bearer token.

Publishing it is safe BECAUSE it is API-only — `/` and `/admin` are 404, there
is no console. That is the difference from cloud's embedded KMS, whose console
had no authentication of its own and is why its brand hosts sit behind an
admin-guard ForwardAuth.

The dead Node-era manifests are gone rather than left to be re-applied: their
kustomization set `namespace: lux-kms`, which would have pushed this very
ingress back into the empty namespace. The ingress now lives beside the
StatefulSet it fronts, and the image pin records what the cluster runs
(v1.12.13, not v1.12.11).

IN-CLUSTER CALLERS STILL USE THE SERVICE, and that is not an oversight: from
inside the cluster the public host answers 404 (the hairpin does not reach the
ingress), which is exactly why the two KMSSecrets that already named
https://kms.lux.cloud were failing to sync. 42 of 51 CRs already use
http://kms.lux-kms-go.svc.cluster.local; the strays now do too.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-08-05 12:41:14 -07:00
co-authored by Hanzo Dev
parent ad338b4b6f
commit 53deab0d3b
7 changed files with 52 additions and 159 deletions
-99
View File
@@ -1,99 +0,0 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: kms
namespace: lux-kms
labels:
app: kms
component: backend
spec:
replicas: 2
selector:
matchLabels:
app: kms
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: kms
component: backend
spec:
containers:
- name: kms
image: ghcr.io/hanzoai/kms:1.0.7
imagePullPolicy: Always
ports:
- name: http
containerPort: 8080
protocol: TCP
env:
- name: NODE_ENV
value: production
- name: SITE_URL
value: https://kms.lux.cloud
# Database (uses shared postgres in hanzo.svc)
- name: DB_CONNECTION_URI
valueFrom:
secretKeyRef:
name: kms-db-credentials
key: connection-uri
# Redis
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: kms-db-credentials
key: redis-url
# Encryption
- name: ENCRYPTION_KEY
valueFrom:
secretKeyRef:
name: kms-encryption
key: encryption-key
- name: AUTH_SECRET
valueFrom:
secretKeyRef:
name: kms-encryption
key: auth-secret
# JWT
- name: JWT_AUTH_SECRET
valueFrom:
secretKeyRef:
name: kms-encryption
key: jwt-auth-secret
# Branding
- name: BRAND
value: lux
- name: DEFAULT_ORG_NAME
value: Lux Network
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
livenessProbe:
httpGet:
path: /api/status
port: http
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /api/status
port: http
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
-25
View File
@@ -1,25 +0,0 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kms-ingress
namespace: lux-kms
annotations:
cert-manager.io/cluster-issuer: agnost-dns01
spec:
ingressClassName: ingress
tls:
- hosts:
- kms.lux.cloud
secretName: kms-tls
rules:
- host: kms.lux.cloud
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kms
port:
number: 80
-10
View File
@@ -1,10 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: lux-kms
resources:
- namespace.yaml
- deployment.yaml
- services.yaml
- ingress.yaml
-7
View File
@@ -1,7 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: lux-kms
labels:
app: lux-kms
component: secrets
-17
View File
@@ -1,17 +0,0 @@
---
apiVersion: v1
kind: Service
metadata:
name: kms
namespace: lux-kms
labels:
app: kms
spec:
type: ClusterIP
ports:
- name: http
port: 80
targetPort: http
protocol: TCP
selector:
app: kms
+1 -1
View File
@@ -55,7 +55,7 @@ spec:
- name: ghcr-luxfi
containers:
- name: kms
image: ghcr.io/luxfi/kms:v1.12.11
image: ghcr.io/luxfi/kms:v1.12.13
imagePullPolicy: IfNotPresent
ports:
- { name: http, containerPort: 8080, protocol: TCP }
+51
View File
@@ -0,0 +1,51 @@
---
# kms.lux.cloud — Lux's OWN secret store, published under Lux's own name.
#
# The brand KMS hosts were removed once because a single path-keyed store sat
# behind them, so a brand host served the HANZO store under another brand's
# name. That is no longer the shape: Lux runs its own KMS on its own cluster
# (ns lux-kms-go, ghcr.io/luxfi/kms), so this host resolves Lux secrets and
# nothing else. White-label by ownership, not by a header.
#
# Two things this file used to get wrong, both of which fail silently:
#
# namespace lux-kms — the Node-era KMS. Its Service still exists and has ZERO
# endpoints, so the ingress would have resolved and then failed on an empty
# backend. The live store is the Go one, in lux-kms-go.
#
# cluster-issuer agnost-dns01 — no such ClusterIssuer on this cluster; only
# letsencrypt-prod exists. No certificate was ever going to be issued, so the
# host would have served the default cert and failed TLS verification.
#
# API ONLY, which is what makes publishing it safe. This image serves no
# console: `/` and `/admin` are 404, and /v1/kms/secrets answers 401
# `missing bearer token`. That is the difference from cloud's embedded KMS,
# whose console had no authentication of its own and is the reason its brand
# hosts sit behind an admin-guard ForwardAuth.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kms-ingress
namespace: lux-kms-go
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
# Both spellings: this fleet's controller reads the annotation, while the
# class field is what kubectl and every other reader show.
kubernetes.io/ingress.class: ingress
spec:
ingressClassName: ingress
tls:
- hosts:
- kms.lux.cloud
secretName: kms-lux-cloud-tls
rules:
- host: kms.lux.cloud
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kms
port:
number: 80