Compare commits

...

195 Commits

Author SHA1 Message Date
Gitea Actions Bot
5cdb11ae39 Auto-update README with current k8s applications
All checks were successful
Terraform / Terraform (pull_request) Successful in 44s
Generated by CI/CD workflow on 2025-11-18 20:14:01

This PR updates the README.md file with the current list of applications found in the k8s/ directory structure.
2025-11-18 20:14:01 +00:00
Ultradesu
39a27c596f Fixed iperf3
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 20s
Auto-update README / Generate README and Create MR (push) Successful in 11s
2025-11-18 22:13:12 +02:00
Ultradesu
353bb877be Fixed iperf3
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 14s
Auto-update README / Generate README and Create MR (push) Successful in 15s
2025-11-18 21:45:26 +02:00
Ultradesu
e523bb8bea Fixed iperf3
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 14s
Auto-update README / Generate README and Create MR (push) Successful in 12s
2025-11-18 21:44:00 +02:00
Ultradesu
b433373725 Fixed iperf3
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 13s
Check with kubeconform / lint (push) Successful in 13s
Auto-update README / Generate README and Create MR (push) Successful in 12s
2025-11-18 21:40:53 +02:00
Ultradesu
3026e53746 Added iperf3-server
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 27s
Auto-update README / Generate README and Create MR (push) Successful in 11s
2025-11-18 21:37:09 +02:00
Ultradesu
63669c69ff Added iperf3-server
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 41s
Auto-update README / Generate README and Create MR (push) Successful in 15s
2025-11-18 21:33:34 +02:00
Ultradesu
fa98e553cd Added iperf3-server
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 14s
Auto-update README / Generate README and Create MR (push) Successful in 12s
2025-11-18 21:32:01 +02:00
Ultradesu
055ef8aa77 Added iperf3-server 2025-11-18 21:31:57 +02:00
Ultradesu
22b359a7ee Added iperf3-server
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 13s
Check with kubeconform / lint (push) Successful in 14s
Auto-update README / Generate README and Create MR (push) Successful in 11s
2025-11-18 20:47:50 +02:00
ab
611e3e31dd Update k8s/core/prom-stack/prom-values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 14s
Auto-update README / Generate README and Create MR (push) Successful in 7s
2025-11-18 18:12:39 +00:00
ab
ddbd53e476 Update k8s/core/prom-stack/prom-values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 14s
Auto-update README / Generate README and Create MR (push) Successful in 8s
2025-11-18 18:07:07 +00:00
ab
f8a9d91932 Update k8s/apps/pasarguard/deployment.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 14s
Auto-update README / Generate README and Create MR (push) Successful in 11s
2025-11-18 17:18:26 +00:00
ab
262fea115d Update k8s/core/prom-stack/prom-values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 14s
Auto-update README / Generate README and Create MR (push) Successful in 7s
2025-11-18 15:36:08 +00:00
Ultradesu
1e1a015dc0 Added xray expoter monitor
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 14s
Auto-update README / Generate README and Create MR (push) Successful in 7s
2025-11-18 14:53:07 +02:00
Ultradesu
e76ebdd8c3 Added xray expoter monitor
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 14s
Auto-update README / Generate README and Create MR (push) Successful in 8s
2025-11-18 14:47:08 +02:00
Ultradesu
0c2ce55a41 Added xray expoter monitor
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 13s
Auto-update README / Generate README and Create MR (push) Successful in 8s
2025-11-18 14:43:35 +02:00
Ultradesu
6e9de5addf Added xray expoter monitor
Some checks failed
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 11s
Auto-update README / Generate README and Create MR (push) Has been cancelled
2025-11-18 14:43:03 +02:00
Ultradesu
887a9a2306 Added xray exporter
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 16s
Auto-update README / Generate README and Create MR (push) Successful in 8s
2025-11-18 14:39:05 +02:00
Ultradesu
776109d795 Added xray exporter
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 13s
Auto-update README / Generate README and Create MR (push) Successful in 8s
2025-11-18 14:32:39 +02:00
Ultradesu
c998426b44 Added xray exporter
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 15s
Auto-update README / Generate README and Create MR (push) Successful in 7s
2025-11-18 14:27:12 +02:00
Ultradesu
536be6a61f Added xray exporter
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 12s
Auto-update README / Generate README and Create MR (push) Successful in 7s
2025-11-18 14:13:55 +02:00
Ultradesu
713481c726 Added xray exporter
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 13s
Check with kubeconform / lint (push) Successful in 14s
Auto-update README / Generate README and Create MR (push) Successful in 7s
2025-11-18 14:12:14 +02:00
ab
f6411b7b65 Update k8s/core/authentik/values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 13s
Auto-update README / Generate README and Create MR (push) Successful in 7s
2025-11-17 20:31:03 +00:00
ab
3af6d98be8 Update k8s/core/authentik/kustomization.yaml
Some checks failed
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Has been cancelled
Auto-update README / Generate README and Create MR (push) Has been cancelled
2025-11-17 20:30:45 +00:00
ab
a45af9d4bc Update k8s/core/external-secrets/bitwarden-store.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 22s
Check with kubeconform / lint (push) Successful in 26s
Auto-update README / Generate README and Create MR (push) Successful in 7s
2025-11-17 20:15:34 +00:00
ab
76937930ce Update k8s/apps/ollama/openweb-ui-values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 12s
Auto-update README / Generate README and Create MR (push) Successful in 8s
2025-11-09 00:39:33 +00:00
ab
d4ff8d4665 Update k8s/apps/ollama/openweb-ui-values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 13s
Auto-update README / Generate README and Create MR (push) Successful in 9s
2025-11-09 00:38:08 +00:00
ab
e0cf9371ae Update k8s/apps/ollama/openweb-ui-values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 14s
Auto-update README / Generate README and Create MR (push) Successful in 10s
2025-11-08 23:45:31 +00:00
ab
1126cb25bc Update k8s/apps/ollama/openweb-ui-values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 13s
Auto-update README / Generate README and Create MR (push) Successful in 10s
2025-11-08 22:42:29 +00:00
ab
44250dc937 Update k8s/apps/ollama/openweb-ui-values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 12s
Auto-update README / Generate README and Create MR (push) Successful in 10s
2025-11-08 22:41:09 +00:00
ab
d9db73e078 Update k8s/apps/ollama/openweb-ui-values.yaml
Some checks failed
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Auto-update README / Generate README and Create MR (push) Has been cancelled
Check with kubeconform / lint (push) Has been cancelled
2025-11-08 22:40:45 +00:00
ab
71ce9f15ef Update k8s/apps/ollama/kustomization.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 13s
Auto-update README / Generate README and Create MR (push) Successful in 9s
2025-11-08 22:37:47 +00:00
ab
6b855294af Add k8s/apps/ollama/external-secrets.yaml
Some checks failed
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 14s
Auto-update README / Generate README and Create MR (push) Has been cancelled
2025-11-08 22:37:16 +00:00
ab
8dd16e24e6 Update k8s/apps/ollama/openweb-ui-values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 12s
Auto-update README / Generate README and Create MR (push) Successful in 8s
2025-11-08 22:29:36 +00:00
ab
3df95f46a5 Update terraform/authentik/oauth2-apps.tfvars
Some checks failed
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Has been cancelled
Terraform / Terraform (push) Successful in 42s
2025-11-08 22:29:24 +00:00
ab
c0151eb2c9 Update k8s/core/authentik/values.yaml
Some checks failed
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 10s
Check with kubeconform / lint (push) Successful in 13s
Auto-update README / Generate README and Create MR (push) Has been cancelled
2025-11-08 22:29:02 +00:00
ab
6d7e365058 Update terraform/authentik/proxy-apps.tfvars
All checks were successful
Terraform / Terraform (push) Successful in 43s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
2025-11-08 22:27:59 +00:00
ab
0b5361323a Update terraform/authentik/oauth2-apps.tfvars
Some checks failed
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Has been cancelled
Terraform / Terraform (push) Has been cancelled
2025-11-08 22:27:35 +00:00
ab
56352fef4b Update k8s/apps/ollama/openweb-ui-values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 24s
Check with kubeconform / lint (push) Successful in 27s
Auto-update README / Generate README and Create MR (push) Successful in 7s
2025-11-08 22:25:26 +00:00
AB
7a1f792391 Added ollama
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 14s
Auto-update README / Generate README and Create MR (push) Successful in 11s
2025-11-07 17:33:21 +02:00
AB
defe0cbdf5 Added ollama
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 14s
Auto-update README / Generate README and Create MR (push) Successful in 35s
2025-11-07 17:22:39 +02:00
AB
7285c62b37 Merge branch 'main' of ssh://gt.hexor.cy:30022/ab/homelab
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 14s
Auto-update README / Generate README and Create MR (push) Successful in 10s
2025-11-07 17:07:14 +02:00
ab
60f8d86fca Merge pull request 'Auto-update README with k8s applications' (#10) from auto-update-readme-20251107-145618 into main
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 13s
Reviewed-on: #10
2025-11-07 15:06:01 +00:00
AB
2387653edd Merge branch 'main' of ssh://gt.hexor.cy:30022/ab/homelab 2025-11-07 17:05:25 +02:00
AB
78a639162b Added ollama 2025-11-07 17:05:11 +02:00
ab
90b197bcbe Update terraform/authentik/proxy-apps.tfvars
All checks were successful
Terraform / Terraform (push) Successful in 44s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
2025-11-07 14:58:47 +00:00
Gitea Actions Bot
156d26aaf9 Auto-update README with current k8s applications
All checks were successful
Terraform / Terraform (pull_request) Successful in 37s
Generated by CI/CD workflow on 2025-11-07 14:56:18

This PR updates the README.md file with the current list of applications found in the k8s/ directory structure.
2025-11-07 14:56:18 +00:00
AB
700b9cf5ff Added ollama
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 53s
Check with kubeconform / lint (push) Successful in 12s
Auto-update README / Generate README and Create MR (push) Successful in 11s
2025-11-07 16:53:16 +02:00
AB
84bd1fc05a Merge branch 'main' of ssh://gt.hexor.cy:30022/ab/homelab
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 15s
Check with kubeconform / lint (push) Successful in 13s
Auto-update README / Generate README and Create MR (push) Successful in 11s
2025-11-07 16:52:27 +02:00
AB
cb3defd28c Added ollama 2025-11-07 16:52:16 +02:00
ab
47320b9df6 Update k8s/core/authentik/values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 13s
Auto-update README / Generate README and Create MR (push) Successful in 12s
2025-11-07 14:34:43 +00:00
AB
0e4204b2ea Merge branch 'main' of ssh://gt.hexor.cy:30022/ab/homelab
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 10s
Check with kubeconform / lint (push) Successful in 13s
Auto-update README / Generate README and Create MR (push) Successful in 11s
2025-11-07 16:32:58 +02:00
AB
3230c67f16 Added ollama 2025-11-07 16:32:48 +02:00
ab
60c764a657 Update terraform/authentik/proxy-apps.tfvars
All checks were successful
Terraform / Terraform (push) Successful in 43s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
2025-11-07 14:32:14 +00:00
AB
869a75f6e4 Merge branch 'main' of ssh://gt.hexor.cy:30022/ab/homelab
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 13s
Auto-update README / Generate README and Create MR (push) Successful in 13s
2025-11-07 16:11:40 +02:00
AB
26429f1d4c Added ollama 2025-11-07 16:11:29 +02:00
ab
5718981b67 Update k8s/apps/ollama/kustomization.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 20s
Check with kubeconform / lint (push) Successful in 12s
Auto-update README / Generate README and Create MR (push) Successful in 11s
2025-11-07 14:10:30 +00:00
AB
8c6ca27523 Added ollama
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 12s
Auto-update README / Generate README and Create MR (push) Successful in 11s
2025-11-07 16:07:59 +02:00
AB
4334ea25b7 Added ollama 2025-11-07 16:07:03 +02:00
AB
18e60344b3 Merge branch 'main' of ssh://gt.hexor.cy:30022/ab/homelab
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 12s
Auto-update README / Generate README and Create MR (push) Successful in 8s
2025-11-07 15:45:16 +02:00
AB
113f1e668d moved prometheus 2025-11-07 15:45:03 +02:00
ab
9b1caa28ef Merge pull request 'Auto-update README with k8s applications' (#3) from auto-update-readme-20251107-134131 into main
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 13s
Reviewed-on: #3
2025-11-07 13:42:55 +00:00
Gitea Actions Bot
bd927b7ea6 Auto-update README with current k8s applications
All checks were successful
Terraform / Terraform (pull_request) Successful in 37s
Generated by CI/CD workflow on 2025-11-07 13:41:31

This PR updates the README.md file with the current list of applications found in the k8s/ directory structure.
2025-11-07 13:41:31 +00:00
AB
1688ac67c2 Merge branch 'main' of ssh://gt.hexor.cy:30022/ab/homelab
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 13s
Auto-update README / Generate README and Create MR (push) Successful in 12s
2025-11-07 15:40:40 +02:00
AB
eda944741e moved prometheus 2025-11-07 15:40:25 +02:00
ab
e744629f85 Merge pull request 'Auto-update README with k8s applications' (#2) from auto-update-readme-20251107-133650 into main
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Reviewed-on: #2
2025-11-07 13:37:53 +00:00
Gitea Actions Bot
e38ba5b70c Auto-update README with current k8s applications
All checks were successful
Terraform / Terraform (pull_request) Successful in 36s
Generated by CI/CD workflow on 2025-11-07 13:36:50

This PR updates the README.md file with the current list of applications found in the k8s/ directory structure.
2025-11-07 13:36:50 +00:00
AB
860f83445a Readme CI
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
2025-11-07 15:34:22 +02:00
AB
8e1e20f478 Readme CI
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
2025-11-07 15:32:07 +02:00
AB
93afe8b187 Merge branch 'main' of ssh://gt.hexor.cy:30022/ab/homelab
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
2025-11-07 15:29:17 +02:00
AB
54e83b0af9 Readme CI 2025-11-07 15:29:04 +02:00
ab
cbed0939fa Update k8s/core/postgresql/nas.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 13s
2025-11-07 12:47:25 +00:00
ab
e545346fec Update k8s/core/postgresql/nas.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 13s
Check with kubeconform / lint (push) Successful in 15s
2025-11-07 12:46:42 +00:00
ab
aae0255843 Update k8s/apps/tg-bots/desubot.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 13s
2025-11-07 00:01:33 +00:00
ab
c7fc3504ba Update k8s/apps/tg-bots/kustomization.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 13s
Check with kubeconform / lint (push) Successful in 14s
2025-11-06 23:48:39 +00:00
ab
b2701a8435 Update k8s/apps/tg-bots/desubot.yaml
Some checks failed
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Has been cancelled
2025-11-06 23:48:22 +00:00
ab
df39182fea Update k8s/apps/tg-bots/kustomization.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 12s
2025-11-06 23:40:09 +00:00
ab
c9bee8c049 Update k8s/apps/tg-bots/external-secrets.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 13s
2025-11-06 23:39:33 +00:00
ab
439ad751e9 Update k8s/apps/tg-bots/get-id-bot.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 14s
2025-11-06 23:37:35 +00:00
ab
4681a3b263 Add k8s/apps/tg-bots/desubot.yaml
Some checks failed
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Has been cancelled
2025-11-06 23:37:17 +00:00
ab
45f208a4ff Update k8s/apps/pasarguard/deployment.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 14s
2025-11-06 20:51:44 +00:00
AB
a19648aacc Merge branch 'main' of ssh://gt.hexor.cy:30022/ab/homelab
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 13s
Check with kubeconform / lint (push) Successful in 14s
2025-11-06 22:49:10 +02:00
AB
7d7906edc7 Changed pasarguard sub page 2025-11-06 22:48:58 +02:00
Ultradesu
30a3b1cd60 Fix xray-nodes
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 12s
2025-11-06 16:53:41 +02:00
Ultradesu
8dfc8e2549 Fix xray-nodes
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 13s
2025-11-06 16:51:32 +02:00
Ultradesu
8e18775e3e Fix xray-nodes
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 12s
2025-11-06 16:46:47 +02:00
ab
1ee1b9a1c6 Update k8s/apps/pasarguard/daemonset.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 11s
2025-11-06 14:19:18 +00:00
ab
407b11f40d Update k8s/apps/pasarguard/daemonset.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 13s
2025-11-06 14:17:31 +00:00
ab
82cf742edb Update terraform/authentik/proxy-apps.tfvars
All checks were successful
Terraform / Terraform (push) Successful in 39s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
2025-11-06 14:13:11 +00:00
ab
360f260cb5 Update k8s/apps/pasarguard/daemonset.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 12s
2025-11-06 14:08:37 +00:00
ab
31fe1bf95d Update k8s/apps/pasarguard/daemonset.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 13s
2025-11-06 14:07:01 +00:00
ab
7c8a3b2aaa Update terraform/authentik/proxy-apps.tfvars
All checks were successful
Terraform / Terraform (push) Successful in 40s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
2025-11-06 13:43:07 +00:00
ab
792421ffc5 Update k8s/apps/pasarguard/deployment.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 13s
Check with kubeconform / lint (push) Successful in 14s
Enabled PasarGuard API
2025-11-06 13:36:29 +00:00
AB
014db9ad81 Merge branch 'main' of ssh://gt.hexor.cy:30022/ab/homelab
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 14s
2025-11-06 02:35:51 +02:00
AB
602b729d97 Added tg bots 2025-11-06 02:35:40 +02:00
ab
fa7b81c1b3 Update terraform/authentik/proxy-apps.tfvars
All checks were successful
Terraform / Terraform (push) Successful in 37s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
2025-11-05 23:34:57 +00:00
ab
7deb5971a5 Update terraform/authentik/proxy-apps.tfvars
All checks were successful
Terraform / Terraform (push) Successful in 43s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
2025-11-05 23:31:56 +00:00
AB
dbfdc0f122 Added pasarguard
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 13s
2025-11-05 16:17:36 +02:00
AB
984f7030a3 Added pasarguard
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 15s
2025-11-05 15:56:01 +02:00
AB
3e2fe905bd Added pasarguard
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 10s
Check with kubeconform / lint (push) Successful in 12s
2025-11-05 15:50:41 +02:00
AB
45b49944b3 Merge branch 'main' of ssh://gt.hexor.cy:30022/ab/homelab
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 13s
2025-11-05 15:50:11 +02:00
AB
bd8caa57bf Added pasarguard 2025-11-05 15:50:00 +02:00
ab
c93b8f2797 Update k8s/core/authentik/values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 16s
Added pasarguard
2025-11-05 13:40:24 +00:00
AB
48fee5ceed Added pasarguard
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 12s
2025-11-05 15:36:02 +02:00
AB
75a9c140af Merge branch 'main' of ssh://gt.hexor.cy:30022/ab/homelab
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 13s
2025-11-05 15:34:32 +02:00
AB
c8930bbebc Added pasarguard 2025-11-05 15:34:21 +02:00
AB
f52b3aab5b Added pasarguard 2025-11-05 15:34:15 +02:00
ab
6f1b6dee5d Update terraform/authentik/proxy-apps.tfvars
All checks were successful
Terraform / Terraform (push) Successful in 45s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Added pasarguard
2025-11-05 13:32:47 +00:00
AB
5e1aa4cd37 Added pasarguard
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 11s
2025-11-05 12:21:01 +02:00
AB
fef5303429 Added pasarguard
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 12s
2025-11-05 12:19:32 +02:00
AB
ba389b8b1e Added pasarguard
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 12s
2025-11-05 12:15:27 +02:00
AB
79d106468a Added pasarguard
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 12s
2025-11-05 12:13:42 +02:00
ab
ca19a1f46a Update k8s/core/postgresql/external-secrets.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 14s
Check with kubeconform / lint (push) Successful in 14s
2025-11-05 09:59:28 +00:00
ab
280fbe41bf Update k8s/core/authentik/kustomization.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 19s
2025-11-04 17:44:48 +00:00
ab
32c2ba2781 Add k8s/core/authentik/worker-restart.yaml
Some checks failed
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 16s
Check with kubeconform / lint (push) Has been cancelled
2025-11-04 17:44:13 +00:00
ab
1587abfba8 Update k8s/apps/paperless/paperless-values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 14s
Check with kubeconform / lint (push) Successful in 14s
2025-10-30 12:48:52 +00:00
ab
704f9dc85e Update k8s/core/postgresql/pgadmin4-values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 13s
Check with kubeconform / lint (push) Successful in 13s
2025-10-29 15:54:38 +00:00
Ultradesu
5ff4b8ecb2 Extend prom PV
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 13s
Check with kubeconform / lint (push) Successful in 25s
2025-10-29 16:10:57 +02:00
AB from home.homenet
1d8c0c1421 Added custom coredns resolve config
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 16s
Check with kubeconform / lint (push) Successful in 13s
2025-10-27 11:03:57 +02:00
ab
661c2c31aa Update k8s/apps/vpn/outfleet-rs.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 15s
Check with kubeconform / lint (push) Successful in 16s
2025-10-25 10:58:57 +00:00
ab
6d76d20d02 Update k8s/apps/vpn/outfleet-rs.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 11s
2025-10-24 15:53:15 +00:00
ab
ab3e687cea Update k8s/apps/vpn/outfleet-rs.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 13s
Check with kubeconform / lint (push) Successful in 15s
2025-10-24 15:51:08 +00:00
AB from home.homenet
6ade2bb13a Pinned xray version
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 13s
Check with kubeconform / lint (push) Successful in 13s
2025-10-24 18:30:38 +03:00
ab
d49c382055 Update k8s/apps/vpn/kustomization.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 10s
Check with kubeconform / lint (push) Successful in 16s
2025-10-24 15:21:48 +00:00
ab
1b9775c63d Update k8s/apps/vpn/outfleet-rs.yaml
Some checks failed
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 15s
Check with kubeconform / lint (push) Has been cancelled
2025-10-24 15:21:32 +00:00
ab
f7838be372 Update README.md
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 10s
2025-10-24 12:18:36 +00:00
ab
ce74590719 Update README.md
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
2025-10-24 12:18:06 +00:00
Ultradesu
280bdd3091 Update readme
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
2025-10-24 15:17:14 +03:00
Ultradesu
c34f5ed0a0 Update readme
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
2025-10-24 15:14:47 +03:00
Ultradesu
107782318b Update readme
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
2025-10-24 15:05:52 +03:00
Ultradesu
ed2a59948f Update readme
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
2025-10-24 15:02:24 +03:00
ab
03f6596262 Update k8s/core/argocd/values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 14s
Check with kubeconform / lint (push) Successful in 13s
Enabled ArgoCD statusbages
2025-10-24 11:56:22 +00:00
ab
bed8f5b7c3 Update k8s/core/authentik/values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 13s
2025-10-22 15:49:23 +00:00
ab
676a81852a Disable hexound
All checks were successful
Terraform / Terraform (push) Successful in 45s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
2025-10-22 15:31:09 +00:00
ab
73c09f80f7 Update k8s/apps/hexound/kustomization.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 10s
Check with kubeconform / lint (push) Successful in 12s
2025-10-22 15:30:09 +00:00
ab
104d67bfb3 Add k8s/apps/hexound/ingress.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 13s
2025-10-22 15:29:34 +00:00
AB from home.homenet
71e5101604 Deployed OF django back
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 13s
Check with kubeconform / lint (push) Successful in 14s
2025-10-22 15:19:13 +03:00
AB from home.homenet
5783db189a Deployed outfleet-rs
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 17s
Check with kubeconform / lint (push) Successful in 18s
2025-10-22 15:08:13 +03:00
ab
5659e4455b Update terraform/authentik/proxy-apps.tfvars
All checks were successful
Terraform / Terraform (push) Successful in 44s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
2025-10-19 11:28:03 +00:00
AB from home.homenet
36e8c5c36b Fix: Drop nginx magic
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 12s
2025-10-12 15:16:59 +03:00
AB from home.homenet
a6e0165027 Fix: Changed syncthing access and auth scheme
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 12s
2025-10-12 14:24:07 +03:00
AB from home.homenet
09526f4e91 Fix: Changed syncthing access and auth scheme
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 12s
2025-10-12 14:18:42 +03:00
AB from home.homenet
d1922019ab Fix: Changed syncthing access and auth scheme
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 12s
2025-10-12 14:17:16 +03:00
AB from home.homenet
118a1c431a Fix: Changed syncthing access and auth scheme
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 11s
2025-10-12 14:13:05 +03:00
AB from home.homenet
b9667ea5e7 Fix: Changed syncthing access and auth scheme
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 13s
2025-10-12 14:09:18 +03:00
AB from home.homenet
b1446c53cd Fix: Changed syncthing access and auth scheme
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 12s
2025-10-12 13:44:39 +03:00
AB from home.homenet
56fa6a5e05 Fix: Changed syncthing access and auth scheme
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 13s
2025-10-12 13:36:39 +03:00
AB from home.homenet
aa19cd8e61 Fix: Changed syncthing access and auth scheme
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 13s
2025-10-12 13:29:10 +03:00
AB from home.homenet
00837fb238 Changed syncthing access and auth scheme
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 12s
2025-10-12 13:16:39 +03:00
ab
479a2a02ea Update k8s/core/authentik/values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
Check with kubeconform / lint (push) Successful in 19s
2025-10-12 10:04:04 +00:00
AB from home.homenet
95e12df43d Changed syncthing access and auth scheme
Some checks failed
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 40s
Check with kubeconform / lint (push) Has been cancelled
2025-10-12 13:02:32 +03:00
ab
5a33337aa1 Update k8s/core/prometheus/grafana-values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 10s
Check with kubeconform / lint (push) Successful in 23s
2025-10-06 13:07:16 +00:00
ab
ce9ba3661b Update terraform/authentik/oauth2-apps.tfvars
All checks were successful
Terraform / Terraform (push) Successful in 38s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 11s
2025-10-06 12:43:58 +00:00
ab
8bcba25f7e Update terraform/authentik/oauth2-apps.tfvars
All checks were successful
Terraform / Terraform (push) Successful in 35s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 9s
2025-10-06 11:26:42 +00:00
ab
be9f42afa7 Update terraform/authentik/oauth2-apps.tfvars
All checks were successful
Terraform / Terraform (push) Successful in 37s
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 10s
2025-10-06 11:09:09 +00:00
ab
656ec121d2 Update k8s/core/postgresql/kustomization.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 10s
Check with kubeconform / lint (push) Successful in 12s
2025-10-04 03:26:37 +00:00
ab
240fc4127f Update k8s/core/postgresql/pgadmin4-values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 12s
Check with kubeconform / lint (push) Successful in 13s
2025-10-04 03:26:01 +00:00
ab
9b19d8ddd8 Update k8s/apps/paperless/paperless-values.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 13s
Check with kubeconform / lint (push) Successful in 13s
2025-10-04 03:22:38 +00:00
ab
0b8fe99ee1 Update k8s/core/system-upgrade/plan.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 8s
Check with kubeconform / lint (push) Successful in 11s
2025-10-04 02:53:55 +00:00
Ultradesu
cff6c28b72 extended shared memory for authentik worker
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 20s
Check with kubeconform / lint (push) Successful in 23s
2025-10-04 03:00:00 +01:00
ab
99a63eb840 Update terraform/authentik/proxy-apps.tfvars
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 10s
Terraform / Terraform (push) Successful in 39s
2025-09-20 16:55:22 +00:00
ab
4f3be5b14a Update k8s/core/postgresql/external-secrets.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 8s
Check with kubeconform / lint (push) Successful in 9s
2025-09-17 14:36:58 +00:00
ab
9f5ec499dc Update k8s/core/postgresql/external-secrets.yaml
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 9s
Check with kubeconform / lint (push) Successful in 12s
2025-09-17 14:35:02 +00:00
AB from home.homenet
7b169b8e6d Added wiki generator
All checks were successful
Update Kubernetes Services Wiki / Generate and Update K8s Wiki (push) Successful in 9s
2025-09-16 16:35:11 +03:00
ab
a79003740a Update .gitea/workflows/authentik-apps.yaml 2025-09-16 13:30:48 +00:00
AB from home.homenet
6276d543bc Added wiki generator
All checks were successful
Terraform / Terraform (push) Successful in 34s
2025-09-16 16:26:18 +03:00
AB from home.homenet
18a9bfa22a Added wiki generator
All checks were successful
Terraform / Terraform (push) Successful in 33s
2025-09-16 16:24:30 +03:00
AB from home.homenet
4b6090910c Added wiki generator
All checks were successful
Terraform / Terraform (push) Successful in 34s
2025-09-16 16:21:24 +03:00
AB from home.homenet
cfa796cc94 Added wiki generator
All checks were successful
Terraform / Terraform (push) Successful in 38s
2025-09-16 16:17:30 +03:00
AB from home.homenet
3e4f5500d2 Added wiki generator
All checks were successful
Terraform / Terraform (push) Successful in 31s
2025-09-16 16:15:39 +03:00
AB from home.homenet
9dd761bc8e Added wiki generator 2025-09-16 16:13:26 +03:00
AB from home.homenet
eb3b5183b0 Added wiki generator 2025-09-16 16:11:28 +03:00
AB from home.homenet
c9c75c791b Added wiki generator
All checks were successful
Terraform / Terraform (push) Successful in 31s
2025-09-16 16:09:48 +03:00
AB from home.homenet
8b959fec49 Added wiki generator
Some checks failed
Terraform / Terraform (push) Has been cancelled
Update Authentik Applications Wiki / Generate and Update Wiki (push) Failing after 20s
2025-09-16 16:04:18 +03:00
AB from home.homenet
1184ff9060 Added wiki generator
Some checks failed
Terraform / Terraform (push) Has been cancelled
Update Authentik Applications Wiki / Generate and Update Wiki (push) Failing after 19s
2025-09-16 16:01:41 +03:00
AB from home.homenet
18c64ef812 Added wiki generator
Some checks failed
Terraform / Terraform (push) Has been cancelled
Update Authentik Applications Wiki / Generate and Update Wiki (push) Failing after 21s
2025-09-16 15:59:44 +03:00
AB from home.homenet
993cf1985d Added wiki generator
Some checks failed
Terraform / Terraform (push) Has been cancelled
Update Authentik Applications Wiki / Generate and Update Wiki (push) Failing after 19s
2025-09-16 15:57:48 +03:00
AB from home.homenet
3daf7cf79a Added wiki generator
Some checks failed
Terraform / Terraform (push) Successful in 29s
Check with kubeconform / lint (push) Successful in 8s
Update Authentik Applications Wiki / Generate and Update Wiki (push) Failing after 20s
2025-09-16 15:56:17 +03:00
AB from home.homenet
caa3354b33 Added wiki generator
Some checks failed
Terraform / Terraform (push) Has been cancelled
Check with kubeconform / lint (push) Successful in 9s
Update Authentik Applications Wiki / Generate and Update Wiki (push) Failing after 42s
2025-09-16 15:53:42 +03:00
AB from home.homenet
68ca195735 Added wiki generator
All checks were successful
Terraform / Terraform (push) Successful in 28s
Check with kubeconform / lint (push) Successful in 9s
2025-09-16 15:52:35 +03:00
AB from home.homenet
93d7cb6bf1 Added Authentik TF code
All checks were successful
Terraform / Terraform (push) Successful in 28s
Check with kubeconform / lint (push) Successful in 10s
2025-09-16 15:51:21 +03:00
ab
600a1dfb6e Update .gitea/workflows/authentik-apps.yaml
All checks were successful
Check with kubeconform / lint (push) Successful in 10s
Terraform / Terraform (push) Successful in 27s
2025-09-16 12:36:03 +00:00
AB from home.homenet
a8c089d9ec Added Authentik TF code
Some checks failed
Terraform / Terraform (push) Has been cancelled
Check with kubeconform / lint (push) Has been cancelled
2025-09-16 15:35:36 +03:00
ab
e516f95f77 Update .gitea/workflows/authentik-apps.yaml
Some checks failed
Terraform / Terraform (push) Failing after 20s
Check with kubeconform / lint (push) Successful in 9s
2025-09-16 12:33:20 +00:00
ab
84dcdc343a Update .gitea/workflows/authentik-apps.yaml
Some checks failed
Terraform / Terraform (push) Failing after 19s
Check with kubeconform / lint (push) Successful in 9s
2025-09-16 12:31:54 +00:00
ab
094d80896a Add .gitea/workflows/authentik-apps.yaml
Some checks failed
Check with kubeconform / lint (push) Successful in 10s
Terraform / Terraform (push) Failing after 9s
2025-09-16 12:29:20 +00:00
AB from home.homenet
4ffc42af97 Added Authentik TF code
All checks were successful
Check with kubeconform / lint (push) Successful in 12s
2025-09-16 15:28:50 +03:00
ab
b1183896f9 Update k8s/core/argocd/values.yaml
All checks were successful
Check with kubeconform / lint (push) Successful in 12s
2025-09-16 11:57:50 +00:00
ab
6f17dc23f1 Update k8s/core/argocd/values.yaml
All checks were successful
Check with kubeconform / lint (push) Successful in 11s
2025-09-16 11:23:59 +00:00
ab
e353751031 Update k8s/apps/paperless/external-secrets.yaml
All checks were successful
Check with kubeconform / lint (push) Successful in 11s
2025-09-16 10:11:17 +00:00
ab
f3baf90672 Update k8s/core/authentik/values.yaml
All checks were successful
Check with kubeconform / lint (push) Successful in 12s
2025-09-15 22:23:52 +00:00
AB from home.homenet
d71935d063 fmt
All checks were successful
Check with kubeconform / lint (push) Successful in 1m11s
2025-09-16 00:28:54 +03:00
AB
7dde0d3f2a Merge branch 'main' of ssh://gt.hexor.cy:30022/ab/homelab
All checks were successful
Check with kubeconform / lint (push) Successful in 14s
2025-09-15 21:42:20 +03:00
AB
00cbd8830b Added authentik terraform 2025-09-15 21:42:01 +03:00
91 changed files with 5476 additions and 38 deletions

View File

@@ -0,0 +1,147 @@
# Auto-Update README Workflow
## Overview
This workflow automatically updates the `README.md` file with the current list of ArgoCD applications based on the directory structure in `k8s/`.
## How it works
1. **Trigger**: Workflow runs automatically when changes are pushed to `k8s/**` paths
2. **Scan**: Python script scans `k8s/` directory structure and finds all applications
3. **Generate**: Creates README.md with badges for all found applications
4. **Create PR**: If changes detected, creates a Merge Request for manual review
## Files
- `.gitea/workflows/update-readme.yaml` - GitHub Actions workflow
- `.gitea/scripts/generate-readme.py` - Python script for README generation
## Directory Structure
The script expects the following k8s directory structure:
```
k8s/
├── core/ # Core infrastructure applications
│ ├── argocd/
│ ├── authentik/
│ └── ...
├── apps/ # User applications
│ ├── gitea/
│ ├── immich/
│ └── ...
└── games/ # Game servers
├── minecraft/
└── ...
```
Each subdirectory name becomes an application name in the README.
## Required Secrets
The workflow requires the following secrets to be configured in Gitea:
| Secret | Description | Example |
|--------|-------------|---------|
| `GT_URL` | Gitea instance URL | `https://gt.hexor.cy` |
| `GT_TOKEN` | Gitea API token with repo write access | `glpat-xxxxx...` |
| `GT_OWNER` | Repository owner (username or org) | `ab` |
| `GT_REPO` | Repository name | `homelab` |
### How to create a Gitea Token
1. Go to Settings → Applications → Generate New Token
2. Give it a name like "README Update Bot"
3. Select scopes: `repo` (Full control of repositories)
4. Generate and copy the token
5. Add it as a secret in repository settings
## Badge Format
Badges are generated using a predictable pattern:
```markdown
[![app-name](https://ag.hexor.cy/api/badge?name=app-name&revision=true)](https://ag.hexor.cy/applications/argocd/app-name)
```
This allows you to immediately see which applications are:
- ✅ Healthy and synced (green badge)
- ⚠️ Degraded or out of sync (yellow badge)
- ❌ Unhealthy or failed (red badge)
## Manual Trigger
You can manually trigger the workflow from Gitea:
1. Go to Actions tab
2. Select "Auto-update README" workflow
3. Click "Run workflow"
4. Select branch and run
## Example Output
The generated README will look like:
```markdown
# homelab
ArgoCD homelab project
## ArgoCD Applications Status
| Application | Status |
| :--- | :---: |
| **argocd** | [![argocd](https://ag.hexor.cy/api/badge?name=argocd&revision=true)](https://ag.hexor.cy/applications/argocd/argocd) |
...
```
## Reviewing Pull Requests
When the workflow creates a PR:
1. Check the Actions tab for the workflow run details
2. Review the PR in the Pull Requests tab
3. Verify the application list matches your k8s/ structure
4. Merge when ready
The PR will include:
- Updated application list
- Timestamp of generation
- Automatic commit message
## Troubleshooting
### No PR created
- Check if there are actually changes in README.md
- Verify secrets are configured correctly
- Check workflow logs in Actions tab
### Wrong applications listed
- Verify k8s/ directory structure
- Ensure folder names match expected application names
- Check for hidden directories (starting with `.`)
### Badge not loading
- Verify ArgoCD badge API is accessible at `https://ag.hexor.cy`
- Check application name matches ArgoCD application name
- Ensure application exists in ArgoCD
## Maintenance
### Update badge URL
If you need to change the badge URL pattern, edit:
- `.gitea/scripts/generate-readme.py` - function `generate_badge_line()`
### Change workflow trigger
To modify when the workflow runs, edit:
- `.gitea/workflows/update-readme.yaml` - `on:` section
### Add new categories
To add new categories (besides core/apps/games), edit:
- `.gitea/scripts/generate-readme.py` - function `scan_k8s_directory()` and `generate_readme_content()`

91
.gitea/scripts/README.md Normal file
View File

@@ -0,0 +1,91 @@
# Wiki Generators for Homelab
Automated Wiki page generation scripts for documenting homelab infrastructure.
## 1. Authentik Applications Wiki Generator
Generates a Wiki page with all applications managed by Authentik from Terraform configuration.
### Files:
- `generate-apps-wiki.py` - Generates Applications.md from Terraform output
- `process-terraform-output.py` - Processes Terraform JSON output
### Workflow:
- **Trigger**: Push to `main` branch with Terraform changes
- **Workflow**: `.gitea/workflows/authentik-apps.yaml`
- **Output**: Applications Wiki page
## 2. Kubernetes Services Wiki Generator
Analyzes k8s/ directory and generates comprehensive documentation for all Kubernetes services.
### Files:
- `generate-k8s-wiki.py` - Main script for analyzing k8s services
### Features:
- **Service Types**: Detects Helm Charts, Kustomize, and YAML manifests
- **ArgoCD Integration**: Shows auto-sync status and project info
- **Service Discovery**: Lists all services, ingresses, and external secrets
- **Categorization**: Groups by apps, core, games categories
- **Detailed Analysis**: Shows deployments, containers, files
### Workflow:
- **Trigger**: Changes in `k8s/` directory
- **Workflow**: `.gitea/workflows/k8s-wiki.yaml`
- **Output**: Kubernetes-Services Wiki page
## GitHub Secrets Configuration
Required secrets in repository settings:
```
GT_URL=https://gt.hexor.cy
GT_WIKI_TOKEN=your_gitea_access_token
GT_OWNER=your_username
GT_REPO=homelab
```
## Generated Wiki Pages Structure
### Applications Page
- Table with icons (32x32), external/internal URLs
- Statistics by type (Proxy vs OAuth2)
- Grouping by categories (Core, Tools, Media, etc.)
### Kubernetes Services Page
- Overview table with service types and status
- Detailed sections by category
- ArgoCD integration status
- Service discovery information
## Local Testing
### Authentik Apps:
```bash
cd terraform/authentik
terraform output -json > terraform-output.json
python3 ../../.gitea/scripts/process-terraform-output.py terraform-output.json processed-output.json
python3 ../../.gitea/scripts/generate-apps-wiki.py processed-output.json
```
### K8s Services:
```bash
pip install pyyaml
python3 .gitea/scripts/generate-k8s-wiki.py k8s/ Kubernetes-Services.md
```
## Troubleshooting
### Common Issues:
1. **Terraform output parsing errors**
- Check for [command] prefix in output
- Verify JSON structure with debug mode
2. **Wiki upload failures**
- Verify Gitea token permissions
- Check network connectivity to Gitea instance
3. **YAML parsing errors in k8s analysis**
- Ensure valid YAML syntax in k8s files
- Check PyYAML installation

View File

@@ -0,0 +1,226 @@
#!/usr/bin/env python3
"""
Script for generating Wiki page with applications list from Terraform outputs
"""
import json
import sys
import os
from datetime import datetime
def generate_markdown_table(apps_data):
"""Generates Markdown table for applications"""
# Combine all applications
all_apps = []
if 'proxy_apps' in apps_data:
for key, app in apps_data['proxy_apps'].items():
all_apps.append({
'key': key,
'name': app['name'],
'type': app['type'],
'url': app['url'],
'internal_url': app.get('internal_url', '-'),
'group': app['group'],
'description': app['description'],
'icon': app['icon'],
'slug': app['slug']
})
if 'oauth_apps' in apps_data:
for key, app in apps_data['oauth_apps'].items():
all_apps.append({
'key': key,
'name': app['name'],
'type': app['type'],
'url': app['url'],
'internal_url': '-', # OAuth apps don't have internal URLs
'group': app['group'],
'description': app['description'],
'icon': app['icon'],
'slug': app['slug']
})
# Sort by groups, then by name
all_apps.sort(key=lambda x: (x['group'], x['name']))
# Generate Markdown
markdown = []
markdown.append("# Authentik Applications")
markdown.append("")
markdown.append(f"*Automatically generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}*")
markdown.append("")
markdown.append("## All Applications")
markdown.append("")
# Table
markdown.append("| Icon | Name | Type | External URL | Internal URL | Group | Description |")
markdown.append("|:----:|------|------|--------------|--------------|-------|-------------|")
for app in all_apps:
# Icon with size constraint
if app['icon']:
icon = f'<img src="{app["icon"]}" width="32" height="32" alt="{app["name"]}">'
else:
icon = "📱"
# External URL link
external_link = f"[🔗 {app['url'].replace('https://', '').replace('http://', '')}]({app['url']})" if app.get('url') else "-"
# Internal URL (only for proxy apps)
internal_url = app.get('internal_url', '-')
if internal_url != '-':
# Show full internal URL without shortening
internal_url = f"`{internal_url}`"
description = app['description'] if app['description'] else "-"
markdown.append(f"| {icon} | **{app['name']}** | {app['type']} | {external_link} | {internal_url} | {app['group']} | {description} |")
markdown.append("")
# Statistics
proxy_count = len(apps_data.get('proxy_apps', {}))
oauth_count = len(apps_data.get('oauth_apps', {}))
total_count = proxy_count + oauth_count
markdown.append("## Statistics")
markdown.append("")
markdown.append(f"- **Total applications**: {total_count}")
markdown.append(f"- **Proxy applications**: {proxy_count}")
markdown.append(f"- **OAuth2/OpenID applications**: {oauth_count}")
markdown.append("")
# Grouping by types
groups = {}
for app in all_apps:
group = app['group']
if group not in groups:
groups[group] = {'proxy': 0, 'oauth': 0}
if app['type'] == 'Proxy':
groups[group]['proxy'] += 1
else:
groups[group]['oauth'] += 1
markdown.append("## Applications by Groups")
markdown.append("")
for group, counts in sorted(groups.items()):
total = counts['proxy'] + counts['oauth']
markdown.append(f"- **{group}**: {total} applications (Proxy: {counts['proxy']}, OAuth: {counts['oauth']})")
markdown.append("")
markdown.append("---")
markdown.append("*This page is automatically generated via Terraform CI/CD*")
return "\n".join(markdown)
def parse_terraform_output(output_data):
"""Parse Terraform output JSON structure"""
# Check if this is a full terraform output (with value, type, sensitive fields)
if isinstance(output_data, dict) and 'applications_for_wiki' in output_data:
# This is full terraform output format
app_output = output_data.get('applications_for_wiki', {})
if isinstance(app_output, dict) and 'value' in app_output:
return app_output['value']
else:
return app_output
else:
# This is already the value extracted
return output_data
def main():
if len(sys.argv) < 2:
print("Usage: python3 generate-apps-wiki.py <terraform-output-json> [--debug]")
sys.exit(1)
output_file = sys.argv[1]
debug = "--debug" in sys.argv
try:
# Check if file exists and has content
if not os.path.exists(output_file):
print(f"ERROR: File {output_file} not found")
sys.exit(1)
file_size = os.path.getsize(output_file)
if file_size == 0:
print(f"ERROR: File {output_file} is empty")
sys.exit(1)
print(f"📄 Reading Terraform output file: {output_file} ({file_size} bytes)")
# Read file content
with open(output_file, 'r') as f:
content = f.read()
if debug:
print(f"🔍 File content preview: {content[:200]}...")
# Clean content - remove command line if present
if content.startswith('[command]'):
print("⚠️ Detected command prefix, removing...")
lines = content.split('\n', 1)
if len(lines) > 1:
content = lines[1]
if debug:
print(f"🔍 Cleaned content preview: {content[:200]}...")
else:
print("ERROR: File contains only command line, no JSON data")
sys.exit(1)
# Parse JSON
try:
terraform_output = json.loads(content)
except json.JSONDecodeError as e:
print(f"ERROR: Invalid JSON in {output_file}: {e}")
print(f"Content starts with: {repr(content[:100])}")
# Try to find where JSON starts
json_start = content.find('{')
if json_start > 0:
print(f"Found JSON starting at position {json_start}, retrying...")
content = content[json_start:]
try:
terraform_output = json.loads(content)
except json.JSONDecodeError as e2:
print(f"ERROR: Still invalid JSON: {e2}")
sys.exit(1)
else:
sys.exit(1)
# Extract application data using helper function
apps_data = parse_terraform_output(terraform_output)
if not apps_data:
print("ERROR: No applications data found in Terraform output")
if debug:
print(f"Full output structure: {json.dumps(terraform_output, indent=2)[:500]}...")
sys.exit(1)
# Check if we have correct structure
if 'proxy_apps' not in apps_data and 'oauth_apps' not in apps_data:
print("ERROR: Expected 'proxy_apps' or 'oauth_apps' in output")
print(f"Available keys: {list(apps_data.keys())}")
if debug and apps_data:
print(f"Data structure: {json.dumps(apps_data, indent=2)[:500]}...")
sys.exit(1)
print(f"📊 Found {len(apps_data.get('proxy_apps', {}))} proxy apps, {len(apps_data.get('oauth_apps', {}))} oauth apps")
# Generate Markdown
markdown_content = generate_markdown_table(apps_data)
# Write result
wiki_file = "Applications.md"
with open(wiki_file, 'w', encoding='utf-8') as f:
f.write(markdown_content)
print(f"✅ Wiki page generated: {wiki_file}")
print(f"📊 Total applications: {len(apps_data.get('proxy_apps', {})) + len(apps_data.get('oauth_apps', {}))}")
except Exception as e:
print(f"ERROR: {e}")
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,298 @@
#!/usr/bin/env python3
"""
Script for generating Wiki page with Kubernetes services from k8s/ directory
"""
import os
import yaml
import json
import sys
from datetime import datetime
from collections import defaultdict
class K8sService:
def __init__(self, name, category, path):
self.name = name
self.category = category
self.path = path
self.namespace = None
self.deployment_type = "Unknown"
self.helm_charts = []
self.services = []
self.ingresses = []
self.external_secrets = []
self.deployments = []
self.pvcs = []
self.argo_app = None
self.files = []
def __repr__(self):
return f"K8sService({self.name}, {self.deployment_type})"
def parse_yaml_file(filepath):
"""Parse YAML file and return content"""
try:
with open(filepath, 'r') as f:
# Load all documents in the file
docs = list(yaml.safe_load_all(f))
return docs if len(docs) > 1 else docs[0] if docs else None
except Exception as e:
print(f" ⚠️ Error parsing {filepath}: {e}")
return None
def analyze_service_directory(service_path, service_name, category):
"""Analyze a service directory and extract information"""
service = K8sService(service_name, category, service_path)
# List all files
for file in os.listdir(service_path):
if file.endswith('.yaml') or file.endswith('.yml'):
service.files.append(file)
filepath = os.path.join(service_path, file)
# Parse YAML content
content = parse_yaml_file(filepath)
if not content:
continue
# Handle multiple documents in one file
documents = content if isinstance(content, list) else [content]
for doc in documents:
if not isinstance(doc, dict) or 'kind' not in doc:
continue
kind = doc['kind']
metadata = doc.get('metadata', {})
# ArgoCD Application
if kind == 'Application' and doc.get('apiVersion', '').startswith('argoproj.io'):
service.argo_app = {
'name': metadata.get('name', ''),
'namespace': doc.get('spec', {}).get('destination', {}).get('namespace', ''),
'project': doc.get('spec', {}).get('project', ''),
'auto_sync': doc.get('spec', {}).get('syncPolicy', {}).get('automated') is not None
}
service.namespace = service.argo_app['namespace']
# Kustomization
elif kind == 'Kustomization':
if 'helmCharts' in doc:
service.deployment_type = "Helm Chart"
for chart in doc.get('helmCharts', []):
service.helm_charts.append({
'name': chart.get('name', ''),
'repo': chart.get('repo', ''),
'version': chart.get('version', ''),
'namespace': chart.get('namespace', service.namespace)
})
else:
service.deployment_type = "Kustomize"
# Deployment
elif kind == 'Deployment':
service.deployments.append({
'name': metadata.get('name', ''),
'namespace': metadata.get('namespace', service.namespace),
'replicas': doc.get('spec', {}).get('replicas', 1),
'containers': [c.get('name', '') for c in doc.get('spec', {}).get('template', {}).get('spec', {}).get('containers', [])]
})
if service.deployment_type == "Unknown":
service.deployment_type = "YAML Manifests"
# Service
elif kind == 'Service':
svc_spec = doc.get('spec', {})
service.services.append({
'name': metadata.get('name', ''),
'type': svc_spec.get('type', 'ClusterIP'),
'ports': svc_spec.get('ports', [])
})
# Ingress
elif kind == 'Ingress':
rules = doc.get('spec', {}).get('rules', [])
hosts = []
for rule in rules:
if 'host' in rule:
hosts.append(rule['host'])
service.ingresses.append({
'name': metadata.get('name', ''),
'hosts': hosts
})
# ExternalSecret
elif kind == 'ExternalSecret':
service.external_secrets.append({
'name': metadata.get('name', ''),
'store': doc.get('spec', {}).get('secretStoreRef', {}).get('name', '')
})
# PersistentVolumeClaim
elif kind == 'PersistentVolumeClaim':
service.pvcs.append({
'name': metadata.get('name', ''),
'size': doc.get('spec', {}).get('resources', {}).get('requests', {}).get('storage', '')
})
# If no specific deployment type found but has YAML files
if service.deployment_type == "Unknown" and service.files:
service.deployment_type = "YAML Manifests"
return service
def generate_markdown_table(services):
"""Generate markdown table for services"""
markdown = []
markdown.append("# Kubernetes Services")
markdown.append("")
markdown.append(f"*Automatically generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')}*")
markdown.append("")
# Group by category
categories = defaultdict(list)
for service in services:
categories[service.category].append(service)
# Statistics
markdown.append("## Statistics")
markdown.append("")
markdown.append(f"- **Total Services**: {len(services)}")
markdown.append(f"- **Categories**: {len(categories)}")
helm_count = sum(1 for s in services if s.deployment_type == "Helm Chart")
kustomize_count = sum(1 for s in services if s.deployment_type == "Kustomize")
yaml_count = sum(1 for s in services if s.deployment_type == "YAML Manifests")
markdown.append(f"- **Helm Charts**: {helm_count}")
markdown.append(f"- **Kustomize**: {kustomize_count}")
markdown.append(f"- **YAML Manifests**: {yaml_count}")
markdown.append("")
# Main table
markdown.append("## All Services")
markdown.append("")
markdown.append("| Service | Category | Type | Namespace | Ingresses | Services | Secrets | Auto-Sync |")
markdown.append("|---------|----------|------|-----------|-----------|----------|---------|-----------|")
for category in sorted(categories.keys()):
for service in sorted(categories[category], key=lambda x: x.name):
# Service name with link to directory
name_link = f"[{service.name}](k8s/{service.category}/{service.name}/)"
# Deployment type with emoji
type_emoji = {
"Helm Chart": "🎩",
"Kustomize": "🔧",
"YAML Manifests": "📄",
"Unknown": ""
}
type_str = f"{type_emoji.get(service.deployment_type, '')} {service.deployment_type}"
# Ingresses
ingresses = []
for ing in service.ingresses:
for host in ing['hosts']:
ingresses.append(f"[{host}](https://{host})")
ingress_str = "<br>".join(ingresses) if ingresses else "-"
# Services
svc_list = []
for svc in service.services:
ports = [f"{p.get('port', '?')}" for p in svc['ports']]
svc_list.append(f"`{svc['name']}:{','.join(ports)}`")
svc_str = "<br>".join(svc_list) if svc_list else "-"
# External Secrets
secrets_str = f"{len(service.external_secrets)} secrets" if service.external_secrets else "-"
# Auto-sync
auto_sync = "" if service.argo_app and service.argo_app.get('auto_sync') else ""
markdown.append(f"| **{name_link}** | {category} | {type_str} | {service.namespace or '-'} | {ingress_str} | {svc_str} | {secrets_str} | {auto_sync} |")
markdown.append("")
# Detailed sections by category
for category in sorted(categories.keys()):
markdown.append(f"## {category.title()} Services")
markdown.append("")
for service in sorted(categories[category], key=lambda x: x.name):
markdown.append(f"### {service.name}")
markdown.append("")
# Basic info
markdown.append(f"- **Type**: {service.deployment_type}")
markdown.append(f"- **Namespace**: {service.namespace or 'Not specified'}")
markdown.append(f"- **Path**: `{service.path}`")
# Helm charts
if service.helm_charts:
markdown.append("- **Helm Charts**:")
for chart in service.helm_charts:
markdown.append(f" - {chart['name']} v{chart['version']} from {chart['repo']}")
# Deployments
if service.deployments:
markdown.append("- **Deployments**:")
for dep in service.deployments:
containers = ', '.join(dep['containers'])
markdown.append(f" - {dep['name']} ({dep['replicas']} replicas) - Containers: {containers}")
# Files
if service.files:
markdown.append(f"- **Files**: {', '.join(sorted(service.files))}")
markdown.append("")
markdown.append("---")
markdown.append("*This page is automatically generated from k8s/ directory via CI/CD*")
return "\n".join(markdown)
def main():
if len(sys.argv) < 2:
print("Usage: generate-k8s-wiki.py <k8s-directory> [output-file]")
sys.exit(1)
k8s_dir = sys.argv[1]
output_file = sys.argv[2] if len(sys.argv) > 2 else "Kubernetes-Services.md"
if not os.path.exists(k8s_dir):
print(f"❌ Directory {k8s_dir} not found")
sys.exit(1)
print(f"📂 Scanning {k8s_dir}...")
services = []
# Scan each category directory
for category in ['apps', 'core', 'games']:
category_path = os.path.join(k8s_dir, category)
if not os.path.exists(category_path):
print(f" ⚠️ Category {category} not found")
continue
print(f"📁 Processing {category}/...")
# Scan each service in category
for service_name in os.listdir(category_path):
service_path = os.path.join(category_path, service_name)
if os.path.isdir(service_path):
print(f" 🔍 Analyzing {service_name}...")
service = analyze_service_directory(service_path, service_name, category)
services.append(service)
print(f"\n✅ Found {len(services)} services")
# Generate markdown
markdown = generate_markdown_table(services)
# Write output
with open(output_file, 'w', encoding='utf-8') as f:
f.write(markdown)
print(f"📄 Wiki page generated: {output_file}")
print(f"📊 Total services: {len(services)}")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,161 @@
#!/usr/bin/env python3
"""
Generate README.md with ArgoCD application status badges.
Scans k8s/ directory structure to find all applications and generates badges for them.
"""
import os
import sys
from pathlib import Path
from typing import Dict, List
def scan_k8s_directory(k8s_path: str) -> Dict[str, List[str]]:
"""
Scan k8s/ directory and return applications grouped by category.
Args:
k8s_path: Path to k8s directory
Returns:
Dictionary with categories as keys and lists of app names as values
"""
apps_by_category = {
'core': [],
'apps': [],
'games': []
}
k8s_dir = Path(k8s_path)
for category in apps_by_category.keys():
category_path = k8s_dir / category
if category_path.exists() and category_path.is_dir():
# Get all subdirectories (each subdirectory is an app)
apps = [
d.name for d in category_path.iterdir()
if d.is_dir() and not d.name.startswith('.')
]
apps_by_category[category] = sorted(apps)
return apps_by_category
def generate_badge_line(app_name: str) -> str:
"""
Generate markdown line with badge for an application.
Args:
app_name: Name of the application
Returns:
Markdown formatted string with badge
"""
badge_url = f"https://ag.hexor.cy/api/badge?name={app_name}&revision=true"
app_url = f"https://ag.hexor.cy/applications/argocd/{app_name}"
return f"| **{app_name}** | [![{app_name}]({badge_url})]({app_url}) |"
def generate_readme_content(apps_by_category: Dict[str, List[str]]) -> str:
"""
Generate README.md content with all applications.
Args:
apps_by_category: Dictionary with apps grouped by category
Returns:
Complete README.md content
"""
lines = [
"# homelab",
"",
"ArgoCD homelab project",
"",
"## ArgoCD Applications Status",
"",
"<table>",
"<tr>",
"<td valign=\"top\" width=\"50%\">",
"",
"### Core Applications",
"",
"| Application | Status |",
"| :--- | :---: |"
]
# Add core applications
for app in apps_by_category.get('core', []):
lines.append(generate_badge_line(app))
lines.extend([
"",
"### Games",
"",
"| Application | Status |",
"| :--- | :---: |"
])
# Add games
for app in apps_by_category.get('games', []):
lines.append(generate_badge_line(app))
lines.extend([
"</td>",
"<td valign=\"top\" width=\"50%\">",
"",
"### Applications",
"",
"| Application | Status |",
"| :--- | :---: |"
])
# Add applications
for app in apps_by_category.get('apps', []):
lines.append(generate_badge_line(app))
lines.extend([
"",
"</td>",
"</tr>",
"</table>"
])
return '\n'.join(lines) + '\n'
def main():
if len(sys.argv) < 2:
print("Usage: generate-readme.py <k8s-directory> [output-file]")
print("Example: generate-readme.py k8s/ README.md")
sys.exit(1)
k8s_path = sys.argv[1]
output_file = sys.argv[2] if len(sys.argv) > 2 else "README.md"
if not os.path.exists(k8s_path):
print(f"Error: Directory {k8s_path} does not exist")
sys.exit(1)
print(f"📁 Scanning {k8s_path}...")
apps_by_category = scan_k8s_directory(k8s_path)
# Print statistics
total_apps = sum(len(apps) for apps in apps_by_category.values())
print(f"✅ Found {total_apps} applications:")
for category, apps in apps_by_category.items():
if apps:
print(f" - {category}: {len(apps)} apps")
print(f"📝 Generating {output_file}...")
readme_content = generate_readme_content(apps_by_category)
with open(output_file, 'w', encoding='utf-8') as f:
f.write(readme_content)
print(f"{output_file} generated successfully")
print(f" Total lines: {len(readme_content.splitlines())}")
print(f" File size: {len(readme_content)} bytes")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,105 @@
#!/usr/bin/env python3
"""
Process Terraform output to extract applications_for_wiki data
Handles various output formats and cleans up invalid JSON
"""
import json
import sys
import os
def clean_command_prefix(content):
"""Remove [command] prefix if present"""
if content.startswith('[command]'):
lines = content.split('\n', 1)
if len(lines) > 1:
return lines[1]
return content
def extract_valid_json(content):
"""Extract valid JSON from content that might have extra data"""
# Find first { and last matching }
start = content.find('{')
if start < 0:
return None
count = 0
end = start
for i in range(start, len(content)):
if content[i] == '{':
count += 1
elif content[i] == '}':
count -= 1
if count == 0:
end = i + 1
break
if end > start and count == 0:
return content[start:end]
return None
def extract_value(data):
"""Extract value from Terraform output format"""
if isinstance(data, dict) and 'value' in data:
return data['value']
return data
def main():
if len(sys.argv) != 3:
print("Usage: process-terraform-output.py <input-file> <output-file>")
sys.exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2]
try:
# Read input file
with open(input_file, 'r') as f:
content = f.read()
# Clean command prefix if present
content = clean_command_prefix(content)
# Try to parse JSON directly
try:
data = json.loads(content)
print("✅ Valid JSON parsed successfully")
except json.JSONDecodeError as e:
print(f"⚠️ Initial JSON parse failed: {e}")
print("🔍 Attempting to extract valid JSON portion...")
# Try to extract valid JSON
valid_json = extract_valid_json(content)
if valid_json:
try:
data = json.loads(valid_json)
print("✅ Extracted valid JSON successfully")
except json.JSONDecodeError as e2:
print(f"❌ Failed to parse extracted JSON: {e2}")
sys.exit(1)
else:
print("❌ Could not extract valid JSON from content")
sys.exit(1)
# Extract value if it's wrapped in Terraform output format
result = extract_value(data)
# Write output
with open(output_file, 'w') as f:
json.dump(result, f, indent=2)
print(f"✅ Processed output written to {output_file}")
# Show preview
preview = json.dumps(result, indent=2)[:200]
print(f"📄 Preview: {preview}...")
except FileNotFoundError:
print(f"❌ Input file {input_file} not found")
sys.exit(1)
except Exception as e:
print(f"❌ Error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,139 @@
name: 'Terraform'
on:
push:
branches: [ "main" ]
paths:
- 'terraform/authentik/**'
pull_request:
permissions:
contents: read
jobs:
terraform:
name: 'Terraform'
runs-on: ubuntu-latest
environment: production
defaults:
run:
shell: bash
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
- name: Terraform Init
run: terraform init
working-directory: ./terraform/authentik
- name: Terraform Format
run: terraform fmt -check
continue-on-error: true
working-directory: ./terraform/authentik
- name: Terraform Apply
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
if: success()
continue-on-error: true
run: |
echo "📋 Starting Wiki generation..."
cd ./terraform/authentik
# Get terraform output
echo "🔍 Generating Terraform output..."
terraform output -json applications_for_wiki > terraform-raw-output.json 2>&1
# Process output to extract clean JSON
echo "📤 Processing Terraform output..."
python3 ../../.gitea/scripts/process-terraform-output.py terraform-raw-output.json terraform-output.json
# Run wiki generation
echo "📊 Running wiki generation script..."
if python3 ../../.gitea/scripts/generate-apps-wiki.py terraform-output.json; then
echo "✅ Wiki content generated successfully"
else
echo "⚠️ Wiki generation failed, retrying with debug..."
python3 ../../.gitea/scripts/generate-apps-wiki.py terraform-output.json --debug || echo "⚠️ Wiki generation failed"
fi
# Check results
if [ -f "Applications.md" ]; then
echo "✅ Wiki file created: $(wc -l < Applications.md) lines"
else
echo "⚠️ Wiki content not generated"
exit 0
fi
working-directory: ./
- name: Upload Wiki to Gitea
if: success()
continue-on-error: true
run: |
cd ./terraform/authentik
# Set variables
GITEA_URL="${{ secrets.GT_URL }}"
GITEA_TOKEN="${{ secrets.GT_WIKI_TOKEN }}"
GITEA_OWNER="${{ secrets.GT_OWNER }}"
GITEA_REPO="${{ secrets.GT_REPO }}"
# Debug variables (without exposing token)
echo "🔍 Checking variables..."
echo "GITEA_URL: ${GITEA_URL:-NOT SET}"
echo "GITEA_OWNER: ${GITEA_OWNER:-NOT SET}"
echo "GITEA_REPO: ${GITEA_REPO:-NOT SET}"
echo "GITEA_TOKEN: $(if [ -n "$GITEA_TOKEN" ]; then echo "SET"; else echo "NOT SET"; fi)"
# Check if file exists
if [ ! -f "Applications.md" ]; then
echo "⚠️ Applications.md not found, skipping wiki update"
exit 0
fi
echo "📤 Uploading to Gitea Wiki..."
# Encode content to base64
CONTENT=$(base64 -w 0 Applications.md)
# Check if wiki page exists
WIKI_PAGE_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: token $GITEA_TOKEN" \
"$GITEA_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/wiki/page/Applications" || echo "000")
if [ "$WIKI_PAGE_EXISTS" = "200" ]; then
echo "📝 Updating existing wiki page..."
curl -X PATCH \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"title\": \"Applications\",
\"content_base64\": \"$CONTENT\",
\"message\": \"Update applications list from CI/CD [$(date)]\"
}" \
"$GITEA_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/wiki/page/Applications" || echo "⚠️ Wiki update failed"
else
echo "📄 Creating new wiki page..."
curl -X POST \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"title\": \"Applications\",
\"content_base64\": \"$CONTENT\",
\"message\": \"Create applications list from CI/CD [$(date)]\"
}" \
"$GITEA_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/wiki/new" || echo "⚠️ Wiki creation failed"
fi
echo "✅ Wiki update process completed"
working-directory: ./

View File

@@ -0,0 +1,111 @@
name: 'Update Kubernetes Services Wiki'
on:
push:
branches: [ "main" ]
# paths:
# - 'k8s/**'
# - '.gitea/scripts/generate-k8s-wiki.py'
workflow_dispatch:
permissions:
contents: read
jobs:
update-k8s-wiki:
name: 'Generate and Update K8s Wiki'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Install Python dependencies
run: |
pip install pyyaml
- name: Generate K8s Services Wiki
run: |
echo "📋 Starting K8s wiki generation..."
python3 .gitea/scripts/generate-k8s-wiki.py k8s/ Kubernetes-Services.md
if [ -f "Kubernetes-Services.md" ]; then
echo "✅ Wiki content generated successfully"
echo "📄 File size: $(wc -c < Kubernetes-Services.md) bytes"
echo "📄 Lines: $(wc -l < Kubernetes-Services.md)"
else
echo "❌ Wiki content not generated"
exit 1
fi
- name: Upload Wiki to Gitea
continue-on-error: true
run: |
# Set variables
GITEA_URL="${{ secrets.GT_URL }}"
GITEA_TOKEN="${{ secrets.GT_WIKI_TOKEN }}"
GITEA_OWNER="${{ secrets.GT_OWNER }}"
GITEA_REPO="${{ secrets.GT_REPO }}"
# Debug variables (without exposing token)
echo "🔍 Checking variables..."
echo "GITEA_URL: ${GITEA_URL:-NOT SET}"
echo "GITEA_OWNER: ${GITEA_OWNER:-NOT SET}"
echo "GITEA_REPO: ${GITEA_REPO:-NOT SET}"
echo "GITEA_TOKEN: $(if [ -n "$GITEA_TOKEN" ]; then echo "SET"; else echo "NOT SET"; fi)"
if [ ! -f "Kubernetes-Services.md" ]; then
echo "❌ Kubernetes-Services.md not found"
exit 1
fi
echo "📤 Uploading to Gitea Wiki..."
# Encode content to base64
CONTENT=$(base64 -w 0 Kubernetes-Services.md)
# Check if wiki page exists
WIKI_PAGE_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: token $GITEA_TOKEN" \
"$GITEA_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/wiki/page/Kubernetes-Services" || echo "000")
if [ "$WIKI_PAGE_EXISTS" = "200" ]; then
echo "📝 Updating existing wiki page..."
curl -X PATCH \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"title\": \"Kubernetes-Services\",
\"content_base64\": \"$CONTENT\",
\"message\": \"Update K8s services list from CI/CD [$(date)]\"
}" \
"$GITEA_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/wiki/page/Kubernetes-Services" || echo "⚠️ Wiki update failed"
else
echo "📄 Creating new wiki page..."
curl -X POST \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"title\": \"Kubernetes-Services\",
\"content_base64\": \"$CONTENT\",
\"message\": \"Create K8s services list from CI/CD [$(date)]\"
}" \
"$GITEA_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/wiki/new" || echo "⚠️ Wiki creation failed"
fi
echo "✅ Wiki update process completed"
echo "🔗 Wiki URL: $GITEA_URL/$GITEA_OWNER/$GITEA_REPO/wiki/Kubernetes-Services"
- name: Summary
if: always()
run: |
echo "## 📊 K8s Wiki Update Summary" >> $GITHUB_STEP_SUMMARY
if [ -f "Kubernetes-Services.md" ]; then
echo "- ✅ K8s services analyzed" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Wiki page generated" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Services found:** $(grep -c '^|' Kubernetes-Services.md || echo 0)" >> $GITHUB_STEP_SUMMARY
else
echo "- ❌ Wiki generation failed" >> $GITHUB_STEP_SUMMARY
fi
echo "**Generated at:** $(date)" >> $GITHUB_STEP_SUMMARY

