Compare commits

..

1 Commits

Author SHA1 Message Date
Gitea Actions Bot
088049c669 Auto-update README with current k8s applications
All checks were successful
Terraform / Terraform (pull_request) Successful in 22s
Generated by CI/CD workflow on 2026-02-05 16:43:43

This PR updates the README.md file with the current list of applications found in the k8s/ directory structure.
2026-02-05 16:43:43 +00:00
81 changed files with 548 additions and 87635 deletions

View File

@@ -25,7 +25,7 @@ jobs:
uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v4.0.0
uses: hashicorp/setup-terraform@v2
with:
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
@@ -45,7 +45,7 @@ jobs:
- name: Terraform Apply
env:
TF_VAR_authentik_token: ${{ secrets.AUTHENTIK_TOKEN }}
run: terraform apply -input=false -auto-approve -parallelism=100
run: terraform apply -var-file proxy-apps.tfvars -var-file oauth2-apps.tfvars -var-file terraform.tfvars -var-file groups.tfvars -input=false -auto-approve -parallelism=100
working-directory: ./terraform/authentik
- name: Generate Wiki Content

1
.gitignore vendored
View File

@@ -13,7 +13,6 @@ crash.*.log
*.tfvars
*.tfvars.json
!*terraform.tfvars
!*.auto.tfvars
# claude ai
.claude/

View File

