Files
felhom.eu/manifests/hub.yaml
T
2026-02-16 14:17:58 +01:00

214 lines
6.1 KiB
YAML

# Felhom Hub — Multi-customer dashboard
# Dashboard: https://hub.felhom.eu
# API: POST /api/v1/report (Bearer token auth)
#
# Receives health reports from customer controllers and displays
# a centralized overview dashboard for the operator (Viktor).
#
# Namespace: felhom-system (shared with healthchecks and other felhom infra)
#
# PREREQUISITES:
# 1. Build and push the hub image:
# cd ~/build/felhom-hub && ./build.sh 0.1.0 --push
#
# 2. Generate a bcrypt password hash for dashboard login:
# htpasswd -nbBC 10 "" "your-password" | cut -d: -f2
# Update the ConfigMap password_hash field below.
#
# 3. Generate a report API key (shared secret for controllers):
# openssl rand -hex 32
# Update the ConfigMap report_api_key field below.
# Then add the same key to each customer's controller.yaml:
# hub:
# enabled: true
# url: "https://hub.felhom.eu"
# api_key: "<same-key>"
#
# 4. Apply this manifest:
# kubectl apply -f manifests/hub.yaml
#
# 5. Configure DNS:
# Add hub.felhom.eu → k3s cluster IP in Cloudflare
#
# DEBUGGING:
# kubectl logs -n felhom-system deploy/hub -f
# kubectl exec -it -n felhom-system deploy/hub -- ls /data/
# kubectl describe ingress -n felhom-system hub
# =============================================================================
# PERSISTENT STORAGE
# =============================================================================
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: hub-data
namespace: felhom-system
labels:
app: hub
recurring-job-group.longhorn.io/default: disabled
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 1Gi
# =============================================================================
# CONFIGURATION
# =============================================================================
---
apiVersion: v1
kind: ConfigMap
metadata:
name: hub-config
namespace: felhom-system
data:
hub.yaml: |
auth:
# Bcrypt hash for dashboard login (Viktor only)
# Generate: htpasswd -nbBC 10 "" "your-password" | cut -d: -f2
password_hash: "$2y$10$N5.O9jBnc.1tIlJT/irx3OlVjJQemlCHRnfqIJg/EyZofnzXSCpeG"
api:
# Shared secret for controller → hub report push
# Generate: openssl rand -hex 32
# Must match hub.api_key in each customer's controller.yaml
report_api_key: "094091de545ce28795c47ac2158fc30750db5c24a621c49329b001ee8db57fb8"
retention:
max_days: 90
prune_schedule: "04:30"
alerting:
stale_threshold: "30m"
server:
listen: ":8080"
data_dir: "/data"
# =============================================================================
# DEPLOYMENT
# =============================================================================
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hub
namespace: felhom-system
labels:
app: hub
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: hub
template:
metadata:
labels:
app: hub
spec:
containers:
- name: hub
image: gitea.dooplex.hu/admin/felhom-hub:latest
ports:
- containerPort: 8080
name: http
env:
- name: TZ
value: "Europe/Budapest"
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
volumeMounts:
- name: data
mountPath: /data
- name: config
mountPath: /etc/felhom-hub
# NOTE: When password_hash is set, GET / returns 401 for unauthenticated
# requests. The httpGet probe accepts 200-399 only, so it would fail.
# TODO: Add a /healthz endpoint in the hub code that bypasses auth.
# For now, probes work because password_hash is empty (no auth).
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 30
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 3
periodSeconds: 10
timeoutSeconds: 3
volumes:
- name: data
persistentVolumeClaim:
claimName: hub-data
- name: config
configMap:
name: hub-config
# =============================================================================
# SERVICE
# =============================================================================
---
apiVersion: v1
kind: Service
metadata:
name: hub
namespace: felhom-system
labels:
app: hub
spec:
selector:
app: hub
ports:
- port: 8080
targetPort: 8080
name: http
# =============================================================================
# INGRESS — hub.felhom.eu
# =============================================================================
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hub
namespace: felhom-system
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-body-size: "2m"
# Geo-restrict to Hungary (operator-only dashboard)
# NOTE: /api/v1/report must also be reachable — all customers are in HU
nginx.ingress.kubernetes.io/configuration-snippet: |
set $geo_allowed 0;
if ($remote_addr ~ "^192\.168\.") { set $geo_allowed 1; }
if ($remote_addr ~ "^10\.") { set $geo_allowed 1; }
if ($geoip2_country_code = "HU") { set $geo_allowed 1; }
if ($geo_allowed = 0) {
return 403 "Access restricted to Hungary";
}
spec:
ingressClassName: nginx-internal
tls:
- hosts:
- hub.felhom.eu
secretName: hub-felhom-eu-tls
rules:
- host: hub.felhom.eu
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hub
port:
number: 8080