View File

@@ -2,6 +2,8 @@ name: Check with kubeconform
on:
push:
branches: [ main ]
paths:
- 'k8s/**'
jobs:
lint:
runs-on: ubuntu-latest

View File

@@ -0,0 +1,164 @@
name: 'Auto-update README'
on:
push:
branches: [ "main" ]
paths:
- 'k8s/**'
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
update-readme:
name: 'Generate README and Create MR'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Configure Git
run: |
git config --global user.name "Gitea Actions Bot"
git config --global user.email "actions@gitea.local"
- name: Generate README
run: |
echo "📋 Starting README generation..."
python3 .gitea/scripts/generate-readme.py k8s/ README.md
if [ -f "README.md" ]; then
echo "✅ README generated successfully"
echo "📄 File size: $(wc -c < README.md) bytes"
echo "📄 Lines: $(wc -l < README.md)"
else
echo "❌ README not generated"
exit 1
fi
- name: Check for changes
id: check_changes
run: |
if git diff --quiet README.md; then
echo "No changes detected in README.md"
echo "has_changes=false" >> $GITHUB_OUTPUT
else
echo "Changes detected in README.md"
echo "has_changes=true" >> $GITHUB_OUTPUT
fi
- name: Create Pull Request
if: steps.check_changes.outputs.has_changes == 'true'
run: |
# Set variables
GITEA_URL="${{ secrets.GT_URL }}"
GITEA_TOKEN="${{ secrets.GT_TOKEN }}"
GITEA_OWNER="${{ secrets.GT_OWNER }}"
GITEA_REPO="${{ secrets.GT_REPO }}"
BRANCH_NAME="auto-update-readme-$(date +%Y%m%d-%H%M%S)"
echo "🔍 Configuration:"
echo "GITEA_URL: ${GITEA_URL:-NOT SET}"
echo "GITEA_OWNER: ${GITEA_OWNER:-NOT SET}"
echo "GITEA_REPO: ${GITEA_REPO:-NOT SET}"
echo "BRANCH_NAME: $BRANCH_NAME"
# Create and push new branch
echo "🌿 Creating branch: $BRANCH_NAME"
git checkout -b "$BRANCH_NAME"
git add README.md
git commit -m "Auto-update README with current k8s applications" \
-m "Generated by CI/CD workflow on $(date +%Y-%m-%d\ %H:%M:%S)" \
-m "This PR updates the README.md file with the current list of applications found in the k8s/ directory structure."
# Push branch to remote
echo "📤 Pushing branch to remote..."
git push origin "$BRANCH_NAME"
# Create Pull Request using Gitea API
echo "🔀 Creating Pull Request..."
PR_TITLE="Auto-update README with k8s applications"
# Create PR body
cat > /tmp/pr_body.json <<EOF
{
"title": "$PR_TITLE",
"body": "This PR automatically updates README.md based on the current k8s/ directory structure.\n\n## Changes\n- Updated application list in README.md\n- Applications are now synced with k8s/ folders\n\n## Review\nPlease review and merge if everything looks correct.\n\n---\n🤖 This PR was automatically generated by CI/CD workflow\n⏰ Generated at: $(date '+%Y-%m-%d %H:%M:%S')",
"head": "$BRANCH_NAME",
"base": "main"
}
EOF
# Create PR via API
echo "Making API request to: $GITEA_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/pulls"
echo "Request body:"
cat /tmp/pr_body.json
RESPONSE=$(curl -s -w "\nHTTP_CODE:%{http_code}" -X POST \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d @/tmp/pr_body.json \
"$GITEA_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/pulls")
# Extract HTTP code and response body
HTTP_CODE=$(echo "$RESPONSE" | grep "HTTP_CODE:" | cut -d':' -f2)
RESPONSE_BODY=$(echo "$RESPONSE" | sed '/HTTP_CODE:/d')
echo "API Response (HTTP $HTTP_CODE):"
echo "$RESPONSE_BODY"
# Extract PR number and URL from response
PR_NUMBER=$(echo "$RESPONSE_BODY" | grep -o '"number":[0-9]*' | head -1 | cut -d':' -f2)
if [ -n "$PR_NUMBER" ] && [ "$HTTP_CODE" = "201" ]; then
echo "✅ Pull Request created successfully!"
echo "📝 PR #$PR_NUMBER"
echo "🔗 URL: $GITEA_URL/$GITEA_OWNER/$GITEA_REPO/pulls/$PR_NUMBER"
# Save PR info for summary
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
echo "pr_url=$GITEA_URL/$GITEA_OWNER/$GITEA_REPO/pulls/$PR_NUMBER" >> $GITHUB_OUTPUT
else
echo "⚠️ Failed to create Pull Request (HTTP $HTTP_CODE)"
echo "Response: $RESPONSE_BODY"
# Check if PR already exists
if echo "$RESPONSE_BODY" | grep -q "already exists"; then
echo " PR already exists for this branch"
exit 0
fi
exit 1
fi
- name: Summary
if: always()
run: |
echo "## 📊 README Update Summary" >> $GITHUB_STEP_SUMMARY
if [ -f "README.md" ]; then
echo "- ✅ README generated successfully" >> $GITHUB_STEP_SUMMARY
if [ "${{ steps.check_changes.outputs.has_changes }}" = "true" ]; then
echo "- ✅ Changes detected" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Pull Request created" >> $GITHUB_STEP_SUMMARY
if [ -n "${{ steps.create_pr.outputs.pr_number }}" ]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "**PR:** [#${{ steps.create_pr.outputs.pr_number }}](${{ steps.create_pr.outputs.pr_url }})" >> $GITHUB_STEP_SUMMARY
fi
else
echo "- No changes detected - README already up to date" >> $GITHUB_STEP_SUMMARY
fi
else
echo "- ❌ README generation failed" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Generated at:** $(date)" >> $GITHUB_STEP_SUMMARY