@@ -16,10 +16,8 @@ ArgoCD homelab project
| **authentik** | [![authentik](https://ag.hexor.cy/api/badge?name=authentik&revision=true)](https://ag.hexor.cy/applications/argocd/authentik) |
| **cert-manager** | [![cert-manager](https://ag.hexor.cy/api/badge?name=cert-manager&revision=true)](https://ag.hexor.cy/applications/argocd/cert-manager) |
| **external-secrets** | [![external-secrets](https://ag.hexor.cy/api/badge?name=external-secrets&revision=true)](https://ag.hexor.cy/applications/argocd/external-secrets) |
| **gpu** | [![gpu](https://ag.hexor.cy/api/badge?name=gpu&revision=true)](https://ag.hexor.cy/applications/argocd/gpu) |
| **kube-system-custom** | [![kube-system-custom](https://ag.hexor.cy/api/badge?name=kube-system-custom&revision=true)](https://ag.hexor.cy/applications/argocd/kube-system-custom) |
| **kubernetes-dashboard** | [![kubernetes-dashboard](https://ag.hexor.cy/api/badge?name=kubernetes-dashboard&revision=true)](https://ag.hexor.cy/applications/argocd/kubernetes-dashboard) |
| **longhorn** | [![longhorn](https://ag.hexor.cy/api/badge?name=longhorn&revision=true)](https://ag.hexor.cy/applications/argocd/longhorn) |
| **postgresql** | [![postgresql](https://ag.hexor.cy/api/badge?name=postgresql&revision=true)](https://ag.hexor.cy/applications/argocd/postgresql) |
| **prom-stack** | [![prom-stack](https://ag.hexor.cy/api/badge?name=prom-stack&revision=true)](https://ag.hexor.cy/applications/argocd/prom-stack) |
| **system-upgrade** | [![system-upgrade](https://ag.hexor.cy/api/badge?name=system-upgrade&revision=true)](https://ag.hexor.cy/applications/argocd/system-upgrade) |
@@ -38,8 +36,6 @@ ArgoCD homelab project
| Application | Status |
| :--- | :---: |
| **comfyui** | [![comfyui](https://ag.hexor.cy/api/badge?name=comfyui&revision=true)](https://ag.hexor.cy/applications/argocd/comfyui) |
| **furumi-server** | [![furumi-server](https://ag.hexor.cy/api/badge?name=furumi-server&revision=true)](https://ag.hexor.cy/applications/argocd/furumi-server) |
| **gitea** | [![gitea](https://ag.hexor.cy/api/badge?name=gitea&revision=true)](https://ag.hexor.cy/applications/argocd/gitea) |
| **greece-notifier** | [![greece-notifier](https://ag.hexor.cy/api/badge?name=greece-notifier&revision=true)](https://ag.hexor.cy/applications/argocd/greece-notifier) |
| **hexound** | [![hexound](https://ag.hexor.cy/api/badge?name=hexound&revision=true)](https://ag.hexor.cy/applications/argocd/hexound) |
@@ -48,8 +44,6 @@ ArgoCD homelab project
| **jellyfin** | [![jellyfin](https://ag.hexor.cy/api/badge?name=jellyfin&revision=true)](https://ag.hexor.cy/applications/argocd/jellyfin) |
| **k8s-secrets** | [![k8s-secrets](https://ag.hexor.cy/api/badge?name=k8s-secrets&revision=true)](https://ag.hexor.cy/applications/argocd/k8s-secrets) |
| **khm** | [![khm](https://ag.hexor.cy/api/badge?name=khm&revision=true)](https://ag.hexor.cy/applications/argocd/khm) |
| **lidarr** | [![lidarr](https://ag.hexor.cy/api/badge?name=lidarr&revision=true)](https://ag.hexor.cy/applications/argocd/lidarr) |
| **mtproxy** | [![mtproxy](https://ag.hexor.cy/api/badge?name=mtproxy&revision=true)](https://ag.hexor.cy/applications/argocd/mtproxy) |
| **n8n** | [![n8n](https://ag.hexor.cy/api/badge?name=n8n&revision=true)](https://ag.hexor.cy/applications/argocd/n8n) |
| **ollama** | [![ollama](https://ag.hexor.cy/api/badge?name=ollama&revision=true)](https://ag.hexor.cy/applications/argocd/ollama) |
| **paperless** | [![paperless](https://ag.hexor.cy/api/badge?name=paperless&revision=true)](https://ag.hexor.cy/applications/argocd/paperless) |

View File

@@ -1,20 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: comfyui
namespace: argocd
spec:
project: apps
destination:
namespace: comfyui
server: https://kubernetes.default.svc
source:
repoURL: ssh://git@gt.hexor.cy:30022/ab/homelab.git
targetRevision: HEAD
path: k8s/apps/comfyui
syncPolicy:
automated:
selfHeal: true
prune: true
syncOptions:
- CreateNamespace=true

View File

@@ -1,57 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: comfyui
namespace: comfyui
labels:
app: comfyui
spec:
replicas: 1
selector:
matchLabels:
app: comfyui
template:
metadata:
labels:
app: comfyui
spec:
runtimeClassName: nvidia
tolerations:
- key: workload
operator: Equal
value: desktop
effect: NoSchedule
nodeSelector:
kubernetes.io/hostname: uk-desktop.tail2fe2d.ts.net
# Fix permissions mismatch usually happening when mapping host paths
securityContext:
runAsUser: 0
initContainers:
- name: create-data-dir
image: busybox
command: ["sh", "-c", "mkdir -p /host.data && chown -R 1000:1000 /host.data"]
volumeMounts:
- name: data
mountPath: /host.data
containers:
- name: comfyui
image: runpod/comfyui:latest-5090
imagePullPolicy: IfNotPresent
env:
- name: COMFYUI_PORT
value: "8188"
ports:
- containerPort: 8188
name: http
protocol: TCP
resources:
limits:
nvidia.com/gpu: 1
volumeMounts:
- name: data
# For ai-dock images, /workspace is the persistent user directory
mountPath: /workspace
volumes:
- name: data
persistentVolumeClaim:
claimName: comfyui-data-pvc

View File

@@ -1,9 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- local-pv.yaml
- pvc.yaml
- deployment.yaml
- service.yaml

View File

@@ -1,22 +0,0 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: comfyui-data-pv
spec:
capacity:
storage: 200Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-path
local:
path: /data/comfyui
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- uk-desktop.tail2fe2d.ts.net

View File

@@ -1,4 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: comfyui

View File

@@ -1,12 +0,0 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: comfyui-data-pvc
namespace: comfyui
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 200Gi

View File

@@ -1,15 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: comfyui
namespace: comfyui
labels:
app: comfyui
spec:
ports:
- name: http
port: 8188
targetPort: 8188
protocol: TCP
selector:
app: comfyui

View File

@@ -1,20 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: furumi-server
namespace: argocd
spec:
project: apps
destination:
namespace: furumi-server
server: https://kubernetes.default.svc
source:
repoURL: ssh://git@gt.hexor.cy:30022/ab/homelab.git
targetRevision: HEAD
path: k8s/apps/furumi-server
syncPolicy:
automated:
selfHeal: true
prune: true
syncOptions:
- CreateNamespace=true

View File

@@ -1,42 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: furumi-server
labels:
app: furumi-server
spec:
replicas: 1
selector:
matchLabels:
app: furumi-server
template:
metadata:
labels:
app: furumi-server
spec:
nodeSelector:
kubernetes.io/hostname: master.tail2fe2d.ts.net
containers:
- name: furumi-server
image: ultradesu/furumi-server:latest
imagePullPolicy: Always
env:
- name: FURUMI_TOKEN
value: "f38387266e75effe891b7953eb9c06b4"
- name: FURUMI_ROOT
value: "/media"
ports:
- name: grpc
containerPort: 50051
protocol: TCP
- name: metrics
containerPort: 9090
protocol: TCP
volumeMounts:
- name: music
mountPath: /media
volumes:
- name: music
hostPath:
path: /k8s/media/downloads/Lidarr_Music
type: DirectoryOrCreate

View File

@@ -1,8 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- app.yaml
- deployment.yaml
- service.yaml
- servicemonitor.yaml

View File

@@ -1,30 +0,0 @@
---
apiVersion: v1
kind: Service
metadata:
name: furumi-server-grpc
spec:
type: LoadBalancer
selector:
app: furumi-server
ports:
- name: grpc
protocol: TCP
port: 50051
targetPort: 50051
---
apiVersion: v1
kind: Service
metadata:
name: furumi-server-metrics
labels:
app: furumi-server
spec:
type: ClusterIP
selector:
app: furumi-server
ports:
- name: metrics
protocol: TCP
port: 9090
targetPort: 9090

View File

@@ -1,21 +0,0 @@
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: furumi-server-metrics
labels:
app: furumi-server
release: prometheus
spec:
selector:
matchLabels:
app: furumi-server
endpoints:
- port: metrics
path: /metrics
interval: 30s
scrapeTimeout: 10s
honorLabels: true
namespaceSelector:
matchNames:
- furumi-server

View File

@@ -1,20 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: lidarr
namespace: argocd
spec:
project: apps
destination:
namespace: lidarr
server: https://kubernetes.default.svc
source:
repoURL: ssh://git@gt.hexor.cy:30022/ab/homelab.git
targetRevision: HEAD
path: k8s/apps/lidarr
syncPolicy:
automated:
selfHeal: true
prune: true
syncOptions:
- CreateNamespace=true

View File

@@ -1,14 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- app.yaml
helmCharts:
- name: lidarr
repo: https://k8s-home-lab.github.io/helm-charts/
version: 15.3.0
releaseName: lidarr
namespace: lidarr
valuesFile: lidarr-values.yaml
includeCRDs: true

View File

@@ -1,27 +0,0 @@
env:
TZ: Asia/Nicosia
resources:
requests:
memory: "512Mi"
cpu: "200m"
limits:
memory: "2Gi"
cpu: "1500m"
nodeSelector:
kubernetes.io/hostname: master.tail2fe2d.ts.net
persistence:
config:
enabled: true
type: hostPath
hostPath: /k8s/lidarr
mountPath: /config
downloads:
enabled: true
type: hostPath
hostPath: /k8s/media/downloads
mountPath: /downloads
accessMode: ReadWriteOnce

View File

@@ -1,53 +0,0 @@
FROM --platform=$BUILDPLATFORM debian:bookworm-slim AS builder
ARG TARGETARCH
RUN apt-get update && apt-get install -y \
git curl make gcc libssl-dev zlib1g-dev \
&& rm -rf /var/lib/apt/lists/*
RUN if [ "$(dpkg --print-architecture)" != "$TARGETARCH" ]; then \
dpkg --add-architecture $TARGETARCH && \
apt-get update && \
case "$TARGETARCH" in \
arm64) apt-get install -y gcc-aarch64-linux-gnu libssl-dev:arm64 zlib1g-dev:arm64 ;; \
amd64) apt-get install -y gcc-x86-64-linux-gnu libssl-dev:amd64 zlib1g-dev:amd64 ;; \
esac && \
rm -rf /var/lib/apt/lists/*; \
fi
RUN git clone https://github.com/TelegramMessenger/MTProxy.git /src
WORKDIR /src
RUN NATIVE=$(dpkg --print-architecture) && \
if [ "$NATIVE" != "$TARGETARCH" ]; then \
case "$TARGETARCH" in \
arm64) export CC=aarch64-linux-gnu-gcc ;; \
amd64) export CC=x86_64-linux-gnu-gcc ;; \
esac; \
fi && \
make -j$(nproc)
FROM debian:bookworm-slim
ENV PROXY_PORT=30443
ENV STATS_PORT=8888
ENV WORKERS=1
ENV RUN_USER=nobody
RUN apt-get update && apt-get install -y \
curl libssl3 zlib1g xxd \
&& rm -rf /var/lib/apt/lists/*
COPY --from=builder /src/objs/bin/mtproto-proxy /usr/local/bin/mtproto-proxy
RUN curl -s https://core.telegram.org/getProxySecret -o /etc/mtproxy/proxy-secret --create-dirs && \
curl -s https://core.telegram.org/getProxyConfig -o /etc/mtproxy/proxy-multi.conf
ENTRYPOINT mtproto-proxy \
-u ${RUN_USER} \
-p ${STATS_PORT} \
-H ${PROXY_PORT} \
-M ${WORKERS} \
--aes-pwd /etc/mtproxy/proxy-secret \
/etc/mtproxy/proxy-multi.conf

View File

@@ -1,20 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: mtproxy
namespace: argocd
spec:
project: apps
destination:
namespace: mtproxy
server: https://kubernetes.default.svc
source:
repoURL: ssh://git@gt.hexor.cy:30022/ab/homelab.git
targetRevision: HEAD
path: k8s/apps/mtproxy
syncPolicy:
automated:
selfHeal: true
prune: true
syncOptions:
- CreateNamespace=true

View File

@@ -1,117 +0,0 @@
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: mtproxy
labels:
app: mtproxy
spec:
selector:
matchLabels:
app: mtproxy
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
app: mtproxy
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: mtproxy
operator: Exists
serviceAccountName: mtproxy
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
initContainers:
- name: register-proxy
image: bitnami/kubectl:latest
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: SECRET
valueFrom:
secretKeyRef:
name: tgproxy-secret
key: SECRET
- name: PORT
valueFrom:
secretKeyRef:
name: tgproxy-secret
key: PORT
volumeMounts:
- name: data
mountPath: /data
command:
- /bin/bash
- -c
- |
set -e
curl -s https://core.telegram.org/getProxySecret -o /data/proxy-secret
curl -s https://core.telegram.org/getProxyConfig -o /data/proxy-multi.conf
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
SERVER=$(kubectl get node "${NODE_NAME}" -o jsonpath='{.metadata.labels.mtproxy}')
if [ -z "${SERVER}" ]; then
echo "ERROR: node ${NODE_NAME} has no mtproxy label"
exit 1
fi
LINK="tg://proxy?server=${SERVER}&port=${PORT}&secret=${SECRET}"
echo "Registering: ${SERVER} -> ${LINK}"
if kubectl get secret mtproxy-links -n "${NAMESPACE}" &>/dev/null; then
kubectl patch secret mtproxy-links -n "${NAMESPACE}" \
--type merge -p "{\"stringData\":{\"${SERVER}\":\"${LINK}\"}}"
else
kubectl create secret generic mtproxy-links -n "${NAMESPACE}" \
--from-literal="${SERVER}=${LINK}"
fi
echo "Done"
containers:
- name: mtproxy
image: telegrammessenger/proxy:latest
# image: ultradesu/mtproxy:v0.02
imagePullPolicy: Always
ports:
- name: proxy
containerPort: 30443
protocol: TCP
command:
- /bin/sh
- -c
- >-
mtproto-proxy
-u nobody
-p 8888
-H $(PORT)
-M 1
-S $(SECRET)
--aes-pwd /data/proxy-secret
/data/proxy-multi.conf
env:
- name: SECRET
valueFrom:
secretKeyRef:
name: tgproxy-secret
key: SECRET
- name: PORT
valueFrom:
secretKeyRef:
name: tgproxy-secret
key: PORT
volumeMounts:
- name: data
mountPath: /data
#resources:
# requests:
# memory: "128Mi"
# cpu: "100m"
# limits:
# memory: "256Mi"
# cpu: "500m"
volumes:
- name: data
emptyDir: {}

View File

@@ -1,25 +0,0 @@
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: tgproxy-secret
spec:
target:
name: tgproxy-secret
deletionPolicy: Delete
template:
type: Opaque
data:
SECRET: |-
{{ .secret }}
PORT: "30443"
data:
- secretKey: secret
sourceRef:
storeRef:
name: vaultwarden-login
kind: ClusterSecretStore
remoteRef:
key: 58a37daf-72d8-430d-86bd-6152aa8f888d
property: fields[0].value

View File

@@ -1,11 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./app.yaml
- ./rbac.yaml
- ./daemonset.yaml
- ./external-secrets.yaml
- ./service.yaml
- ./secret-reader.yaml
# - ./storage.yaml

View File

@@ -1,58 +0,0 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: mtproxy
labels:
app: mtproxy
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: mtproxy-node-reader
labels:
app: mtproxy
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: mtproxy-node-reader
labels:
app: mtproxy
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mtproxy-node-reader
subjects:
- kind: ServiceAccount
name: mtproxy
namespace: mtproxy
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: mtproxy-secret-manager
labels:
app: mtproxy
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "create", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: mtproxy-secret-manager
labels:
app: mtproxy
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: mtproxy-secret-manager
subjects:
- kind: ServiceAccount
name: mtproxy

View File

@@ -1,63 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: secret-reader
labels:
app: secret-reader
spec:
replicas: 1
selector:
matchLabels:
app: secret-reader
template:
metadata:
labels:
app: secret-reader
spec:
serviceAccountName: mtproxy
nodeSelector:
kubernetes.io/os: linux
containers:
- name: secret-reader
image: ultradesu/k8s-secrets:0.2.1
imagePullPolicy: Always
args:
- "--secrets"
- "mtproxy-links"
- "--namespace"
- "mtproxy"
- "--port"
- "3000"
ports:
- containerPort: 3000
name: http
env:
- name: RUST_LOG
value: "info"
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "150m"
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 5
periodSeconds: 5
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL

View File

@@ -1,16 +0,0 @@
---
apiVersion: v1
kind: Service
metadata:
name: secret-reader
labels:
app: secret-reader
spec:
type: ClusterIP
selector:
app: secret-reader
ports:
- port: 80
targetPort: 3000
protocol: TCP
name: http

View File

@@ -1,87 +0,0 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: n8n-runner
labels:
app: n8n
component: runner
spec:
replicas: 2
selector:
matchLabels:
app: n8n
component: runner
template:
metadata:
labels:
app: n8n
component: runner
spec:
serviceAccountName: n8n
containers:
- name: n8n-runner
image: n8nio/runners:latest
ports:
- containerPort: 5680
name: health
env:
- name: PATH
value: "/opt/tools:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
- name: HOME
value: "/home/node"
- name: N8N_RUNNERS_TASK_BROKER_URI
value: "http://n8n:5679"
- name: N8N_RUNNERS_LAUNCHER_LOG_LEVEL
value: "info"
- name: N8N_RUNNERS_MAX_CONCURRENCY
value: "10"
- name: GENERIC_TIMEZONE
value: "Europe/Moscow"
- name: TZ
value: "Europe/Moscow"
- name: N8N_RUNNERS_AUTH_TOKEN
valueFrom:
secretKeyRef:
name: credentials
key: runnertoken
volumeMounts:
- name: n8n-data
mountPath: /home/node/.n8n
- name: tools
mountPath: /opt/tools
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 2000m
memory: 2048Mi
livenessProbe:
httpGet:
path: /healthz
port: 5680
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /healthz
port: 5680
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
volumes:
- name: n8n-data
persistentVolumeClaim:
claimName: n8n-data
- name: tools
persistentVolumeClaim:
claimName: n8n-tools
securityContext:
runAsUser: 1000
runAsGroup: 1000
runAsNonRoot: true
fsGroup: 1000

View File

@@ -1,21 +1,19 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# Updated: Fixed n8n volume permissions issue
resources:
- external-secrets.yaml
- storage.yaml
- rbac.yaml
- redis-deployment.yaml
- redis-service.yaml
- paddleocr-deployment.yaml
- paddleocr-service.yaml
- deployment-main.yaml
- deployment-worker.yaml
- deployment-runner.yaml
- service.yaml
- ingress.yaml
- plain/
helmCharts:
# - name: n8n
# repo: https://community-charts.github.io/helm-charts
# version: 1.16.28
# releaseName: n8n
# namespace: n8n
# valuesFile: values-n8n.yaml
# includeCRDs: true
- name: yacy
repo: https://gt.hexor.cy/api/packages/ab/helm
version: 0.1.2
@@ -23,6 +21,3 @@ helmCharts:
namespace: n8n
valuesFile: values-yacy.yaml
includeCRDs: true
commonLabels:
app.kubernetes.io/name: n8n

View File

@@ -1,43 +0,0 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: paddleocr
labels:
app: paddleocr
component: n8n
spec:
replicas: 1
selector:
matchLabels:
app: paddleocr
component: n8n
template:
metadata:
labels:
app: paddleocr
component: n8n
spec:
containers:
- name: paddleocr
image: c403/paddleocr
ports:
- containerPort: 5000
name: http
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: 1000m
memory: 2Gi
livenessProbe:
tcpSocket:
port: 5000
initialDelaySeconds: 60
periodSeconds: 30
readinessProbe:
tcpSocket:
port: 5000
initialDelaySeconds: 30
periodSeconds: 10

View File

@@ -1,18 +0,0 @@
---
apiVersion: v1
kind: Service
metadata:
name: paddleocr
labels:
app: paddleocr
component: n8n
spec:
selector:
app: paddleocr
component: n8n
ports:
- name: http
port: 80
targetPort: 5000
protocol: TCP
type: ClusterIP

View File

@@ -18,47 +18,13 @@ spec:
app: n8n
component: main
spec:
serviceAccountName: n8n
initContainers:
- name: install-tools
image: alpine:3.22
command:
- /bin/sh
- -c
- |
set -e
if [ -x /tools/kubectl ]; then
echo "kubectl already exists, skipping download"
/tools/kubectl version --client
exit 0
fi
echo "Downloading kubectl..."
ARCH=$(uname -m)
case $ARCH in
x86_64) ARCH="amd64" ;;
aarch64) ARCH="arm64" ;;
esac
wget -O /tools/kubectl "https://dl.k8s.io/release/$(wget -qO- https://dl.k8s.io/release/stable.txt)/bin/linux/${ARCH}/kubectl"
chmod +x /tools/kubectl
/tools/kubectl version --client
volumeMounts:
- name: tools
mountPath: /tools
securityContext:
runAsUser: 1000
runAsGroup: 1000
runAsNonRoot: true
containers:
- name: n8n
image: n8nio/n8n:latest
image: docker.n8n.io/n8nio/n8n:latest
ports:
- containerPort: 5678
name: http
- containerPort: 5679
name: task-broker
env:
- name: PATH
value: "/opt/tools:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
- name: HOME
value: "/home/node"
- name: N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS
@@ -75,24 +41,14 @@ spec:
value: "true"
- name: N8N_RUNNERS_MODE
value: "external"
- name: N8N_RUNNERS_BROKER_LISTEN_ADDRESS
value: "0.0.0.0"
- name: N8N_LISTEN_ADDRESS
value: "0.0.0.0"
- name: N8N_RUNNERS_BROKER_PORT
value: "5679"
- name: EXECUTIONS_MODE
value: "queue"
- name: QUEUE_BULL_REDIS_HOST
value: "n8n-redis"
- name: QUEUE_BULL_REDIS_PORT
value: "6379"
- name: NODE_ENV
value: "production"
- name: WEBHOOK_URL
value: "https://n8n.hexor.cy/"
- name: N8N_PROXY_HOPS
value: "1"
- name: GENERIC_TIMEZONE
value: "Europe/Moscow"
- name: TZ
@@ -126,38 +82,33 @@ spec:
volumeMounts:
- name: n8n-data
mountPath: /home/node/.n8n
- name: tools
mountPath: /opt/tools
resources:
requests:
cpu: 2000m
memory: 512Mi
cpu: 100m
memory: 128Mi
limits:
cpu: 4000m
memory: 2048Mi
cpu: 512m
memory: 512Mi
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 240
initialDelaySeconds: 120
periodSeconds: 30
timeoutSeconds: 20
failureThreshold: 10
timeoutSeconds: 10
failureThreshold: 6
readinessProbe:
httpGet:
path: /healthz/readiness
port: http
initialDelaySeconds: 120
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 15
failureThreshold: 10
volumes:
- name: n8n-data
persistentVolumeClaim:
claimName: n8n-data
- name: tools
persistentVolumeClaim:
claimName: n8n-tools
securityContext:
runAsUser: 1000
runAsGroup: 1000

View File

@@ -7,7 +7,7 @@ metadata:
app: n8n
component: worker
spec:
replicas: 2
replicas: 1
selector:
matchLabels:
app: n8n
@@ -18,24 +18,27 @@ spec:
app: n8n
component: worker
spec:
serviceAccountName: n8n
containers:
- name: n8n-worker
image: n8nio/n8n:latest
command:
- n8n
- worker
image: docker.n8n.io/n8nio/n8n:latest
command: ["n8n", "worker"]
env:
- name: HOME
value: "/home/node"
- name: N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS
value: "true"
- name: N8N_RUNNERS_ENABLED
value: "true"
- name: N8N_RUNNERS_MODE
value: "external"
- name: N8N_PORT
value: "80"
- name: EXECUTIONS_MODE
value: "queue"
- name: QUEUE_BULL_REDIS_HOST
value: "n8n-redis"
- name: QUEUE_BULL_REDIS_PORT
value: "6379"
- name: N8N_RUNNERS_TASK_BROKER_URI
value: "http://n8n:80"
- name: NODE_ENV
value: "production"
- name: GENERIC_TIMEZONE
@@ -63,16 +66,31 @@ spec:
secretKeyRef:
name: credentials
key: encryptionkey
- name: N8N_RUNNERS_AUTH_TOKEN
valueFrom:
secretKeyRef:
name: credentials
key: runnertoken
volumeMounts:
- name: n8n-data
mountPath: /home/node/.n8n
resources:
requests:
cpu: 500m
memory: 512Mi
cpu: 100m
memory: 256Mi
limits:
cpu: 2000m
memory: 2048Mi
cpu: 1000m
memory: 1Gi
livenessProbe:
exec:
command:
- /bin/sh
- -c
- "ps aux | grep '[n]8n worker' || exit 1"
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
volumes:
- name: n8n-data
persistentVolumeClaim:

View File

@@ -0,0 +1,15 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- storage.yaml
- redis-deployment.yaml
- redis-service.yaml
- deployment-main.yaml
- deployment-worker.yaml
- service.yaml
- ingress.yaml
commonLabels:
app.kubernetes.io/name: n8n
app.kubernetes.io/instance: n8n-plain

View File

@@ -14,8 +14,4 @@ spec:
port: 80
targetPort: 5678
protocol: TCP
- name: task-broker
port: 5679
targetPort: 5679
protocol: TCP
type: ClusterIP

View File

@@ -2,11 +2,11 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mtproxy-data
name: n8n-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
- ReadWriteMany
storageClassName: nfs-csi
resources:
requests:
storage: 1Gi
storage: 10Gi

View File

@@ -1,45 +1,71 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: n8n
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: n8n-clusterrole
name: n8n-readonly
rules:
# Core API group ("")
- apiGroups: [""]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources:
- pods
- services
- endpoints
- persistentvolumeclaims
- persistentvolumes
- configmaps
- secrets
- nodes
- namespaces
- events
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources:
- deployments
- replicasets
- statefulsets
- daemonsets
verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
resources:
- ingresses
- networkpolicies
verbs: ["get", "list", "watch"]
- apiGroups: ["extensions"]
resources:
- ingresses
verbs: ["get", "list", "watch"]
- apiGroups: ["autoscaling"]
resources:
- horizontalpodautoscalers
verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
resources:
- jobs
- cronjobs
verbs: ["get", "list", "watch"]
- apiGroups: ["metrics.k8s.io"]
resources:
- pods
- nodes
verbs: ["get", "list"]
- apiGroups: ["storage.k8s.io"]
resources:
- storageclasses
verbs: ["get", "list", "watch"]
- apiGroups: ["policy"]
resources:
- poddisruptionbudgets
verbs: ["get", "list", "watch"]
# Common built-in API groups
- apiGroups: ["apps", "batch", "autoscaling", "extensions", "policy"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io", "rbac.authorization.k8s.io", "apiextensions.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["coordination.k8s.io", "discovery.k8s.io", "events.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: ["storage.k8s.io", "admissionregistration.k8s.io", "authentication.k8s.io", "authorization.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: n8n-clusterrolebinding
name: n8n-readonly
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: n8n-clusterrole
name: n8n-readonly
subjects:
- kind: ServiceAccount
name: n8n
name: n8n-readonly
namespace: n8n

View File

@@ -2,23 +2,11 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: n8n-data
name: n8n-home
spec:
accessModes:
- ReadWriteMany
storageClassName: longhorn
storageClassName: nfs-csi
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: n8n-tools
spec:
accessModes:
- ReadWriteMany
storageClassName: longhorn
resources:
requests:
storage: 20Gi

View File

@@ -0,0 +1,86 @@
nodeSelector:
kubernetes.io/hostname: master.tail2fe2d.ts.net
db:
type: postgresdb
podSecurityContext:
runAsUser: 1000
runAsGroup: 1000
runAsNonRoot: true
# Configure health probes for slow startup
main:
extraEnvVars:
NODES_EXCLUDE: "[]"
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 512m
memory: 512Mi
persistence:
enabled: true
existingClaim: n8n-home
mountPath: /home/node/.n8n
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 120
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 6
readinessProbe:
httpGet:
path: /healthz/readiness
port: http
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 10
worker:
mode: regular
webhook:
url: https://n8n.hexor.cy
redis:
enabled: true
existingEncryptionKeySecret: credentials
serviceAccount:
create: true
automount: true
annotations: {}
name: "n8n-readonly"
externalPostgresql:
existingSecret: credentials
host: "psql.psql.svc"
username: "n8n"
database: "n8n"
ingress:
enabled: true
className: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt
traefik.ingress.kubernetes.io/router.middlewares: kube-system-https-redirect@kubernetescrd
hosts:
- host: n8n.hexor.cy
paths:
- path: /
pathType: Prefix
tls:
- secretName: n8n-tls
hosts:
- '*.hexor.cy'

View File

@@ -21,4 +21,4 @@ yacy:
network.unit.dhtredundancy.senior: "1"
index.receive.allow: "false"
index.distribute.allow: "false"
crawl.response.timeout: "10000"
crawl.response.timeout: "10000"

View File

@@ -3,23 +3,19 @@ kind: Kustomization
resources:
- external-secrets.yaml
- local-pv.yaml
helmCharts:
- name: ollama
repo: https://otwld.github.io/ollama-helm/
version: 1.49.0
version: 0.4.0
releaseName: ollama
namespace: ollama
valuesFile: ollama-values.yaml
includeCRDs: true
- name: open-webui
repo: https://helm.openwebui.com/
version: 12.8.1
version: 8.14.0
releaseName: openweb-ui
namespace: ollama
valuesFile: openweb-ui-values.yaml
includeCRDs: true
patches:
- path: patch-runtimeclass.yaml
includeCRDs: true

View File

@@ -1,22 +0,0 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: ollama-local-pv
spec:
capacity:
storage: 100Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-path
local:
path: /var/lib/ollama
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- uk-desktop.tail2fe2d.ts.net

View File

@@ -3,20 +3,6 @@ image:
pullPolicy: Always
tag: "latest"
nodeSelector:
kubernetes.io/hostname: uk-desktop.tail2fe2d.ts.net
tolerations:
- key: workload
operator: Equal
value: desktop
effect: NoSchedule
kubernetes.io/hostname: master.tail2fe2d.ts.net
ingress:
enabled: false
ollama:
gpu:
enabled: true
type: 'nvidia'
number: 1
persistentVolume:
enabled: true
size: 100Gi
storageClass: "local-path"

View File

@@ -1,4 +1,4 @@
clusterDomain: cluster.local
clusterDomain: ai.hexor.cy
extraEnvVars:
GLOBAL_LOG_LEVEL: debug
@@ -32,22 +32,12 @@ ollama:
pipelines:
enabled: true
nodeSelector:
kubernetes.io/hostname: master.tail2fe2d.ts.net
tika:
enabled: true
nodeSelector:
kubernetes.io/hostname: master.tail2fe2d.ts.net
websocket:
enabled: true
nodeSelector:
kubernetes.io/hostname: master.tail2fe2d.ts.net
redis:
master:
nodeSelector:
kubernetes.io/hostname: master.tail2fe2d.ts.net
ingress:
enabled: true
@@ -56,5 +46,7 @@ ingress:
cert-manager.io/cluster-issuer: letsencrypt
traefik.ingress.kubernetes.io/router.middlewares: kube-system-https-redirect@kubernetescrd
host: "ai.hexor.cy"
tls: true
existingSecret: ollama-tls
tls:
- hosts:
- '*.hexor.cy'
secretName: ollama-tls

View File

@@ -1,9 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: ollama
namespace: ollama
spec:
template:
spec:
runtimeClassName: nvidia

View File

@@ -1,5 +1,5 @@
image:
tag: latest
tag: 2.20.3
resources:
requests:
memory: "1Gi"
@@ -9,7 +9,7 @@ resources:
cpu: "3000m"
initContainers:
install-tesseract-langs:
image: ghcr.io/paperless-ngx/paperless-ngx:latest
image: ghcr.io/paperless-ngx/paperless-ngx:2.18.2
resources:
requests:
memory: "256Mi"

View File

@@ -10,7 +10,7 @@ resources:
helmCharts:
- name: argo-cd
repo: https://argoproj.github.io/argo-helm
version: 9.4.10
version: 9.1.4
releaseName: argocd
namespace: argocd
valuesFile: values.yaml

View File

@@ -28,9 +28,8 @@ configs:
issuer: https://idm.hexor.cy/application/o/argocd/
clientID: $oidc-creds:id
clientSecret: $oidc-creds:secret
requestedScopes: ["openid", "profile", "email", "groups", "offline_access"]
requestedScopes: ["openid", "profile", "email", "groups"]
requestedIDTokenClaims: {"groups": {"essential": true}}
refreshTokenThreshold: 2m
rbac:
create: true
policy.default: ""

View File

@@ -18,4 +18,4 @@ spec:
prune: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true

View File

@@ -19,14 +19,6 @@ spec:
{{ .password }}
AUTHENTIK_SECRET_KEY: |-
{{ .secret_key }}
POSTGRES_PASSWORD: |-
{{ .password }}
POSTGRES_USER: |-
{{ .username }}
username: |-
{{ .password }}
password: |-
{{ .username }}
data:
- secretKey: password
sourceRef:

View File

@@ -5,12 +5,12 @@ resources:
- app.yaml
- external-secrets.yaml
- https-middleware.yaml
# - worker-restart.yaml
- worker-restart.yaml
helmCharts:
- name: authentik
repo: https://charts.goauthentik.io
version: 2026.2.1
version: 2025.10.1
releaseName: authentik
namespace: authentik
valuesFile: values.yaml

View File

@@ -1,6 +1,8 @@
global:
image:
tag: "2026.2.1"
tag: "2025.10.1"
nodeSelector:
kubernetes.io/hostname: master.tail2fe2d.ts.net
authentik:
error_reporting:
@@ -13,35 +15,14 @@ worker:
envFrom:
- secretRef:
name: authentik-creds
# volumes:
# - name: dshm
# emptyDir:
# medium: Memory
# sizeLimit: 512Mi
# volumeMounts:
# - name: dshm
# mountPath: /dev/shm
# livenessProbe:
# exec:
# command: ["/bin/sh", "-c", "kill -0 1"]
# initialDelaySeconds: 5
# periodSeconds: 10
# failureThreshold: 3
# timeoutSeconds: 3
# readinessProbe:
# exec:
# command: ["/bin/sh", "-c", "kill -0 1"]
# initialDelaySeconds: 5
# periodSeconds: 10
# failureThreshold: 3
# timeoutSeconds: 3
# startupProbe:
# exec:
# command: ["/bin/sh", "-c", "kill -0 1"]
# initialDelaySeconds: 30
# periodSeconds: 10
# failureThreshold: 60
# timeoutSeconds: 3
volumes:
- name: dshm
emptyDir:
medium: Memory
sizeLimit: 512Mi
volumeMounts:
- name: dshm
mountPath: /dev/shm
server:
envFrom:
- secretRef:
@@ -72,5 +53,5 @@ server:
hosts:
- '*.hexor.cy'
redis:
enabled: false
enabled: true

View File

@@ -1,20 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: gpu-system
namespace: argocd
spec:
project: core
destination:
namespace: gpu-system
server: https://kubernetes.default.svc
source:
repoURL: ssh://git@gt.hexor.cy:30022/ab/homelab.git
targetRevision: HEAD
path: k8s/core/gpu
syncPolicy:
automated:
selfHeal: true
prune: true
syncOptions:
- CreateNamespace=true

View File

@@ -1,15 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- app.yaml
- runtime-class.yaml
helmCharts:
- name: nvidia-device-plugin
repo: https://nvidia.github.io/k8s-device-plugin
version: 0.17.0
releaseName: nvidia-device-plugin
namespace: gpu-system
valuesFile: values.yaml
includeCRDs: true

View File

@@ -1,5 +0,0 @@
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: nvidia
handler: nvidia

View File

@@ -1,23 +0,0 @@
nodeSelector:
kubernetes.io/hostname: uk-desktop.tail2fe2d.ts.net
tolerations:
- key: workload
operator: Equal
value: desktop
effect: NoSchedule
runtimeClassName: nvidia
setAsDefault: false
config:
default: any
map:
any: |-
version: v1
sharing:
timeSlicing:
resources:
- name: nvidia.com/gpu
replicas: 4

View File

@@ -1,21 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: longhorn
namespace: argocd
spec:
project: core
destination:
namespace: longhorn
server: https://kubernetes.default.svc
source:
repoURL: ssh://git@gt.hexor.cy:30022/ab/homelab.git
targetRevision: HEAD
path: k8s/core/longhorn
syncPolicy:
automated:
selfHeal: true
prune: true
syncOptions:
- CreateNamespace=true

View File

@@ -1,15 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
#resources:
# - app.yaml
helmCharts:
- name: longhorn
repo: https://charts.longhorn.io
version: 1.11.0
releaseName: longhorn
namespace: longhorn
valuesFile: values.yaml
includeCRDs: true

View File

@@ -1,4 +0,0 @@
longhornUI:
replicas: 1
persistence:
reclaimPolicy: "Retain"

View File

@@ -1,46 +0,0 @@
apiVersion: monitoring.coreos.com/v1alpha1
kind: AlertmanagerConfig
metadata:
name: telegram-notifications
namespace: prometheus
labels:
app: kube-prometheus-stack-alertmanager
release: prometheus
spec:
route:
groupBy: ['alertname', 'cluster', 'service']
groupWait: 10s
groupInterval: 5m
repeatInterval: 12h
receiver: telegram
routes:
- matchers:
- name: alertname
value: Watchdog
matchType: "="
receiver: 'null'
receivers:
- name: telegram
telegramConfigs:
- botToken:
name: alertmanager-telegram-secret
key: TELEGRAM_BOT_TOKEN
chatID: 124317807
parseMode: HTML
sendResolved: true
disableNotifications: false
message: |
{{ if eq .Status "firing" }}🔥 FIRING{{ else }}✅ RESOLVED{{ end }}
{{ range .Alerts }}
📊 <b>{{ .Labels.alertname }}</b>
{{ .Annotations.summary }}
{{ if .Annotations.node }}🖥 <b>Node:</b> <code>{{ .Annotations.node }}</code>{{ end }}
{{ if .Annotations.pod }}📦 <b>Pod:</b> <code>{{ .Annotations.pod }}</code>{{ end }}
{{ if .Annotations.namespace }}📁 <b>Namespace:</b> <code>{{ .Annotations.namespace }}</code>{{ end }}
{{ if .Annotations.throttle_rate }}⚠️ <b>Throttling rate:</b> {{ .Annotations.throttle_rate }}{{ end }}
🔗 <a href="{{ .GeneratorURL }}">View in Grafana</a>
{{ end }}
- name: 'null'

View File

@@ -1,647 +0,0 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": false,
"panels": [
{
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calc": "lastNotNull",
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "furumi_active_streams",
"refId": "A"
}
],
"title": "Active Streams",
"type": "stat"
},
{
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 0
},
"id": 2,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calc": "rate",
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "rate(furumi_bytes_read_total[$__rate_interval])",
"refId": "A"
}
],
"title": "Bytes Read / Sec",
"type": "stat",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "Bps"
},
"overrides": []
}
},
{
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 0
},
"id": 3,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calc": "lastNotNull",
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "sum(increase(furumi_file_open_errors_total[$__rate_interval]))",
"refId": "A"
}
],
"title": "File Open Errors (Rate)",
"type": "stat",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "green", "value": null },
{ "color": "red", "value": 1 }
]
}
},
"overrides": []
}
},
{
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 0
},
"id": 4,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calc": "lastNotNull",
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "sum(increase(furumi_auth_failures_total[$__rate_interval]))",
"refId": "A"
}
],
"title": "Auth Failures (Rate)",
"type": "stat",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "green", "value": null },
{ "color": "red", "value": 1 }
]
}
},
"overrides": []
}
},
{
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 4
},
"id": 5,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "sum by (method, status) (rate(furumi_grpc_requests_total[$__rate_interval]))",
"legendFormat": "{{method}} - {{status}}",
"refId": "A"
}
],
"title": "gRPC Request Rate by Method & Status",
"type": "timeseries",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 2,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "reqps"
},
"overrides": []
}
},
{
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 4
},
"id": 6,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "histogram_quantile(0.95, sum(rate(furumi_grpc_request_duration_seconds_bucket[$__rate_interval])) by (le, method))",
"legendFormat": "p95 {{method}}",
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "histogram_quantile(0.99, sum(rate(furumi_grpc_request_duration_seconds_bucket[$__rate_interval])) by (le, method))",
"legendFormat": "p99 {{method}}",
"refId": "B"
}
],
"title": "gRPC Request Duration (p95, p99)",
"type": "timeseries",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 2,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "s"
},
"overrides": []
}
},
{
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 12
},
"id": 7,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "process_resident_memory_bytes / 1024 / 1024",
"legendFormat": "Resident Memory",
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "process_virtual_memory_bytes / 1024 / 1024",
"legendFormat": "Virtual Memory",
"refId": "B"
}
],
"title": "Process Memory Usage",
"type": "timeseries",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 15,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 2,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "green", "value": null }
]
},
"unit": "megbytes"
},
"overrides": []
}
},
{
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 12
},
"id": 8,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "process_open_fds",
"legendFormat": "Open FDs",
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "process_max_fds",
"legendFormat": "Max FDs",
"refId": "B"
}
],
"title": "Process File Descriptors",
"type": "timeseries",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 2,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "green", "value": null }
]
},
"unit": "short"
},
"overrides": []
}
}
],
"refresh": "10s",
"schemaVersion": 38,
"style": "dark",
"tags": [
"furumi-server",
"grpc"
],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "Prometheus",
"value": "Prometheus"
},
"hide": 0,
"includeAll": false,
"multi": false,
"name": "datasource",
"options": [],
"query": "prometheus",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"type": "datasource"
}
]
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Furumi Server Metrics",
"uid": "furumi-metrics",
"version": 1
}

View File

@@ -1,669 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: furumi-server-dashboard
labels:
grafana_dashboard: "1"
data:
furumi-server.json: |-
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [],
"liveNow": false,
"panels": [
{
"gridPos": {
"h": 4,
"w": 6,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calc": "lastNotNull",
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "sum(furumi_active_streams)",
"refId": "A"
}
],
"title": "Active Streams",
"type": "stat"
},
{
"gridPos": {
"h": 4,
"w": 6,
"x": 6,
"y": 0
},
"id": 2,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calc": "rate",
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "sum(rate(furumi_bytes_read_total[$__rate_interval]))",
"refId": "A"
}
],
"title": "Bytes Read / Sec",
"type": "stat",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "Bps"
},
"overrides": []
}
},
{
"gridPos": {
"h": 4,
"w": 6,
"x": 12,
"y": 0
},
"id": 3,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calc": "lastNotNull",
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "sum(increase(furumi_file_open_errors_total[$__rate_interval]))",
"refId": "A"
}
],
"title": "File Open Errors (Rate)",
"type": "stat",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "green", "value": null },
{ "color": "red", "value": 1 }
]
}
},
"overrides": []
}
},
{
"gridPos": {
"h": 4,
"w": 6,
"x": 18,
"y": 0
},
"id": 4,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calc": "lastNotNull",
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "sum(increase(furumi_auth_failures_total[$__rate_interval]))",
"refId": "A"
}
],
"title": "Auth Failures (Rate)",
"type": "stat",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "green", "value": null },
{ "color": "red", "value": 1 }
]
}
},
"overrides": []
}
},
{
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 4
},
"id": 5,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "sum by (method, status) (rate(furumi_grpc_requests_total[$__rate_interval]))",
"legendFormat": "{{method}} - {{status}}",
"refId": "A"
}
],
"title": "gRPC Request Rate by Method & Status",
"type": "timeseries",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 2,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "reqps"
},
"overrides": []
}
},
{
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 4
},
"id": 6,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "histogram_quantile(0.95, sum(rate(furumi_grpc_request_duration_seconds_bucket[$__rate_interval])) by (le, method))",
"legendFormat": "p95 {{method}}",
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "histogram_quantile(0.99, sum(rate(furumi_grpc_request_duration_seconds_bucket[$__rate_interval])) by (le, method))",
"legendFormat": "p99 {{method}}",
"refId": "B"
}
],
"title": "gRPC Request Duration (p95, p99)",
"type": "timeseries",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 2,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "s"
},
"overrides": []
}
},
{
"collapsed": true,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 12
},
"id": 99,
"panels": [
{
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 13
},
"id": 7,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "process_resident_memory_bytes / 1024 / 1024",
"legendFormat": "Resident Memory",
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "process_virtual_memory_bytes / 1024 / 1024",
"legendFormat": "Virtual Memory",
"refId": "B"
}
],
"title": "Process Memory Usage",
"type": "timeseries",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 15,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 2,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "green", "value": null }
]
},
"unit": "megbytes"
},
"overrides": []
}
},
{
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 13
},
"id": 8,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "10.0.0",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "process_open_fds",
"legendFormat": "Open FDs",
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "${datasource}"
},
"expr": "process_max_fds",
"legendFormat": "Max FDs",
"refId": "B"
}
],
"title": "Process File Descriptors",
"type": "timeseries",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 2,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{ "color": "green", "value": null }
]
},
"unit": "short"
},
"overrides": []
}
}
],
"title": "Process Metrics (Memory, FDs)",
"type": "row"
}
],
"refresh": "10s",
"schemaVersion": 38,
"style": "dark",
"tags": [
"furumi-server",
"grpc"
],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "Prometheus",
"value": "Prometheus"
},
"hide": 0,
"includeAll": false,
"multi": false,
"name": "datasource",
"options": [],
"query": "prometheus",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"type": "datasource"
}
]
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Furumi Server Metrics",
"uid": "furumi-metrics",
"version": 1
}

View File

@@ -20,7 +20,7 @@ data:
relativeTimeRange:
from: 600
to: 0
datasourceUid: prometheus
datasourceUid: P76F38748CEC837F0
model:
expr: 'rate(container_cpu_cfs_throttled_periods_total{container="pasarguard-node"}[5m])'
refId: A
@@ -45,7 +45,7 @@ data:
type: __expr__
uid: __expr__
expression: A
reducer: min
reducer: last
refId: B
type: reduce
noDataState: NoData
@@ -63,7 +63,7 @@ data:
- orgId: 1
name: kubernetes_alerts
folder: Kubernetes
interval: 2m
interval: 30s
rules:
- uid: node_not_ready
title: Kubernetes Node Not Ready
@@ -71,17 +71,17 @@ data:
data:
- refId: A
relativeTimeRange:
from: 600
from: 300
to: 0
datasourceUid: prometheus
datasourceUid: P76F38748CEC837F0
model:
expr: 'kube_node_status_condition{condition="Ready",status="false"}'
expr: 'kube_node_status_condition{condition="Ready",status="true"} == 0'
refId: A
intervalMs: 1000
maxDataPoints: 43200
- refId: B
relativeTimeRange:
from: 600
from: 300
to: 0
datasourceUid: __expr__
model:
@@ -98,248 +98,18 @@ data:
type: __expr__
uid: __expr__
expression: A
reducer: min
reducer: last
refId: B
type: reduce
noDataState: NoData
noDataState: Alerting
execErrState: Alerting
for: 10m
for: 0s
annotations:
node: '{{ $labels.node }}'
condition: '{{ $labels.condition }}'
summary: 'Kubernetes node is not ready'
labels:
severity: critical
- uid: node_high_memory_usage
title: High Node Memory Usage
condition: B
data:
- refId: A
relativeTimeRange:
from: 300
to: 0
datasourceUid: prometheus
model:
expr: '(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100'
refId: A
intervalMs: 1000
maxDataPoints: 43200
- refId: B
relativeTimeRange:
from: 300
to: 0
datasourceUid: __expr__
model:
conditions:
- evaluator:
params:
- 80
type: gt
operator:
type: and
query:
params: []
datasource:
type: __expr__
uid: __expr__
expression: A
reducer: max
refId: B
type: reduce
noDataState: NoData
execErrState: Alerting
for: 5m
annotations:
node: '{{ $labels.instance }}'
memory_usage: '{{ printf "%.1f%%" $values.A }}'
summary: 'Node memory usage is critically high'
labels:
severity: warning
- uid: node_high_cpu_usage
title: High Node CPU Usage
condition: B
data:
- refId: A
relativeTimeRange:
from: 300
to: 0
datasourceUid: prometheus
model:
expr: '100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)'
refId: A
intervalMs: 1000
maxDataPoints: 43200
- refId: B
relativeTimeRange:
from: 300
to: 0
datasourceUid: __expr__
model:
conditions:
- evaluator:
params:
- 80
type: gt
operator:
type: and
query:
params: []
datasource:
type: __expr__
uid: __expr__
expression: A
reducer: max
refId: B
type: reduce
noDataState: NoData
execErrState: Alerting
for: 10m
annotations:
node: '{{ $labels.instance }}'
cpu_usage: '{{ printf "%.1f%%" $values.A }}'
summary: 'Node CPU usage is critically high'
labels:
severity: warning
- uid: node_high_disk_usage
title: High Node Disk Usage
condition: B
data:
- refId: A
relativeTimeRange:
from: 300
to: 0
datasourceUid: prometheus
model:
expr: '(1 - (node_filesystem_avail_bytes{fstype=~"ext[234]|xfs|zfs|btrfs"} / node_filesystem_size_bytes)) * 100'
refId: A
intervalMs: 1000
maxDataPoints: 43200
- refId: B
relativeTimeRange:
from: 300
to: 0
datasourceUid: __expr__
model:
conditions:
- evaluator:
params:
- 85
type: gt
operator:
type: and
query:
params: []
datasource:
type: __expr__
uid: __expr__
expression: A
reducer: max
refId: B
type: reduce
noDataState: NoData
execErrState: Alerting
for: 5m
annotations:
node: '{{ $labels.instance }}'
filesystem: '{{ $labels.mountpoint }}'
disk_usage: '{{ printf "%.1f%%" $values.A }}'
summary: 'Node disk usage is critically high'
labels:
severity: critical
- uid: node_load_average_high
title: High Node Load Average
condition: B
data:
- refId: A
relativeTimeRange:
from: 300
to: 0
datasourceUid: prometheus
model:
expr: 'node_load15 / on(instance) group_left count by(instance)(node_cpu_seconds_total{mode="idle"})'
refId: A
intervalMs: 1000
maxDataPoints: 43200
- refId: B
relativeTimeRange:
from: 300
to: 0
datasourceUid: __expr__
model:
conditions:
- evaluator:
params:
- 2
type: gt
operator:
type: and
query:
params: []
datasource:
type: __expr__
uid: __expr__
expression: A
reducer: last
refId: B
type: reduce
noDataState: NoData
execErrState: Alerting
for: 15m
annotations:
node: '{{ $labels.instance }}'
load_average: '{{ printf "%.2f" $values.A }}'
summary: 'Node load average is critically high relative to CPU count'
labels:
severity: warning
- uid: node_exporter_down
title: Node Exporter Down
condition: B
data:
- refId: A
relativeTimeRange:
from: 300
to: 0
datasourceUid: prometheus
model:
expr: 'up{job="node-exporter"}'
refId: A
intervalMs: 1000
maxDataPoints: 43200
- refId: B
relativeTimeRange:
from: 300
to: 0
datasourceUid: __expr__
model:
conditions:
- evaluator:
params:
- 1
type: lt
operator:
type: and
query:
params: []
datasource:
type: __expr__
uid: __expr__
expression: A
reducer: min
refId: B
type: reduce
noDataState: NoData
execErrState: Alerting
for: 2m
annotations:
node: '{{ $labels.instance }}'
summary: 'Node exporter is down - unable to collect metrics'
labels:
severity: critical
contactpoints.yaml: |
apiVersion: 1
@@ -379,4 +149,4 @@ data:
- alertname
group_wait: 10s
group_interval: 5m
repeat_interval: 12h
repeat_interval: 4h

View File

@@ -0,0 +1,85 @@
envFromSecret: grafana-admin
nodeSelector:
kubernetes.io/hostname: master.tail2fe2d.ts.net
admin:
existingSecret: grafana-admin
userKey: username
passwordKey: password
grafana.ini:
auth:
signout_redirect_url: https://idm.hexor.cy/application/o/grafana/end-session/
# oauth_auto_login: true
auth.generic_oauth:
name: authentik
enabled: true
scopes: "openid profile email"
auth_url: https://idm.hexor.cy/application/o/authorize/
token_url: https://idm.hexor.cy/application/o/token/
api_url: https://idm.hexor.cy/application/o/userinfo/
role_attribute_path: >-
contains(groups, 'Grafana Admin') && 'Admin' ||
contains(groups, 'Grafana Editors') && 'Editor' ||
contains(groups, 'Grafana Viewer') && 'Viewer'
database:
type: postgres
host: psql.psql.svc:5432
name: grafana
user: grafana
ssl_mode: disable
datasources:
datasources.yaml:
apiVersion: 1
datasources:
- name: Prometheus Local
type: prometheus
url: http://prometheus-kube-prometheus-prometheus.prometheus.svc:9090
access: proxy
isDefault: true
- name: Loki
type: loki
url: http://loki-gateway.prometheus.svc:80
access: proxy
ingress:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt
traefik.ingress.kubernetes.io/router.middlewares: kube-system-https-redirect@kubernetescrd
hosts:
- gf.hexor.cy
tls:
- secretName: grafana-tls
hosts:
- '*.hexor.cy'
extraConfigmapMounts:
- name: grafana-alerting-rules
mountPath: /etc/grafana/provisioning/alerting/rules.yaml
configMap: grafana-alerting
subPath: rules.yaml
readOnly: true
- name: grafana-alerting-contactpoints
mountPath: /etc/grafana/provisioning/alerting/contactpoints.yaml
configMap: grafana-alerting
subPath: contactpoints.yaml
readOnly: true
- name: grafana-alerting-policies
mountPath: /etc/grafana/provisioning/alerting/policies.yaml
configMap: grafana-alerting
subPath: policies.yaml
readOnly: true
envValueFrom:
TELEGRAM_BOT_TOKEN:
secretKeyRef:
name: grafana-telegram
key: bot-token
TELEGRAM_CHAT_ID:
secretKeyRef:
name: grafana-telegram
key: chat-id

View File

@@ -5,8 +5,6 @@ resources:
- persistentVolume.yaml
- external-secrets.yaml
- grafana-alerting-configmap.yaml
- alertmanager-config.yaml
- furumi-dashboard-cm.yaml
helmCharts:
- name: kube-prometheus-stack
@@ -17,6 +15,14 @@ helmCharts:
valuesFile: prom-values.yaml
includeCRDs: true
- name: grafana
repo: https://grafana.github.io/helm-charts
version: 10.2.0
releaseName: grafana
namespace: prometheus
valuesFile: grafana-values.yaml
includeCRDs: true
- name: loki
repo: https://grafana.github.io/helm-charts
version: 6.29.0

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
grafana:
enabled: false
alertmanager:
config:
@@ -25,41 +26,11 @@ alertmanager:
{{ if .Annotations.description }}<b>Description:</b> {{ .Annotations.description }}{{ end }}
{{ end }}
ingress:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt
traefik.ingress.kubernetes.io/router.middlewares: kube-system-https-redirect@kubernetescrd
hosts:
- prom.hexor.cy
paths:
- /alertmanager
tls:
- secretName: alertmanager-tls
hosts:
- prom.hexor.cy
alertmanagerSpec:
secrets:
- alertmanager-telegram-secret
externalUrl: https://prom.hexor.cy/alertmanager
routePrefix: /alertmanager
prometheus:
ingress:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt
traefik.ingress.kubernetes.io/router.middlewares: kube-system-https-redirect@kubernetescrd
hosts:
- prom.hexor.cy
paths:
- /
tls:
- secretName: prometheus-tls
hosts:
- prom.hexor.cy
prometheusSpec:
enableRemoteWriteReceiver: true
additionalScrapeConfigs:
@@ -91,88 +62,3 @@ prometheus:
requests:
storage: 400Gi
grafana:
enabled: true
serviceAccount:
create: true
name: "prom-grafana-sa"
envFromSecret: grafana-admin
nodeSelector:
kubernetes.io/hostname: master.tail2fe2d.ts.net
admin:
existingSecret: grafana-admin
userKey: username
passwordKey: password
grafana.ini:
auth:
signout_redirect_url: https://idm.hexor.cy/application/o/grafana/end-session/
auth.generic_oauth:
name: authentik
enabled: true
scopes: "openid profile email"
auth_url: https://idm.hexor.cy/application/o/authorize/
token_url: https://idm.hexor.cy/application/o/token/
api_url: https://idm.hexor.cy/application/o/userinfo/
role_attribute_path: >-
contains(groups, 'Grafana Admin') && 'Admin' ||
contains(groups, 'Grafana Editors') && 'Editor' ||
contains(groups, 'Grafana Viewer') && 'Viewer'
database:
type: postgres
host: psql.psql.svc:5432
name: grafana
user: grafana
ssl_mode: disable
# The Loki datasource config needs to be preserved,
# but instead of "datasources.datasources.yaml", we define it like this for the prometheus-stack chart:
additionalDataSources:
- name: Loki
type: loki
url: http://loki-gateway.prometheus.svc:80
access: proxy
orgId: 1
ingress:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt
traefik.ingress.kubernetes.io/router.middlewares: kube-system-https-redirect@kubernetescrd
hosts:
- gf.hexor.cy
tls:
- secretName: grafana-tls
hosts:
- '*.hexor.cy'
extraConfigmapMounts:
- name: grafana-alerting-rules
mountPath: /etc/grafana/provisioning/alerting/rules.yaml
configMap: grafana-alerting
subPath: rules.yaml
readOnly: true
- name: grafana-alerting-contactpoints
mountPath: /etc/grafana/provisioning/alerting/contactpoints.yaml
configMap: grafana-alerting
subPath: contactpoints.yaml
readOnly: true
- name: grafana-alerting-policies
mountPath: /etc/grafana/provisioning/alerting/policies.yaml
configMap: grafana-alerting
subPath: policies.yaml
readOnly: true
envValueFrom:
TELEGRAM_BOT_TOKEN:
secretKeyRef:
name: grafana-telegram
key: bot-token
TELEGRAM_CHAT_ID:
secretKeyRef:
name: grafana-telegram
key: chat-id

View File

@@ -1,563 +0,0 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
namespace: minecraft
data:
nginx.conf: |
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80;
# Custom 502 error page with auto-refresh
error_page 502 /502.html;
location = /502.html {
internal;
return 200 '<!DOCTYPE html><html><head><meta charset="utf-8"><title>Server Loading</title><style>body{font-family:Arial,sans-serif;text-align:center;margin-top:100px;background:#f0f0f0}h1{color:#333}p{color:#666;font-size:18px}</style></head><body><h1>Server is loading probably...</h1><p>Please wait a moment and try refreshing the page.</p><script>setTimeout(function(){window.location.reload();}, 10000);</script></body></html>';
add_header Content-Type text/html;
}
# Main location - proxy to Minecraft Dynmap
location / {
# Proxy configuration for Dynmap server
proxy_pass http://localhost:8123;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Inject user authentication meta tag into HTML head
sub_filter '<head>' '<head><meta name="remote-user" content="$http_x_authentik_username">';
# Replace default Dynmap title with custom server name
sub_filter 'Minecraft Dynamic Map' "Hexor's MC server";
# Inject custom script before closing body tag
sub_filter "</body>" '<script src="/clients/inject.js"></script></body>';
# Apply sub_filter replacements globally (not just once)
sub_filter_once off;
}
# Serve inject.js and .ps1 scripts inline (no forced download)
location = /clients/inject.js {
alias /mc/clients/inject.js;
default_type application/javascript;
}
location ~ ^/clients/(.+\.ps1)$ {
alias /mc/clients/$1;
default_type text/plain;
}
# Static file serving for client downloads
location /clients/ {
alias /mc/clients/;
sendfile on; # Enable efficient file serving
add_header Content-Disposition "attachment"; # Force download
autoindex on; # Enable directory listing
gzip off; # Disable compression for downloads
chunked_transfer_encoding off; # Disable chunked encoding
}
}
}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: client-scripts
namespace: minecraft
data:
inject.js: |
(function() {
function getUsername() {
var metas = document.querySelectorAll("meta");
for (var i = 0; i < metas.length; i++) {
if (metas[i].getAttribute("name") === "remote-user") {
return metas[i].getAttribute("content");
}
}
var jwt = document.cookie.split("; ").find(function(row) {
return row.startsWith("authentik_session=");
});
if (jwt) {
try {
var token = jwt.split("=")[1];
var payload = JSON.parse(atob(token.split(".")[1]));
return payload.sub || payload.username || "web-user";
} catch(e) {}
}
return "web-user";
}
var username = getUsername();
console.log("Username found:", username);
if (username && username !== "web-user" &&
window.location.search.indexOf("playername=") === -1) {
var currentUrl = new URL(window.location.href);
currentUrl.searchParams.set("playername", username);
console.log("Redirecting to:", currentUrl.href);
window.location.href = currentUrl.href;
}
document.addEventListener("DOMContentLoaded", function() {
// User block
var userBlock = document.createElement("div");
userBlock.style.cssText = "background-color:#CEC6CB;color:black;padding:8px;text-align:center;font-size:medium;border-radius:4px;position:absolute;top:10px;right:150px;max-width:200px;";
userBlock.innerHTML = "Logged in as: <b>" + username + "</b>";
document.body.appendChild(userBlock);
// Info block
var infoBlock = document.createElement("p");
infoBlock.style.cssText = "background-color:#CEC6CB;color:black;padding:10px;text-align:center;font-size:large;display:inline-block;border-radius:4px;position:absolute;top:10px;left:150px;";
infoBlock.innerHTML = 'GEYMERSKIY SOYUZ Server<br>'
+ 'Get <a href="https://github.com/PrismLauncher/PrismLauncher/releases/tag/8.4">Prism Launcher</a> '
+ 'and <a href="/clients/1.12.2.zip">client.zip</a> for this server. '
+ 'Server address <b>minecraft.hexor.cy:30565</b><br>'
+ 'Requires <a href="https://www.java.com/en/download/manual.jsp">Java 8</a><br><br>'
+ '<a href="#" id="showInstallBtn" style="color:black;text-decoration:underline;">Windows Install Script</a>';
document.body.appendChild(infoBlock);
// Modal
var modal = document.createElement("div");
modal.id = "installModal";
modal.style.cssText = "display:none;position:fixed;z-index:1000;left:0;top:0;width:100%;height:100%;background-color:rgba(0,0,0,0.5);";
modal.innerHTML = '<div style="background-color:#CEC6CB;margin:15% auto;padding:10px;border-radius:4px;width:70%;max-width:500px;text-align:center;color:black;font-size:large;">'
+ '<h3 style="margin-top:0;color:black;">Windows Installation</h3>'
+ '<p style="color:black;">Copy and paste this command into PowerShell:</p>'
+ '<textarea id="scriptCommand" readonly style="width:90%;height:60px;font-family:monospace;padding:8px;border:1px solid #888;border-radius:4px;resize:none;background-color:white;color:black;"></textarea>'
+ '<br><br>'
+ '<button id="copyButton" style="background-color:#CEC6CB;color:black;padding:10px 15px;border:1px solid #888;border-radius:4px;cursor:pointer;margin-right:10px;font-size:large;">Copy</button>'
+ '<button id="closeButton" style="background-color:#CEC6CB;color:black;padding:10px 15px;border:1px solid #888;border-radius:4px;cursor:pointer;font-size:large;">Close</button>'
+ '</div>';
document.body.appendChild(modal);
// Generate PowerShell command with username
function buildPsCommand() {
var d = "$";
var q = "'";
return d + 'f="' + d + 'env:TEMP\\mc-install.ps1"; iwr -useb https://minecraft.hexor.cy/clients/win-install.ps1 -OutFile '
+ d + 'f; powershell -ExecutionPolicy Bypass -File ' + d + 'f -Username ' + q + username + q + '; Remove-Item ' + d + 'f';
}
document.getElementById("showInstallBtn").addEventListener("click", function(e) {
e.preventDefault();
modal.style.display = "block";
document.getElementById("scriptCommand").value = buildPsCommand();
});
document.getElementById("closeButton").addEventListener("click", function() {
modal.style.display = "none";
});
document.getElementById("copyButton").addEventListener("click", function() {
var textarea = document.getElementById("scriptCommand");
textarea.select();
textarea.setSelectionRange(0, 99999);
document.execCommand("copy");
var btn = document.getElementById("copyButton");
btn.style.borderColor = "#4CAF50";
setTimeout(function() { btn.style.borderColor = "#888"; }, 2000);
});
modal.addEventListener("click", function(event) {
if (event.target === modal) {
modal.style.display = "none";
}
});
});
})();
win-install.ps1: |
# Game Setup Script for PrismLauncher and Minecraft Client
# This script downloads and configures PrismLauncher with Hexor client
param(
[string]$Username = "",
[string]$InstallPath = "$env:USERPROFILE\Games\PrismLauncher"
)
# Enable TLS 1.2 for downloads
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# Function to write colored log messages
function Write-Log {
param(
[string]$Message,
[string]$Level = "INFO"
)
switch ($Level) {
"INFO" {
Write-Host "[" -NoNewline
Write-Host "INFO" -ForegroundColor Blue -NoNewline
Write-Host "] $Message"
}
"WARN" {
Write-Host "[" -NoNewline
Write-Host "WARN" -ForegroundColor Yellow -NoNewline
Write-Host "] $Message"
}
"ERROR" {
Write-Host "[" -NoNewline
Write-Host "ERROR" -ForegroundColor Red -NoNewline
Write-Host "] $Message"
}
"SUCCESS" {
Write-Host "[" -NoNewline
Write-Host "SUCCESS" -ForegroundColor Green -NoNewline
Write-Host "] $Message"
}
}
}
# Function to download file with progress
function Download-File {
param(
[string]$Url,
[string]$OutputPath
)
try {
Write-Log "Downloading from: $Url"
Write-Log "Saving to: $OutputPath"
if (Test-Path $OutputPath) {
Remove-Item $OutputPath -Force
Write-Log "Removed existing file to avoid corruption" "WARN"
}
$webClient = New-Object System.Net.WebClient
$webClient.DownloadFile($Url, $OutputPath)
if (Test-Path $OutputPath) {
$fileSize = (Get-Item $OutputPath).Length
Write-Log "Download completed successfully ($fileSize bytes)" "SUCCESS"
return $true
} else {
Write-Log "Download failed - file not found" "ERROR"
return $false
}
}
catch {
Write-Log "Download failed: $($_.Exception.Message)" "ERROR"
return $false
}
}
# Function to extract ZIP archive
function Extract-Archive {
param(
[string]$ArchivePath,
[string]$DestinationPath
)
try {
Write-Log "Extracting archive: $ArchivePath"
Write-Log "Destination: $DestinationPath"
if (!(Test-Path $ArchivePath)) {
Write-Log "Archive file not found: $ArchivePath" "ERROR"
return $false
}
$fileSize = (Get-Item $ArchivePath).Length
if ($fileSize -lt 1000) {
Write-Log "Archive file too small ($fileSize bytes), probably corrupted" "ERROR"
return $false
}
if (!(Test-Path $DestinationPath)) {
New-Item -ItemType Directory -Path $DestinationPath -Force | Out-Null
}
$existingFiles = Get-ChildItem -Path $DestinationPath -File | Where-Object { $_.Extension -ne ".zip" }
$existingDirs = Get-ChildItem -Path $DestinationPath -Directory
if ($existingFiles.Count -gt 0 -or $existingDirs.Count -gt 0) {
Write-Log "Clearing existing files in destination directory" "WARN"
$existingFiles | Remove-Item -Force -ErrorAction SilentlyContinue
$existingDirs | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
}
Add-Type -AssemblyName System.IO.Compression.FileSystem
try {
$zip = [System.IO.Compression.ZipFile]::OpenRead($ArchivePath)
$entryCount = $zip.Entries.Count
$zip.Dispose()
Write-Log "ZIP file validated ($entryCount entries)" "SUCCESS"
}
catch {
Write-Log "ZIP file validation failed: $($_.Exception.Message)" "ERROR"
return $false
}
[System.IO.Compression.ZipFile]::ExtractToDirectory($ArchivePath, $DestinationPath)
Write-Log "Archive extracted successfully" "SUCCESS"
return $true
}
catch {
Write-Log "Archive extraction failed: $($_.Exception.Message)" "ERROR"
if (Test-Path $ArchivePath) {
Write-Log "Removing potentially corrupted archive file" "WARN"
Remove-Item $ArchivePath -Force -ErrorAction SilentlyContinue
}
return $false
}
}
# Function to check Java installation
function Check-JavaInstallation {
try {
$javaVersion = java -version 2>&1 | Select-String "version"
if ($javaVersion) {
Write-Log "Java is installed: $($javaVersion.ToString().Trim())" "SUCCESS"
return $true
}
}
catch {
Write-Log "Java is not installed or not in PATH" "WARN"
Write-Log "Please download Java from: https://www.java.com/en/download/manual.jsp" "WARN"
Write-Log "Look for 'Windows Offline (64-bit)' version" "WARN"
$response = Read-Host "Do you want to continue without Java? (y/n)"
if ($response -eq 'y' -or $response -eq 'Y') {
Write-Log "Continuing without Java verification" "WARN"
return $true
} else {
Write-Log "Installation cancelled. Please install Java first." "ERROR"
return $false
}
}
}
# Function to generate a random hex string (UUID-like without dashes)
function New-RandomHexId {
param([int]$Length = 32)
$bytes = New-Object byte[] ($Length / 2)
$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
$rng.GetBytes($bytes)
$rng.Dispose()
return ($bytes | ForEach-Object { $_.ToString("x2") }) -join ''
}
# Function to create accounts.json file with offline account
function Create-AccountsFile {
param(
[string]$TargetPath,
[string]$PlayerName
)
$accountsPath = Join-Path $TargetPath "accounts.json"
$profileId = New-RandomHexId -Length 32
$clientToken = New-RandomHexId -Length 32
$iat = [int][double]::Parse((Get-Date -UFormat %s))
$accountsObj = @{
accounts = @(
@{
entitlement = @{
canPlayMinecraft = $true
ownsMinecraft = $true
}
"msa-client-id" = ""
type = "MSA"
},
@{
active = $true
entitlement = @{
canPlayMinecraft = $true
ownsMinecraft = $true
}
profile = @{
capes = @()
id = $profileId
name = $PlayerName
skin = @{
id = ""
url = ""
variant = ""
}
}
type = "Offline"
ygg = @{
extra = @{
clientToken = $clientToken
userName = $PlayerName
}
iat = $iat
token = "0"
}
}
)
formatVersion = 3
}
try {
$accountsObj | ConvertTo-Json -Depth 10 | Out-File -FilePath $accountsPath -Encoding UTF8
Write-Log "Created accounts.json at: $accountsPath" "SUCCESS"
Write-Log "Player name: $PlayerName" "SUCCESS"
return $true
}
catch {
Write-Log "Failed to create accounts.json: $($_.Exception.Message)" "ERROR"
return $false
}
}
# Main installation process
Write-Log "Starting PrismLauncher and Minecraft client setup"
Write-Log "Player name: $Username"
Write-Log "Installation path: $InstallPath"
# Create installation directory and clear if exists (but keep ZIP files)
if (Test-Path $InstallPath) {
Write-Log "Installation directory exists, clearing contents" "WARN"
$existingFiles = Get-ChildItem -Path $InstallPath -File | Where-Object { $_.Extension -ne ".zip" }
$existingDirs = Get-ChildItem -Path $InstallPath -Directory
$existingFiles | Remove-Item -Force -ErrorAction SilentlyContinue
$existingDirs | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
} else {
New-Item -ItemType Directory -Path $InstallPath -Force | Out-Null
Write-Log "Created installation directory: $InstallPath"
}
# Step 1: Download PrismLauncher
Write-Log "Step 1: Downloading PrismLauncher..."
$launcherUrl = "https://github.com/PrismLauncher/PrismLauncher/releases/download/8.4/PrismLauncher-Windows-MSVC-Portable-8.4.zip"
$launcherZip = Join-Path $InstallPath "PrismLauncher-8.4.zip"
$launcherExtractPath = Join-Path $InstallPath "PrismLauncher-Windows-MSVC-Portable-8.4"
if (!(Download-File -Url $launcherUrl -OutputPath $launcherZip)) {
Write-Log "Failed to download PrismLauncher. Exiting." "ERROR"
exit 1
}
# Step 2: Extract PrismLauncher
Write-Log "Step 2: Extracting PrismLauncher..."
if (!(Extract-Archive -ArchivePath $launcherZip -DestinationPath $InstallPath)) {
Write-Log "Failed to extract PrismLauncher. Exiting." "ERROR"
exit 1
}
# Find the actual extracted directory
$extractedDirs = Get-ChildItem -Path $InstallPath -Directory | Where-Object { $_.Name -like "*PrismLauncher*" }
if ($extractedDirs.Count -gt 0) {
$launcherExtractPath = $extractedDirs[0].FullName
Write-Log "Found PrismLauncher directory: $launcherExtractPath" "SUCCESS"
} else {
Write-Log "Could not find extracted PrismLauncher directory. Checking for direct extraction..." "WARN"
$prismExe = Join-Path $InstallPath "prismlauncher.exe"
if (Test-Path $prismExe) {
$launcherExtractPath = $InstallPath
Write-Log "PrismLauncher extracted directly to: $launcherExtractPath" "SUCCESS"
} else {
Write-Log "Failed to locate PrismLauncher files. Exiting." "ERROR"
exit 1
}
}
# Step 3: Create accounts.json in InstallPath (only if username provided)
if ($Username -and $Username -ne "web-user") {
Write-Log "Step 3: Creating accounts configuration for $Username..."
if (!(Create-AccountsFile -TargetPath $InstallPath -PlayerName $Username)) {
Write-Log "Failed to create accounts.json. Exiting." "ERROR"
exit 1
}
} else {
Write-Log "Step 3: No username provided, skipping accounts.json creation" "WARN"
}
# Step 4: Check Java installation
Write-Log "Step 4: Checking Java installation..."
if (!(Check-JavaInstallation)) {
Write-Log "Java check failed. Exiting." "ERROR"
exit 1
}
# Step 5: Download Minecraft client
Write-Log "Step 5: Downloading Minecraft client..."
$minecraftUrl = "https://minecraft.hexor.cy/clients/1.12.2.zip"
$minecraftZip = Join-Path $InstallPath "minecraft-1.12.2.zip"
$instancesPath = Join-Path $launcherExtractPath "instances"
$hexorPath = Join-Path $instancesPath "Hexor"
if (!(Download-File -Url $minecraftUrl -OutputPath $minecraftZip)) {
Write-Log "Failed to download Minecraft client. Exiting." "ERROR"
exit 1
}
# Step 6: Create instances directory and extract Minecraft client
Write-Log "Step 6: Setting up Minecraft client..."
if (!(Test-Path $instancesPath)) {
New-Item -ItemType Directory -Path $instancesPath -Force | Out-Null
Write-Log "Created instances directory"
}
if (!(Test-Path $hexorPath)) {
New-Item -ItemType Directory -Path $hexorPath -Force | Out-Null
Write-Log "Created Hexor instance directory"
}
if (!(Extract-Archive -ArchivePath $minecraftZip -DestinationPath $hexorPath)) {
Write-Log "Failed to extract Minecraft client. Exiting." "ERROR"
exit 1
}
# Step 7: Cleanup temporary files
Write-Log "Step 7: Cleaning up temporary files..."
try {
Remove-Item $launcherZip -Force
Remove-Item $minecraftZip -Force
Write-Log "Temporary files cleaned up" "SUCCESS"
}
catch {
Write-Log "Could not remove temporary files: $($_.Exception.Message)" "WARN"
}
# Final success message
Write-Log "=== INSTALLATION COMPLETED SUCCESSFULLY ===" "SUCCESS"
Write-Log "PrismLauncher location: $launcherExtractPath" "SUCCESS"
Write-Log "Executable: $(Join-Path $launcherExtractPath 'prismlauncher.exe')" "SUCCESS"
Write-Log "Minecraft client installed in: $hexorPath" "SUCCESS"
if ($Username -and $Username -ne "web-user") {
Write-Log "Player name configured: $Username" "SUCCESS"
Write-Log "Accounts file: $(Join-Path $InstallPath 'accounts.json')" "SUCCESS"
}
Write-Log ""
Write-Log "You can now run PrismLauncher and the Hexor instance should be available!"
Write-Log "If Java was not installed, please download it from: https://www.java.com/en/download/manual.jsp"
# Ask if user wants to launch the game
$launchResponse = Read-Host "Do you want to launch PrismLauncher now? (y/n)"
if ($launchResponse -eq 'y' -or $launchResponse -eq 'Y') {
$launcherExe = Join-Path $launcherExtractPath "prismlauncher.exe"
if (Test-Path $launcherExe) {
Write-Log "Launching PrismLauncher..." "INFO"
Start-Process -FilePath $launcherExe -WorkingDirectory $launcherExtractPath
} else {
Write-Log "Launcher executable not found!" "ERROR"
}
}

View File

@@ -1,3 +1,69 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
namespace: minecraft
data:
nginx.conf: |
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80;
# Custom 502 error page with auto-refresh
error_page 502 /502.html;
location = /502.html {
internal;
return 200 '<!DOCTYPE html><html><head><meta charset="utf-8"><title>Server Loading</title><style>body{font-family:Arial,sans-serif;text-align:center;margin-top:100px;background:#f0f0f0}h1{color:#333}p{color:#666;font-size:18px}</style></head><body><h1>Server is loading probably...</h1><p>Please wait a moment and try refreshing the page.</p><script>setTimeout(function(){window.location.reload();}, 10000);</script></body></html>';
add_header Content-Type text/html;
}
# Main location - proxy to Minecraft Dynmap
location / {
# Proxy configuration for Dynmap server
proxy_pass http://localhost:8123;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Inject user authentication meta tag into HTML head
sub_filter '<head>' '<head><meta name="remote-user" content="$http_x_authentik_username">';
# Replace default Dynmap title with custom server name
sub_filter 'Minecraft Dynamic Map' "Hexor's MC server";
# Inject all custom content before closing body tag (single replacement)
sub_filter "</body>" '<script>function getUsername(){var headers=document.querySelectorAll("meta");for(var i=0;i<headers.length;i++){if(headers[i].getAttribute("name")==="remote-user"){return headers[i].getAttribute("content");}}var jwt=document.cookie.split("; ").find(row=>row.startsWith("authentik_session="));if(jwt){try{var token=jwt.split("=")[1];var payload=JSON.parse(atob(token.split(".")[1]));return payload.sub||payload.username||"web-user";}catch(e){}}return "web-user";}var username=getUsername();console.log("Username found:", username);if(username && username!=="web-user" && window.location.search.indexOf("playername=")===-1){var currentUrl=new URL(window.location.href);currentUrl.searchParams.set("playername",username);console.log("Redirecting to:", currentUrl.href);window.location.href=currentUrl.href;}document.addEventListener("DOMContentLoaded",function(){var userBlock=document.createElement("div");userBlock.style.cssText="background-color: #CEC6CB; color: black; padding: 8px; text-align: center; font-size: medium; border-radius: 4px; position: absolute; top: 10px; right: 150px; max-width: 200px;";userBlock.innerHTML="Logged in as: <b>"+username+"</b>";document.body.appendChild(userBlock);});</script><p style="background-color: #CEC6CB; color: black; padding: 10px 10px; text-align: center; font-size: large; text-decoration: none; display: inline-block; border-radius: 4px; position: absolute; top: 10px; left: 150px;">GEYMERSKIY SOYUZ Server <br>Get <a href="https://github.com/PrismLauncher/PrismLauncher/releases/tag/8.4" >Prism Launcher</a> and <a href="/clients/1.12.2.zip" >client.zip</a> for this server. Server address <b>minecraft.hexor.cy:30565</b><br><br><a href="#" onclick="showInstallModal(); return false;" style="color: black; text-decoration: underline;">Windows Install Script</a></p><div id="installModal" style="display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5);"><div style="background-color: #CEC6CB; margin: 15% auto; padding: 10px; border-radius: 4px; width: 70%; max-width: 500px; text-align: center; color: black; font-size: large;"><h3 style="margin-top: 0; color: black;">Windows Installation</h3><p style="color: black;">Copy and paste this command into PowerShell:</p><textarea id="scriptCommand" readonly style="width: 90%; height: 60px; font-family: monospace; padding: 8px; border: 1px solid #888; border-radius: 4px; resize: none; background-color: white; color: black;">iwr -useb https://minecraft.hexor.cy/clients/win-install.ps1 | iex</textarea><br><br><button id="copyButton" onclick="copyToClipboard()" style="background-color: #CEC6CB; color: black; padding: 10px 15px; border: 1px solid #888; border-radius: 4px; cursor: pointer; margin-right: 10px; font-size: large; text-decoration: none;">Copy</button><button onclick="closeInstallModal()" style="background-color: #CEC6CB; color: black; padding: 10px 15px; border: 1px solid #888; border-radius: 4px; cursor: pointer; font-size: large; text-decoration: none;">Close</button></div></div><script>function showInstallModal() { document.getElementById("installModal").style.display = "block"; } function closeInstallModal() { document.getElementById("installModal").style.display = "none"; } function copyToClipboard() { var textarea = document.getElementById("scriptCommand"); textarea.select(); textarea.setSelectionRange(0, 99999); if (document.execCommand("copy")) { var button = document.getElementById("copyButton"); button.style.borderColor = "#4CAF50"; setTimeout(function() { button.style.borderColor = "#888"; }, 2000); } } window.onclick = function(event) { var modal = document.getElementById("installModal"); if (event.target == modal) { closeInstallModal(); } }</script></body>';
# Apply sub_filter replacements globally (not just once)
sub_filter_once off;
}
# Static file serving for client downloads
location /clients/ {
alias /mc/clients/;
sendfile on; # Enable efficient file serving
add_header Content-Disposition "attachment"; # Force download
autoindex on; # Enable directory listing
gzip off; # Disable compression for downloads
chunked_transfer_encoding off; # Disable chunked encoding
}
}
}
---
apiVersion: apps/v1
kind: Deployment
@@ -28,9 +94,6 @@ spec:
- name: nginx-config
configMap:
name: nginx-config
- name: client-scripts
configMap:
name: client-scripts
terminationGracePeriodSeconds: 10
containers:
@@ -79,22 +142,6 @@ spec:
- name: webstatus-mod
containerPort: 8080
protocol: TCP
livenessProbe:
httpGet:
path: /
port: 8123
initialDelaySeconds: 120
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /
port: 8123
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
volumeMounts:
- name: storage
mountPath: /mc
@@ -115,12 +162,6 @@ spec:
subPath: nginx.conf
- name: storage
mountPath: /mc
- name: client-scripts
mountPath: /mc/clients/win-install.ps1
subPath: win-install.ps1
- name: client-scripts
mountPath: /mc/clients/inject.js
subPath: inject.js
---
apiVersion: v1

View File

@@ -5,4 +5,4 @@ resources:
- app.yaml
- deployments.yaml
- services.yaml
- configmaps.yaml
#- ingress.yaml

View File

@@ -8,16 +8,9 @@
"Bash(\"C:\\Users\\ab\\AppData\\Local\\Microsoft\\WinGet\\Packages\\Hashicorp.Terraform_Microsoft.Winget.Source_8wekyb3d8bbwe\\terraform.exe\" apply -auto-approve -lock=false)",
"Bash(\"C:\\Users\\ab\\AppData\\Local\\Microsoft\\WinGet\\Packages\\Hashicorp.Terraform_Microsoft.Winget.Source_8wekyb3d8bbwe\\terraform.exe\" plan -lock=false)",
"Bash(\"C:\\Users\\ab\\AppData\\Local\\Microsoft\\WinGet\\Packages\\Hashicorp.Terraform_Microsoft.Winget.Source_8wekyb3d8bbwe\\terraform.exe\" apply -replace=\"authentik_outpost.outposts[\"\"kubernetes-outpost\"\"]\" -auto-approve -lock=false)",
"Bash(terraform plan:*)",
"Bash(terraform state:*)",
"Bash(TF_VAR_authentik_token=ZDTbu4OKl0UcmdYKG5XgkRThZO7vWX2xz0w5vP2d8sudIr44ccwKOby6iRUa terraform plan:*)",
"Bash(TF_VAR_authentik_token=ZDTbu4OKl0UcmdYKG5XgkRThZO7vWX2xz0w5vP2d8sudIr44ccwKOby6iRUa terraform force-unlock:*)",
"Bash(git:*)",
"Bash(TF_VAR_authentik_token=ZDTbu4OKl0UcmdYKG5XgkRThZO7vWX2xz0w5vP2d8sudIr44ccwKOby6iRUa terraform state:*)",
"Bash(terraform version:*)",
"Bash(curl:*)"
"Bash(terraform plan:*)"
],
"deny": [],
"ask": []
}
}
}

View File

@@ -2,43 +2,43 @@
# Manual edits may be lost in future updates.
provider "registry.terraform.io/goauthentik/authentik" {
version = "2025.12.1"
constraints = ">= 2023.10.0, 2025.12.1"
version = "2025.8.1"
constraints = ">= 2023.10.0, 2025.8.1"
hashes = [
"h1:p9AGeRqK50wTHEIp7z7O4MUP83cs+lt7wPajZ9m9TB8=",
"zh:0e856d3b13614bc32346a236a8e84ba55ecd17238c2008d4b3e71aa8cb49f515",
"zh:2dcc44cd499c18ebbc4f763eff97a7b725763c8ac8fbb5d69c935413ccdc4962",
"zh:434100fc75ec7cd6b64cc9497e8273e79325fa8d285e9fd9d341c1a67421643b",
"zh:483484f66d2e8ce6fa4bfd91e824ceebf07d10acb5df5f366397c55227c4ae91",
"zh:596743a6f1c77a6f103b06ef8d932fe8f2376793b92478853dc84571d17c429f",
"zh:5ed2d5eb7db13229baaf042c725d5c64b58ffdcc641370175e0a88900af94bf1",
"zh:8aecd4cf782c82bee01098f72fe4ffff83707516007b32a01c7fcb19a9260338",
"zh:928c05ecac309287ff7d73ed6e478350fe3003557658ae5dc2be817a4268dba7",
"zh:9b9fd36dfb3e75da8b4478485272505ae9a3c67b10db173e1d2d76cfe2b637b8",
"zh:ab7cd8c61ab67a045854e32f0be1940a92746770dbf3c17bbe923e0259c4f897",
"zh:bb1360ec19a4fc1095d0ef1b7b6c5c3c1a91daac7cd1957d43a4cdbb7356a2e3",
"zh:d2186f4063aa1a547b52a53745d472e43f5343bc1674f2bbb91421c61b0fab50",
"zh:d74bbb67a77951b18ffd7b2863954e70ac03450ad2023cc305c66a5ff25d8d18",
"zh:f5970569ea0a479bbfbf2d452f5962e1c9bd472b82756db822d0e951363daa25",
"h1:R3h8ADB0Kkv/aoY0AaHkBiX2/P4+GnW8sSgkN30kJfQ=",
"zh:0c3f1083fd48f20ed06959401ff1459fbb5d454d81c8175b5b6d321b308c0be3",
"zh:21c6d93f8d26e688da38a660d121b5624e3597c426c671289f31a17a9771abbf",
"zh:301b5763ffc4c5fe47aa7e851ce0b19f71bab4fae5c81003ad81b38775e85f78",
"zh:4f7ee6473f6a687340538ddac0ec4a0453664186b15fdb0bb2fb5fcd8fb3ad30",
"zh:7927f4f634c9e072d4aa6620d09e97dc83eeb1dbd0667102086779cd5fc495c1",
"zh:84e7c2a3f3de721a54abe4c971d9a163127f5e4af91d023260fea305ac74bcf4",
"zh:92af52aaac518c426164eb731d282f51a5825e64e6a02b0695952177a7af7d9c",
"zh:a6920a54d5df69342f4ea2d903676145b00e7375d2f2eecc0840858d83b3b4a8",
"zh:ac8a60801fc55fd05b3471778f908ed43072e046997c0082644c9602b84dafec",
"zh:b1cc29e2878aa94a3827fd5e1dd8cffb98397aa4093d6a4852c6e53157e9b35f",
"zh:c2d78f308c4d70a16ef4f6d1f4822a64f8f160d0a207f2121904cdd6f4942db4",
"zh:ca970e5776f408059a84b4e17f6ac257ec92afae956be74f3807c548e4567eaa",
"zh:eb2e3650ee0eec033207b6d72fcb938dc5846c6feb8a61ae30d61981ea411269",
"zh:fcb93e51c84ba592bc2b075d7342e475126e5029620959666999b5b1bd11cb98",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.8.1"
version = "3.7.2"
constraints = ">= 3.5.0"
hashes = [
"h1:u8AKlWVDTH5r9YLSeswoVEjiY72Rt4/ch7U+61ZDkiQ=",
"zh:08dd03b918c7b55713026037c5400c48af5b9f468f483463321bd18e17b907b4",
"zh:0eee654a5542dc1d41920bbf2419032d6f0d5625b03bd81339e5b33394a3e0ae",
"zh:229665ddf060aa0ed315597908483eee5b818a17d09b6417a0f52fd9405c4f57",
"zh:2469d2e48f28076254a2a3fc327f184914566d9e40c5780b8d96ebf7205f8bc0",
"zh:37d7eb334d9561f335e748280f5535a384a88675af9a9eac439d4cfd663bcb66",
"zh:741101426a2f2c52dee37122f0f4a2f2d6af6d852cb1db634480a86398fa3511",
"h1:356j/3XnXEKr9nyicLUufzoF4Yr6hRy481KIxRVpK0c=",
"zh:14829603a32e4bc4d05062f059e545a91e27ff033756b48afbae6b3c835f508f",
"zh:1527fb07d9fea400d70e9e6eb4a2b918d5060d604749b6f1c361518e7da546dc",
"zh:1e86bcd7ebec85ba336b423ba1db046aeaa3c0e5f921039b3f1a6fc2f978feab",
"zh:24536dec8bde66753f4b4030b8f3ef43c196d69cccbea1c382d01b222478c7a3",
"zh:29f1786486759fad9b0ce4fdfbbfece9343ad47cd50119045075e05afe49d212",
"zh:4d701e978c2dd8604ba1ce962b047607701e65c078cb22e97171513e9e57491f",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:a902473f08ef8df62cfe6116bd6c157070a93f66622384300de235a533e9d4a9",
"zh:b85c511a23e57a2147355932b3b6dce2a11e856b941165793a0c3d7578d94d05",
"zh:c5172226d18eaac95b1daac80172287b69d4ce32750c82ad77fa0768be4ea4b8",
"zh:dab4434dba34aad569b0bc243c2d3f3ff86dd7740def373f2a49816bd2ff819b",
"zh:f49fd62aa8c5525a5c17abd51e27ca5e213881d58882fd42fec4a545b53c9699",
"zh:7b8434212eef0f8c83f5a90c6d76feaf850f6502b61b53c329e85b3b281cba34",
"zh:ac8a23c212258b7976e1621275e3af7099e7e4a3d4478cf8d5d2a27f3bc3e967",
"zh:b516ca74431f3df4c6cf90ddcdb4042c626e026317a33c53f0b445a3d93b720d",
"zh:dc76e4326aec2490c1600d6871a95e78f9050f9ce427c71707ea412a2f2f1a62",
"zh:eac7b63e86c749c7d48f527671c7aee5b4e26c10be6ad7232d6860167f99dbb0",
]
}

View File

@@ -1,81 +1,55 @@
# Authentik Terraform Configuration
# Authentik Terraform Module
Root Terraform configuration for managing Authentik SSO — applications (OAuth2/OIDC, Proxy, SAML), groups, outposts, flows, certificates, and property mappings.
State is stored in Terraform Cloud (organization `ultradesu`, workspace `Authentik`).
## Structure
```
.
├── main.tf # Resources: groups, outposts, policy bindings, module calls
├── variables.tf # Input variable definitions
├── outputs.tf # Outputs (app details, groups, flows, wiki data)
├── providers.tf # Authentik provider (goauthentik/authentik 2025.12.1)
├── state.tf # Terraform Cloud backend
├── terraform.tfvars # General settings: authentik_url, outposts, flows, tags
├── oauth2-apps.auto.tfvars # OAuth2/OIDC application definitions
├── proxy-apps.auto.tfvars # Proxy application definitions
├── groups.auto.tfvars # Group definitions
└── modules/
├── oauth-provider/ # OAuth2/OIDC provider + application
├── proxy-provider/ # Proxy provider + application
└── saml-provider/ # SAML provider + application
```
Terraform module for managing Authentik applications with OAuth2/OpenID and Proxy providers, including automatic Outpost assignment.
## Usage
```bash
# Set the API token
export TF_VAR_authentik_token="..."
terraform init
terraform plan
terraform apply
```
All `*.auto.tfvars` files are loaded automatically — no `-var-file` flags needed.
## Adding applications
OAuth2/OIDC — add to `oauth2-apps.auto.tfvars`:
```hcl
oauth_applications = {
"my-app" = {
name = "My App"
slug = "my-app"
group = "Tools"
redirect_uris = ["https://my-app.example.com/callback"]
create_group = true
access_groups = ["admins"]
module "authentik" {
source = "./authentik"
authentik_url = "https://auth.example.com"
authentik_token = var.authentik_token
oauth_applications = {
"gitlab" = {
name = "GitLab OAuth"
slug = "gitlab"
redirect_uris = ["https://gitlab.example.com/users/auth/openid_connect/callback"]
}
}
proxy_applications = {
"portainer" = {
name = "Portainer"
slug = "portainer"
external_host = "https://portainer.example.com"
internal_host = "http://portainer:9000"
outpost = "k8s-outpost"
}
}
outposts = {
"k8s-outpost" = {
name = "Kubernetes Outpost"
type = "proxy"
service_connection = "k8s-local"
}
}
}
```
Proxy — add to `proxy-apps.auto.tfvars`:
## Structure
```hcl
proxy_applications = {
"my-proxy" = {
name = "My Proxy"
slug = "my-proxy"
group = "Tools"
external_host = "https://my-proxy.example.com"
internal_host = "http://my-service.namespace.svc:80"
outpost = "kubernetes-outpost"
create_group = true
access_groups = ["admins"]
}
}
```
## CI/CD
Managed via Gitea Actions (`.gitea/workflows/authentik-apps.yaml`). Runs `terraform apply` on push to `main` when files in `terraform/authentik/` change. Also generates a wiki page with the applications list.
- `main.tf` - Main configuration
- `variables.tf` - Input variables
- `outputs.tf` - Output values
- `modules/oauth-provider/` - OAuth2/OIDC provider module
- `modules/proxy-provider/` - Proxy provider module
- `terraform.tfvars.example` - Configuration example
## Requirements
- Terraform >= 1.0
- goauthentik/authentik provider 2025.12.1
- Authentik API token with admin permissions
- Authentik provider >= 2023.10.0
- Authentik API token with admin permissions

View File

@@ -31,7 +31,7 @@ resource "authentik_group" "child_groups" {
name = each.value.name
is_superuser = each.value.is_superuser
parents = authentik_group.root_groups[each.value.parent].id
parent = authentik_group.root_groups[each.value.parent].id
attributes = jsonencode(each.value.attributes)
depends_on = [authentik_group.root_groups]
@@ -305,4 +305,4 @@ resource "authentik_outpost" "outposts" {
module.oauth_applications,
module.proxy_applications
]
}
}

View File

@@ -126,15 +126,15 @@ oauth_applications = {
}
"home-assistant-lms" = {
name = "Home Assistant LMS"
slug = "home-assistant-lms"
group = "Internal"
meta_description = "Home Assistant Limassol"
meta_icon = "https://img.icons8.com/stickers/100/smart-home-automation.png"
redirect_uris = [
name = "Home Assistant LMS"
slug = "home-assistant-lms"
group = "Internal"
meta_description = "Home Assistant Limassol"
meta_icon = "https://img.icons8.com/stickers/100/smart-home-automation.png"
redirect_uris = [
"http://ha-lms:8123/auth/oidc/callback",
"http://ha-lms.homenet:8123/auth/oidc/callback",
]
]
meta_launch_url = "http://ha-lms:8123/auth/oidc/welcome"
client_type = "confidential"
include_claims_in_id_token = true
@@ -147,15 +147,15 @@ oauth_applications = {
signing_key = "1b1b5bec-034a-4d96-871a-133f11322360"
}
"home-assistant-london" = {
name = "Home Assistant London"
slug = "home-assistant-london"
group = "Internal"
meta_description = "Home Assistant London"
meta_icon = "https://img.icons8.com/stickers/100/smart-home-automation.png"
redirect_uris = [
name = "Home Assistant London"
slug = "home-assistant-london"
group = "Internal"
meta_description = "Home Assistant London"
meta_icon = "https://img.icons8.com/stickers/100/smart-home-automation.png"
redirect_uris = [
"http://ha-london:8123/auth/oidc/callback",
"http://ha-london.tail2fe2d.ts.net:8123/auth/oidc/callback",
]
]
meta_launch_url = "http://ha-london:8123/auth/oidc/welcome"
client_type = "confidential"
include_claims_in_id_token = true
@@ -169,14 +169,14 @@ oauth_applications = {
}
"openwebui" = {
name = "OpenWeb UI"
slug = "openwebui"
group = "Tools"
meta_description = "OpenWeb UI"
meta_icon = "https://ollama.com/public/ollama.png"
redirect_uris = [
name = "OpenWeb UI"
slug = "openwebui"
group = "Tools"
meta_description = "OpenWeb UI"
meta_icon = "https://ollama.com/public/ollama.png"
redirect_uris = [
"https://ai.hexor.cy/oauth/oidc/callback",
]
]
meta_launch_url = "https://ai.hexor.cy"
client_type = "confidential"
include_claims_in_id_token = true

View File

@@ -2,7 +2,7 @@ terraform {
required_providers {
authentik = {
source = "goauthentik/authentik"
version = "2025.12.1"
version = "2025.8.1"
}
}
}
@@ -10,4 +10,4 @@ terraform {
provider "authentik" {
url = var.authentik_url
token = var.authentik_token
}
}

View File

@@ -60,25 +60,47 @@ EOT
create_group = true
access_groups = ["admins"]
}
"mtproxy-links" = {
name = "mtproxy-links"
slug = "mtproxy-links"
group = "Core"
external_host = "https://proxy.hexor.cy"
internal_host = "http://secret-reader.mtproxy.svc:80"
# Tools applications
"vpn" = {
name = "VPN"
slug = "vpn"
group = "Tools"
external_host = "https://of.hexor.cy"
internal_host = "http://outfleet.vpn.svc"
internal_host_ssl_validation = false
meta_description = ""
skip_path_regex = <<-EOT
/webhook
/u/
/stat/
/ss/
/xray/
/dynamic/
EOT
meta_icon = "https://img.icons8.com/ios-filled/50/password.png"
meta_icon = "https://img.icons8.com/?size=100&id=fqAD3lAB6zTe&format=png&color=000000"
mode = "proxy"
outpost = "kubernetes-outpost"
create_group = true
access_groups = ["admins"]
}
# Tools applications
"outfleet-rs" = {
name = "OutFleet"
slug = "outfleet-rs"
group = "Tools"
external_host = "https://vpn.hexor.cy"
internal_host = "http://outfleet-rs.vpn.svc"
internal_host_ssl_validation = false
meta_description = ""
skip_path_regex = <<-EOT
/sub/
EOT
meta_icon = "https://img.icons8.com/?size=100&id=fqAD3lAB6zTe&format=png&color=000000"
mode = "proxy"
outpost = "kubernetes-outpost"
create_group = true
access_groups = ["admins"]
}
"qbittorrent" = {
name = "qBittorent"
slug = "qbittorent"
@@ -167,9 +189,6 @@ EOT
meta_icon = "https://img.icons8.com/color/48/minecraft-grass-cube.png"
mode = "proxy"
outpost = "kubernetes-outpost"
skip_path_regex = <<-EOT
/clients
EOT
}
"pasarguard" = {
name = "PasarGuard"

View File

@@ -4,7 +4,7 @@ variable "oauth_applications" {
name = string
slug = string
group = optional(string, "")
policy_engine_mode = optional(string, "any")
policy_engine_mode = optional(string, "all")
meta_description = optional(string, "")
meta_launch_url = optional(string, "")
meta_icon = optional(string, "")
@@ -32,7 +32,7 @@ variable "proxy_applications" {
name = string
slug = string
group = optional(string, "")
policy_engine_mode = optional(string, "any")
policy_engine_mode = optional(string, "all")
meta_description = optional(string, "")
meta_launch_url = optional(string, "")
meta_icon = optional(string, "")
@@ -60,7 +60,7 @@ variable "saml_applications" {
name = string
slug = string
group = optional(string, "")
policy_engine_mode = optional(string, "any")
policy_engine_mode = optional(string, "all")
meta_description = optional(string, "")
meta_launch_url = optional(string, "")
meta_icon = optional(string, "")
@@ -95,7 +95,7 @@ variable "flows" {
title = string
slug = string
designation = string
policy_engine_mode = optional(string, "any")
policy_engine_mode = optional(string, "all")
compatibility_mode = optional(bool, false)
layout = optional(string, "stacked")
denied_action = optional(string, "message_continue")