7
.gitignore vendored
View File

@@ -10,13 +10,12 @@
crash.log
crash.*.log
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars
*.tfvars.json
!*terraform.tfvars
# claude ai
.claude/
# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf

View File

@@ -1,3 +1,61 @@
# homelab
ArgoCD homelab project
## ArgoCD Applications Status
<table>
<tr>
<td valign="top" width="50%">
### Core Applications
| Application | Status |
| :--- | :---: |
| **argocd** | [![argocd](https://ag.hexor.cy/api/badge?name=argocd&revision=true)](https://ag.hexor.cy/applications/argocd/argocd) |
| **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) |
| **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) |
| **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) |
### Games
| Application | Status |
| :--- | :---: |
| **beam-ng** | [![beam-ng](https://ag.hexor.cy/api/badge?name=beam-ng&revision=true)](https://ag.hexor.cy/applications/argocd/beam-ng) |
| **counter-strike-16** | [![counter-strike-16](https://ag.hexor.cy/api/badge?name=counter-strike-16&revision=true)](https://ag.hexor.cy/applications/argocd/counter-strike-16) |
| **minecraft** | [![minecraft](https://ag.hexor.cy/api/badge?name=minecraft&revision=true)](https://ag.hexor.cy/applications/argocd/minecraft) |
</td>
<td valign="top" width="50%">
### Applications
| Application | Status |
| :--- | :---: |
| **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) |
| **immich** | [![immich](https://ag.hexor.cy/api/badge?name=immich&revision=true)](https://ag.hexor.cy/applications/argocd/immich) |
| **iperf3** | [![iperf3](https://ag.hexor.cy/api/badge?name=iperf3&revision=true)](https://ag.hexor.cy/applications/argocd/iperf3) |
| **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) |
| **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) |
| **pasarguard** | [![pasarguard](https://ag.hexor.cy/api/badge?name=pasarguard&revision=true)](https://ag.hexor.cy/applications/argocd/pasarguard) |
| **qbittorent-nas** | [![qbittorent-nas](https://ag.hexor.cy/api/badge?name=qbittorent-nas&revision=true)](https://ag.hexor.cy/applications/argocd/qbittorent-nas) |
| **rustdesk** | [![rustdesk](https://ag.hexor.cy/api/badge?name=rustdesk&revision=true)](https://ag.hexor.cy/applications/argocd/rustdesk) |
| **sonarr-stack** | [![sonarr-stack](https://ag.hexor.cy/api/badge?name=sonarr-stack&revision=true)](https://ag.hexor.cy/applications/argocd/sonarr-stack) |
| **stirling-pdf** | [![stirling-pdf](https://ag.hexor.cy/api/badge?name=stirling-pdf&revision=true)](https://ag.hexor.cy/applications/argocd/stirling-pdf) |
| **syncthing** | [![syncthing](https://ag.hexor.cy/api/badge?name=syncthing&revision=true)](https://ag.hexor.cy/applications/argocd/syncthing) |
| **tg-bots** | [![tg-bots](https://ag.hexor.cy/api/badge?name=tg-bots&revision=true)](https://ag.hexor.cy/applications/argocd/tg-bots) |
| **vaultwarden** | [![vaultwarden](https://ag.hexor.cy/api/badge?name=vaultwarden&revision=true)](https://ag.hexor.cy/applications/argocd/vaultwarden) |
| **vpn** | [![vpn](https://ag.hexor.cy/api/badge?name=vpn&revision=true)](https://ag.hexor.cy/applications/argocd/vpn) |
</td>
</tr>
</table>

View File

@@ -0,0 +1,37 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hexound-tls-ingress
annotations:
ingressClassName: traefik
cert-manager.io/cluster-issuer: letsencrypt
traefik.ingress.kubernetes.io/router.middlewares: kube-system-https-redirect@kubernetescrd
acme.cert-manager.io/http01-edit-in-place: "true"
spec:
rules:
- host: hexound.hexor.cy
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hexound
port:
number: 80
- host: hexound.hexor.ru
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hexound
port:
number: 80
tls:
- secretName: hexound-tls
hosts:
- hexound.hexor.cy
- hexound.hexor.ru

View File

@@ -5,4 +5,5 @@ resources:
- app.yaml
- deployment.yaml
- service.yaml
- ingress.yaml

21
k8s/apps/iperf3/app.yaml Normal file
View File

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

View File

@@ -0,0 +1,92 @@
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: iperf3-server
spec:
selector:
matchLabels:
app: iperf3-server
template:
metadata:
labels:
app: iperf3-server
spec:
serviceAccountName: iperf3-server
subdomain: iperf3
initContainers:
- name: create-service
image: bitnami/kubectl:latest
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
command:
- /bin/bash
- -c
- |
# Clean node name for service name
NODE_CLEAN=$(echo "$NODE_NAME" | cut -d'.' -f1 | tr '[:upper:]' '[:lower:]' | tr '_' '-')
SERVICE_NAME="iperf3-${NODE_CLEAN}"
# Create service for this pod
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: ${SERVICE_NAME}
namespace: iperf3
labels:
app: iperf3-node-service
target-node: "${NODE_NAME}"
spec:
type: ClusterIP
ports:
- name: iperf3
port: 5201
protocol: TCP
---
apiVersion: v1
kind: Endpoints
metadata:
name: ${SERVICE_NAME}
namespace: iperf3
labels:
app: iperf3-node-service
target-node: "${NODE_NAME}"
subsets:
- addresses:
- ip: ${POD_IP}
ports:
- name: iperf3
port: 5201
protocol: TCP
EOF
containers:
- name: iperf3-server
image: networkstatic/iperf3:latest
args: ["-s"]
ports:
- containerPort: 5201
protocol: TCP
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
tolerations:
- effect: NoSchedule
operator: Exists
- effect: NoExecute
operator: Exists

View File

@@ -0,0 +1,40 @@
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: iperf3-exporter
labels:
app: iperf3-exporter
spec:
selector:
matchLabels:
app: iperf3-exporter
template:
metadata:
labels:
app: iperf3-exporter
spec:
containers:
- name: iperf3-exporter
image: ghcr.io/edgard/iperf3_exporter:1.2.2
ports:
- containerPort: 9579
name: metrics
protocol: TCP
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "200m"
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
tolerations:
- effect: NoSchedule
operator: Exists
- effect: NoExecute
operator: Exists

View File

@@ -0,0 +1,15 @@
---
apiVersion: v1
kind: Service
metadata:
name: iperf3-exporter
labels:
app: iperf3-exporter
spec:
selector:
app: iperf3-exporter
ports:
- name: metrics
protocol: TCP
port: 9579
targetPort: 9579

View File

@@ -0,0 +1,11 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- rbac.yaml
- daemonset.yaml
- service-headless.yaml
- iperf3-exporter-daemonset.yaml
- iperf3-exporter-service.yaml
- servicemonitor.yaml

36
k8s/apps/iperf3/rbac.yaml Normal file
View File

@@ -0,0 +1,36 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: iperf3-server
namespace: iperf3
labels:
app: iperf3-server
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: iperf3-service-manager
namespace: iperf3
labels:
app: iperf3-server
rules:
- apiGroups: [""]
resources: ["services", "endpoints"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: iperf3-service-manager
namespace: iperf3
labels:
app: iperf3-server
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: iperf3-service-manager
subjects:
- kind: ServiceAccount
name: iperf3-server
namespace: iperf3

View File

@@ -0,0 +1,14 @@
---
apiVersion: v1
kind: Service
metadata:
name: iperf3
spec:
clusterIP: None
selector:
app: iperf3-server
ports:
- name: iperf3
protocol: TCP
port: 5201
targetPort: 5201

View File

@@ -0,0 +1,36 @@
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: iperf3-exporter
labels:
app: iperf3-exporter
release: prometheus
spec:
selector:
matchLabels:
app: iperf3-exporter
endpoints:
- port: metrics
path: /probe
interval: 5m
scrapeTimeout: 30s
params:
target:
- "iperf3.iperf3.svc.cluster.local:5201"
duration:
- "10"
streams:
- "4"
relabelings:
- sourceLabels: [__param_target]
targetLabel: instance
- sourceLabels: [__param_target]
targetLabel: __param_target
- targetLabel: __address__
replacement: iperf3-exporter.iperf3.svc.cluster.local:9579
metricRelabelings:
- sourceLabels: [__name__]
regex: iperf3_(.+)
targetLabel: __name__
replacement: network_${1}

21
k8s/apps/ollama/app.yaml Normal file
View File

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

View File

@@ -0,0 +1,33 @@
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: oidc-secret
spec:
target:
name: oidc-secret
deletionPolicy: Delete
template:
type: Opaque
data:
OAUTH_CLIENT_SECRET: |-
{{ .OAUTH_CLIENT_SECRET }}
OAUTH_CLIENT_ID: |-
{{ .OAUTH_CLIENT_ID }}
data:
- secretKey: OAUTH_CLIENT_SECRET
sourceRef:
storeRef:
name: vaultwarden-login
kind: ClusterSecretStore
remoteRef:
key: 97959a8b-e3b2-4b34-bc54-ddb6476a12ea
property: fields[0].value
- secretKey: OAUTH_CLIENT_ID
sourceRef:
storeRef:
name: vaultwarden-login
kind: ClusterSecretStore
remoteRef:
key: 97959a8b-e3b2-4b34-bc54-ddb6476a12ea
property: fields[1].value

View File

@@ -0,0 +1,21 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- external-secrets.yaml
helmCharts:
- name: ollama
repo: https://otwld.github.io/ollama-helm/
version: 0.4.0
releaseName: ollama
namespace: ollama
valuesFile: ollama-values.yaml
includeCRDs: true
- name: open-webui
repo: https://helm.openwebui.com/
version: 8.14.0
releaseName: openweb-ui
namespace: ollama
valuesFile: openweb-ui-values.yaml
includeCRDs: true

View File

@@ -0,0 +1,8 @@
image:
repository: ollama/ollama
pullPolicy: Always
tag: "latest"
nodeSelector:
kubernetes.io/hostname: master.tail2fe2d.ts.net
ingress:
enabled: false

View File

@@ -0,0 +1,52 @@
clusterDomain: ai.hexor.cy
extraEnvVars:
GLOBAL_LOG_LEVEL: debug
OAUTH_PROVIDER_NAME: authentik
OPENID_PROVIDER_URL: https://idm.hexor.cy/application/o/openwebui/.well-known/openid-configuration
OPENID_REDIRECT_URI: https://ai.hexor.cy/oauth/oidc/callback
WEBUI_URL: https://ai.hexor.cy
# Allows auto-creation of new users using OAuth. Must be paired with ENABLE_LOGIN_FORM=false.
ENABLE_OAUTH_SIGNUP: true
# Disables user/password login form. Required when ENABLE_OAUTH_SIGNUP=true.
ENABLE_LOGIN_FORM: false
OAUTH_MERGE_ACCOUNTS_BY_EMAIL: true
extraEnvFrom:
- secretRef:
name: oidc-secret
nodeSelector:
kubernetes.io/hostname: master.tail2fe2d.ts.net
ollamaUrls:
- http://ollama.ollama.svc:11434
ollama:
enabled: false
ollama:
gpu:
enabled: false
models:
pull:
- qwen3-vl:8b
run:
- qwen3-vl:8b
pipelines:
enabled: true
tika:
enabled: true
websocket:
enabled: true
ingress:
enabled: true
class: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt
traefik.ingress.kubernetes.io/router.middlewares: kube-system-https-redirect@kubernetescrd
host: "ai.hexor.cy"
tls:
- hosts:
- '*.hexor.cy'
secretName: ollama-tls

View File

@@ -2,7 +2,7 @@
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: postgres-creds
name: postgres-and-oauth-creds
spec:
target:
name: postgres-creds

View File

@@ -1,5 +1,5 @@
image:
tag: 2.18.2
tag: 2.19.3
resources:
requests:
memory: "1Gi"

View File

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

View File

@@ -0,0 +1,14 @@
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: pasarguard-tls
labels:
app: pasarguard
spec:
secretName: pasarguard-tls
issuerRef:
name: letsencrypt
kind: ClusterIssuer
dnsNames:
- ps.hexor.cy

View File

@@ -0,0 +1,264 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: pasarguard-scripts
labels:
app: pasarguard-node
data:
init-uuid.sh: |
#!/bin/bash
set -e
echo "Started"
# NODE_NAME is already set via environment variable
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
# Get DNS name from node label xray-node-address
DNS_NAME=$(kubectl get node "${NODE_NAME}" -o jsonpath='{.metadata.labels.xray-node-address}')
if [ -z "${DNS_NAME}" ]; then
echo "ERROR: Node ${NODE_NAME} does not have label 'xray-node-address'"
exit 1
fi
echo "Node: ${NODE_NAME}"
echo "DNS Name from label: ${DNS_NAME}"
# Use DNS name for ConfigMap name to ensure uniqueness
CONFIGMAP_NAME="node-uuid-${DNS_NAME//./-}"
echo "Checking ConfigMap: ${CONFIGMAP_NAME}"
# Check if ConfigMap exists and get UUID
if kubectl get configmap "${CONFIGMAP_NAME}" -n "${NAMESPACE}" &>/dev/null; then
echo "ConfigMap exists, reading UUID..."
API_KEY=$(kubectl get configmap "${CONFIGMAP_NAME}" -n "${NAMESPACE}" -o jsonpath='{.data.API_KEY}')
if [ -z "${API_KEY}" ]; then
echo "UUID not found in ConfigMap, generating new one..."
API_KEY=$(cat /proc/sys/kernel/random/uuid)
kubectl patch configmap "${CONFIGMAP_NAME}" -n "${NAMESPACE}" --type merge -p "{\"data\":{\"API_KEY\":\"${API_KEY}\"}}"
else
echo "Using existing UUID from ConfigMap"
fi
else
echo "ConfigMap does not exist, creating new one..."
API_KEY=$(cat /proc/sys/kernel/random/uuid)
kubectl create configmap "${CONFIGMAP_NAME}" -n "${NAMESPACE}" \
--from-literal=API_KEY="${API_KEY}" \
--from-literal=NODE_NAME="${NODE_NAME}"
fi
# Save UUID and node info to shared volume for the main container
echo -n "${API_KEY}" > /shared/api-key
echo -n "${NODE_NAME}" > /shared/node-name
echo -n "${CONFIGMAP_NAME}" > /shared/configmap-name
echo "UUID initialized: ${API_KEY}"
echo "Node name: ${NODE_NAME}"
echo "ConfigMap: ${CONFIGMAP_NAME}"
# Create Certificate for this node using DNS name from label
CERT_NAME="pasarguard-node-${DNS_NAME//./-}"
echo "Creating Certificate: ${CERT_NAME} for ${DNS_NAME}"
# Check if Certificate already exists
if ! kubectl get certificate "${CERT_NAME}" -n "${NAMESPACE}" &>/dev/null; then
echo "Certificate does not exist, creating..."
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: ${CERT_NAME}
namespace: ${NAMESPACE}
spec:
secretName: ${CERT_NAME}-tls
issuerRef:
name: letsencrypt
kind: ClusterIssuer
dnsNames:
- ${DNS_NAME}
EOF
else
echo "Certificate already exists"
fi
# Wait for certificate to be ready
echo "Waiting for certificate to be ready..."
for i in {1..600}; do
if kubectl get secret "${CERT_NAME}-tls" -n "${NAMESPACE}" &>/dev/null; then
echo "Certificate secret is ready!"
break
fi
echo "Waiting for certificate... ($i/600)"
sleep 1
done
if ! kubectl get secret "${CERT_NAME}-tls" -n "${NAMESPACE}" &>/dev/null; then
echo "WARNING: Certificate secret not ready after 600 seconds"
else
# Extract certificate and key from secret to shared volume
echo "Extracting certificate and key..."
kubectl get secret "${CERT_NAME}-tls" -n "${NAMESPACE}" -o jsonpath='{.data.tls\.crt}' | base64 -d > /shared/tls.crt
kubectl get secret "${CERT_NAME}-tls" -n "${NAMESPACE}" -o jsonpath='{.data.tls\.key}' | base64 -d > /shared/tls.key
echo "Certificate and key extracted successfully."
cat /shared/tls.crt
fi
# Create individual Service and Endpoints for this node
# Take only first part of node name before first dot
NODE_SHORT_NAME="${NODE_NAME%%.*}"
SERVICE_NAME="${NODE_SHORT_NAME}"
# Get node internal IP (take only first IP if multiple)
NODE_IP=$(kubectl get node "${NODE_NAME}" -o jsonpath='{.status.addresses[?(@.type=="InternalIP")].address}' | awk '{print $1}')
echo "Creating Service: ${SERVICE_NAME} for node ${NODE_NAME} (short: ${NODE_SHORT_NAME}) with IP ${NODE_IP}"
# Create Service without selector
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: ${SERVICE_NAME}
namespace: ${NAMESPACE}
labels:
app: pasarguard-node
node: ${NODE_NAME}
spec:
clusterIP: None
ports:
- name: api
port: 62050
protocol: TCP
targetPort: 62050
- name: metrics
port: 9550
protocol: TCP
targetPort: 9550
---
apiVersion: v1
kind: Endpoints
metadata:
name: ${SERVICE_NAME}
namespace: ${NAMESPACE}
labels:
app: pasarguard-node
node: ${NODE_NAME}
subsets:
- addresses:
- ip: ${NODE_IP}
nodeName: ${NODE_NAME}
ports:
- name: api
port: 62050
protocol: TCP
- name: metrics
port: 9550
protocol: TCP
EOF
echo "Service created: ${SERVICE_NAME}.${NAMESPACE}.svc.cluster.local -> ${NODE_IP}:62050"
exporter-start.sh: |
#!/bin/sh
# Install required tools
apk add --no-cache wget curl iproute2-ss bash
# Download v2ray-exporter
echo "Downloading v2ray-exporter..."
ARCH=$(uname -m)
case $ARCH in
x86_64)
BINARY_ARCH="amd64"
;;
aarch64|arm64)
BINARY_ARCH="arm64"
;;
*)
echo "Unsupported architecture: $ARCH"
exit 1
;;
esac
echo "Detected architecture: $ARCH, using binary: v2ray-exporter_linux_$BINARY_ARCH"
wget -L -O /tmp/v2ray-exporter "https://github.com/wi1dcard/v2ray-exporter/releases/download/v0.6.0/v2ray-exporter_linux_$BINARY_ARCH"
mv /tmp/v2ray-exporter /usr/local/bin/v2ray-exporter
chmod +x /usr/local/bin/v2ray-exporter
# Wait for initial API port file
echo "Waiting for initial xray API port file..."
while [ ! -f /shared/xray-api-port ]; do
echo "Waiting for API port file..."
sleep 2
done
# Main loop - restart exporter if it crashes or port changes
while true; do
if [ -f /shared/xray-api-port ]; then
API_PORT=$(cat /shared/xray-api-port)
if [ -n "$API_PORT" ]; then
echo "Starting v2ray-exporter with endpoint 127.0.0.1:$API_PORT"
/usr/local/bin/v2ray-exporter --v2ray-endpoint "127.0.0.1:$API_PORT" --listen ":9550" &
EXPORTER_PID=$!
# Wait for exporter to exit or port file to change
while kill -0 $EXPORTER_PID 2>/dev/null; do
if [ -f /shared/xray-api-port ]; then
NEW_PORT=$(cat /shared/xray-api-port)
if [ "$NEW_PORT" != "$API_PORT" ]; then
echo "API port changed from $API_PORT to $NEW_PORT, restarting exporter"
kill $EXPORTER_PID 2>/dev/null
wait $EXPORTER_PID 2>/dev/null
break
fi
fi
sleep 5
done
echo "Exporter stopped, restarting..."
wait $EXPORTER_PID 2>/dev/null
fi
fi
sleep 2
done
pasarguard-start.sh: |
#!/bin/sh
# Read API_KEY from shared volume created by init container
if [ -f /shared/api-key ]; then
export API_KEY=$(cat /shared/api-key)
echo "Loaded API_KEY from shared volume"
else
echo "WARNING: API_KEY file not found, using default"
fi
cd /app
# Start main process in background
./main &
MAIN_PID=$!
# Start continuous port monitoring in background
{
sleep 10 # Wait for xray to start initially
LAST_PORT=""
while true; do
API_PORT=$(netstat -tlpn | grep xray | grep 127.0.0.1 | awk '{print $4}' | cut -d: -f2 | head -1)
if [ -n "$API_PORT" ] && [ "$API_PORT" != "$LAST_PORT" ]; then
echo "Found xray API port: $API_PORT"
echo -n "$API_PORT" > /shared/xray-api-port
LAST_PORT="$API_PORT"
fi
sleep 5 # Check every 5 seconds
done
} &
PORT_MONITOR_PID=$!
# Wait for main process to finish
wait $MAIN_PID
# Clean up port monitor
kill $PORT_MONITOR_PID 2>/dev/null

View File

@@ -0,0 +1,220 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: pasarguard-node
labels:
app: pasarguard-node
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pasarguard-node-configmap
labels:
app: pasarguard-node
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "create", "update", "patch"]
- apiGroups: ["cert-manager.io"]
resources: ["certificates"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["services", "endpoints"]
verbs: ["get", "list", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pasarguard-node-configmap
labels:
app: pasarguard-node
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: pasarguard-node-configmap
subjects:
- kind: ServiceAccount
name: pasarguard-node
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pasarguard-node-reader
labels:
app: pasarguard-node
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: pasarguard-node-reader
labels:
app: pasarguard-node
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: pasarguard-node-reader
subjects:
- kind: ServiceAccount
name: pasarguard-node
namespace: pasarguard
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: pasarguard-node
labels:
app: pasarguard-node
spec:
selector:
matchLabels:
app: pasarguard-node
revisionHistoryLimit: 3
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
app: pasarguard-node
spec:
serviceAccountName: pasarguard-node
hostNetwork: true
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: xray-node-address
operator: Exists
initContainers:
- name: init-uuid
image: bitnami/kubectl:latest
env:
- name: GODEBUG
value: "x509sha1=1"
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
command:
- /bin/bash
- /scripts/init-uuid.sh
volumeMounts:
- name: shared-data
mountPath: /shared
- name: scripts
mountPath: /scripts
containers:
- name: xray-exporter
image: alpine:3.18
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- /scripts/exporter-start.sh
ports:
- name: metrics
containerPort: 9550
protocol: TCP
livenessProbe:
httpGet:
path: /scrape
port: metrics
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /scrape
port: metrics
initialDelaySeconds: 45
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "100m"
volumeMounts:
- name: shared-data
mountPath: /shared
readOnly: true
- name: scripts
mountPath: /scripts
- name: pasarguard-node
image: 'pasarguard/node:v0.1.1'
imagePullPolicy: Always
command:
- /bin/sh
- /scripts/pasarguard-start.sh
ports:
- name: api
containerPort: 62050
protocol: TCP
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: NODE_HOST
value: "0.0.0.0"
- name: SERVICE_PORT
value: "62050"
- name: SERVICE_PROTOCOL
value: "grpc"
- name: DEBUG
value: "true"
- name: SSL_CERT_FILE
value: "/shared/tls.crt"
- name: SSL_KEY_FILE
value: "/shared/tls.key"
- name: XRAY_EXECUTABLE_PATH
value: "/usr/local/bin/xray"
- name: XRAY_ASSETS_PATH
value: "/usr/local/share/xray"
- name: API_KEY
value: "change-this-to-a-secure-uuid"
livenessProbe:
tcpSocket:
port: 62050
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
tcpSocket:
port: 62050
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
volumeMounts:
- name: shared-data
mountPath: /shared
readOnly: false
- name: scripts
mountPath: /scripts
volumes:
- name: shared-data
emptyDir: {}
- name: scripts
configMap:
name: pasarguard-scripts
defaultMode: 0755

View File

@@ -0,0 +1,84 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: pasarguard
labels:
app: pasarguard
annotations:
reloader.stakater.com/auto: "true"
spec:
selector:
matchLabels:
app: pasarguard
replicas: 1
strategy:
type: RollingUpdate
template:
metadata:
labels:
app: pasarguard
spec:
nodeSelector:
kubernetes.io/hostname: master.tail2fe2d.ts.net
initContainers:
- name: download-template
image: busybox:latest
command:
- sh
- -c
- |
wget -O /templates/subscription/index.html https://github.com/PasarGuard/subscription-template/releases/latest/download/index.html
volumeMounts:
- name: subscription-template
mountPath: /templates/subscription
containers:
- name: pasarguard-web
image: 'pasarguard/panel:v1.7.2'
imagePullPolicy: Always
envFrom:
- secretRef:
name: pasarguard-secrets
env:
- name: UVICORN_HOST
value: "0.0.0.0"
- name: UVICORN_PORT
value: "8000"
- name: DOCS
value: "true"
- name: UVICORN_SSL_CERTFILE
value: "/app/tls/tls.crt"
- name: UVICORN_SSL_KEYFILE
value: "/app/tls/tls.key"
- name: CUSTOM_TEMPLATES_DIRECTORY
value: "/code/app/templates/"
- name: SUBSCRIPTION_PAGE_TEMPLATE
value: "subscription/index.html"
ports:
- name: http
containerPort: 8000
protocol: TCP
volumeMounts:
- name: tls
mountPath: /app/tls
readOnly: true
- name: subscription-template
mountPath: /code/app/templates/subscription
volumes:
- name: tls
secret:
secretName: pasarguard-tls
- name: subscription-template
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: pasarguard
spec:
selector:
app: pasarguard
ports:
- protocol: TCP
port: 80
targetPort: 8000

View File

@@ -0,0 +1,44 @@
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: pasarguard-secrets
spec:
target:
name: pasarguard-secrets
deletionPolicy: Delete
template:
type: Opaque
data:
SUDO_PASSWORD: |-
{{ .admin_password }}
SUDO_USERNAME: |-
{{ .admin_username }}
SQLALCHEMY_DATABASE_URL : |-
postgresql+asyncpg://pasarguard:{{ .pg_pass }}@psql.psql.svc:5432/pasarguard
data:
- secretKey: pg_pass
sourceRef:
storeRef:
name: vaultwarden-login
kind: ClusterSecretStore
remoteRef:
key: 2a9deb39-ef22-433e-a1be-df1555625e22
property: fields[9].value
- secretKey: admin_password
sourceRef:
storeRef:
name: vaultwarden-login
kind: ClusterSecretStore
remoteRef:
key: 35ec5880-2576-401b-a89a-3c9d56b9c1de
property: login.password
- secretKey: admin_username
sourceRef:
storeRef:
name: vaultwarden-login
kind: ClusterSecretStore
remoteRef:
key: 35ec5880-2576-401b-a89a-3c9d56b9c1de
property: login.username

View File

@@ -0,0 +1,11 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ./app.yaml
- ./external-secrets.yaml
- ./deployment.yaml
- ./daemonset.yaml
- ./certificate.yaml
- ./configmap-scripts.yaml
- ./servicemonitor.yaml

View File

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

View File

@@ -4,6 +4,8 @@ kind: Kustomization
resources:
- app.yaml
- nginx-router.yaml
- traefik-simple.yaml
helmCharts:
- name: syncthing

View File

@@ -0,0 +1,276 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
namespace: syncthing
data:
default.conf: |
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name _;
# Landing page
location = / {
root /usr/share/nginx/html;
try_files /index.html =404;
}
# NAS instance
location /nas {
rewrite ^/nas$ /nas/ permanent;
}
# NAS API endpoints
location ~ ^/nas/(rest|meta)/ {
rewrite ^/nas/(.*) /$1 break;
proxy_pass http://syncthing-nas:8384;
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;
# Handle websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 86400;
}
location /nas/ {
proxy_pass http://syncthing-nas:8384/;
# Important: tell syncthing that we're using /nas as base path
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;
# Handle websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 86400;
# Sub filter to fix asset paths
sub_filter 'href="/' 'href="/nas/';
sub_filter 'src="/' 'src="/nas/';
sub_filter 'url(/' 'url(/nas/';
sub_filter '"/meta' '"/nas/meta';
sub_filter '"/rest' '"/nas/rest';
sub_filter '"/vendor' '"/nas/vendor';
sub_filter '"/theme-assets' '"/nas/theme-assets';
sub_filter '"/syncthing' '"/nas/syncthing';
sub_filter_once off;
sub_filter_types text/html text/css application/javascript;
}
# Master instance
location /master {
rewrite ^/master$ /master/ permanent;
}
# Master API endpoints
location ~ ^/master/(rest|meta)/ {
rewrite ^/master/(.*) /$1 break;
proxy_pass http://syncthing-master:8384;
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;
# Handle websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 86400;
}
location /master/ {
proxy_pass http://syncthing-master:8384/;
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;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
sub_filter 'href="/' 'href="/master/';
sub_filter 'src="/' 'src="/master/';
sub_filter 'url(/' 'url(/master/';
sub_filter '"/meta' '"/master/meta';
sub_filter '"/rest' '"/master/rest';
sub_filter '"/vendor' '"/master/vendor';
sub_filter '"/theme-assets' '"/master/theme-assets';
sub_filter '"/syncthing' '"/master/syncthing';
sub_filter_once off;
sub_filter_types text/html text/css application/javascript;
}
# Iris instance
location /iris {
rewrite ^/iris$ /iris/ permanent;
}
# Iris API endpoints
location ~ ^/iris/(rest|meta)/ {
rewrite ^/iris/(.*) /$1 break;
proxy_pass http://syncthing-khv:8384;
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;
# Handle websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 86400;
}
location /iris/ {
proxy_pass http://syncthing-khv:8384/;
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;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
sub_filter 'href="/' 'href="/iris/';
sub_filter 'src="/' 'src="/iris/';
sub_filter 'url(/' 'url(/iris/';
sub_filter '"/meta' '"/iris/meta';
sub_filter '"/rest' '"/iris/rest';
sub_filter '"/vendor' '"/iris/vendor';
sub_filter '"/theme-assets' '"/iris/theme-assets';
sub_filter '"/syncthing' '"/iris/syncthing';
sub_filter_once off;
sub_filter_types text/html text/css application/javascript;
}
}
index.html: |
<!DOCTYPE html>
<html>
<head>
<title>Syncthing Instances</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
.container {
text-align: center;
background: white;
padding: 40px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #333;
margin-bottom: 30px;
}
.links {
display: flex;
flex-direction: column;
gap: 15px;
}
a {
display: inline-block;
padding: 15px 30px;
background-color: #0078e7;
color: white;
text-decoration: none;
border-radius: 5px;
transition: background-color 0.3s;
}
a:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<div class="container">
<h1>Syncthing Instances</h1>
<div class="links">
<a href="/nas/">NAS Instance</a>
<a href="/master/">Master Instance</a>
<a href="/iris/">Iris Instance</a>
</div>
</div>
</body>
</html>
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: syncthing-router
namespace: syncthing
spec:
replicas: 1
selector:
matchLabels:
app: syncthing-router
template:
metadata:
labels:
app: syncthing-router
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: config
mountPath: /etc/nginx/conf.d
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: config
configMap:
name: nginx-config
items:
- key: default.conf
path: default.conf
- name: html
configMap:
name: nginx-config
items:
- key: index.html
path: index.html
---
apiVersion: v1
kind: Service
metadata:
name: syncthing-router
namespace: syncthing
spec:
selector:
app: syncthing-router
ports:
- protocol: TCP
port: 80
targetPort: 80

View File

@@ -0,0 +1,36 @@
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: syncthing-ingressroute
namespace: syncthing
spec:
entryPoints:
- websecure
routes:
- match: Host(`ss.hexor.cy`)
kind: Rule
services:
- name: syncthing-router
port: 80
middlewares:
- name: authentik-forward-auth
namespace: syncthing
tls:
secretName: syncthing-tls
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: authentik-forward-auth
namespace: syncthing
spec:
forwardAuth:
address: http://authentik-server.authentik.svc.cluster.local/outpost.goauthentik.io/auth/traefik
trustForwardHeader: true
authResponseHeaders:
- X-authentik-username
- X-authentik-groups
- X-authentik-email
- X-authentik-name
- X-authentik-uid

21
k8s/apps/tg-bots/app.yaml Normal file
View File

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

View File

@@ -0,0 +1,42 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: desubot
labels:
app: desubot
annotations:
reloader.stakater.com/auto: "true"
spec:
selector:
matchLabels:
app: desubot
replicas: 1
strategy:
type: RollingUpdate
template:
metadata:
labels:
app: desubot
spec:
nodeSelector:
kubernetes.io/hostname: home.homenet
containers:
- name: desubot
image: 'ultradesu/desubot:latest'
imagePullPolicy: Always
envFrom:
- secretRef:
name: desubot
env:
- name: RUST_LOG
value: "info"
volumeMounts:
- mountPath: /storage
name: storage
volumes:
- name: storage
nfs:
server: nas.homenet
path: /mnt/storage/Storage/k8s/desubot/
readOnly: false

View File

@@ -0,0 +1,49 @@
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: get-id-bot
spec:
target:
name: get-id-bot
deletionPolicy: Delete
template:
type: Opaque
data:
TELOXIDE_TOKEN: |-
{{ .token }}
data:
- secretKey: token
sourceRef:
storeRef:
name: vaultwarden-login
kind: ClusterSecretStore
remoteRef:
key: 97bd0af9-54ab-429a-b060-09626525f4cd
property: fields[0].value
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: desubot
spec:
target:
name: desubot
deletionPolicy: Delete
template:
type: Opaque
data:
TELEGRAM_BOT_TOKEN: |-
{{ .token }}
data:
- secretKey: token
sourceRef:
storeRef:
name: vaultwarden-login
kind: ClusterSecretStore
remoteRef:
key: 97bd0af9-54ab-429a-b060-09626525f4cd
property: fields[1].value

View File

@@ -0,0 +1,36 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: get-id-bot
labels:
app: get-id-bot
annotations:
reloader.stakater.com/auto: "true"
spec:
selector:
matchLabels:
app: get-id-bot
replicas: 1
strategy:
type: RollingUpdate
template:
metadata:
labels:
app: get-id-bot
spec:
nodeSelector:
kubernetes.io/hostname: master.tail2fe2d.ts.net
containers:
- name: get-id-bot
image: 'ghcr.io/house-of-vanity/get_id_bot:main'
imagePullPolicy: Always
envFrom:
- secretRef:
name: get-id-bot
env:
- name: RUST_LOG
value: "info"

View File

@@ -0,0 +1,9 @@
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- app.yaml
- get-id-bot.yaml
- external-secrets.yaml
- desubot.yaml

25
k8s/apps/vpn/config.yaml Normal file
View File

@@ -0,0 +1,25 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: outfleet-rs-config
data:
config.toml: |-
[database]
url = "postgres://outfleet_rs:FMj#bA0XW14Pd2@psql.psql.svc:5432/outfleet_rs"
[web]
host = "0.0.0.0"
port = 8080
base_url = "https://vpn.hexor.cy"
[telegram]
enabled = false
admin_chat_ids = []
allowed_users = []
[xray]
config_path = "./templates"
[log]
level = "debug"

View File

@@ -6,4 +6,7 @@ resources:
- ./external-secrets.yaml
- ./outfleet.yaml
- ./shadowsocks.yaml
- ./outfleet-rs.yaml
- ./config.yaml
- ./xray.yaml

View File

@@ -0,0 +1,66 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: outfleet-rs
labels:
app: outfleet-rs
annotations:
reloader.stakater.com/auto: "true"
spec:
selector:
matchLabels:
app: outfleet-rs
replicas: 1
strategy:
type: RollingUpdate
template:
metadata:
labels:
app: outfleet-rs
spec:
hostname: outfleet-rs
nodeSelector:
kubernetes.io/hostname: master.tail2fe2d.ts.net
containers:
- name: outfleet-rs
image: 'ultradesu/outfleet:rs-0.1.3'
imagePullPolicy: Always
command: ["/bin/sh"]
args:
- "-c"
- |
set -x
/app/xray-admin --host 0.0.0.0 --port 8080
ports:
- name: http
containerPort: 8080
protocol: TCP
env:
- name: RUST_LOG
value: "info"
volumeMounts:
- name: outfleet-config
mountPath: /app/config.toml # <-- target path inside container
subPath: config.toml # <-- use the specific key as a file
readOnly: true
volumes:
- name: outfleet-config
configMap:
name: outfleet-rs-config
items:
- key: config.toml
path: config.toml
---
apiVersion: v1
kind: Service
metadata:
name: outfleet-rs
spec:
selector:
app: outfleet-rs
ports:
- protocol: TCP
port: 80
targetPort: 8080

209
k8s/apps/vpn/xray.yaml Normal file
View File

@@ -0,0 +1,209 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: xray-config-template
data:
config.json.template: |
{
"log": {
"loglevel": "warning"
},
"api": {
"tag": "api",
"listen": "TAILSCALE_IP:10086",
"services": [
"HandlerService",
"StatsService",
"LoggerService",
"RoutingService",
"ReflectionService"
]
},
"stats": {},
"policy": {
"system": {
"statsInboundDownlink": true,
"statsInboundUplink": true,
"statsOutboundDownlink": true,
"statsOutboundUplink": true
}
},
"inbounds": [],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
}
],
"routing": {
"rules": []
}
}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: xray-init-script
data:
init.sh: |
#!/bin/sh
set -e
echo "Starting Xray configuration setup..."
# Find xray binary location
XRAY_BIN=""
for path in /usr/bin/xray /usr/local/bin/xray /bin/xray /opt/xray/xray; do
if [ -x "$path" ]; then
XRAY_BIN="$path"
echo "Found Xray binary at: $XRAY_BIN"
break
fi
done
if [ -z "$XRAY_BIN" ]; then
echo "Error: Xray binary not found"
echo "Available files in common locations:"
ls -la /usr/bin/xray* 2>/dev/null || echo "No xray in /usr/bin/"
ls -la /usr/local/bin/xray* 2>/dev/null || echo "No xray in /usr/local/bin/"
ls -la /bin/xray* 2>/dev/null || echo "No xray in /bin/"
exit 1
fi
# Get Tailscale IP address
TAILSCALE_IP=""
# Try different ways to get Tailscale IP
if command -v ip >/dev/null 2>&1; then
TAILSCALE_IP=$(ip addr show tailscale0 2>/dev/null | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1 | head -n1)
fi
# Fallback: try to find any interface with 100.x.x.x IP (typical Tailscale range)
if [ -z "$TAILSCALE_IP" ]; then
TAILSCALE_IP=$(ip route get 8.8.8.8 2>/dev/null | grep -o 'src [0-9\.]*' | grep '100\.' | awk '{print $2}' | head -n1)
fi
# Another fallback: check all interfaces for 100.x.x.x
if [ -z "$TAILSCALE_IP" ]; then
TAILSCALE_IP=$(ip addr show 2>/dev/null | grep -o 'inet 100\.[0-9\.]*' | awk '{print $2}' | head -n1)
fi
# Final fallback: use localhost if no Tailscale IP found
if [ -z "$TAILSCALE_IP" ]; then
echo "Warning: Could not find Tailscale IP, using 127.0.0.1"
TAILSCALE_IP="127.0.0.1"
else
echo "Found Tailscale IP: $TAILSCALE_IP"
fi
# Create config directory
mkdir -p /usr/local/etc/xray
# Replace TAILSCALE_IP placeholder in config template
sed "s/TAILSCALE_IP/$TAILSCALE_IP/g" /config-template/config.json.template > /usr/local/etc/xray/config.json
echo "Generated Xray config:"
cat /usr/local/etc/xray/config.json
# Increase file descriptor limits
ulimit -n 65536 2>/dev/null || echo "Warning: Could not increase file descriptor limit"
echo "Starting Xray with binary: $XRAY_BIN"
exec "$XRAY_BIN" run -c /usr/local/etc/xray/config.json
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: xray-daemon
labels:
app: xray
spec:
selector:
matchLabels:
app: xray
template:
metadata:
labels:
app: xray
spec:
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
nodeSelector:
xray: "true"
tolerations:
- operator: Exists
effect: NoSchedule
containers:
- name: xray
image: teddysun/xray:25.7.26
command: ["/bin/sh"]
args: ["/scripts/init.sh"]
securityContext:
privileged: true
capabilities:
add:
- NET_ADMIN
- NET_RAW
volumeMounts:
- name: config-template
mountPath: /config-template
readOnly: true
- name: init-script
mountPath: /scripts
readOnly: true
- name: xray-config
mountPath: /usr/local/etc/xray
ports:
- containerPort: 10086
protocol: TCP
name: api
livenessProbe:
tcpSocket:
port: 10086
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
tcpSocket:
port: 10086
initialDelaySeconds: 5
periodSeconds: 5
resources:
limits:
memory: "512Mi"
cpu: "500m"
requests:
memory: "256Mi"
cpu: "250m"
volumes:
- name: config-template
configMap:
name: xray-config-template
defaultMode: 0644
- name: init-script
configMap:
name: xray-init-script
defaultMode: 0755
- name: xray-config
emptyDir: {}
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: xray-api-service
labels:
app: xray
spec:
type: ClusterIP
ports:
- port: 10086
targetPort: 10086
protocol: TCP
name: api
selector:
app: xray

View File

@@ -13,6 +13,9 @@ spec:
targetRevision: HEAD
path: k8s/core/argocd
syncPolicy:
automated:
selfHeal: true
prune: true
syncOptions:
- CreateNamespace=true

View File

@@ -21,6 +21,7 @@ configs:
kustomize.buildOptions: --enable-helm
application.instanceLabelKey: argocd.argoproj.io/instance
admin.enabled: false
statusbadge.enabled: true
timeout.reconciliation: 60s
oidc.config: |
name: Authentik

View File

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

View File

@@ -1,6 +1,6 @@
global:
image:
tag: "2025.8.1"
tag: "2025.10.1"
nodeSelector:
kubernetes.io/hostname: master.tail2fe2d.ts.net
@@ -15,8 +15,14 @@ worker:
envFrom:
- secretRef:
name: authentik-creds
#nodeSelector:
# kubernetes.io/hostname: home.homenet
volumes:
- name: dshm
emptyDir:
medium: Memory
sizeLimit: 512Mi
volumeMounts:
- name: dshm
mountPath: /dev/shm
server:
envFrom:
- secretRef:
@@ -32,16 +38,15 @@ server:
- nas.hexor.cy # TrueNAS Limassol
- nc.hexor.cy # NaxtCloud
- of.hexor.cy # Outfleet-v2
- master.hexor.cy # k8s dashboard
- k8s.hexor.cy # k8s dashboard
- qbt.hexor.cy # qBittorent for Jellyfin
- prom.hexor.cy # Prometheus
- ss.hexor.cy # Syncthing UI
- khm.hexor.cy # Known Hosts keys Manager
- backup.hexor.cy # Kopia Backup UI
- fm.hexor.cy # Filemanager
- hexound.hexor.cy # Hexound
- minecraft.hexor.cy # Minecraft UI and server
- pass.hexor.cy # k8s-secret for openai
- ps.hexor.cy # pasarguard UI
tls:
- secretName: idm-tls
hosts:

View File

@@ -0,0 +1,55 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: worker-restart-sa
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: worker-restart-role
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: worker-restart-rb
subjects:
- kind: ServiceAccount
name: worker-restart-sa
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: worker-restart-role
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: worker-daily-restart
spec:
schedule: "0 * * * *" # every day at 04:00
jobTemplate:
spec:
template:
spec:
serviceAccountName: worker-restart-sa
restartPolicy: OnFailure
containers:
- name: kubectl
image: bitnami/kubectl:latest
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
command:
- /bin/sh
- -c
- |
kubectl -n "$POD_NAMESPACE" rollout restart deployment/authentik-worker

View File

@@ -8,7 +8,7 @@
# BW_HOST: base64(url)
# BW_USERNAME: base64(name)
# BW_PASSWORD: base64(pass)
# 81212111-6350-4069-8bcf-19a67d3964a5
# Vaultwarden bot - 81212111-6350-4069-8bcf-19a67d3964a5
---
apiVersion: apps/v1
kind: Deployment

View File

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

View File

@@ -0,0 +1,25 @@
apiVersion: v1
kind: ConfigMap
metadata:
annotations:
name: coredns-custom
namespace: kube-system
data:
homenet.server: |
homenet:53 {
errors
cache 30
forward . 10.0.5.1
}
khv.server: |
khv:53 {
errors
cache 30
forward . 10.0.5.1
}
tailscale.server: |
tail2fe2d.ts.net:53 {
errors
cache 30
forward . 100.100.100.100
}

View File

@@ -0,0 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- app.yaml
- coredns-internal-resolve.yaml

View File

@@ -115,6 +115,10 @@ spec:
{{ .khm }}
USER_kanjai: |-
{{ .kanjai }}
USER_outfleet_rs: |-
{{ .outfleet_rs }}
USER_pasarguard: |-
{{ .pasarguard }}
data:
- secretKey: authentik
sourceRef:
@@ -193,3 +197,25 @@ spec:
metadataPolicy: None
key: 2a9deb39-ef22-433e-a1be-df1555625e22
property: fields[7].value
- secretKey: outfleet_rs
sourceRef:
storeRef:
name: vaultwarden-login
kind: ClusterSecretStore
remoteRef:
conversionStrategy: Default
decodingStrategy: None
metadataPolicy: None
key: 2a9deb39-ef22-433e-a1be-df1555625e22
property: fields[8].value
- secretKey: pasarguard
sourceRef:
storeRef:
name: vaultwarden-login
kind: ClusterSecretStore
remoteRef:
conversionStrategy: Default
decodingStrategy: None
metadataPolicy: None
key: 2a9deb39-ef22-433e-a1be-df1555625e22
property: fields[9].value

View File

@@ -13,7 +13,7 @@ resources:
helmCharts:
- name: pgadmin4
repo: https://helm.runix.net
version: 1.37.0
version: 1.50.0
releaseName: pgmanager
namespace: psql
valuesFile: pgadmin4-values.yaml

View File

@@ -6,7 +6,7 @@ metadata:
namespace: immich
spec:
capacity:
storage: 50Gi
storage: 55Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
@@ -79,14 +79,14 @@ spec:
- CREATE EXTENSION IF NOT EXISTS earthdistance;
storage:
size: 50Gi
size: 55Gi
storageClass: nfs-storage
pvcTemplate:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
storage: 55Gi
storageClassName: nfs-storage
volumeMode: Filesystem

View File

@@ -1,5 +1,5 @@
image:
tag: "9.3"
tag: "9.9"
pullPolicy: Always
env:
email: "postgres@hexor.cy"

View File

@@ -11,7 +11,7 @@ spec:
source:
repoURL: ssh://git@gt.hexor.cy:30022/ab/homelab.git
targetRevision: HEAD
path: k8s/core/prometheus
path: k8s/core/prom-stack
syncPolicy:
automated:
selfHeal: true

View File

@@ -10,7 +10,7 @@ admin:
grafana.ini:
auth:
signout_redirect_url: https://idm.hexor.cy/application/o/grafana/end-session/
oauth_auto_login: true
# oauth_auto_login: true
auth.generic_oauth:
name: authentik
enabled: true

View File

@@ -5,7 +5,7 @@ metadata:
name: prometheus-pv
spec:
capacity:
storage: 200Gi
storage: 400Gi
accessModes:
- ReadWriteOnce
hostPath:

View File

@@ -4,16 +4,16 @@ prometheus:
prometheusSpec:
enableRemoteWriteReceiver: true
additionalScrapeConfigs:
- job_name: outline_vpn
- job_name: xray_vpn
metrics_path: /scrape
static_configs:
- targets: ['100.117.24.104:9095']
labels: {instance: cy}
- targets: ['100.117.24.104:9096']
labels: {instance: am}
- targets: ['100.117.24.104:9097']
labels: {instance: jp}
- targets: ['100.117.24.104:9098']
labels: {instance: bg}
- targets: ['cy.tail2fe2d.ts.net:9550']
labels: {job: cy}
- targets: ['x86.tail2fe2d.ts.net:9550']
labels: {job: am}
- targets: ['jp.tail2fe2d.ts.net:9550']
labels: {job: jp}
- job_name: cs_16_server
static_configs:
- targets: ['prom-a2s-exporter.counter-strike.svc:9841']
@@ -30,5 +30,5 @@ prometheus:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 200Gi
storage: 400Gi

View File

@@ -1,7 +1,7 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: k3s-system-upgrade
name: system-upgrade
namespace: argocd
spec:
project: core

View File

@@ -16,7 +16,7 @@ spec:
serviceAccountName: system-upgrade
upgrade:
image: rancher/k3s-upgrade
version: v1.33.4+k3s1
version: v1.34.1+k3s1
---
# Agent plan
apiVersion: upgrade.cattle.io/v1
@@ -39,5 +39,5 @@ spec:
serviceAccountName: system-upgrade
upgrade:
image: rancher/k3s-upgrade
version: v1.33.4+k3s1
version: v1.34.1+k3s1

View File

@@ -0,0 +1,16 @@
{
"permissions": {
"allow": [
"WebSearch",
"WebFetch(domain:registry.terraform.io)",
"Bash(C:UsersabAppDataLocalMicrosoftWinGetPackagesHashicorp.Terraform_Microsoft.Winget.Source_8wekyb3d8bbweterraform.exe apply -auto-approve)",
"Bash(\"C:\\Users\\ab\\AppData\\Local\\Microsoft\\WinGet\\Packages\\Hashicorp.Terraform_Microsoft.Winget.Source_8wekyb3d8bbwe\\terraform.exe\" apply -auto-approve)",
"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:*)"
],
"deny": [],
"ask": []
}
}

44
terraform/authentik/.terraform.lock.hcl generated Normal file
View File

@@ -0,0 +1,44 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/goauthentik/authentik" {
version = "2025.8.1"
constraints = ">= 2023.10.0, 2025.8.1"
hashes = [
"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.7.2"
constraints = ">= 3.5.0"
hashes = [
"h1:356j/3XnXEKr9nyicLUufzoF4Yr6hRy481KIxRVpK0c=",
"zh:14829603a32e4bc4d05062f059e545a91e27ff033756b48afbae6b3c835f508f",
"zh:1527fb07d9fea400d70e9e6eb4a2b918d5060d604749b6f1c361518e7da546dc",
"zh:1e86bcd7ebec85ba336b423ba1db046aeaa3c0e5f921039b3f1a6fc2f978feab",
"zh:24536dec8bde66753f4b4030b8f3ef43c196d69cccbea1c382d01b222478c7a3",
"zh:29f1786486759fad9b0ce4fdfbbfece9343ad47cd50119045075e05afe49d212",
"zh:4d701e978c2dd8604ba1ce962b047607701e65c078cb22e97171513e9e57491f",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:7b8434212eef0f8c83f5a90c6d76feaf850f6502b61b53c329e85b3b281cba34",
"zh:ac8a23c212258b7976e1621275e3af7099e7e4a3d4478cf8d5d2a27f3bc3e967",
"zh:b516ca74431f3df4c6cf90ddcdb4042c626e026317a33c53f0b445a3d93b720d",
"zh:dc76e4326aec2490c1600d6871a95e78f9050f9ce427c71707ea412a2f2f1a62",
"zh:eac7b63e86c749c7d48f527671c7aee5b4e26c10be6ad7232d6860167f99dbb0",
]
}

View File

@@ -0,0 +1,55 @@
# Authentik Terraform Module
Terraform module for managing Authentik applications with OAuth2/OpenID and Proxy providers, including automatic Outpost assignment.
## Usage
```hcl
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"
}
}
}
```
## Structure
- `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
- Authentik provider >= 2023.10.0
- Authentik API token with admin permissions

View File

@@ -0,0 +1,10 @@
groups = {
"admins" = {
name = "Administrators"
is_superuser = true
attributes = {
notes = "Managed by Terraform"
}
}
}

309
terraform/authentik/main.tf Normal file
View File

@@ -0,0 +1,309 @@
data "authentik_flow" "default_authorization_flow" {
slug = var.default_authorization_flow
}
data "authentik_flow" "default_authentication_flow" {
slug = var.default_authentication_flow
}
data "authentik_flow" "default_invalidation_flow" {
slug = var.default_invalidation_flow
}
# Root groups (without parent)
resource "authentik_group" "root_groups" {
for_each = {
for k, v in var.groups : k => v
if v.parent == null
}
name = each.value.name
is_superuser = each.value.is_superuser
attributes = jsonencode(each.value.attributes)
}
# Child groups (with parent)
resource "authentik_group" "child_groups" {
for_each = {
for k, v in var.groups : k => v
if v.parent != null
}
name = each.value.name
is_superuser = each.value.is_superuser
parent = authentik_group.root_groups[each.value.parent].id
attributes = jsonencode(each.value.attributes)
depends_on = [authentik_group.root_groups]
}
# Auto-created groups for proxy applications
resource "authentik_group" "proxy_app_groups" {
for_each = {
for k, v in var.proxy_applications : k => v
if v.create_group == true
}
name = "TF-${each.value.name} Users"
is_superuser = false
attributes = jsonencode({
notes = "Auto-created for ${each.value.name} application"
app_slug = each.value.slug
})
}
# Auto-created groups for OAuth applications
resource "authentik_group" "oauth_app_groups" {
for_each = {
for k, v in var.oauth_applications : k => v
if v.create_group == true
}
name = "TF-${each.value.name} Users"
is_superuser = false
attributes = jsonencode({
notes = "Auto-created for ${each.value.name} application"
app_slug = each.value.slug
})
}
resource "authentik_certificate_key_pair" "certificates" {
for_each = var.certificates
name = each.value.name
certificate_data = each.value.certificate_data
key_data = each.value.key_data
}
data "authentik_service_connection_kubernetes" "local_k8s" {
name = "Local Kubernetes Cluster"
}
resource "authentik_flow" "flows" {
for_each = var.flows
name = each.value.name
title = each.value.title
slug = each.value.slug
designation = each.value.designation
policy_engine_mode = each.value.policy_engine_mode
compatibility_mode = each.value.compatibility_mode
layout = each.value.layout
denied_action = each.value.denied_action
}
resource "authentik_property_mapping_provider_scope" "oidc_mappings" {
for_each = {
for k, v in var.property_mappings : k => v
if v.oidc_scope != null
}
name = each.value.name
scope_name = each.value.oidc_scope
expression = each.value.expression
}
resource "authentik_property_mapping_provider_saml" "saml_mappings" {
for_each = {
for k, v in var.property_mappings : k => v
if v.saml_name != null
}
name = each.value.name
saml_name = each.value.saml_name
expression = each.value.expression
}
module "oauth_applications" {
source = "./modules/oauth-provider"
for_each = var.oauth_applications
name = each.value.name
app_name = each.value.name
app_slug = each.value.slug
app_group = each.value.group
client_id = each.value.client_id
authorization_flow = try(authentik_flow.flows[each.value.authorization_flow].id, data.authentik_flow.default_authorization_flow.id)
invalidation_flow = data.authentik_flow.default_invalidation_flow.id
redirect_uris = each.value.redirect_uris
client_type = each.value.client_type
include_claims_in_id_token = each.value.include_claims_in_id_token
access_code_validity = each.value.access_code_validity
access_token_validity = each.value.access_token_validity
refresh_token_validity = each.value.refresh_token_validity
property_mappings = each.value.property_mappings
signing_key = each.value.signing_key
policy_engine_mode = each.value.policy_engine_mode
meta_description = each.value.meta_description
meta_launch_url = each.value.meta_launch_url
meta_icon = each.value.meta_icon
scope_mappings = each.value.scope_mappings
# Access control - only pass explicitly defined groups
access_groups = [
for group_key in each.value.access_groups :
try(
authentik_group.root_groups[group_key].id,
authentik_group.child_groups[group_key].id
)
]
}
module "proxy_applications" {
source = "./modules/proxy-provider"
for_each = var.proxy_applications
name = each.value.name
app_name = each.value.name
app_slug = each.value.slug
app_group = each.value.group
external_host = each.value.external_host
internal_host = each.value.internal_host
internal_host_ssl_validation = each.value.internal_host_ssl_validation
authorization_flow = try(authentik_flow.flows[each.value.authorization_flow].id, data.authentik_flow.default_authorization_flow.id)
invalidation_flow = data.authentik_flow.default_invalidation_flow.id
mode = each.value.mode
intercept_header_auth = each.value.intercept_header_auth
basic_auth_enabled = each.value.basic_auth_enabled
basic_auth_user_attribute = each.value.basic_auth_username_attribute
basic_auth_password_attribute = each.value.basic_auth_password_attribute
cookie_domain = each.value.cookie_domain
skip_path_regex = each.value.skip_path_regex
policy_engine_mode = each.value.policy_engine_mode
meta_description = each.value.meta_description
meta_launch_url = each.value.meta_launch_url
meta_icon = each.value.meta_icon
# Access control - only pass explicitly defined groups
access_groups = [
for group_key in each.value.access_groups :
try(
authentik_group.root_groups[group_key].id,
authentik_group.child_groups[group_key].id
)
]
}
# Binding auto-created groups to their applications
resource "authentik_policy_binding" "auto_group_bindings" {
for_each = {
for k, v in var.proxy_applications : k => v
if v.create_group == true
}
target = module.proxy_applications[each.key].application_uuid
group = authentik_group.proxy_app_groups[each.key].id
order = 100
depends_on = [
module.proxy_applications,
authentik_group.proxy_app_groups
]
}
# Binding auto-created groups to their OAuth applications
resource "authentik_policy_binding" "oauth_auto_group_bindings" {
for_each = {
for k, v in var.oauth_applications : k => v
if v.create_group == true
}
target = module.oauth_applications[each.key].application_uuid
group = authentik_group.oauth_app_groups[each.key].id
order = 100
depends_on = [
module.oauth_applications,
authentik_group.oauth_app_groups
]
}
module "saml_applications" {
source = "./modules/saml-provider"
for_each = var.saml_applications
name = each.value.name
app_name = each.value.name
app_slug = each.value.slug
app_group = each.value.group
authorization_flow = try(authentik_flow.flows[each.value.authorization_flow].id, data.authentik_flow.default_authorization_flow.id)
invalidation_flow = data.authentik_flow.default_invalidation_flow.id
acs_url = each.value.acs_url
issuer = each.value.issuer
audience = each.value.audience
sp_binding = each.value.sp_binding
signing_key = each.value.signing_key
property_mappings = [for pm in each.value.property_mappings : authentik_property_mapping_provider_saml.saml_mappings[pm].id]
name_id_mapping = each.value.name_id_mapping != null ? authentik_property_mapping_provider_saml.saml_mappings[each.value.name_id_mapping].id : null
assertion_valid_not_before = each.value.assertion_valid_not_before
assertion_valid_not_on_or_after = each.value.assertion_valid_not_on_or_after
session_valid_not_on_or_after = each.value.session_valid_not_on_or_after
policy_engine_mode = each.value.policy_engine_mode
meta_description = each.value.meta_description
meta_launch_url = each.value.meta_launch_url
meta_icon = each.value.meta_icon
}
locals {
oauth_outpost_assignments = {
for app_key, app in var.oauth_applications : app_key => app.outpost
if app.outpost != null
}
proxy_outpost_assignments = {
for app_key, app in var.proxy_applications : app_key => app.outpost
if app.outpost != null
}
outpost_providers = {
for outpost_key, outpost in var.outposts : outpost_key => concat(
[for app_key, app_outpost in local.oauth_outpost_assignments :
module.oauth_applications[app_key].provider_id if app_outpost == outpost_key],
[for app_key, app_outpost in local.proxy_outpost_assignments :
module.proxy_applications[app_key].provider_id if app_outpost == outpost_key]
)
}
}
resource "authentik_outpost" "outposts" {
for_each = {
for k, v in var.outposts : k => v
if length(lookup(local.outpost_providers, k, [])) > 0
}
name = each.value.name
type = "proxy"
protocol_providers = local.outpost_providers[each.key]
service_connection = data.authentik_service_connection_kubernetes.local_k8s.id
config = jsonencode({
log_level = "info"
docker_labels = null
authentik_host = var.authentik_url
docker_network = null
container_image = null
docker_map_ports = true
refresh_interval = "minutes=5"
kubernetes_replicas = 1
kubernetes_namespace = "authentik"
authentik_host_browser = ""
object_naming_template = "ak-outpost-%(name)s"
authentik_host_insecure = false
kubernetes_json_patches = null
kubernetes_service_type = "ClusterIP"
kubernetes_image_pull_secrets = []
kubernetes_ingress_class_name = null
kubernetes_disabled_components = []
kubernetes_ingress_annotations = {}
kubernetes_ingress_secret_name = "authentik-outpost-tls"
})
depends_on = [
module.oauth_applications,
module.proxy_applications
]
}

View File

@@ -0,0 +1,103 @@
terraform {
required_providers {
authentik = {
source = "goauthentik/authentik"
version = ">= 2023.10.0"
}
random = {
source = "hashicorp/random"
version = ">= 3.5.0"
}
}
}
# Get all available scope mappings
data "authentik_property_mapping_provider_scope" "all_scopes" {
managed_list = [
"goauthentik.io/providers/oauth2/scope-email",
"goauthentik.io/providers/oauth2/scope-openid",
"goauthentik.io/providers/oauth2/scope-profile"
]
}
# Filter scope mappings based on requested scopes
locals {
scope_name_mapping = {
"openid" = "goauthentik.io/providers/oauth2/scope-openid"
"profile" = "goauthentik.io/providers/oauth2/scope-profile"
"email" = "goauthentik.io/providers/oauth2/scope-email"
}
selected_scope_ids = [
for scope in var.scope_mappings :
data.authentik_property_mapping_provider_scope.all_scopes.ids[index(data.authentik_property_mapping_provider_scope.all_scopes.managed_list, local.scope_name_mapping[scope])]
if contains(keys(local.scope_name_mapping), scope)
]
}
resource "random_password" "client_secret" {
count = var.client_secret == null ? 1 : 0
length = 40
special = true
}
resource "authentik_provider_oauth2" "provider" {
name = var.name
client_id = var.client_id != null ? var.client_id : random_id.client_id[0].hex
client_secret = var.client_secret != null ? var.client_secret : random_password.client_secret[0].result
client_type = var.client_type
authorization_flow = var.authorization_flow
invalidation_flow = var.invalidation_flow
include_claims_in_id_token = var.include_claims_in_id_token
access_code_validity = var.access_code_validity
access_token_validity = var.access_token_validity
refresh_token_validity = var.refresh_token_validity
signing_key = var.signing_key
allowed_redirect_uris = [
for uri in var.redirect_uris : {
matching_mode = "strict"
url = uri
}
]
property_mappings = length(var.property_mappings) > 0 ? var.property_mappings : local.selected_scope_ids
}
resource "random_id" "client_id" {
count = var.client_id == null ? 1 : 0
byte_length = 20
}
resource "authentik_application" "app" {
name = var.app_name
slug = var.app_slug
protocol_provider = authentik_provider_oauth2.provider.id
group = var.app_group
policy_engine_mode = var.policy_engine_mode
meta_description = var.meta_description
meta_launch_url = var.meta_launch_url
meta_icon = var.meta_icon
}
resource "authentik_policy_binding" "app_access" {
for_each = var.access_policies
target = authentik_application.app.id
policy = each.value.policy_id
order = each.value.order
enabled = lookup(each.value, "enabled", true)
timeout = lookup(each.value, "timeout", 30)
negate = lookup(each.value, "negate", false)
failure_result = lookup(each.value, "failure_result", true)
}
# Binding groups to the application
resource "authentik_policy_binding" "group_bindings" {
for_each = { for idx, group_id in var.access_groups : idx => group_id }
target = authentik_application.app.uuid
group = each.value
order = 10 + each.key
}

View File

@@ -0,0 +1,30 @@
output "provider_id" {
description = "ID of the OAuth2 provider"
value = authentik_provider_oauth2.provider.id
}
output "application_id" {
description = "ID of the application"
value = authentik_application.app.id
}
output "application_uuid" {
description = "UUID of the application"
value = authentik_application.app.uuid
}
output "client_id" {
description = "OAuth2 Client ID"
value = authentik_provider_oauth2.provider.client_id
}
output "client_secret" {
description = "OAuth2 Client Secret"
value = authentik_provider_oauth2.provider.client_secret
sensitive = true
}
output "application_slug" {
description = "Application slug"
value = authentik_application.app.slug
}

View File

@@ -0,0 +1,150 @@
variable "name" {
description = "Name of the OAuth2 provider"
type = string
}
variable "app_name" {
description = "Name of the application"
type = string
}
variable "app_slug" {
description = "Slug of the application"
type = string
}
variable "app_group" {
description = "Group for the application"
type = string
default = ""
}
variable "client_id" {
description = "OAuth2 Client ID"
type = string
default = null
}
variable "client_secret" {
description = "OAuth2 Client Secret"
type = string
default = null
sensitive = true
}
variable "client_type" {
description = "OAuth2 Client type (confidential or public)"
type = string
default = "confidential"
validation {
condition = contains(["confidential", "public"], var.client_type)
error_message = "Client type must be either 'confidential' or 'public'."
}
}
variable "authorization_flow" {
description = "Authorization flow UUID"
type = string
}
variable "invalidation_flow" {
description = "Invalidation flow UUID"
type = string
}
variable "redirect_uris" {
description = "List of allowed redirect URIs"
type = list(string)
default = []
}
variable "access_code_validity" {
description = "Access code validity duration"
type = string
default = "minutes=1"
}
variable "access_token_validity" {
description = "Access token validity duration"
type = string
default = "minutes=5"
}
variable "refresh_token_validity" {
description = "Refresh token validity duration"
type = string
default = "days=30"
}
variable "include_claims_in_id_token" {
description = "Include claims in ID token"
type = bool
default = true
}
variable "signing_key" {
description = "Signing key UUID"
type = string
default = null
}
variable "property_mappings" {
description = "List of property mapping UUIDs"
type = list(string)
default = []
}
variable "policy_engine_mode" {
description = "Policy engine mode"
type = string
default = "all"
validation {
condition = contains(["all", "any"], var.policy_engine_mode)
error_message = "Policy engine mode must be either 'all' or 'any'."
}
}
variable "meta_description" {
description = "Application meta description"
type = string
default = ""
}
variable "meta_launch_url" {
description = "Application launch URL"
type = string
default = ""
}
variable "meta_icon" {
description = "Application icon URL"
type = string
default = ""
}
variable "access_policies" {
description = "Access policies for the application"
type = map(object({
policy_id = string
order = number
enabled = optional(bool, true)
timeout = optional(number, 30)
negate = optional(bool, false)
failure_result = optional(bool, true)
}))
default = {}
}
variable "access_groups" {
description = "List of group IDs that have access to the application"
type = list(string)
default = []
}
variable "scope_mappings" {
description = "List of scope mappings for the OAuth provider"
type = list(string)
default = ["openid", "profile", "email"]
}

View File

@@ -0,0 +1,58 @@
terraform {
required_providers {
authentik = {
source = "goauthentik/authentik"
version = ">= 2023.10.0"
}
}
}
resource "authentik_provider_proxy" "provider" {
name = var.name
external_host = var.external_host
internal_host = var.internal_host
internal_host_ssl_validation = var.internal_host_ssl_validation
authorization_flow = var.authorization_flow
invalidation_flow = var.invalidation_flow
mode = var.mode
cookie_domain = var.cookie_domain
skip_path_regex = var.skip_path_regex
intercept_header_auth = var.intercept_header_auth
basic_auth_enabled = var.basic_auth_enabled
basic_auth_password_attribute = var.basic_auth_password_attribute
property_mappings = var.property_mappings
}
resource "authentik_application" "app" {
name = var.app_name
slug = var.app_slug
protocol_provider = authentik_provider_proxy.provider.id
group = var.app_group
policy_engine_mode = var.policy_engine_mode
meta_description = var.meta_description
meta_launch_url = var.meta_launch_url
meta_icon = var.meta_icon
}
resource "authentik_policy_binding" "app_access" {
for_each = var.access_policies
target = authentik_application.app.id
policy = each.value.policy_id
order = each.value.order
enabled = lookup(each.value, "enabled", true)
timeout = lookup(each.value, "timeout", 30)
negate = lookup(each.value, "negate", false)
failure_result = lookup(each.value, "failure_result", true)
}
# Binding groups to the application
resource "authentik_policy_binding" "group_bindings" {
for_each = { for idx, group_id in var.access_groups : idx => group_id }
target = authentik_application.app.uuid
group = each.value
order = 10 + each.key
}

View File

@@ -0,0 +1,35 @@
output "provider_id" {
description = "ID of the Proxy provider"
value = authentik_provider_proxy.provider.id
}
output "application_id" {
description = "ID of the application"
value = authentik_application.app.id
}
output "application_uuid" {
description = "UUID of the application"
value = authentik_application.app.uuid
}
output "application_slug" {
description = "Application slug"
value = authentik_application.app.slug
}
output "launch_url" {
description = "Application launch URL"
value = authentik_application.app.meta_launch_url
}
output "external_host" {
description = "External host URL"
value = authentik_provider_proxy.provider.external_host
}
output "internal_host" {
description = "Internal host URL"
value = authentik_provider_proxy.provider.internal_host
}

View File

@@ -0,0 +1,151 @@
variable "name" {
description = "Name of the Proxy provider"
type = string
}
variable "app_name" {
description = "Name of the application"
type = string
}
variable "app_slug" {
description = "Slug of the application"
type = string
}
variable "app_group" {
description = "Group for the application"
type = string
default = ""
}
variable "external_host" {
description = "External hostname for the proxy"
type = string
}
variable "internal_host" {
description = "Internal hostname for the proxy"
type = string
default = ""
}
variable "internal_host_ssl_validation" {
description = "Enable SSL validation for internal host"
type = bool
default = true
}
variable "authorization_flow" {
description = "Authorization flow UUID"
type = string
}
variable "invalidation_flow" {
description = "Invalidation flow UUID"
type = string
}
variable "mode" {
description = "Proxy mode (proxy, forward_single, forward_domain)"
type = string
default = "proxy"
validation {
condition = contains(["proxy", "forward_single", "forward_domain"], var.mode)
error_message = "Mode must be one of: proxy, forward_single, forward_domain."
}
}
variable "cookie_domain" {
description = "Cookie domain for the proxy"
type = string
default = ""
}
variable "skip_path_regex" {
description = "Regular expression for paths to skip authentication"
type = string
default = ""
}
variable "intercept_header_auth" {
description = "Intercept header authentication"
type = bool
default = false
}
variable "basic_auth_enabled" {
description = "Enable basic authentication"
type = bool
default = false
}
variable "basic_auth_password_attribute" {
description = "Attribute for basic auth password"
type = string
default = ""
}
variable "basic_auth_user_attribute" {
description = "Attribute for basic auth username"
type = string
default = ""
}
variable "property_mappings" {
description = "List of property mapping UUIDs"
type = list(string)
default = []
}
variable "policy_engine_mode" {
description = "Policy engine mode"
type = string
default = "all"
validation {
condition = contains(["all", "any"], var.policy_engine_mode)
error_message = "Policy engine mode must be either 'all' or 'any'."
}
}
variable "meta_description" {
description = "Application meta description"
type = string
default = ""
}
variable "meta_launch_url" {
description = "Application launch URL"
type = string
default = ""
}
variable "meta_icon" {
description = "Application icon URL"
type = string
default = ""
}
variable "access_policies" {
description = "Access policies for the application"
type = map(object({
policy_id = string
order = number
enabled = optional(bool, true)
timeout = optional(number, 30)
negate = optional(bool, false)
failure_result = optional(bool, true)
}))
default = {}
}
variable "access_groups" {
description = "List of group IDs that have access to the application"
type = list(string)
default = []
}

View File

@@ -0,0 +1,53 @@
terraform {
required_providers {
authentik = {
source = "goauthentik/authentik"
version = ">= 2023.10.0"
}
}
}
data "authentik_certificate_key_pair" "default" {
name = "authentik Self-signed Certificate"
}
resource "authentik_provider_saml" "provider" {
name = var.name
authorization_flow = var.authorization_flow
invalidation_flow = var.invalidation_flow
acs_url = var.acs_url
issuer = var.issuer
audience = var.audience
sp_binding = var.sp_binding
signing_kp = var.signing_key != null ? var.signing_key : data.authentik_certificate_key_pair.default.id
property_mappings = var.property_mappings
name_id_mapping = var.name_id_mapping
assertion_valid_not_before = var.assertion_valid_not_before
assertion_valid_not_on_or_after = var.assertion_valid_not_on_or_after
session_valid_not_on_or_after = var.session_valid_not_on_or_after
}
resource "authentik_application" "app" {
name = var.app_name
slug = var.app_slug
protocol_provider = authentik_provider_saml.provider.id
group = var.app_group
policy_engine_mode = var.policy_engine_mode
meta_description = var.meta_description
meta_launch_url = var.meta_launch_url
meta_icon = var.meta_icon
}
resource "authentik_policy_binding" "app_access" {
for_each = var.access_policies
target = authentik_application.app.id
policy = each.value.policy_id
order = each.value.order
enabled = lookup(each.value, "enabled", true)
timeout = lookup(each.value, "timeout", 30)
negate = lookup(each.value, "negate", false)
failure_result = lookup(each.value, "failure_result", true)
}

View File

@@ -0,0 +1,24 @@
output "provider_id" {
description = "ID of the SAML provider"
value = authentik_provider_saml.provider.id
}
output "application_id" {
description = "ID of the application"
value = authentik_application.app.id
}
output "provider_name" {
description = "Name of the SAML provider"
value = authentik_provider_saml.provider.name
}
output "acs_url" {
description = "Assertion Consumer Service URL"
value = authentik_provider_saml.provider.acs_url
}
output "issuer" {
description = "SAML Issuer"
value = authentik_provider_saml.provider.issuer
}

View File

@@ -0,0 +1,124 @@
variable "name" {
description = "Name of the SAML provider"
type = string
}
variable "app_name" {
description = "Name of the application"
type = string
}
variable "app_slug" {
description = "Slug of the application"
type = string
}
variable "app_group" {
description = "Group of the application"
type = string
default = ""
}
variable "authorization_flow" {
description = "Authorization flow ID"
type = string
}
variable "invalidation_flow" {
description = "Invalidation flow ID"
type = string
}
variable "acs_url" {
description = "Assertion Consumer Service URL"
type = string
}
variable "issuer" {
description = "SAML Issuer"
type = string
}
variable "audience" {
description = "SAML Audience"
type = string
}
variable "sp_binding" {
description = "Service Provider binding (post or redirect)"
type = string
default = "post"
}
variable "signing_key" {
description = "Certificate key pair ID for signing"
type = string
default = null
}
variable "property_mappings" {
description = "List of property mapping IDs"
type = list(string)
default = []
}
variable "name_id_mapping" {
description = "Property mapping ID for NameID"
type = string
default = null
}
variable "assertion_valid_not_before" {
description = "Assertion valid not before"
type = string
default = "minutes=-5"
}
variable "assertion_valid_not_on_or_after" {
description = "Assertion valid not on or after"
type = string
default = "minutes=5"
}
variable "session_valid_not_on_or_after" {
description = "Session valid not on or after"
type = string
default = "minutes=86400"
}
variable "policy_engine_mode" {
description = "Policy engine mode"
type = string
default = "all"
}
variable "meta_description" {
description = "Application description"
type = string
default = ""
}
variable "meta_launch_url" {
description = "Application launch URL"
type = string
default = ""
}
variable "meta_icon" {
description = "Application icon URL"
type = string
default = ""
}
variable "access_policies" {
description = "Access policies for the application"
type = map(object({
policy_id = string
order = number
enabled = optional(bool, true)
timeout = optional(number, 30)
negate = optional(bool, false)
failure_result = optional(bool, true)
}))
default = {}
}

View File

@@ -0,0 +1,192 @@
oauth_applications = {
"paperless" = {
name = "Paperless-NGX"
slug = "paperless"
group = "Tools"
meta_description = "Document management system"
meta_icon = "https://img.icons8.com/fluency/48/documents.png"
redirect_uris = ["https://docs.hexor.cy/accounts/oidc/authentik/login/callback/"]
client_type = "confidential"
include_claims_in_id_token = true
access_code_validity = "minutes=1"
access_token_validity = "minutes=5"
refresh_token_validity = "days=30"
scope_mappings = ["openid", "profile", "email"]
create_group = true
access_groups = ["admins"]
}
"gitea" = {
name = "Gitea"
slug = "gitea"
group = "Tools"
meta_description = "Git repository hosting"
meta_icon = "https://img.icons8.com/?size=100&id=20906&format=png&color=000000"
redirect_uris = ["https://gt.hexor.cy/user/oauth2/Authentik/callback"]
client_type = "confidential"
include_claims_in_id_token = true
access_code_validity = "minutes=1"
access_token_validity = "minutes=10"
refresh_token_validity = "days=30"
scope_mappings = ["openid", "profile", "email"]
access_groups = ["admins"]
}
"jellyfin" = {
name = "Jellyfin"
slug = "jellyfin"
group = "Media and Storage"
meta_description = "Media streaming server"
meta_icon = "https://img.icons8.com/plasticine/100/jellyfin.png"
redirect_uris = [
"https://jf.hexor.cy/sso/OID/r/authentik",
"https://jf.hexor.cy/sso/OID/redirect/authentik"
]
client_type = "confidential"
include_claims_in_id_token = true
access_code_validity = "minutes=1"
access_token_validity = "minutes=10"
refresh_token_validity = "days=30"
scope_mappings = ["openid", "profile", "email"]
access_groups = ["admins"]
}
"argocd" = {
name = "ArgoCD"
slug = "argocd"
group = "Core"
meta_description = "GitOps deployment tool"
meta_icon = "https://img.icons8.com/color-glass/48/octopus.png"
redirect_uris = ["https://ag.hexor.cy/auth/callback"]
client_type = "confidential"
include_claims_in_id_token = true
access_code_validity = "minutes=1"
access_token_validity = "minutes=5"
refresh_token_validity = "days=30"
scope_mappings = ["openid", "profile", "email"]
signing_key = "1b1b5bec-034a-4d96-871a-133f11322360"
access_groups = ["admins"]
}
"grafana" = {
name = "Grafana"
slug = "grafana"
group = "Core"
meta_description = "Monitoring and observability"
meta_icon = "https://img.icons8.com/fluency/48/grafana.png"
redirect_uris = ["https://gf.hexor.cy/login/generic_oauth"]
client_type = "confidential"
include_claims_in_id_token = true
access_code_validity = "minutes=1"
access_token_validity = "minutes=5"
refresh_token_validity = "days=30"
scope_mappings = ["openid", "profile", "email"]
access_groups = ["admins"]
}
"immich" = {
name = "Immich"
slug = "immich"
group = "Media and Storage"
meta_description = "Photo and video management"
meta_icon = "https://img.icons8.com/fluency/48/photos.png"
redirect_uris = [
"https://photos.hexor.cy/auth/login",
"https://photos.hexor.cy/user-settings",
"app.immich:///oauth-callback",
"http://photos.homenet:30283/auth/login",
"http://photos.homenet:30283/user-settings"
]
client_type = "confidential"
include_claims_in_id_token = true
access_code_validity = "minutes=1"
access_token_validity = "minutes=5"
refresh_token_validity = "days=30"
scope_mappings = ["openid", "profile", "email"]
signing_key = "1b1b5bec-034a-4d96-871a-133f11322360"
access_groups = ["admins"]
create_group = true
}
"pgadmin" = {
name = "Postgres WEB Admin"
slug = "pgadmin"
group = "Core"
meta_description = "PostgreSQL WEB administration"
meta_icon = "https://img.icons8.com/?size=100&id=JRnxU7ZWP4mi&format=png&color=000000"
redirect_uris = ["https://pg.hexor.cy/oauth2/authorize"]
client_type = "confidential"
include_claims_in_id_token = true
access_code_validity = "minutes=1"
access_token_validity = "minutes=5"
refresh_token_validity = "days=30"
scope_mappings = ["openid", "profile", "email"]
access_groups = ["admins"]
signing_key = "1b1b5bec-034a-4d96-871a-133f11322360"
}
"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 = [
"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
access_code_validity = "minutes=1"
access_token_validity = "minutes=5"
refresh_token_validity = "days=30"
scope_mappings = ["openid", "profile", "email"]
access_groups = ["admins"]
create_group = true
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 = [
"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
access_code_validity = "minutes=1"
access_token_validity = "minutes=5"
refresh_token_validity = "days=30"
scope_mappings = ["openid", "profile", "email"]
access_groups = ["admins"]
create_group = true
signing_key = "1b1b5bec-034a-4d96-871a-133f11322360"
}
"openwebui" = {
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
access_code_validity = "minutes=1"
access_token_validity = "minutes=5"
refresh_token_validity = "days=30"
scope_mappings = ["openid", "profile", "email"]
access_groups = ["admins"]
create_group = true
signing_key = "1b1b5bec-034a-4d96-871a-133f11322360"
}
}

View File

@@ -0,0 +1,124 @@
output "oauth_applications" {
description = "OAuth2/OpenID applications details"
value = {
for k, v in module.oauth_applications : k => {
application_id = v.application_id
application_uuid = v.application_uuid
client_id = v.client_id
client_secret = v.client_secret
slug = v.application_slug
}
}
sensitive = true
}
output "proxy_applications" {
description = "Proxy applications details"
value = {
for k, v in module.proxy_applications : k => {
application_id = v.application_id
application_uuid = v.application_uuid
external_host = v.external_host
internal_host = v.internal_host
slug = v.application_slug
}
}
}
output "outposts" {
description = "Outposts details"
value = {
for k, v in authentik_outpost.outposts : k => {
id = v.id
name = v.name
type = v.type
}
}
}
output "groups" {
description = "Groups details"
value = merge(
{
for k, v in authentik_group.root_groups : k => {
id = v.id
name = v.name
}
},
{
for k, v in authentik_group.child_groups : k => {
id = v.id
name = v.name
}
},
{
for k, v in authentik_group.proxy_app_groups : k => {
id = v.id
name = v.name
auto_created = true
type = "proxy"
}
},
{
for k, v in authentik_group.oauth_app_groups : k => {
id = v.id
name = v.name
auto_created = true
type = "oauth"
}
}
)
}
output "flows" {
description = "Custom flows details"
value = {
for k, v in authentik_flow.flows : k => {
id = v.id
slug = v.slug
name = v.name
}
}
}
output "certificates" {
description = "Certificates details"
value = {
for k, v in authentik_certificate_key_pair.certificates : k => {
id = v.id
name = v.name
fingerprint_sha256 = v.fingerprint_sha256
fingerprint_sha1 = v.fingerprint_sha1
}
}
}
# Output for applications table generation
output "applications_for_wiki" {
description = "Applications data formatted for wiki table generation"
value = {
proxy_apps = {
for k, v in var.proxy_applications : k => {
name = v.name
type = "Proxy"
url = v.external_host
internal_url = v.internal_host
group = v.group
description = v.meta_description
icon = v.meta_icon
slug = v.slug
}
}
oauth_apps = {
for k, v in var.oauth_applications : k => {
name = v.name
type = "OAuth2/OpenID"
url = length(v.redirect_uris) > 0 ? "https://${split("/", replace(v.redirect_uris[0], "https://", ""))[0]}" : ""
group = v.group
description = v.meta_description
icon = v.meta_icon
slug = v.slug
}
}
}
}

View File

@@ -0,0 +1,13 @@
terraform {
required_providers {
authentik = {
source = "goauthentik/authentik"
version = "2025.8.1"
}
}
}
provider "authentik" {
url = "https://idm.hexor.cy"
token = "qXcuoCg77JaRgqnU6rqIIBa8MBJ4UNyLPTL89dZI8zeC2jfaWqQ7k56BJs8F"
}

View File

@@ -0,0 +1,210 @@
proxy_applications = {
"k8s-dashboard" = {
name = "K8S dashboard"
slug = "k8s-dashboard-ns"
group = "Core"
external_host = "https://k8s.hexor.cy"
internal_host = "http://kubernetes-dashboard.kubernetes-dashboard.svc"
internal_host_ssl_validation = false
meta_description = "K8S dashboard chart"
mode = "proxy"
outpost = "kubernetes-outpost"
meta_icon = "https://img.icons8.com/color/48/kubernetes.png"
create_group = true
access_groups = ["admins"]
}
"filemanager" = {
name = "FM filemanager"
slug = "fm-filemanager"
group = "Core"
external_host = "https://fm.hexor.cy"
internal_host = "http://fb-filemanager-filebrowser.syncthing.svc"
internal_host_ssl_validation = false
meta_description = "K8S dashboard chart"
mode = "proxy"
outpost = "kubernetes-outpost"
meta_icon = "https://img.icons8.com/external-anggara-flat-anggara-putra/32/external-folder-basic-user-interface-anggara-flat-anggara-putra.png"
create_group = true
access_groups = ["admins"]
}
"prometheus" = {
name = "Prometheus"
slug = "prometheus"
group = "Core"
external_host = "https://prom.hexor.cy"
internal_host = "http://prometheus-kube-prometheus-prometheus.prometheus.svc:9090"
meta_description = ""
meta_icon = "https://img.icons8.com/fluency/48/prometheus-app.png"
mode = "proxy"
outpost = "kubernetes-outpost"
internal_host_ssl_validation = false
create_group = true
access_groups = ["admins"]
}
"kubernetes-secrets" = {
name = "kubernetes-secrets"
slug = "k8s-secret"
group = "Core"
external_host = "https://pass.hexor.cy"
internal_host = "http://secret-reader.k8s-secret.svc:80"
internal_host_ssl_validation = false
meta_description = ""
meta_icon = "https://img.icons8.com/ios-filled/50/password.png"
mode = "proxy"
outpost = "kubernetes-outpost"
create_group = true
access_groups = ["admins"]
}
# 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
/u/
/stat/
/ss/
/xray/
/dynamic/
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"]
}
"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"
group = "Tools"
external_host = "https://qbt.hexor.cy"
internal_host = "http://qbittorrent.jellyfin.svc"
internal_host_ssl_validation = false
meta_description = ""
meta_icon = "https://img.icons8.com/nolan/64/qbittorrent--v2.png"
mode = "proxy"
outpost = "kubernetes-outpost"
create_group = true
access_groups = ["admins"]
}
# Media and Storage applications
"kopia" = {
name = "Kopia"
slug = "kopia"
group = "Media and Storage"
external_host = "https://backup.hexor.cy"
internal_host = "http://100.72.135.2:51515"
internal_host_ssl_validation = false
meta_description = ""
meta_icon = "https://img.icons8.com/external-flaticons-lineal-color-flat-icons/64/external-backup-productivity-flaticons-lineal-color-flat-icons.png"
mode = "proxy"
outpost = "kubernetes-outpost"
create_group = true
access_groups = ["admins"]
}
"syncthing-master" = {
name = "Syncthing-master"
slug = "syncthing-master"
group = "Media and Storage"
external_host = "https://ss.hexor.cy"
internal_host = "https://syncthing-master.syncthing.svc:8384"
internal_host_ssl_validation = false
meta_description = ""
meta_icon = "https://img.icons8.com/?size=100&id=Id4NcEcXcYzF&format=png&color=000000"
mode = "proxy"
outpost = "kubernetes-outpost"
create_group = true
access_groups = ["admins"]
}
"truenas" = {
name = "TrueNAS"
slug = "truenas-proxy"
group = "Media and Storage"
external_host = "https://nas.hexor.cy"
internal_host = "http://10.0.5.107:81"
internal_host_ssl_validation = false
meta_description = ""
meta_icon = "https://img.icons8.com/dusk/64/nas.png"
mode = "proxy"
outpost = "kubernetes-outpost"
create_group = true
access_groups = ["admins"]
}
"khm" = {
name = "KHM"
slug = "khm"
group = "Media and Storage"
external_host = "https://khm.hexor.cy"
internal_host = "http://khm.khm.svc:8080"
internal_host_ssl_validation = false
meta_description = ""
meta_icon = "https://img.icons8.com/liquid-glass/48/key.png"
mode = "proxy"
outpost = "kubernetes-outpost"
access_groups = ["admins", "khm"] # Используем существующие группы
create_group = true
access_groups = ["admins"]
}
"minecraft" = {
name = "Minecraft"
slug = "minecraft"
group = "Media and Storage"
external_host = "https://minecraft.hexor.cy"
internal_host = "http://minecraft-dynmap.minecraft.svc"
internal_host_ssl_validation = false
meta_description = ""
meta_icon = "https://img.icons8.com/color/48/minecraft-grass-cube.png"
mode = "proxy"
outpost = "kubernetes-outpost"
}
"pasarguard" = {
name = "PasarGuard"
slug = "pasarguard"
group = "Tools"
external_host = "https://ps.hexor.cy"
internal_host = "https://pasarguard.pasarguard.svc:80"
internal_host_ssl_validation = false
meta_description = ""
skip_path_regex = <<-EOT
/sub/
/dashboard/
/
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"]
}
}

View File

@@ -0,0 +1,8 @@
terraform {
cloud {
organization = "ultradesu"
workspaces {
name = "Authentik"
}
}
}

View File

@@ -0,0 +1,28 @@
authentik_url = "https://idm.hexor.cy"
flows = {
}
tags = {
environment = "production"
managed_by = "terraform"
project = "homelab"
}
outposts = {
"kubernetes-outpost" = {
name = "authentik Embedded Outpost"
type = "proxy"
service_connection = "k8s-cluster"
config = {
authentik_host = "https://idm.hexor.cy"
authentik_host_insecure = false
log_level = "info"
error_reporting = true
#container_image = null
}
}
}

View File

@@ -0,0 +1,168 @@
variable "oauth_applications" {
description = "Map of OAuth2/OpenID applications"
type = map(object({
name = string
slug = string
group = optional(string, "")
policy_engine_mode = optional(string, "all")
meta_description = optional(string, "")
meta_launch_url = optional(string, "")
meta_icon = optional(string, "")
redirect_uris = list(string)
client_type = optional(string, "confidential")
client_id = optional(string, null)
include_claims_in_id_token = optional(bool, true)
access_code_validity = optional(string, "minutes=1")
access_token_validity = optional(string, "minutes=5")
refresh_token_validity = optional(string, "days=30")
property_mappings = optional(list(string), [])
authorization_flow = optional(string, null)
signing_key = optional(string, null)
outpost = optional(string, null)
create_group = optional(bool, false)
access_groups = optional(list(string), [])
scope_mappings = optional(list(string), ["openid", "profile", "email"])
}))
default = {}
}
variable "proxy_applications" {
description = "Map of Proxy applications"
type = map(object({
name = string
slug = string
group = optional(string, "")
policy_engine_mode = optional(string, "all")
meta_description = optional(string, "")
meta_launch_url = optional(string, "")
meta_icon = optional(string, "")
external_host = string
internal_host = optional(string, "")
internal_host_ssl_validation = optional(bool, true)
mode = optional(string, "proxy")
intercept_header_auth = optional(bool, false)
basic_auth_enabled = optional(bool, false)
basic_auth_username_attribute = optional(string, "")
basic_auth_password_attribute = optional(string, "")
cookie_domain = optional(string, "")
authorization_flow = optional(string, null)
skip_path_regex = optional(string, "")
outpost = optional(string, null)
create_group = optional(bool, false)
access_groups = optional(list(string), [])
}))
default = {}
}
variable "saml_applications" {
description = "Map of SAML applications"
type = map(object({
name = string
slug = string
group = optional(string, "")
policy_engine_mode = optional(string, "all")
meta_description = optional(string, "")
meta_launch_url = optional(string, "")
meta_icon = optional(string, "")
acs_url = string
issuer = string
audience = string
sp_binding = optional(string, "post")
signing_key = optional(string, null)
property_mappings = optional(list(string), [])
name_id_mapping = optional(string, null)
assertion_valid_not_before = optional(string, "minutes=-5")
assertion_valid_not_on_or_after = optional(string, "minutes=5")
session_valid_not_on_or_after = optional(string, "minutes=86400")
authorization_flow = optional(string, null)
}))
default = {}
}
variable "outposts" {
description = "Map of Outposts (only proxy type supported)"
type = map(object({
name = string
config = optional(map(any), {})
}))
default = {}
}
variable "flows" {
description = "Map of authentication flows"
type = map(object({
name = string
title = string
slug = string
designation = string
policy_engine_mode = optional(string, "all")
compatibility_mode = optional(bool, false)
layout = optional(string, "stacked")
denied_action = optional(string, "message_continue")
}))
default = {}
}
variable "groups" {
description = "Map of user groups"
type = map(object({
name = string
is_superuser = optional(bool, false)
parent = optional(string, null)
attributes = optional(map(any), {})
}))
default = {}
}
variable "certificates" {
description = "Map of certificates for HTTPS"
type = map(object({
name = string
certificate_data = string
key_data = string
managed = optional(string, null)
}))
default = {}
}
variable "property_mappings" {
description = "Custom property mappings for SAML/OAuth"
type = map(object({
name = string
expression = string
saml_name = optional(string, null)
oidc_scope = optional(string, null)
}))
default = {}
}
variable "default_authorization_flow" {
description = "Default authorization flow slug"
type = string
default = "default-provider-authorization-implicit-consent"
}
variable "default_authentication_flow" {
description = "Default authentication flow slug"
type = string
default = "default-authentication-flow"
}
variable "default_invalidation_flow" {
description = "Default invalidation flow slug"
type = string
default = "default-provider-invalidation-flow"
}
variable "tags" {
description = "Tags to apply to all resources"
type = map(string)
default = {}
}
variable "authentik_url" {
description = "Authentik URL"
type = string
default = "https://idm.hexor.cy"
}