Compare commits

..
118 Commits
Author SHA1 Message Date
renovate-bot 19f70d46f6 chore(deps): update renovate/renovate docker tag to v44.90.1
ci / lint-prettier (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 5s
ci / lint-prettier (pull_request) Successful in 11s
ci / lint-ruff (pull_request) Successful in 4s
ci / lint-yaml (pull_request) Successful in 7s
ci / lint-dockerfiles (pull_request) Successful in 5s
ci / validate (pull_request) Successful in 6s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 8s
renovate-ci / validate-renovate (pull_request) Successful in 11s
ci / build (pull_request) Has been skipped
ci / build (push) Has been skipped
ci / deploy-userbot-panel (pull_request) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
2026-09-14 22:18:02 +00:00
forust 68fb5eb45e Merge pull request 'chore(deps): update renovate/renovate docker tag to v44.85.0' (#25) from renovate/renovate-renovate-44.x into main
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 5s
ci / deploy-userbot-panel (push) Has been skipped
ci / lint-prettier (push) Successful in 7s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 6s
renovate-ci / validate-renovate (push) Successful in 7s
ci / build (push) Successful in 2s
Reviewed-on: https://gitea.forust.xyz/forust/homelab/pulls/25
2026-09-14 09:48:39 +00:00
renovate-bot 1c58c78892 chore(deps): update renovate/renovate docker tag to v44.85.0
ci / lint-prettier (pull_request) Successful in 7s
ci / lint-ruff (pull_request) Successful in 4s
ci / lint-yaml (pull_request) Successful in 6s
ci / lint-dockerfiles (pull_request) Successful in 4s
ci / validate (pull_request) Successful in 5s
ci / lint-prettier (push) Successful in 9s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 9s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 5s
renovate-ci / validate-renovate (pull_request) Successful in 7s
ci / build (pull_request) Has been skipped
ci / build (push) Has been skipped
ci / deploy-userbot-panel (pull_request) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
2026-09-14 09:48:21 +00:00
forust 82124017c0 Merge pull request 'chore(deps): update redis docker tag to v8.10.1' (#23) from renovate/redis-8.x into main
ci / lint-prettier (push) Successful in 8s
ci / lint-ruff (push) Successful in 5s
ci / lint-yaml (push) Successful in 6s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 5s
renovate-ci / validate-renovate (push) Successful in 8s
ci / build (push) Successful in 17s
ci / deploy-userbot-panel (push) Has been skipped
Reviewed-on: https://gitea.forust.xyz/forust/homelab/pulls/23
2026-09-14 09:46:49 +00:00
renovate-bot e502f46f43 chore(deps): update redis docker tag to v8.10.1 2026-09-14 09:46:49 +00:00
forust a4f8218b5e Merge pull request 'chore(deps): update valkey/valkey docker tag to v9' (#24) from renovate/valkey-valkey-9.x into main
ci / lint-prettier (push) Successful in 6s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 6s
renovate-ci / validate-renovate (push) Successful in 10s
ci / build (push) Successful in 1s
ci / deploy-userbot-panel (push) Has been skipped
Reviewed-on: https://gitea.forust.xyz/forust/homelab/pulls/24
2026-09-14 09:46:24 +00:00
renovate-bot d162a50bba chore(deps): update valkey/valkey docker tag to v9
ci / lint-prettier (pull_request) Successful in 8s
ci / lint-ruff (pull_request) Successful in 4s
ci / lint-yaml (pull_request) Successful in 7s
ci / lint-dockerfiles (pull_request) Successful in 4s
ci / validate (pull_request) Successful in 5s
ci / lint-prettier (push) Successful in 8s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 4s
renovate-ci / validate-renovate (pull_request) Successful in 6s
ci / build (pull_request) Has been skipped
ci / build (push) Has been skipped
ci / deploy-userbot-panel (pull_request) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
2026-09-14 09:44:41 +00:00
forust 9ca8514a0a Merge pull request 'chore(deps): update renovate/renovate docker tag to v44.84.0' (#21) from renovate/renovate-renovate-44.x into main
ci / lint-prettier (push) Successful in 8s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 3s
ci / deploy-userbot-panel (push) Has been skipped
ci / validate (push) Successful in 5s
renovate-ci / validate-renovate (push) Successful in 8s
ci / build (push) Successful in 2s
Reviewed-on: https://gitea.forust.xyz/forust/homelab/pulls/21
2026-09-14 09:07:55 +00:00
renovate-bot 6fa809a8d2 chore(deps): update renovate/renovate docker tag to v44.84.0 2026-09-14 09:07:55 +00:00
forust c6d8df2317 Merge pull request 'chore(deps): update ghcr.io/goauthentik/server docker tag to v2026' (#22) from renovate/ghcr.io-goauthentik-server-2026.x into main
renovate-ci / validate-renovate (push) Successful in 7s
ci / lint-prettier (push) Successful in 7s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 6s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 5s
ci / build (push) Successful in 2s
ci / deploy-userbot-panel (push) Has been skipped
Reviewed-on: https://gitea.forust.xyz/forust/homelab/pulls/22
2026-09-14 09:07:41 +00:00
renovate-bot c28d7323b3 chore(deps): update ghcr.io/goauthentik/server docker tag to v2026
ci / lint-prettier (pull_request) Successful in 7s
ci / lint-ruff (pull_request) Successful in 4s
ci / lint-yaml (pull_request) Successful in 7s
ci / lint-dockerfiles (pull_request) Successful in 6s
ci / validate (pull_request) Successful in 5s
renovate-ci / validate-renovate (pull_request) Successful in 7s
ci / lint-prettier (push) Successful in 7s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 4s
ci / build (push) Has been skipped
ci / deploy-userbot-panel (pull_request) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
ci / build (pull_request) Has been skipped
2026-09-14 09:06:56 +00:00
forust 25f547ff78 Merge pull request 'chore(deps): update docker.n8n.io/n8nio/n8n docker tag to v2.39.5' (#18) from renovate/docker.n8n.io-n8nio-n8n-2.x into main
renovate-ci / validate-renovate (push) Successful in 7s
ci / lint-prettier (push) Successful in 7s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 6s
ci / lint-dockerfiles (push) Successful in 3s
ci / validate (push) Successful in 4s
ci / build (push) Successful in 2s
ci / deploy-userbot-panel (push) Has been skipped
Reviewed-on: https://gitea.forust.xyz/forust/homelab/pulls/18
2026-09-14 09:04:42 +00:00
renovate-bot 50911b4ec1 chore(deps): update docker.n8n.io/n8nio/n8n docker tag to v2.39.5
ci / lint-prettier (push) Successful in 7s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 4s
ci / lint-prettier (pull_request) Successful in 6s
ci / lint-ruff (pull_request) Successful in 4s
ci / lint-yaml (pull_request) Successful in 6s
ci / lint-dockerfiles (pull_request) Successful in 5s
ci / validate (pull_request) Successful in 4s
renovate-ci / validate-renovate (pull_request) Successful in 7s
ci / build (push) Has been skipped
ci / build (pull_request) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
ci / deploy-userbot-panel (pull_request) Has been skipped
2026-09-14 09:04:30 +00:00
forust 663675f9a0 Merge pull request 'chore(deps): update ghcr.io/goauthentik/server docker tag to v2025.12.6' (#19) from renovate/ghcr.io-goauthentik-server-2025.x into main
ci / lint-prettier (push) Successful in 7s
ci / lint-ruff (push) Successful in 5s
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 6s
renovate-ci / validate-renovate (push) Successful in 7s
ci / build (push) Successful in 2s
ci / deploy-userbot-panel (push) Has been skipped
Reviewed-on: https://gitea.forust.xyz/forust/homelab/pulls/19
2026-09-14 09:03:35 +00:00
renovate-bot 8b28bd24ff chore(deps): update ghcr.io/goauthentik/server docker tag to v2025.12.6
ci / lint-yaml (push) Successful in 6s
ci / lint-dockerfiles (push) Successful in 6s
ci / validate (push) Successful in 7s
ci / lint-prettier (pull_request) Successful in 14s
ci / lint-ruff (pull_request) Successful in 4s
ci / lint-prettier (push) Successful in 8s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (pull_request) Successful in 7s
ci / validate (pull_request) Successful in 5s
ci / lint-dockerfiles (pull_request) Successful in 4s
renovate-ci / validate-renovate (pull_request) Successful in 10s
ci / build (push) Has been skipped
ci / build (pull_request) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
ci / deploy-userbot-panel (pull_request) Has been skipped
2026-09-14 09:03:24 +00:00
forust b5d6f75330 Merge pull request 'chore(deps): update mcr.microsoft.com/playwright docker tag to v1.63.0' (#20) from renovate/mcr.microsoft.com-playwright-1.x into main
ci / lint-ruff (push) Successful in 5s
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 5s
ci / lint-prettier (push) Successful in 7s
renovate-ci / validate-renovate (push) Successful in 7s
ci / build (push) Successful in 24s
ci / deploy-userbot-panel (push) Has been skipped
Reviewed-on: https://gitea.forust.xyz/forust/homelab/pulls/20
2026-09-14 09:02:49 +00:00
renovate-bot f5b5ecaafa chore(deps): update mcr.microsoft.com/playwright docker tag to v1.63.0
ci / lint-prettier (pull_request) Successful in 8s
ci / lint-ruff (pull_request) Successful in 4s
ci / lint-yaml (pull_request) Successful in 6s
ci / lint-dockerfiles (pull_request) Successful in 4s
ci / validate (pull_request) Successful in 5s
ci / lint-prettier (push) Successful in 7s
ci / lint-ruff (push) Successful in 3s
ci / lint-yaml (push) Successful in 6s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 5s
renovate-ci / validate-renovate (pull_request) Successful in 8s
ci / build (pull_request) Has been skipped
ci / build (push) Has been skipped
ci / deploy-userbot-panel (pull_request) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
2026-09-14 09:01:58 +00:00
forust 7799b676ca ci: validate Renovate manifests with kubeconform
ci / lint-prettier (push) Successful in 11s
ci / lint-ruff (push) Successful in 5s
ci / lint-yaml (push) Successful in 7s
ci / validate (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 5s
renovate-ci / validate-renovate (push) Successful in 1m21s
ci / build (push) Successful in 3s
ci / deploy-userbot-panel (push) Has been skipped
2026-09-14 10:51:40 +02:00
forustandCopilot 24d3686f60 ci: fix formatting and YAML lint scope
ci / lint-prettier (push) Successful in 13s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 6s
renovate-ci / validate-renovate (push) Failing after 2s
ci / build (push) Successful in 2s
ci / deploy-userbot-panel (push) Has been skipped
Lint tracked YAML files without scanning generated dependencies.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-14 09:30:55 +02:00
forust b8b3bba264 Merge branch 'feat/renovate'
ci / lint-yaml (push) Failing after 6s
ci / lint-prettier (push) Failing after 8s
ci / lint-ruff (push) Successful in 4s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 5s
renovate-ci / validate-renovate (push) Failing after 2s
ci / build (push) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
2026-09-14 09:16:43 +02:00
forustandCopilot abfbc04067 fix(infra): align monitoring and Gitea database config
Keep CrowdSec scraping explicit and define Gitea's database name.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-14 09:16:01 +02:00
forustandCopilot 23ed72826a chore(images): pin service image updates
Replace floating service images with reviewable tags or digests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-14 09:16:01 +02:00
forustandCopilot 7288058df6 feat(renovate): add Gitea update automation
Run Renovate in Kubernetes to create reviewed image update PRs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-14 09:16:01 +02:00
forust 6715f9e9af chore(k8s): raise resource limits for glance, edu and crowdsec 2026-09-14 01:29:28 +02:00
forust ccec1102ef ci(deploy): helm upgrade kube-prometheus-stack when active 2026-09-14 01:29:24 +02:00
forust 360a6fc5dc feat(prometheus): add alerting rules and alertmanager config 2026-09-14 01:21:27 +02:00
forust 9a806724af chore(crowdsec): remove crowdsec bouncer from dns and headscale ingresses 2026-09-14 01:15:55 +02:00
forustandCopilot ed1ddaad5d feat(crowdsec): restore web traffic protection
Protect public Traefik routes with CrowdSec HTTP decisions and restore access logging for web traffic analysis.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-12 21:05:12 +02:00
forustandCopilot 726b3ee544 fix(edu): run redis as statefulset
ci / deploy-userbot-panel (push) Has been skipped
ci / lint-ruff (push) Successful in 5s
ci / lint-yaml (push) Successful in 7s
ci / build (push) Successful in 3s
ci / lint-prettier (push) Successful in 11s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 5s
Keep the existing Redis PVC and data while migrating the edu-master workload from Deployment to StatefulSet.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-10 23:35:59 +02:00
forust 13309b26e0 fix: add checkmk agent entrypoint
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 5s
ci / deploy-userbot-panel (push) Has been skipped
ci / lint-prettier (push) Successful in 10s
ci / lint-ruff (push) Successful in 5s
ci / lint-yaml (push) Successful in 7s
ci / build (push) Successful in 2s
update traefik to v3.7.13
2026-09-10 14:52:49 +02:00
forust eddc256bed chore: remove esp32/ingress.yaml from main
ci / lint-prettier (push) Successful in 7s
ci / lint-ruff (push) Successful in 4s
ci / deploy-userbot-panel (push) Has been skipped
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 5s
ci / build (push) Successful in 3s
2026-09-10 12:12:54 +02:00
forust 52f821cbba Merge branch 'main' of ssh://gitssh.forust.xyz:2221/forust/homelab
ci / lint-prettier (push) Failing after 8s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Failing after 7s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 5s
ci / build (push) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
2026-09-10 11:47:25 +02:00
forust 0dbc2fff13 Merge branch 'sidetree' 2026-09-10 11:47:25 +02:00
forust 91ec83bc1c Merge branch 'main' of ssh://gitssh.forust.xyz:2221/forust/homelab
ci / lint-ruff (push) Successful in 4s
ci / lint-prettier (push) Failing after 8s
ci / lint-yaml (push) Failing after 7s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 6s
ci / build (push) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
2026-09-10 11:47:20 +02:00
forust 9fec1dae39 Merge branch 'sidetree' 2026-09-10 11:47:15 +02:00
forust e5626b7b2d chore: remove untracked README.md
ci / lint-prettier (push) Successful in 7s
ci / lint-ruff (push) Successful in 5s
ci / lint-yaml (push) Successful in 6s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 4s
ci / build (push) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
2026-09-10 11:45:00 +02:00
forust 8fb12a2176 chore: remove untracked README.md
ci / lint-prettier (push) Successful in 7s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 6s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 5s
ci / build (push) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
2026-09-10 11:44:23 +02:00
forust fe0c7067b6 Merge branch 'main' of ssh://gitssh.forust.xyz:2221/forust/homelab
ci / validate (push) Successful in 4s
ci / build (push) Has been skipped
ci / lint-prettier (push) Failing after 8s
ci / lint-ruff (push) Successful in 7s
ci / lint-yaml (push) Failing after 6s
ci / lint-dockerfiles (push) Successful in 4s
ci / deploy-userbot-panel (push) Has been skipped
2026-09-10 11:42:17 +02:00
forust d0f0843774 Merge branch 'sidetree' 2026-09-10 11:41:35 +02:00
forust 81a5b207ac Merge branch 'main' of ssh://gitssh.forust.xyz:2221/forust/homelab
ci / lint-prettier (push) Failing after 8s
ci / lint-ruff (push) Successful in 5s
ci / lint-yaml (push) Failing after 7s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 5s
ci / build (push) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
2026-09-10 11:41:31 +02:00
forust e3d5970ae3 chore: remove untracked README.md
ci / lint-prettier (push) Failing after 11s
ci / lint-ruff (push) Successful in 5s
ci / lint-yaml (push) Failing after 7s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 5s
ci / build (push) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
2026-09-10 11:39:20 +02:00
forust 85f05c26cb feat(edu): activate k8s management for edu-master 2026-09-10 00:55:22 +02:00
forust 68630eb773 fix(edu): read diary event times directly from DOM, drop 25 AJAX clicks
ci / lint-prettier (push) Successful in 9s
ci / lint-ruff (push) Successful in 6s
ci / lint-yaml (push) Successful in 10s
ci / lint-dockerfiles (push) Successful in 7s
ci / validate (push) Successful in 6s
ci / build (push) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
_collect_event_times re-clicked every a.event-link and waited ~3s per
event for a visible span.data, but the calendar embeds all times in
div.event-full-info[data-event-full-info-id] span.data already. The old
loop took ~109s for 25 events and collected 0 (original divs stay
sf-hidden), effectively hanging /diary. Now a single evaluate reads all
times (~3.7s), parsing HH:MM from p.date span.data.
2026-09-10 00:31:04 +02:00
forust dad9cf2104 feat(edu): parse event times in /diary and drop weekend days
ci / lint-prettier (push) Successful in 9s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 6s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 6s
ci / build (push) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
- fetch each event's time via Playwright (click event-link, read span.data, close fancybox) and render as 'title (HH:MM)'
- '08:00' placeholder renders as localized 'unknown' (time_unknown key in ru/uk/en)
- diary week view shows Mon-Fri only (title ends at Friday)
- diary month view skips Sat/Sun by weekday_idx with name-based fallback
- schedule keyboard drops Sat/Sun day buttons
2026-09-10 00:05:56 +02:00
forust 60886e8be2 style(edu): ruff-format webinar-checker/checker.py
ci / lint-prettier (push) Successful in 13s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 6s
ci / build (push) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
2026-09-09 23:47:04 +02:00
forust 4528321225 fix(edu): add TZ to .env.example 2026-09-09 11:46:35 +02:00
forust b246a3dea1 fix(edu): review findings for checker.py i18n — group chat language via resolve_lang/chat: keys (default uk), weekday normalization with _norm_day + logging, drop dead translation keys, html.escape, single lang lookup, distinct whitelist emoji 2026-09-09 11:44:17 +02:00
forustandassistant 4c59a2d2fe lang(edu): translate k8s manifest comments to English only
- restore-seed-job.yaml.example: translate runbook to English
- secrets.yaml.example: translate section headers to English
- webinar-checker.yaml: translate initContainer dependency-order comments to English

Co-authored-by: assistant
2026-09-09 10:46:36 +02:00
forust fcc7b0d611 ci(deploy): gate redeploy behind manual workflow_dispatch
ci / lint-prettier (push) Successful in 8s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 5s
ci / build (push) Successful in 2s
ci / deploy-userbot-panel (push) Has been skipped
removes automatic redeploy on push to main; deploy now runs only on
explicit manual trigger
2026-09-06 20:55:40 +02:00
forust 9633fe3a10 style(ci): add trailing newline to deploy workflows
ci / lint-prettier (push) Successful in 7s
ci / lint-ruff (push) Successful in 5s
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 5s
deploy / redeploy (push) Failing after 1s
ci / build (push) Successful in 2s
ci / deploy-userbot-panel (push) Has been skipped
2026-09-06 20:45:30 +02:00
forust d9f1c8325a feat(userbot): prereqs at startup, SPA path guard, provision lock
ci / lint-prettier (push) Failing after 8s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Failing after 7s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 5s
deploy / redeploy (push) Failing after 0s
ci / build (push) Has been skipped
ci / deploy-userbot-panel (push) Has been skipped
- ensure_prerequisites runs on startup, not per-request; kube config
  errors surface as 503 PanelError
- serialize provisioning with a lock; drop per-endpoint prereq checks
- guard SPA fallback against path traversal (relative_to)
- add backend tests for auth flow, k8s service, spa routing; ci comment
  for legacy userbot deployments
2026-09-06 20:39:12 +02:00
forust 861d89d36a ci(deploy): split runtime by k8s/active marker
services marked k8s/active are applied via kubectl; the rest via docker
compose. inactive services with k8s/ keep only routing manifests
(external Services, EndpointSlices, Ingresses) to reach docker backends.
headscale/nextcloud routing moved to k8s/routing/.

validations: compose config --quiet + kubectl apply --dry-run=client.
namespace manifests applied first. pull_policy:build stacks get
build+push before up so the registry image stays fresh.
2026-09-06 20:39:12 +02:00
forust 71cddd6a91 feat(userbot): add Kubernetes control panel
Manage Telegram instances through Kubernetes with legacy adoption for forust and anna. Build and deploy the panel image alongside the runtime.
2026-09-06 20:39:12 +02:00
forust 30e6f05584 fix(edu_master): satisfy ruff in schedule scraper
- rename ambiguous loop var, merge nested if (E741, SIM102)
- use tempfile.gettempdir() for debug dump (S108)
- drop unused total_lessons assignment (F841)
2026-09-06 20:37:18 +02:00
forust bc8d74fd28 feat(schedule): per-user class picker, parser fixes
ci / lint-prettier (push) Successful in 8s
ci / lint-ruff (push) Failing after 5s
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 4s
ci / build (push) Has been skipped
- fix parser: capture tr attrs via finditer, strip HTML comments before
  cell parse (was leaving '-->' in subject names)
- store class choice in redis: user:{id}:schedule_class (private) and
  chat:{id}:schedule_class (groups, admin-only via /setclass)
- /schedule renders day for stored class, /setclass sets it directly
- drop teacher emoji, format grade as "N клас"
2026-09-06 16:17:48 +02:00
forust d2d4efb0d7 feat: add schedule scraper for lessons table
ci / lint-prettier (push) Successful in 9s
ci / lint-ruff (push) Failing after 4s
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 6s
ci / validate (push) Successful in 7s
ci / build (push) Has been skipped
Add /schedule command to scrape edu.edu.vn.ua/lessons/table via Playwright.
Inline keyboard flow: pick weekday (with today/tomorrow shortcuts),
then pick class. Cache 5h per user. Parse subjects/notes/teachers,
multi-lesson cells (hr-separated).
2026-09-06 15:46:41 +02:00
forust b752bf88bd feat: add edu_master k8s manifests for k0s migration
ci / lint-prettier (push) Successful in 10s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 5s
ci / build (push) Successful in 35s
2026-09-06 15:00:51 +02:00
forust 587611ca88 chore: remove empty middlewares blocks from k8s ingresses 2026-09-02 12:17:04 +02:00
forust ace23ad1f9 fix: remove www.xdfnx.cfd from ingress, add Gitea access log config
ci / lint-prettier (push) Successful in 13s
ci / lint-ruff (push) Successful in 7s
ci / lint-yaml (push) Successful in 17s
ci / lint-dockerfiles (push) Successful in 14s
ci / validate (push) Successful in 15s
ci / build (push) Successful in 26s
2026-07-20 11:40:41 +02:00
forust 92aa731e44 deleted crowdsec stack from the repo. will figure something else
ci / lint-prettier (push) Successful in 18s
ci / lint-ruff (push) Successful in 6s
ci / lint-yaml (push) Successful in 6s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 6s
ci / build (push) Successful in 43s
Signed-off-by: mr-forust <vzlomdsisma@gmail.com>
2026-07-19 23:16:14 +02:00
forust 87ca3fd40c chore: updatet chernuha's pfp, added projects sections for forust.xyz
ci / lint-prettier (push) Successful in 8s
ci / lint-ruff (push) Successful in 3s
ci / lint-yaml (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 6s
ci / validate (push) Successful in 7s
ci / build (push) Successful in 18s
2026-07-05 02:59:31 +02:00
forust 82bcd30ed8 updated xdfnx's page
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 6s
ci / lint-prettier (push) Successful in 8s
ci / lint-dockerfiles (push) Successful in 4s
ci / validate (push) Successful in 11s
ci / build (push) Successful in 19s
2026-07-04 01:01:05 +02:00
forust 620262d98c ci: fetch full history for change detection after rebase
ci / lint-prettier (push) Successful in 9s
ci / lint-ruff (push) Successful in 6s
ci / lint-yaml (push) Successful in 9s
ci / build (push) Successful in 3s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 5s
2026-07-04 00:55:46 +02:00
forust 831f3a46b0 updated xdfnx's page
ci / lint-prettier (push) Successful in 8s
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 6s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 5s
ci / build (push) Successful in 3s
2026-07-04 00:46:13 +02:00
forust f7902e74e8 ci: require lint and validate before build
ci / lint-ruff (push) Successful in 4s
ci / lint-yaml (push) Successful in 6s
ci / validate (push) Successful in 5s
ci / build (push) Successful in 2s
ci / lint-prettier (push) Successful in 7s
ci / lint-dockerfiles (push) Successful in 3s
2026-06-30 01:23:53 +02:00
forust eb8d1b361e ci: add branch tags to docker builds
ci / lint (push) Successful in 15s
ci / validate (push) Successful in 8s
ci / build (push) Successful in 3s
2026-06-30 01:20:51 +02:00
forust 6e2cafb206 ci: merge lint validate and docker builds
ci / lint (push) Successful in 14s
ci / validate (push) Successful in 19s
ci / build (push) Successful in 2s
2026-06-30 01:17:31 +02:00
forust 67b0c0824f ci: restore docker-based lint and validate jobs
lint / prettier (push) Successful in 7s
lint / ruff (push) Successful in 3s
lint / yamllint (push) Successful in 5s
lint / hadolint (push) Successful in 4s
validate / yaml (push) Successful in 6s
validate / k8s (push) Successful in 4s
2026-06-30 01:12:23 +02:00
forust 8e72e0a920 ci: run lint and validate tools locally
lint / prettier (push) Failing after 2s
lint / ruff (push) Successful in 2s
lint / yamllint (push) Successful in 3s
lint / hadolint (push) Failing after 2s
validate / yaml (push) Successful in 2s
validate / k8s (push) Failing after 1s
2026-06-30 00:14:14 +02:00
forust 73a1132beb Align Traefik Helm log values with chart v41
lint / prettier (push) Successful in 1m59s
lint / ruff (push) Successful in 4s
lint / yamllint (push) Successful in 5s
lint / hadolint (push) Successful in 4s
validate / yaml (push) Successful in 6s
validate / k8s (push) Successful in 3s
2026-06-29 23:02:26 +02:00
forust a128523c24 Fix CrowdSec middleware references in Traefik ingresses 2026-06-29 22:47:21 +02:00
forust ee881acd0e refactor: update xdfnx homepage index.html
lint / prettier (push) Successful in 14s
lint / ruff (push) Successful in 5s
lint / yamllint (push) Successful in 9s
lint / hadolint (push) Successful in 17s
validate / yaml (push) Successful in 8s
validate / k8s (push) Successful in 7s
2026-06-28 11:59:57 +02:00
forust bacb2f4b9f fix: correct Traefik rule syntax for local IngressRoutes
Move parentheses outside Host() calls so that || and && operators
are properly grouped in Traefik rule expressions.
2026-06-28 11:59:52 +02:00
forust 91211e7b78 fix: resolve CrowdSec Helm upgrade failure - remove duplicate DISABLE_ONLINE_API env and add metrics config 2026-06-28 11:59:46 +02:00
forust 9b3a7aadb4 fix: resolve pyrogram imports by adding venv and pyright config
lint / prettier (push) Successful in 7s
lint / ruff (push) Successful in 4s
lint / yamllint (push) Successful in 6s
lint / hadolint (push) Successful in 4s
validate / yaml (push) Successful in 5s
validate / k8s (push) Successful in 5s
- Create .venv with pyrofork and all dependencies installed
- Add pyrightconfig.json at workspace root and in userbot/
- Enable pyright as language server for Python in Zed settings
- Update uv.lock with resolved dependency tree
2026-06-21 22:33:04 +02:00
forust b123621ead chore: remove github obsolete prod deploy workflow
lint / prettier (push) Successful in 7s
lint / ruff (push) Successful in 4s
lint / yamllint (push) Successful in 6s
lint / hadolint (push) Successful in 4s
validate / yaml (push) Successful in 6s
validate / k8s (push) Successful in 4s
2026-06-21 22:24:30 +02:00
forust a2df8504f5 Fix DL3013: pin pip version in webinar-checker Dockerfile
lint / prettier (push) Successful in 8s
lint / ruff (push) Successful in 4s
lint / yamllint (push) Successful in 6s
lint / hadolint (push) Successful in 4s
validate / yaml (push) Successful in 6s
validate / k8s (push) Successful in 4s
2026-06-21 22:07:44 +02:00
forust fb43306571 Fix all lint issues: Dockerfiles (DL3015/DL3013/DL4006) + Ruff (173→0 errors)
lint / prettier (push) Successful in 8s
lint / ruff (push) Successful in 5s
lint / yamllint (push) Successful in 7s
lint / hadolint (push) Failing after 4s
validate / yaml (push) Successful in 5s
validate / k8s (push) Successful in 5s
Dockerfile fixes:
- edu_master/phpsessid-bot: add --no-install-recommends, pin pip versions
- edu_master/webinar-checker: pin pip versions with --no-cache-dir
- userbot: add SHELL with pipefail for pipe operations

Ruff fixes (173 → 0):
- W293/W291/W292: whitespace clean via ruff format
- N806: camelCase → snake_case (anilist, safone, hearts, flux, etc.)
- ARG001/ARG002: prefix unused params with _
- SIM115: use context managers for file I/O
- SIM117: combine nested with statements
- S608: noqa on SQL f-strings (module name is validated)
- E402/N812/N817: import fixes
- B023: pass loop variable as argument
- I001: auto-sorted imports
- syntax: fixed = vs == in dtek_notif/main.py
2026-06-21 22:01:51 +02:00
forust f424d91405 Tighten lint workflow scope
lint / prettier (push) Successful in 8s
lint / ruff (push) Failing after 5s
lint / yamllint (push) Successful in 6s
lint / hadolint (push) Failing after 4s
validate / yaml (push) Successful in 6s
validate / k8s (push) Successful in 4s
2026-06-21 21:51:20 +02:00
forust 88a8f2987f Unify workflow job naming
lint / prettier (push) Failing after 9s
lint / ruff (push) Failing after 5s
lint / yamllint (push) Successful in 7s
lint / hadolint (push) Failing after 13s
validate / yaml (push) Successful in 6s
validate / k8s (push) Successful in 5s
2026-06-21 21:46:22 +02:00
forust 17027b232b Rename workflows to yaml 2026-06-21 21:45:16 +02:00
forust 6ecbbb39b4 Fix yamllint workflow invocation
validate / k8s (push) Successful in 5s
validate / yaml (push) Successful in 7s
2026-06-21 21:40:11 +02:00
forust 175cbc8860 Fix validation workflows for self-hosted runner
validate / yaml (push) Failing after 15s
validate / k8s (push) Successful in 9s
2026-06-21 21:37:30 +02:00
forust 6363d050b0 Add YAML validation workflows 2026-06-21 21:27:31 +02:00
forust c855764bc6 feat: add vaultwarden deployment config (compose + k8s)
Deploy to Server / deploy (push) Failing after 1s
2026-06-21 01:02:09 +02:00
forust 7d92b85e21 Merge commit '4355f451d4b4288d43468e08cad1f377e511f98a'
Deploy to Server / deploy (push) Has been cancelled
2026-06-19 23:23:28 +02:00
forust 9364392bc0 Merge commit '3eaef5dc90b716cc0fa391cbe2394be56d5f6041' as 'userbot'
Deploy to Server / deploy (push) Has been cancelled
2026-06-19 23:20:23 +02:00
forust 4355f451d4 Merge commit '3eaef5dc90b716cc0fa391cbe2394be56d5f6041' as 'userbot' 2026-06-19 23:20:23 +02:00
forust 3eaef5dc90 Squashed 'userbot/' content from commit 7fb0a0e
git-subtree-dir: userbot
git-subtree-split: 7fb0a0e179
2026-06-19 23:20:23 +02:00
forust 69ffd3682e refactor: userbot to subtree 2026-06-19 23:20:13 +02:00
forust 1930600c40 Revert "refactor: extract userbot to standalone repo, add as git submodule"
This reverts commit 3c383db9a7.
2026-06-19 23:11:27 +02:00
forust d74a705d53 chore(k8s): rewritten ingressroute to include headplane AND headscale-admin ui
Deploy to Server / deploy (push) Has been cancelled
new routes groupping style
2026-06-19 23:02:22 +02:00
forust 3c383db9a7 refactor: extract userbot to standalone repo, add as git submodule
Deploy to Server / deploy (push) Has been cancelled
userbot now lives at ssh://git@gitssh.forust.xyz:2221/forust/userbot.git
and is included in homelab as a submodule at userbot/
2026-06-19 15:39:00 +02:00
forust 7fb0a0e179 chore: batch lint fixes across userbot and edu_master
- S113: Add timeout=10 to all requests calls (74 fixes)
- E722: Replace bare except: with except Exception:
- B904: Replace redundant re-raise with bare raise
- E402: Add noqa for intentional late imports after import_library()
- S102/S307/S310/S311/S603/S605/S606/S607/S108: Add noqa for intentional usage
- F601: Fix duplicate dict key in unsplash.py
- N802: Rename ReplyCheck -> reply_check with backward compat alias
- N813: Rename bs -> BS in icons.py
- B007/B020: Rename loop var _j in animations.py
- SIM102: Collapse nested if in autofwd.py
- SIM113: Use enumerate() in calculator.py
- A002: Add noqa for builtin shadowing in admlist.py
- F811: Add noqa for cohere redefinition
- edu_master: Fix ARG001, S108, S110, SIM117, apply --unsafe-fixes
- Add modules_list.txt with full module inventory
2026-06-19 15:36:25 +02:00
forust 227e5fda27 chore: batch lint fixes across userbot and edu_master
- S113: Add timeout=10 to all requests calls (74 fixes)
- E722: Replace bare except: with except Exception:
- B904: Replace redundant re-raise with bare raise
- E402: Add noqa for intentional late imports after import_library()
- S102/S307/S310/S311/S603/S605/S606/S607/S108: Add noqa for intentional usage
- F601: Fix duplicate dict key in unsplash.py
- N802: Rename ReplyCheck -> reply_check with backward compat alias
- N813: Rename bs -> BS in icons.py
- B007/B020: Rename loop var _j in animations.py
- SIM102: Collapse nested if in autofwd.py
- SIM113: Use enumerate() in calculator.py
- A002: Add noqa for builtin shadowing in admlist.py
- F811: Add noqa for cohere redefinition
- edu_master: Fix ARG001, S108, S110, SIM117, apply --unsafe-fixes
- Add modules_list.txt with full module inventory
2026-06-19 15:36:25 +02:00
forust 20bce5b31c fix(userbot): add missing safone.py imports, apply ruff --unsafe-fixes
- Add missing aiohttp, PIL, BytesIO imports to safone.py (F821 runtime bugs)
- Apply ruff --unsafe-fixes (27 fixes): ternary operators, .get() patterns,
  contextlib.suppress, enumerate(), collapsible if/else, remove .keys()
2026-06-19 15:27:12 +02:00
forust 70d7855f06 fix(userbot): add missing safone.py imports, apply ruff --unsafe-fixes
- Add missing aiohttp, PIL, BytesIO imports to safone.py (F821 runtime bugs)
- Apply ruff --unsafe-fixes (27 fixes): ternary operators, .get() patterns,
  contextlib.suppress, enumerate(), collapsible if/else, remove .keys()
2026-06-19 15:27:12 +02:00
forust c905bbd039 chore(userbot): optimize Dockerfile with multi-stage build and static ffmpeg
- Multi-stage build: pip deps built in separate stage
- Static ffmpeg binary instead of apt package (avoid 200+ deps)
- Keep only git, mediainfo, wget via apt
2026-06-19 14:56:37 +02:00
forust 7ba6bc44f2 chore(userbot): apply ruff check --fix and ruff format
- ruff check --fix: 210 auto-fixed errors (import sorting, trailing
  whitespace, unused imports, f-string fixups, deprecated annotations)
- ruff format: 104 files reformatted to consistent style
- 268 non-auto-fixable issues remain (S113 requests timeout, etc.)
2026-06-19 12:19:01 +02:00
forust 95cec59263 chore(userbot): apply prettier formatting across manifests 2026-06-19 12:03:13 +02:00
forust 20b8c93275 chore(k8s): make dockmon statefulset
+ adjusted userbot sys reqs
2026-06-10 19:40:45 +02:00
forust bb821e138a chore(k8): adjusted system resources requests and limits based on manual monitoring 2026-06-10 19:37:16 +02:00
forust b7854447af lint: yaml spaces and tabs 2026-06-09 12:59:36 +02:00
forust 444bb97f8e feat: add userbot k8s deployment method
- Kustomize: base + overlays/dev + overlays/prod
2026-06-07 19:41:28 +02:00
forust 4f01cdac31 fix: removed git checkout (was destructive)
- Remove git init/fetch/checkout from utils/misc.py
- Hardcode userbot_version to 2.5.0
2026-06-07 19:40:52 +02:00
forust cb40b10ecf chore: add gitea container registry compose support for localy builded apps 2026-06-07 14:37:57 +02:00
forust bed58c84ef refactor: update .dockerignore
pull_policy never to use local images
2026-05-25 01:43:46 +02:00
forust 56e5d79e3e refactor: improved error handling, timeouts and use temp files 2026-05-21 22:14:29 +02:00
forust cd0ce06246 refactor: imporved layer caching for dockerfile
using existing built image for account 2
2026-05-21 22:12:33 +02:00
forust a699ceb935 refactor: userbots' compose-files cleanup 2025-12-09 21:45:24 +01:00
forust ce66a546f1 chore: update gitignore, add translations 2025-11-27 18:58:17 +01:00
forust a30bda4940 Renamed userbot's dockercompose for easy access 2025-11-13 04:00:23 +01:00
forust b722467991 Remove unnecessary network and development configurations from userbot Docker Compose file 2025-11-13 03:48:50 +01:00
forust 5f8fd05266 Add userbot Docker Compose configuration for userbot_forust and userbot_anna services 2025-11-13 00:17:26 +01:00
forust 1c7afe5bb3 Move userbot Docker Compose configuration for forust and anna services 2025-11-11 01:57:44 +01:00
forust 4ff80c07b0 deleting that install shit 2025-11-11 01:41:24 +01:00
forust 3a5652daa7 userbot/install.sh, start 2025-11-11 01:39:53 +01:00
forust 4e18cd0eb3 init, .gitignore 2025-11-11 00:02:49 +01:00
256 changed files with 14134 additions and 2853 deletions

No files matched your search

+191
View File
@@ -0,0 +1,191 @@
# Инструкция: Анализ хранилища Kubernetes и настройка NFS
## Цель
Проанализировать текущую конфигурацию хранилища Kubernetes и подготовить план внедрения NFS StorageClass для сохранения данных при удалении namespace.
## 1. Собрать информацию о кластере
### 1.1. Версия Kubernetes и тип дистрибутива
```bash
kubectl version --short
# или
kubectl version
```
Определить, используется ли k3s, k8s, microk8s и т.д.:
```bash
# Проверить наличие k3s
which k3s
# Проверить процесс
ps aux | grep -E 'kube|k3s'
```
### 1.2. StorageClass
```bash
kubectl get storageclass -o wide
```
Запомнить:
- `PROVISIONER` — какой драйвер используется
- `RECLAIMPOLICY` — Delete или Retain
- Какой StorageClass помечен как `(default)`
### 1.3. Существующие PV и PVC
```bash
kubectl get pv -o wide
kubectl get pvc --all-namespaces
```
Посмотреть, какие PVC привязаны к каким PV, и какой reclaimPolicy у PV.
### 1.4. Нода и диски
```bash
# Список нод
kubectl get nodes -o wide
# На каждой ноде (через ssh или локально):
lsblk
df -h
cat /etc/fstab
```
Определить:
- Есть ли отдельный раздел/диск для данных
- Куда смонтированы разделы
- Сколько свободного места
- Есть ли монтирование NTFS-разделов (как `/media/forust/Programs`)
### 1.5. Где local-path хранит данные (для k3s)
```bash
ls -la /var/lib/rancher/k3s/storage/ 2>/dev/null
# или для microk8s
ls -la /var/snap/microk8s/common/ 2>/dev/null
```
## 2. Анализ: сохраняются ли данные при удалении namespace?
| Сценарий | Результат |
|---|---|
| `kubectl delete ns <ns>` | Все PVC в namespace удаляются |
| PVC → PV c `reclaimPolicy: Delete` | PV и данные удалены |
| PVC → PV c `reclaimPolicy: Retain` | PV остаётся (статус Released), данные целы |
**Вывод:** Если reclaimPolicy в StorageClass = `Delete`, то данные **пропадут**. Если `Retain` — сохранятся.
## 3. План внедрения NFS
### 3.1. Проверить, установлен ли NFS
```bash
which nfsstat exportfs mount.nfs
systemctl status nfs-server 2>/dev/null || systemctl status nfs-kernel-server 2>/dev/null
```
### 3.2. Выбрать директорию для NFS-экспорта
Варианты (выбрать подходящий):
- `/var/lib/k8s-nfs/` — на корневом разделе
- `<путь к отдельному разделу>/k8s-nfs/` — если есть отдельный диск/раздел
- Не рекомендуется использовать NTFS-раздел (проблемы с правами и производительностью)
Требования:
- Файловая система: ext4 или xfs (не ntfs!)
- Достаточно свободного места
- Права: `755`, владелец root
### 3.3. Установить NFS-сервер
```bash
# Debian/Ubuntu
apt update && apt install -y nfs-kernel-server
# RHEL/Fedora
dnf install -y nfs-utils
```
### 3.4. Настроить экспорт
Создать директорию:
```bash
mkdir -p /var/lib/k8s-nfs
chmod 755 /var/lib/k8s-nfs
```
Добавить в `/etc/exports`:
```
/var/lib/k8s-nfs *(rw,sync,no_subtree_check,no_root_squash)
```
Применить:
```bash
exportfs -rav
```
Проверить:
```bash
showmount -e localhost
```
### 3.5. Выбрать способ интеграции с Kubernetes
#### Вариант A: nfs-subdir-external-provisioner (проще)
```bash
helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm install nfs-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
--namespace kube-system \
--set nfs.server=127.0.0.1 \
--set nfs.path=/var/lib/k8s-nfs \
--set storageClass.name=nfs \
--set storageClass.defaultClass=false \
--set storageClass.reclaimPolicy=Retain
```
#### Вариант B: NFS CSI Driver
```bash
helm repo add csi-driver-nfs https://raw.githubusercontent.com/kubernetes-csi/csi-driver-nfs/master/charts
helm install csi-driver-nfs csi-driver-nfs/csi-driver-nfs --namespace kube-system
```
После установки CSI драйвера создать StorageClass:
```yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs
provisioner: nfs.csi.k8s.io
parameters:
server: 127.0.0.1
share: /var/lib/k8s-nfs
reclaimPolicy: Retain
volumeBindingMode: Immediate
```
### 3.6. Проверить результат
```bash
kubectl get storageclass
kubectl get pods -n kube-system | grep -E 'nfs|provisioner'
```
## 4. Итоговая конфигурация
После внедрения в кластере будет два StorageClass:
| Имя | Provisioner | ReclaimPolicy | Назначение |
|---|---|---|---|
| `local-path` (default) | rancher.io/local-path | Delete | Временные данные, stateless |
| `nfs` | nfs-subdir-external-provisioner или nfs.csi.k8s.io | Retain | Данные, которые нужно сохранять |
**Главное преимущество:** PVC c `storageClassName: nfs` при удалении namespace сохраняют данные на диске, так как NFS-провизор использует `reclaimPolicy: Retain` или файлы физически остаются в NFS-экспорте.
## 5. Ответы на частые вопросы
**В:** Не упадёт ли local-path при установке NFS?
**О:** Нет, они независимы. local-path продолжает работать как обычно.
**В:** Данные NFS и local-path будут на одном диске?
**О:** Да, можно настроить оба на одном разделе, в разных каталогах.
**В:** Что если у меня несколько нод?
**О:** NFS сервер нужно поднять на одной ноде, а с других нод должна быть доступна шари. Для multi-node лучше использовать отдельный сервер или distributed storage (Longhorn, Rook/Ceph).
**В:** Можно ли использовать существующий NTFS-раздел для NFS?
**О:** Не рекомендуется — NTFS не поддерживает права Linux (no_root_squash не сработает корректно), возможны проблемы с блокировками и производительностью.
+370
View File
@@ -0,0 +1,370 @@
name: ci
on:
push:
branches:
- "**"
pull_request:
workflow_dispatch:
env:
REGISTRY: gcr.forust.xyz
jobs:
lint-prettier:
runs-on: [self-hosted, linux, arch, homelab]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check formatting with Prettier
shell: bash
run: |
mapfile -t prettier_files < <(
git ls-files \
| grep -E '\.(md|json|ya?ml|html|css)$' \
| grep -Ev '^(\.docs/|\.zed/|errorpages/html/|homepages/(forust_files|xdfnx_files)/)'
)
if [ "${#prettier_files[@]}" -eq 0 ]; then
echo "No Prettier-managed files found."
exit 0
fi
docker run --rm \
-v "$PWD:/work" \
-w /work \
node:22-alpine \
sh -lc 'npx --yes prettier@3 --check --ignore-unknown "$@"' sh "${prettier_files[@]}"
lint-ruff:
runs-on: [self-hosted, linux, arch, homelab]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Lint Python with Ruff
shell: bash
run: |
docker run --rm \
-v "$PWD:/work" \
-w /work \
ghcr.io/astral-sh/ruff:latest \
check .
lint-yaml:
runs-on: [self-hosted, linux, arch, homelab]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Lint YAML syntax
shell: bash
run: |
mapfile -t yaml_files < <(
git ls-files '*.yaml' '*.yml' \
':!node_modules/**' \
':!**/.venv/**'
)
if [ "${#yaml_files[@]}" -eq 0 ]; then
echo "No YAML files found."
exit 0
fi
docker run --rm \
-v "$PWD:/work" \
-w /work \
cytopia/yamllint:latest \
-c .yamllint "${yaml_files[@]}"
lint-dockerfiles:
runs-on: [self-hosted, linux, arch, homelab]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Lint Dockerfiles
shell: bash
run: |
mapfile -t dockerfiles < <(
git ls-files ':(glob)**/Dockerfile' ':(glob)**/Dockerfile.*'
)
if [ "${#dockerfiles[@]}" -eq 0 ]; then
echo "No Dockerfiles found."
exit 0
fi
docker run --rm \
-v "$PWD:/work" \
-w /work \
--entrypoint hadolint \
hadolint/hadolint:latest-debian \
-c .hadolint.yaml "${dockerfiles[@]}"
validate:
runs-on: [self-hosted, linux, arch, homelab]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Validate Kubernetes manifests
shell: bash
run: |
mapfile -t manifests < <(
git ls-files ':(glob)**/k8s/**/*.yaml' ':(glob)**/k8s/**/*.yml' \
| grep -Ev '(^|/)(kustomization\.ya?ml|.*\.example\.ya?ml|.*values\.ya?ml|patch-.*\.ya?ml)$'
)
if [ "${#manifests[@]}" -eq 0 ]; then
echo "No Kubernetes manifests found."
exit 0
fi
docker run --rm \
-v "$PWD:/work" \
-w /work \
ghcr.io/yannh/kubeconform:latest \
-strict \
-ignore-missing-schemas \
-summary \
"${manifests[@]}"
build:
needs: [lint-prettier, lint-ruff, lint-yaml, lint-dockerfiles, validate]
if: github.event_name != 'pull_request' && (github.ref_name == 'main' || github.ref_name == 'dev')
runs-on: [self-hosted, linux, arch, homelab]
outputs:
services: ${{ steps.services.outputs.services }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect changed docker-built services
id: services
shell: bash
run: |
base="${{ github.event.before }}"
if [ -z "$base" ] || [ "$base" = "0000000000000000000000000000000000000000" ]; then
base="$(git rev-list --max-parents=0 HEAD)"
fi
mapfile -t changed_files < <(git diff --name-only "$base" "${GITHUB_SHA}")
services=()
add_service() {
local name="$1"
local seen=0
for existing in "${services[@]}"; do
if [ "$existing" = "$name" ]; then
seen=1
break
fi
done
if [ "$seen" -eq 0 ]; then
services+=("$name")
fi
}
for file in "${changed_files[@]}"; do
case "$file" in
dtek_notif/*)
add_service dtek_notif
;;
errorpages/*)
add_service errorpages
;;
userbot/*)
add_service userbot
;;
homepages/*)
add_service homepages
;;
edu_master/phpsessid-bot/*|edu_master/webinar-checker/*|edu_master/compose.yaml)
add_service edu_master
;;
esac
done
if [ "${#services[@]}" -eq 0 ]; then
echo "No docker-built services changed."
echo "services=" >> "$GITHUB_OUTPUT"
exit 0
fi
printf '%s\n' "${services[@]}" | tee /tmp/services.txt
echo "services=$(paste -sd, /tmp/services.txt)" >> "$GITHUB_OUTPUT"
- name: Log in to registry
if: steps.services.outputs.services != ''
shell: bash
run: |
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login "${REGISTRY}" \
-u "${{ secrets.REGISTRY_USERNAME }}" \
--password-stdin
- name: Build and push changed images
if: steps.services.outputs.services != ''
shell: bash
run: |
IFS=, read -r -a services <<< "${{ steps.services.outputs.services }}"
for service in "${services[@]}"; do
case "$service" in
dtek_notif)
image="${REGISTRY}/forust/dtek-notif"
tags=("latest")
case "${GITHUB_REF_NAME}" in
main)
tags+=("main" "prod")
;;
dev)
tags+=("dev")
;;
esac
build_args=()
for tag in "${tags[@]}"; do
build_args+=(-t "${image}:${tag}")
done
docker build "${build_args[@]}" dtek_notif
for tag in "${tags[@]}"; do
docker push "${image}:${tag}"
done
;;
errorpages)
image="${REGISTRY}/forust/error-pages"
tags=("latest")
case "${GITHUB_REF_NAME}" in
main)
tags+=("main" "prod")
;;
dev)
tags+=("dev")
;;
esac
build_args=()
for tag in "${tags[@]}"; do
build_args+=(-t "${image}:${tag}")
done
docker build "${build_args[@]}" errorpages
for tag in "${tags[@]}"; do
docker push "${image}:${tag}"
done
;;
userbot)
tags=("latest")
case "${GITHUB_REF_NAME}" in
main)
tags+=("main" "prod")
;;
dev)
tags+=("dev")
;;
esac
for target in runtime panel; do
case "$target" in
runtime)
context="userbot"
image="${REGISTRY}/forust/userbot"
;;
panel)
context="userbot/panel"
image="${REGISTRY}/forust/userbot-panel"
;;
esac
build_args=()
for tag in "${tags[@]}"; do
build_args+=(-t "${image}:${tag}")
done
docker build "${build_args[@]}" "$context"
for tag in "${tags[@]}"; do
docker push "${image}:${tag}"
done
done
;;
homepages)
for service in forust xdfnx; do
case "$service" in
forust)
image="${REGISTRY}/forust/forust-homepage"
;;
xdfnx)
image="${REGISTRY}/forust/xdfnx-homepage"
;;
esac
tags=("latest")
case "${GITHUB_REF_NAME}" in
main)
tags+=("main" "prod")
;;
dev)
tags+=("dev")
;;
esac
build_args=()
for tag in "${tags[@]}"; do
build_args+=(-t "${image}:${tag}")
done
docker build "${build_args[@]}" -f "homepages/Dockerfile.${service}" homepages
for tag in "${tags[@]}"; do
docker push "${image}:${tag}"
done
done
;;
edu_master)
for service in session-keeper webinar-checker; do
case "$service" in
session-keeper)
context="edu_master/phpsessid-bot"
image="${REGISTRY}/forust/session-keeper"
;;
webinar-checker)
context="edu_master/webinar-checker"
image="${REGISTRY}/forust/webinar-checker"
;;
esac
tags=("latest")
case "${GITHUB_REF_NAME}" in
main)
tags+=("main" "prod")
;;
dev)
tags+=("dev")
;;
esac
build_args=()
for tag in "${tags[@]}"; do
build_args+=(-t "${image}:${tag}")
done
docker build "${build_args[@]}" "$context"
for tag in "${tags[@]}"; do
docker push "${image}:${tag}"
done
done
;;
esac
done
deploy-userbot-panel:
needs: build
if: github.ref_name == 'main' && contains(needs.build.outputs.services, 'userbot')
runs-on: [self-hosted, linux, arch, homelab, prod]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Apply and roll out userbot panel
shell: bash
run: |
kubectl apply -f userbot/k8s/base/panel.yaml
kubectl get secret userbot-common-secrets -n default -o json \
| jq 'del(.metadata.annotations,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.uid,.metadata.managedFields) | .metadata.namespace = "userbot"' \
| kubectl apply -f -
# Keep legacy deployments (forust/anna) in sync with manifests; they have no replicas field, so apply leaves scaling to the user manager only.
kubectl apply -f userbot/k8s/base/userbots.yaml
kubectl rollout restart deployment/userbot-panel -n userbot
kubectl rollout status deployment/userbot-panel -n userbot --timeout=180s
+152
View File
@@ -0,0 +1,152 @@
name: deploy
on:
workflow_dispatch:
concurrency:
group: deploy-main
cancel-in-progress: false
jobs:
redeploy:
runs-on: [self-hosted, linux, arch, homelab, prod]
steps:
- name: Redeploy workstation
shell: bash
env:
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_PORT: ${{ secrets.DEPLOY_PORT }}
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
# Set APPLY_PRUNE=true to enable kubectl apply --prune. Requires every
# manifest to carry label app.kubernetes.io/managed-by=homelab-deploy,
# otherwise previously applied resources get deleted on the next run.
APPLY_PRUNE: ${{ vars.APPLY_PRUNE }}
run: |
set -euo pipefail
: "${DEPLOY_HOST:?missing DEPLOY_HOST}"
: "${DEPLOY_USER:?missing DEPLOY_USER}"
: "${DEPLOY_KEY:?missing DEPLOY_SSH_KEY}"
deploy_port="${DEPLOY_PORT:-22}"
deploy_path="${DEPLOY_PATH:-/srv/homelab}"
ssh_key="$RUNNER_TEMP/deploy_key"
mkdir -p "$RUNNER_TEMP"
printf '%s\n' "$DEPLOY_KEY" > "$ssh_key"
chmod 600 "$ssh_key"
ssh_opts=(
-i "$ssh_key"
-p "$deploy_port"
-o BatchMode=yes
-o StrictHostKeyChecking=accept-new
)
ssh "${ssh_opts[@]}" "${DEPLOY_USER}@${DEPLOY_HOST}" \
"DEPLOY_PATH=$(printf '%q' \"$deploy_path\") APPLY_PRUNE=$(printf '%q' \"${APPLY_PRUNE:-false}\") bash -se" <<'EOF'
set -euo pipefail
repo="${DEPLOY_PATH:-/srv/homelab}"
if [ ! -d "$repo/.git" ]; then
echo "Repository not found at $repo"
exit 1
fi
git -C "$repo" fetch origin main
git -C "$repo" reset --hard origin/main
# Runtime selection: a service is k8s-managed when $SERVICE/k8s/active
# exists. Otherwise it is compose-managed, and only k8s/routing/*
# manifests (external Services / EndpointSlices / ServersTransport /
# Ingresses that route to docker backends) are applied.
# migrate: touch SERVICE/k8s/active (+ move routing files up)
# rollback: rm SERVICE/k8s/active
collect_k8s() {
find "$1" -type f \( -name '*.yaml' -o -name '*.yml' \) \
! -path '*/routing/*' ! -path '*/overlays/*' \
! -name 'kustomization.y*ml' ! -name '*.example.y*ml' \
! -name '*values.y*ml' ! -name 'patch-*.y*ml' \
| sort
}
collect_k8s_inactive() {
find "$1" -type f \( -name '*.yaml' -o -name '*.yml' \) \
\( -name 'namespace.y*ml' -o -path '*/routing/*' \) \
! -path '*/overlays/*' ! -name '*.example.y*ml' \
| sort
}
mapfile -t compose_stacks < <(
find "$repo" -type f \( -name 'compose.yaml' -o -name 'compose.yml' \) | sort
)
mapfile -t k8s_manifests < <(
for kd in $(find "$repo" -type d -name k8s ! -path '*/.git/*' | sort); do
if [ -f "$kd/active" ]; then
collect_k8s "$kd"
else
collect_k8s_inactive "$kd"
fi
done
)
echo "== Validate compose stacks =="
for cf in "${compose_stacks[@]}"; do
dir=$(dirname "$cf")
if [ -f "$dir/k8s/active" ]; then
echo " skip (k8s-managed): $dir"
continue
fi
echo " config: $cf"
docker compose -f "$cf" config --quiet
done
echo "== Validate k8s manifests (kubectl dry-run) =="
for m in "${k8s_manifests[@]}"; do
echo " apply --dry-run=client $m"
kubectl apply --dry-run=client -f "$m" >/dev/null
done
echo "== Applying Kubernetes manifests =="
ns_files=()
other_files=()
for m in "${k8s_manifests[@]}"; do
case "$m" in
*/namespace.y?ml) ns_files+=("$m") ;;
*) other_files+=("$m") ;;
esac
done
prune_opts=()
if [ "${APPLY_PRUNE:-false}" = "true" ]; then
prune_opts=(--prune -l app.kubernetes.io/managed-by=homelab-deploy)
fi
if [ "${#ns_files[@]}" -gt 0 ]; then
echo " namespaces first: ${ns_files[*]}"
kubectl apply -f "${ns_files[@]}"
fi
if [ "${#other_files[@]}" -gt 0 ]; then
echo " resources: ${other_files[*]}"
kubectl apply "${prune_opts[@]}" -f "${other_files[@]}"
fi
echo "== Redeploying docker compose stacks =="
for cf in "${compose_stacks[@]}"; do
dir=$(dirname "$cf")
if [ -f "$dir/k8s/active" ]; then
echo " skip (k8s-managed): $dir"
continue
fi
echo " compose: $dir"
if grep -Eq '^\s+pull_policy:\s*build\b' "$cf"; then
docker compose -f "$cf" build
docker compose -f "$cf" push
fi
docker compose -f "$cf" up -d --pull always --remove-orphans
done
EOF
-39
View File
@@ -1,39 +0,0 @@
name: Deploy to Server
run-name: Deploying to ${{ runner.os}} server on ${{ gitea.ref }}
on:
push:
branches:
- main
- ci/gitea-actions
jobs:
deploy:
runs-on: prod
steps:
- name: Fetch and Diff Analysis
id: diff
run: |
cd ${{ secrets.PROD_DIR }}
git fetch origin main
CHANGES=$(git diff --name-only HEAD origin/main | cut -d/ -f1 | sort -u | tr '\n' ' ')
echo "dirs=$CHANGES" >> $GITHUB_OUTPUT
echo "Changed dirs: $CHANGES"
- name: Sync Server Files
run: |
cd ${{ secrets.PROD_DIR }}
git reset --hard origin/main
echo "Server files synced with origin/main"
- name: Deploy Services
run: |
cd ${{ secrets.PROD_DIR }}
for dir in ${{ steps.diff.outputs.dirs }}; do
if [ -d "$dir" ] && ([ -f "$dir/compose.yaml" ] || [ -f "$dir/docker-compose.yaml" ]); then
echo ">>> Deploying $dir"
cd "$dir"
DOCKER_BUILDKIT=1 BUILDKIT_PROGRESS=plain docker compose up -d --build --no-color
cd ..
else
echo ">>> Skipping $dir: no compose file found"
fi
done
+52
View File
@@ -0,0 +1,52 @@
name: renovate-ci
on:
pull_request:
push:
branches:
- main
workflow_dispatch:
jobs:
validate-renovate:
runs-on: [self-hosted, linux, arch, homelab]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Validate Renovate Compose draft
shell: bash
run: |
set -euo pipefail
trap 'rm -f renovate/.env' EXIT
printf '%s\n' \
'RENOVATE_ENDPOINT=https://gitea.example/api/v1' \
'RENOVATE_TOKEN=test-token' \
'RENOVATE_REPOSITORIES=forust/homelab' \
> renovate/.env
docker compose -f renovate/renovate-compose.yaml config --quiet
- name: Validate Kubernetes manifests
shell: bash
run: |
set -euo pipefail
docker run --rm \
-v "$PWD:/work" \
-w /work \
ghcr.io/yannh/kubeconform:latest \
-strict \
-ignore-missing-schemas \
-summary \
renovate/k8s/namespace.yaml \
renovate/k8s/configmap.yaml \
renovate/k8s/cronjob.yaml
- name: Validate Renovate repository config
shell: bash
run: |
set -euo pipefail
docker run --rm \
-v "$PWD:/work" \
-w /work \
renovate/renovate:44.83.2 \
renovate-config-validator renovate.json
+370
View File
@@ -0,0 +1,370 @@
name: ci
on:
push:
branches:
- "**"
pull_request:
workflow_dispatch:
env:
REGISTRY: gcr.forust.xyz
jobs:
lint-prettier:
runs-on: [self-hosted, linux, arch, homelab]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Check formatting with Prettier
shell: bash
run: |
mapfile -t prettier_files < <(
git ls-files \
| grep -E '\.(md|json|ya?ml|html|css)$' \
| grep -Ev '^(\.docs/|\.zed/|errorpages/html/|homepages/(forust_files|xdfnx_files)/)'
)
if [ "${#prettier_files[@]}" -eq 0 ]; then
echo "No Prettier-managed files found."
exit 0
fi
docker run --rm \
-v "$PWD:/work" \
-w /work \
node:22-alpine \
sh -lc 'npx --yes prettier@3 --check --ignore-unknown "$@"' sh "${prettier_files[@]}"
lint-ruff:
runs-on: [self-hosted, linux, arch, homelab]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Lint Python with Ruff
shell: bash
run: |
docker run --rm \
-v "$PWD:/work" \
-w /work \
ghcr.io/astral-sh/ruff:latest \
check .
lint-yaml:
runs-on: [self-hosted, linux, arch, homelab]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Lint YAML syntax
shell: bash
run: |
mapfile -t yaml_files < <(
git ls-files '*.yaml' '*.yml' \
':!node_modules/**' \
':!**/.venv/**'
)
if [ "${#yaml_files[@]}" -eq 0 ]; then
echo "No YAML files found."
exit 0
fi
docker run --rm \
-v "$PWD:/work" \
-w /work \
cytopia/yamllint:latest \
-c .yamllint "${yaml_files[@]}"
lint-dockerfiles:
runs-on: [self-hosted, linux, arch, homelab]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Lint Dockerfiles
shell: bash
run: |
mapfile -t dockerfiles < <(
git ls-files ':(glob)**/Dockerfile' ':(glob)**/Dockerfile.*'
)
if [ "${#dockerfiles[@]}" -eq 0 ]; then
echo "No Dockerfiles found."
exit 0
fi
docker run --rm \
-v "$PWD:/work" \
-w /work \
--entrypoint hadolint \
hadolint/hadolint:latest-debian \
-c .hadolint.yaml "${dockerfiles[@]}"
validate:
runs-on: [self-hosted, linux, arch, homelab]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Validate Kubernetes manifests
shell: bash
run: |
mapfile -t manifests < <(
git ls-files ':(glob)**/k8s/**/*.yaml' ':(glob)**/k8s/**/*.yml' \
| grep -Ev '(^|/)(kustomization\.ya?ml|.*\.example\.ya?ml|.*values\.ya?ml|patch-.*\.ya?ml)$'
)
if [ "${#manifests[@]}" -eq 0 ]; then
echo "No Kubernetes manifests found."
exit 0
fi
docker run --rm \
-v "$PWD:/work" \
-w /work \
ghcr.io/yannh/kubeconform:latest \
-strict \
-ignore-missing-schemas \
-summary \
"${manifests[@]}"
build:
needs: [lint-prettier, lint-ruff, lint-yaml, lint-dockerfiles, validate]
if: github.event_name != 'pull_request' && (github.ref_name == 'main' || github.ref_name == 'dev')
runs-on: [self-hosted, linux, arch, homelab]
outputs:
services: ${{ steps.services.outputs.services }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect changed docker-built services
id: services
shell: bash
run: |
base="${{ github.event.before }}"
if [ -z "$base" ] || [ "$base" = "0000000000000000000000000000000000000000" ]; then
base="$(git rev-list --max-parents=0 HEAD)"
fi
mapfile -t changed_files < <(git diff --name-only "$base" "${GITHUB_SHA}")
services=()
add_service() {
local name="$1"
local seen=0
for existing in "${services[@]}"; do
if [ "$existing" = "$name" ]; then
seen=1
break
fi
done
if [ "$seen" -eq 0 ]; then
services+=("$name")
fi
}
for file in "${changed_files[@]}"; do
case "$file" in
dtek_notif/*)
add_service dtek_notif
;;
errorpages/*)
add_service errorpages
;;
userbot/*)
add_service userbot
;;
homepages/*)
add_service homepages
;;
edu_master/phpsessid-bot/*|edu_master/webinar-checker/*|edu_master/compose.yaml)
add_service edu_master
;;
esac
done
if [ "${#services[@]}" -eq 0 ]; then
echo "No docker-built services changed."
echo "services=" >> "$GITHUB_OUTPUT"
exit 0
fi
printf '%s\n' "${services[@]}" | tee /tmp/services.txt
echo "services=$(paste -sd, /tmp/services.txt)" >> "$GITHUB_OUTPUT"
- name: Log in to registry
if: steps.services.outputs.services != ''
shell: bash
run: |
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login "${REGISTRY}" \
-u "${{ secrets.REGISTRY_USERNAME }}" \
--password-stdin
- name: Build and push changed images
if: steps.services.outputs.services != ''
shell: bash
run: |
IFS=, read -r -a services <<< "${{ steps.services.outputs.services }}"
for service in "${services[@]}"; do
case "$service" in
dtek_notif)
image="${REGISTRY}/forust/dtek-notif"
tags=("latest")
case "${GITHUB_REF_NAME}" in
main)
tags+=("main" "prod")
;;
dev)
tags+=("dev")
;;
esac
build_args=()
for tag in "${tags[@]}"; do
build_args+=(-t "${image}:${tag}")
done
docker build "${build_args[@]}" dtek_notif
for tag in "${tags[@]}"; do
docker push "${image}:${tag}"
done
;;
errorpages)
image="${REGISTRY}/forust/error-pages"
tags=("latest")
case "${GITHUB_REF_NAME}" in
main)
tags+=("main" "prod")
;;
dev)
tags+=("dev")
;;
esac
build_args=()
for tag in "${tags[@]}"; do
build_args+=(-t "${image}:${tag}")
done
docker build "${build_args[@]}" errorpages
for tag in "${tags[@]}"; do
docker push "${image}:${tag}"
done
;;
userbot)
tags=("latest")
case "${GITHUB_REF_NAME}" in
main)
tags+=("main" "prod")
;;
dev)
tags+=("dev")
;;
esac
for target in runtime panel; do
case "$target" in
runtime)
context="userbot"
image="${REGISTRY}/forust/userbot"
;;
panel)
context="userbot/panel"
image="${REGISTRY}/forust/userbot-panel"
;;
esac
build_args=()
for tag in "${tags[@]}"; do
build_args+=(-t "${image}:${tag}")
done
docker build "${build_args[@]}" "$context"
for tag in "${tags[@]}"; do
docker push "${image}:${tag}"
done
done
;;
homepages)
for service in forust xdfnx; do
case "$service" in
forust)
image="${REGISTRY}/forust/forust-homepage"
;;
xdfnx)
image="${REGISTRY}/forust/xdfnx-homepage"
;;
esac
tags=("latest")
case "${GITHUB_REF_NAME}" in
main)
tags+=("main" "prod")
;;
dev)
tags+=("dev")
;;
esac
build_args=()
for tag in "${tags[@]}"; do
build_args+=(-t "${image}:${tag}")
done
docker build "${build_args[@]}" -f "homepages/Dockerfile.${service}" homepages
for tag in "${tags[@]}"; do
docker push "${image}:${tag}"
done
done
;;
edu_master)
for service in session-keeper webinar-checker; do
case "$service" in
session-keeper)
context="edu_master/phpsessid-bot"
image="${REGISTRY}/forust/session-keeper"
;;
webinar-checker)
context="edu_master/webinar-checker"
image="${REGISTRY}/forust/webinar-checker"
;;
esac
tags=("latest")
case "${GITHUB_REF_NAME}" in
main)
tags+=("main" "prod")
;;
dev)
tags+=("dev")
;;
esac
build_args=()
for tag in "${tags[@]}"; do
build_args+=(-t "${image}:${tag}")
done
docker build "${build_args[@]}" "$context"
for tag in "${tags[@]}"; do
docker push "${image}:${tag}"
done
done
;;
esac
done
deploy-userbot-panel:
needs: build
if: github.ref_name == 'main' && contains(needs.build.outputs.services, 'userbot')
runs-on: [self-hosted, linux, arch, homelab, prod]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Apply and roll out userbot panel
shell: bash
run: |
kubectl apply -f userbot/k8s/base/panel.yaml
kubectl get secret userbot-common-secrets -n default -o json \
| jq 'del(.metadata.annotations,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.uid,.metadata.managedFields) | .metadata.namespace = "userbot"' \
| kubectl apply -f -
# Keep legacy deployments (forust/anna) in sync with manifests; they have no replicas field, so apply leaves scaling to the user manager only.
kubectl apply -f userbot/k8s/base/userbots.yaml
kubectl rollout restart deployment/userbot-panel -n userbot
kubectl rollout status deployment/userbot-panel -n userbot --timeout=180s
+161
View File
@@ -0,0 +1,161 @@
name: deploy
on:
workflow_dispatch:
concurrency:
group: deploy-main
cancel-in-progress: false
jobs:
redeploy:
runs-on: [self-hosted, linux, arch, homelab, prod]
steps:
- name: Redeploy workstation
shell: bash
env:
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_PORT: ${{ secrets.DEPLOY_PORT }}
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
# Set APPLY_PRUNE=true to enable kubectl apply --prune. Requires every
# manifest to carry label app.kubernetes.io/managed-by=homelab-deploy,
# otherwise previously applied resources get deleted on the next run.
APPLY_PRUNE: ${{ vars.APPLY_PRUNE }}
run: |
set -euo pipefail
: "${DEPLOY_HOST:?missing DEPLOY_HOST}"
: "${DEPLOY_USER:?missing DEPLOY_USER}"
: "${DEPLOY_KEY:?missing DEPLOY_SSH_KEY}"
deploy_port="${DEPLOY_PORT:-22}"
deploy_path="${DEPLOY_PATH:-/srv/homelab}"
ssh_key="$RUNNER_TEMP/deploy_key"
mkdir -p "$RUNNER_TEMP"
printf '%s\n' "$DEPLOY_KEY" > "$ssh_key"
chmod 600 "$ssh_key"
ssh_opts=(
-i "$ssh_key"
-p "$deploy_port"
-o BatchMode=yes
-o StrictHostKeyChecking=accept-new
)
ssh "${ssh_opts[@]}" "${DEPLOY_USER}@${DEPLOY_HOST}" \
"DEPLOY_PATH=$(printf '%q' \"$deploy_path\") APPLY_PRUNE=$(printf '%q' \"${APPLY_PRUNE:-false}\") bash -se" <<'EOF'
set -euo pipefail
repo="${DEPLOY_PATH:-/srv/homelab}"
if [ ! -d "$repo/.git" ]; then
echo "Repository not found at $repo"
exit 1
fi
git -C "$repo" fetch origin main
git -C "$repo" reset --hard origin/main
# Runtime selection: a service is k8s-managed when $SERVICE/k8s/active
# exists. Otherwise it is compose-managed, and only k8s/routing/*
# manifests (external Services / EndpointSlices / ServersTransport /
# Ingresses that route to docker backends) are applied.
# migrate: touch SERVICE/k8s/active (+ move routing files up)
# rollback: rm SERVICE/k8s/active
collect_k8s() {
find "$1" -type f \( -name '*.yaml' -o -name '*.yml' \) \
! -path '*/routing/*' ! -path '*/overlays/*' \
! -name 'kustomization.y*ml' ! -name '*.example.y*ml' \
! -name '*values.y*ml' ! -name 'patch-*.y*ml' \
| sort
}
collect_k8s_inactive() {
find "$1" -type f \( -name '*.yaml' -o -name '*.yml' \) \
\( -name 'namespace.y*ml' -o -path '*/routing/*' \) \
! -path '*/overlays/*' ! -name '*.example.y*ml' \
| sort
}
mapfile -t compose_stacks < <(
find "$repo" -type f \( -name 'compose.yaml' -o -name 'compose.yml' \) | sort
)
mapfile -t k8s_manifests < <(
for kd in $(find "$repo" -type d -name k8s ! -path '*/.git/*' | sort); do
if [ -f "$kd/active" ]; then
collect_k8s "$kd"
else
collect_k8s_inactive "$kd"
fi
done
)
echo "== Validate compose stacks =="
for cf in "${compose_stacks[@]}"; do
dir=$(dirname "$cf")
if [ -f "$dir/k8s/active" ]; then
echo " skip (k8s-managed): $dir"
continue
fi
echo " config: $cf"
docker compose -f "$cf" config --quiet
done
echo "== Validate k8s manifests (kubectl dry-run) =="
for m in "${k8s_manifests[@]}"; do
echo " apply --dry-run=client $m"
kubectl apply --dry-run=client -f "$m" >/dev/null
done
echo "== Applying Kubernetes manifests =="
ns_files=()
other_files=()
for m in "${k8s_manifests[@]}"; do
case "$m" in
*/namespace.y?ml) ns_files+=("$m") ;;
*) other_files+=("$m") ;;
esac
done
prune_opts=()
if [ "${APPLY_PRUNE:-false}" = "true" ]; then
prune_opts=(--prune -l app.kubernetes.io/managed-by=homelab-deploy)
fi
if [ "${#ns_files[@]}" -gt 0 ]; then
echo " namespaces first: ${ns_files[*]}"
kubectl apply -f "${ns_files[@]}"
fi
if [ -f "$repo/prometheus-stack/k8s/active" ]; then
echo "== Upgrading kube-prometheus-stack =="
helm upgrade --install prometheus-stack prometheus-community/kube-prometheus-stack \
--namespace prometheus \
--version 86.2.3 \
--values "$repo/prometheus-stack/k8s/grafana-values.yaml" \
--wait
fi
if [ "${#other_files[@]}" -gt 0 ]; then
echo " resources: ${other_files[*]}"
kubectl apply "${prune_opts[@]}" -f "${other_files[@]}"
fi
echo "== Redeploying docker compose stacks =="
for cf in "${compose_stacks[@]}"; do
dir=$(dirname "$cf")
if [ -f "$dir/k8s/active" ]; then
echo " skip (k8s-managed): $dir"
continue
fi
echo " compose: $dir"
if grep -Eq '^\s+pull_policy:\s*build\b' "$cf"; then
docker compose -f "$cf" build
docker compose -f "$cf" push
fi
docker compose -f "$cf" up -d --pull always --remove-orphans
done
EOF
-41
View File
@@ -1,41 +0,0 @@
## BINARY MODE, USE WITH THE GITHUB RUNNER BINARY INSTALLED ON THE SERVER
name: Deploy to Server
run-name: Deploying onto server on ${{ github.ref }}
on:
push:
branches:
- main
- ci/actions
jobs:
deploy:
runs-on: [prod, self-hosted]
steps:
- name: Fetch and Diff Analysis
id: diff
run: |
cd ${{ secrets.PROD_DIR }}
git fetch origin main
CHANGES=$(git diff --name-only HEAD origin/main | cut -d/ -f1 | sort -u | tr '\n' ' ')
echo "dirs=$CHANGES" >> $GITHUB_OUTPUT
echo "Changed dirs: $CHANGES"
- name: Sync Server Files
run: |
cd ${{ secrets.PROD_DIR }}
git reset --hard origin/main
echo "Server files synced with origin/main"
- name: Deploy Services
run: |
cd ${{ secrets.PROD_DIR }}
for dir in ${{ steps.diff.outputs.dirs }}; do
if [ -d "$dir" ] && ([ -f "$dir/compose.yaml" ] || [ -f "$dir/docker-compose.yaml" ]); then
echo ">>> Deploying $dir"
cd "$dir"
DOCKER_BUILDKIT=1 BUILDKIT_PROGRESS=plain docker compose up -d --build --no-color
cd ..
else
echo ">>> Skipping $dir: no compose file found"
fi
done
+5 -5
View File
@@ -32,7 +32,7 @@ streaming/data/*
streaming/qbittorrent/*
streaming/prowlarr/*
# Homepage
# Homepage
homepages/forust_files/.well-known/*
# Traefik files
@@ -40,7 +40,8 @@ traefik/letsencrypt/acme.json
traefik/dynamic/fileservers.yml
traefik/dynamic/*.local.y*ml.*
traefik/dynamic/*.external.y*ml
traefik/k8s/fileservers.y*ml
traefik/k8s/aliasHeadersStrategy.md
traefik/logs/*
@@ -52,7 +53,7 @@ certs/
# Monitoring
monitoring/prometheus.yml
# Python
# Python
.python-version
venv/
pyc
@@ -93,7 +94,7 @@ replacements.txt
edu_master/temp/
temp/*
# Environment
# Environment
.env
.env.anna
.env.forust
@@ -106,4 +107,3 @@ temp/*
traefik/k8s/local-tls.yaml
converters/k8s/config.yaml
convertx/k8s/config.yaml
traefik/k8s/crowdsec-middleware.yaml
+13 -12
View File
@@ -9,31 +9,32 @@
"hard_tabs": false,
"format_on_save": "on",
"formatter": {
"language_server": { "name": "yaml-language-server" }
}
"language_server": { "name": "yaml-language-server" },
},
},
"Python": {
"tab_size": 4,
"format_on_save": "on",
"language_servers": ["pyright", "ruff"],
"formatter": {
"language_server": { "name": "ruff" }
}
}
"language_server": { "name": "ruff" },
},
},
},
"lsp": {
"yaml-language-server": {
"settings": {
"yaml": {
"schemas": {
"kubernetes": ["**/k8s/*.yaml", "**/k8s/*.yml"]
"kubernetes": ["**/k8s/*.yaml", "**/k8s/*.yml"],
},
"validate": true,
"completion": true,
"format": {
"enable": true
}
}
}
}
}
"enable": true,
},
},
},
},
},
}
+1 -1
View File
@@ -1,6 +1,6 @@
services:
adguard:
image: adguard/adguardhome:latest
image: adguard/adguardhome:v0.107.79
container_name: adguardhome
restart: unless-stopped
ports:
View File
Whitespace-only changes.
+1 -1
View File
@@ -65,7 +65,7 @@ spec:
spec:
containers:
- name: adguard
image: adguard/adguardhome:latest
image: adguard/adguardhome:v0.107.79
resources:
limits:
memory: "1.5Gi"
+8 -16
View File
@@ -10,7 +10,13 @@ spec:
- match: Host(`adguard.forust.xyz`) || Host(`dns.forust.xyz`)
kind: Rule
middlewares:
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
- name: crowdsec-bouncer
namespace: crowdsec
services:
- name: adguard-service
port: 3000
- match: (Host(`adguard.forust.xyz`) || Host(`dns.forust.xyz`)) && PathPrefix(`/dns-query`)
kind: Rule
services:
- name: adguard-service
port: 3000
@@ -31,22 +37,8 @@ spec:
services:
- name: adguard-service
port: 3000
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: adguard-doh
namespace: adguard
spec:
entryPoints:
- websecure
routes:
- match: (Host(`adguard.forust.xyz`) || Host(`dns.forust.xyz`)) && PathPrefix(`/dns-query`)
- match: (Host(`adguard.workstation.internal`) || Host(`dns.workstation.internal`) || Host(`adguard.gigaforust.internal`) || Host(`dns.gigaforust.internal`)) && PathPrefix(`/dns-query`)
kind: Rule
middlewares:
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
services:
- name: adguard-service
port: 3000
tls:
certResolver: letsencrypt
+1 -1
View File
@@ -1,6 +1,6 @@
services:
postgresql:
image: docker.io/library/postgres:15-alpine
image: docker.io/library/postgres:15.19-alpine
restart: unless-stopped
env_file:
- .env
View File
Whitespace-only changes.
+2 -2
View File
@@ -41,7 +41,7 @@ spec:
spec:
containers:
- name: authentik-server
image: ghcr.io/goauthentik/server:2025.10.2
image: ghcr.io/goauthentik/server:2026.8.2
args: ["server"]
envFrom:
- configMapRef:
@@ -75,7 +75,7 @@ spec:
spec:
containers:
- name: authentik-worker
image: ghcr.io/goauthentik/server:2025.10.2
image: ghcr.io/goauthentik/server:2026.8.2
args: ["worker"]
securityContext:
runAsUser: 0
+2 -1
View File
@@ -10,7 +10,8 @@ spec:
- match: Host(`auth.forust.xyz`)
kind: Rule
middlewares:
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
- name: crowdsec-bouncer
namespace: crowdsec
services:
- name: authentik-server-service
port: 9000
+1 -1
View File
@@ -29,7 +29,7 @@ spec:
spec:
containers:
- name: postgres
image: docker.io/library/postgres:15-alpine
image: docker.io/library/postgres:15.19-alpine
env:
- name: POSTGRES_DB
value: authentik
+1 -1
View File
@@ -1,6 +1,6 @@
services:
cloudflare-ddns:
image: timothyjmiller/cloudflare-ddns:latest
image: timothyjmiller/cloudflare-ddns:2.2.0
container_name: cloudflare-ddns
restart: unless-stopped
security_opt:
+1 -1
View File
@@ -7,7 +7,7 @@
"api_key": {
"api_key": "api_key_here",
"account_email": "your_email_here"
}
},
"zone_id": "your_zone-id",
"subdomains": [
{ "name": "", "proxied": true },
View File
Whitespace-only changes.
+1 -1
View File
@@ -18,7 +18,7 @@ spec:
dnsPolicy: ClusterFirstWithHostNet
containers:
- name: cloudflare-ddns
image: timothyjmiller/cloudflare-ddns:latest
image: timothyjmiller/cloudflare-ddns:2.2.0
imagePullPolicy: Always
resources:
requests:
+1 -1
View File
@@ -1,6 +1,6 @@
services:
checkmk:
image: "checkmk/check-mk-raw:2.4.0-latest"
image: "checkmk/check-mk-raw:2.4.0-2026.09.14"
container_name: "checkmk"
restart: unless-stopped
# ports:
View File
Whitespace-only changes.
+10 -3
View File
@@ -7,8 +7,12 @@ spec:
selector:
app: checkmk
ports:
- port: 5000
- name: web
port: 5000
targetPort: 5000
- name: agent-receiver
port: 8000
targetPort: 8000
---
apiVersion: apps/v1
kind: Deployment
@@ -27,14 +31,17 @@ spec:
spec:
containers:
- name: checkmk
image: checkmk/check-mk-raw:2.4.0-latest
image: checkmk/check-mk-raw:2.4.0-2026.09.14
envFrom:
- secretRef:
name: checkmk-secrets
- configMapRef:
name: checkmk-config
ports:
- containerPort: 5000
- name: web
containerPort: 5000
- name: agent-receiver
containerPort: 8000
volumeMounts:
- name: sites
mountPath: /omd/sites
+18 -1
View File
@@ -10,7 +10,8 @@ spec:
- match: Host(`cmk.forust.xyz`)
kind: Rule
middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd
- name: crowdsec-bouncer
namespace: crowdsec
services:
- name: checkmk-service
port: 5000
@@ -18,6 +19,22 @@ spec:
certResolver: letsencrypt
---
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
name: checkmk-agent-receiver
namespace: checkmk
spec:
entryPoints:
- checkmk-agent
routes:
- match: HostSNI(`*`)
services:
- name: checkmk-service
port: 8000
tls:
passthrough: true
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: checkmk-local
+2 -2
View File
@@ -1,7 +1,7 @@
services:
convertx:
container_name: convertx
image: ghcr.io/c4illin/convertx:latest
image: ghcr.io/c4illin/convertx:v0.18.0
restart: unless-stopped
ports:
- "9992:3000"
@@ -42,7 +42,7 @@ services:
bentopdf:
container_name: bentopdf
image: bentopdf/bentopdf:latest
image: bentopdf/bentopdf@sha256:4eb4ec8f5030faf87c29a73d3d5a2781f28a597cf440c3ab111eb96aee550871
restart: unless-stopped
labels:
- "traefik.enable=true"
View File
Whitespace-only changes.
+1 -1
View File
@@ -26,7 +26,7 @@ spec:
app: bentopdf
spec:
containers:
- image: bentopdf/bentopdf:latest
- image: bentopdf/bentopdf@sha256:4eb4ec8f5030faf87c29a73d3d5a2781f28a597cf440c3ab111eb96aee550871
imagePullPolicy: Always
name: bentopdf
ports:
+1 -1
View File
@@ -26,7 +26,7 @@ spec:
app: convertx
spec:
containers:
- image: ghcr.io/c4illin/convertx:latest
- image: ghcr.io/c4illin/convertx:v0.18.0
name: convertx
envFrom:
- configMapRef:
+28 -28
View File
@@ -2,23 +2,9 @@ container_runtime: containerd
agent:
env:
- name: COLLECTIONS
value: "crowdsecurity/traefik crowdsecurity/base-http-scenarios crowdsecurity/sshd"
extraVolumes:
- name: journal-dir
hostPath:
path: /var/log/journal
type: DirectoryOrCreate
- name: run-journal-dir
hostPath:
path: /run/log/journal
type: DirectoryOrCreate
extraVolumeMounts:
- name: journal-dir
mountPath: /var/log/journal
readOnly: true
- name: run-journal-dir
mountPath: /run/log/journal
readOnly: true
value: "crowdsecurity/traefik crowdsecurity/base-http-scenarios"
- name: DISABLE_COLLECTIONS
value: "crowdsecurity/linux crowdsecurity/sshd"
acquisition:
- namespace: traefik
@@ -26,13 +12,6 @@ agent:
program: traefik
poll_without_inotify: true
acquisitionCustom: |
- source: journalctl
journalctl_filter:
- _SYSTEMD_UNIT=sshd.service
labels:
type: syslog
resources:
requests:
cpu: 50m
@@ -44,14 +23,35 @@ agent:
lapi:
env:
- name: COLLECTIONS
value: "crowdsecurity/traefik crowdsecurity/base-http-scenarios crowdsecurity/sshd"
value: "crowdsecurity/traefik crowdsecurity/base-http-scenarios"
- name: DISABLE_COLLECTIONS
value: "crowdsecurity/linux crowdsecurity/sshd"
service:
type: NodePort
nodePort: 30011
type: ClusterIP
persistentVolume:
data:
enabled: true
storageClassName: local-path-retain
size: 1Gi
config:
enabled: true
storageClassName: local-path-retain
size: 100Mi
storeLAPICscliCredentialsInSecret: true
resources:
requests:
cpu: 50m
memory: 150Mi
limits:
cpu: 200m
cpu: 400m
memory: 500Mi
metrics:
enabled: true
serviceMonitor:
additionalLabels:
release: prometheus-stack
enabled: true
interval: 30s
scrapeTimeout: 10s
namespace: prometheus
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: crowdsec
labels:
app.kubernetes.io/part-of: crowdsec
+27
View File
@@ -0,0 +1,27 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: crowdsec-lapi
namespace: crowdsec
spec:
podSelector:
matchLabels:
k8s-app: crowdsec
type: lapi
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: traefik
podSelector:
matchLabels:
app.kubernetes.io/name: traefik
- podSelector:
matchLabels:
k8s-app: crowdsec
type: agent
ports:
- protocol: TCP
port: 8080
+1 -1
View File
@@ -1,6 +1,6 @@
services:
dockmon:
image: darthnorse/dockmon:latest
image: darthnorse/dockmon:2.4.5
container_name: dockmon
restart: unless-stopped
# ports:
View File
Whitespace-only changes.
+1 -1
View File
@@ -29,7 +29,7 @@ spec:
spec:
containers:
- name: dockmon
image: darthnorse/dockmon:latest
image: darthnorse/dockmon:2.4.5
ports:
- containerPort: 443
volumeMounts:
+2 -1
View File
@@ -18,7 +18,8 @@ spec:
- match: Host(`dockmon.forust.xyz`)
kind: Rule
middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd
- name: crowdsec-bouncer
namespace: crowdsec
- name: security-headers@file
services:
- name: dockmon-service
+1 -1
View File
@@ -1,7 +1,7 @@
services:
downtify:
container_name: downtify
image: ghcr.io/henriquesebastiao/downtify:latest
image: ghcr.io/henriquesebastiao/downtify:2.12.0
restart: unless-stopped
# ports:
# - '7077:8000'
+1 -1
View File
@@ -27,7 +27,7 @@ spec:
spec:
containers:
- name: downtify
image: ghcr.io/henriquesebastiao/downtify:latest
image: ghcr.io/henriquesebastiao/downtify:2.12.0
ports:
- containerPort: 8000
volumeMounts:
+2 -1
View File
@@ -10,7 +10,8 @@ spec:
- match: Host(`downtify.forust.xyz`)
kind: Rule
middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd
- name: crowdsec-bouncer
namespace: crowdsec
- name: security-chain@file
services:
- name: downtify-service
+388 -418
View File
File diff suppressed because it is too large. Load diff
+1
View File
@@ -9,5 +9,6 @@ WEBINAR_CHECK_INTERVAL=60
REDIS_HOST=redis
REDIS_PORT=6379
PLAYWRIGHT_WS=ws://playwright-service:3000/ws
TZ=Europe/Kyiv
WEBINAR_TELEGRAM_TOKEN=your_telegram_bot_token_here
WEBINAR_ADMIN_ID=123456789
+2 -2
View File
@@ -1,6 +1,6 @@
services:
redis:
image: redis:alpine
image: redis:8.10.1-alpine
restart: unless-stopped
volumes:
- redis-data:/data
@@ -11,7 +11,7 @@ services:
retries: 5
playwright-service:
image: mcr.microsoft.com/playwright:v1.56.0-jammy
image: mcr.microsoft.com/playwright:v1.63.0-jammy
restart: unless-stopped
command: npx -y playwright@1.56.0 run-server --port 3000 --path /ws
View File
Whitespace-only changes.
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: edu-master
+57
View File
@@ -0,0 +1,57 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: playwright-service
namespace: edu-master
labels:
app: edu-master-playwright
spec:
replicas: 1
selector:
matchLabels:
app: edu-master-playwright
template:
metadata:
labels:
app: edu-master-playwright
spec:
containers:
- name: playwright
image: mcr.microsoft.com/playwright:v1.63.0-jammy
imagePullPolicy: IfNotPresent
command:
- npx
- -y
- playwright@1.56.0
- run-server
- --port
- "3000"
- --path
- /ws
ports:
- containerPort: 3000
readinessProbe:
tcpSocket:
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
livenessProbe:
tcpSocket:
port: 3000
initialDelaySeconds: 15
periodSeconds: 20
timeoutSeconds: 3
---
apiVersion: v1
kind: Service
metadata:
name: playwright-service
namespace: edu-master
spec:
selector:
app: edu-master-playwright
ports:
- name: ws
port: 3000
targetPort: 3000
+75
View File
@@ -0,0 +1,75 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
namespace: edu-master
labels:
app: edu-master-redis
spec:
serviceName: redis
replicas: 1
selector:
matchLabels:
app: edu-master-redis
template:
metadata:
labels:
app: edu-master-redis
spec:
containers:
- name: redis
image: redis:8.10.1-alpine
imagePullPolicy: IfNotPresent
ports:
- containerPort: 6379
volumeMounts:
- name: redis-data
mountPath: /data
resources:
requests:
cpu: 25m
memory: 64Mi
limits:
cpu: 250m
memory: 256Mi
readinessProbe:
exec:
command: ["redis-cli", "ping"]
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
livenessProbe:
exec:
command: ["redis-cli", "ping"]
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 3
volumes:
- name: redis-data
persistentVolumeClaim:
claimName: redis-data-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: redis-data-pvc
namespace: edu-master
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: edu-master
spec:
selector:
app: edu-master-redis
ports:
- name: redis
port: 6379
targetPort: 6379
@@ -0,0 +1,50 @@
# One-time Job to migrate redis state from docker compose to k8s (maintenance window).
# The .example file is not applied by the deploy pipeline (mask *.example.yaml).
#
# Runbook:
# 1. docker compose -f <repo>/edu_master/compose.yaml stop # SIGTERM -> redis will flush dump.rdb
# 2. docker run --rm -v edu_master_redis-data:/data \
# -v /tmp/edu-master-backup:/backup \
# redis:alpine sh -c "cp /data/dump.rdb /backup/ && ls -la /backup"
# 3. kubectl apply -f edu_master/k8s/namespace.yaml
# 4. kubectl apply -f <only the PVC from redis.yaml> # seed must come BEFORE redis pod starts
# 5. kubectl apply -f edu_master/k8s/restore-seed-job.yaml.example
# kubectl wait --for=condition=complete job/redis-restore-seed -n edu-master --timeout=120s
# 6. kubectl delete job redis-restore-seed -n edu-master
# 7. kubectl apply -f edu_master/k8s/ -R # apply remaining manifests
apiVersion: batch/v1
kind: Job
metadata:
name: redis-restore-seed
namespace: edu-master
spec:
backoffLimit: 2
ttlSecondsAfterFinished: 3600
template:
spec:
restartPolicy: Never
containers:
- name: seed
image: redis:alpine
command:
- /bin/sh
- -ec
- |
ls -la /backup
cp /backup/dump.rdb /data/dump.rdb
chmod 644 /data/dump.rdb
ls -la /data
volumeMounts:
- name: redis-data
mountPath: /data
- name: backup
mountPath: /backup
readOnly: true
volumes:
- name: redis-data
persistentVolumeClaim:
claimName: redis-data-pvc
- name: backup
hostPath:
path: /tmp/edu-master-backup
type: DirectoryOrCreate
+27
View File
@@ -0,0 +1,27 @@
apiVersion: v1
kind: Secret
metadata:
name: edu-master-secrets
namespace: edu-master
type: Opaque
stringData:
# Session keeper credentials
KEEPER_LOGIN: ""
KEEPER_PASSWORD: ""
KEEPER_INTERVAL: "10"
# EDU links
EDU_URL_BASE: "https://edu.edu.vn.ua"
EDU_URL_LOGIN: "/user/login"
EDU_URL_COURSES: "/course/userlist"
EDU_URL_WEBINAR: "/webinar/useractive"
# Playwright
USER_AGENT: ""
PLAYWRIGHT_WS: "ws://playwright-service:3000/ws"
# Webinar-checker
WEBINAR_TELEGRAM_TOKEN: ""
WEBINAR_ADMIN_ID: ""
WEBINAR_CHECK_INTERVAL: "60"
# Database
REDIS_HOST: "redis"
REDIS_PORT: "6379"
TZ: "Europe/Kyiv"
+52
View File
@@ -0,0 +1,52 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: session-keeper
namespace: edu-master
labels:
app: edu-master-session-keeper
spec:
replicas: 1
selector:
matchLabels:
app: edu-master-session-keeper
template:
metadata:
labels:
app: edu-master-session-keeper
spec:
initContainers:
- name: wait-redis
image: redis:8.10.1-alpine
command:
- /bin/sh
- -ec
- |
i=0
until redis-cli -h redis ping | grep -q PONG; do
i=$((i+1))
[ "$i" -ge 300 ] && echo "TIMEOUT: redis not ready" && exit 1
sleep 2
done
echo "redis is ready"
containers:
- name: session-keeper
image: gcr.forust.xyz/forust/session-keeper:latest
imagePullPolicy: Always
envFrom:
- secretRef:
name: edu-master-secrets
resources:
requests:
cpu: 25m
memory: 96Mi
limits:
cpu: 250m
memory: 256Mi
readinessProbe:
exec:
command: ["/bin/sh", "-ec", "redis-cli -h redis EXISTS EDU_PHPSESSID | grep -q 1"]
initialDelaySeconds: 15
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 10
+62
View File
@@ -0,0 +1,62 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: webinar-checker
namespace: edu-master
labels:
app: edu-master-webinar-checker
spec:
replicas: 1
selector:
matchLabels:
app: edu-master-webinar-checker
template:
metadata:
labels:
app: edu-master-webinar-checker
spec:
# Enforces dependency order like compose depends_on:
# redis healthy -> session-keeper healthy (EXISTS EDU_PHPSESSID) -> playwright started
initContainers:
- name: wait-deps
image: redis:8.10.1-alpine
command:
- /bin/sh
- -ec
- |
i=0
until redis-cli -h redis ping | grep -q PONG; do
i=$((i+1))
[ "$i" -ge 300 ] && echo "TIMEOUT: redis not ready" && exit 1
sleep 2
done
echo "redis ok"
until [ "$(redis-cli -h redis EXISTS EDU_PHPSESSID)" = "1" ]; do
i=$((i+1))
[ "$i" -ge 300 ] && echo "TIMEOUT: no PHPSESSID (session-keeper down?)" && exit 1
sleep 2
done
echo "PHPSESSID ok"
until nc -z playwright-service 3000; do
i=$((i+1))
[ "$i" -ge 300 ] && echo "TIMEOUT: playwright-service not reachable" && exit 1
sleep 2
done
echo "playwright ok"
containers:
- name: webinar-checker
image: gcr.forust.xyz/forust/webinar-checker:latest
imagePullPolicy: Always
envFrom:
- secretRef:
name: edu-master-secrets
env:
- name: TZ
value: "Europe/Kyiv"
resources:
requests:
cpu: "50m"
memory: "128Mi"
limits:
cpu: "600m"
memory: "512Mi"
+2 -2
View File
@@ -3,10 +3,10 @@ FROM python:3.11-slim
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y redis-tools && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y --no-install-recommends redis-tools && rm -rf /var/lib/apt/lists/*
# Install dependencies
RUN pip install requests redis
RUN pip install --no-cache-dir requests==2.32.3 redis==5.2.1
# Copy application code
COPY . .
+46 -43
View File
@@ -1,17 +1,16 @@
import logging
import os
import time
import requests
import logging
import redis
from datetime import datetime
import redis
import requests
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Load configuration (adapted to .env keys)
def _env(key, default=None):
v = os.getenv(key, default)
@@ -19,21 +18,26 @@ def _env(key, default=None):
return v[1:-1]
return v
LOGIN = _env('KEEPER_LOGIN')
PASSWORD = _env('KEEPER_PASSWORD')
EDU_BASE = _env('EDU_URL_BASE', 'https://edu.edu.vn.ua')
EDU_LOGIN_PATH = _env('EDU_URL_LOGIN', '/user/login')
EDU_COURSES_PATH = _env('EDU_URL_COURSES', '/course/userlist')
URL_LOGIN = f"{EDU_BASE.rstrip('/')}/{EDU_LOGIN_PATH.lstrip('/')}"
URL_VERIFY = f"{EDU_BASE.rstrip('/')}/{EDU_COURSES_PATH.lstrip('/')}"
URL_LOGIN = f'{EDU_BASE.rstrip("/")}/{EDU_LOGIN_PATH.lstrip("/")}'
URL_VERIFY = f'{EDU_BASE.rstrip("/")}/{EDU_COURSES_PATH.lstrip("/")}'
INTERVAL = int(_env('KEEPER_INTERVAL', 10))
USER_AGENT = _env('USER_AGENT', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36')
USER_AGENT = _env(
'USER_AGENT',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36',
)
REDIS_HOST = _env('REDIS_HOST', 'redis')
REDIS_PORT = int(_env('REDIS_PORT', 6379))
SUCCESS_FILE = '/tmp/last_success'
SUCCESS_FILE = '/tmp/last_success' # noqa: S108
def touch_success_file():
"""Updates the timestamp of the success file for healthchecks."""
@@ -41,22 +45,23 @@ def touch_success_file():
with open(SUCCESS_FILE, 'w') as f:
f.write(str(datetime.now().timestamp()))
except Exception as e:
logger.error(f"Failed to touch success file: {e}")
logger.error(f'Failed to touch success file: {e}')
def main():
logger.info("Starting Session Keeper Bot")
logger.info('Starting Session Keeper Bot')
# Connect to Redis
try:
redis_client = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, decode_responses=True)
redis_client.ping()
logger.info(f"Connected to Redis at {REDIS_HOST}:{REDIS_PORT}")
logger.info(f'Connected to Redis at {REDIS_HOST}:{REDIS_PORT}')
except Exception as e:
logger.error(f"Failed to connect to Redis: {e}")
logger.error(f'Failed to connect to Redis: {e}')
return
session = requests.Session()
# Set headers
headers = {
'User-Agent': USER_AGENT,
@@ -72,58 +77,56 @@ def main():
'Sec-Ch-Ua-Mobile': '?0',
'Sec-Ch-Ua-Platform': '"Linux"',
'Accept-Encoding': 'gzip, deflate, br',
'Priority': 'u=0, i'
'Priority': 'u=0, i',
}
session.headers.update(headers)
while True:
try:
logger.info("Attempting login...")
logger.info('Attempting login...')
# Login payload
payload = {
'login': LOGIN,
'password': PASSWORD
}
payload = {'login': LOGIN, 'password': PASSWORD}
# Perform Login
# Note: The user request shows a POST to /user/login with form data
# We need to make sure we handle the PHPSESSID correctly.
# If we already have a PHPSESSID, requests will send it.
login_response = session.post(URL_LOGIN, data=payload, allow_redirects=True)
logger.info(f"Login Response Status: {login_response.status_code}")
logger.info(f"Cookies after login: {session.cookies.get_dict()}")
logger.info(f'Login Response Status: {login_response.status_code}')
logger.info(f'Cookies after login: {session.cookies.get_dict()}')
# Verify Session
logger.info("Verifying session...")
logger.info('Verifying session...')
verify_response = session.get(URL_VERIFY, allow_redirects=False)
logger.info(f"Verify Response Status: {verify_response.status_code}")
logger.info(f'Verify Response Status: {verify_response.status_code}')
if verify_response.status_code == 200:
logger.info("Session verification SUCCESS (200 OK).")
logger.info('Session verification SUCCESS (200 OK).')
touch_success_file()
# Save PHPSESSID to Redis
phpsessid = session.cookies.get('PHPSESSID')
if phpsessid:
try:
redis_client.set('EDU_PHPSESSID', phpsessid)
logger.info(f"Saved PHPSESSID to Redis: {phpsessid}")
logger.info(f'Saved PHPSESSID to Redis: {phpsessid}')
except Exception as e:
logger.error(f"Failed to save PHPSESSID to Redis: {e}")
logger.error(f'Failed to save PHPSESSID to Redis: {e}')
elif verify_response.status_code == 302:
logger.warning("Session verification FAILED (302 Redirect). Session might be invalid.")
logger.warning('Session verification FAILED (302 Redirect). Session might be invalid.')
else:
logger.warning(f"Session verification returned unexpected status: {verify_response.status_code}")
logger.warning(f'Session verification returned unexpected status: {verify_response.status_code}')
except Exception as e:
logger.error(f"An error occurred: {e}")
logger.error(f'An error occurred: {e}')
logger.info(f"Sleeping for {INTERVAL} minutes...")
logger.info(f'Sleeping for {INTERVAL} minutes...')
time.sleep(INTERVAL * 60)
if __name__ == "__main__":
if __name__ == '__main__':
main()
+1 -1
View File
@@ -3,7 +3,7 @@ FROM python:3.11-slim
WORKDIR /app
# Install dependencies
RUN pip install --upgrade pip && pip install playwright==1.56.0 redis requests "python-telegram-bot[job-queue]"
RUN pip install --no-cache-dir pip==25.0.1 && pip install --no-cache-dir playwright==1.56.0 redis==5.2.1 requests==2.32.3 "python-telegram-bot[job-queue]==21.10"
COPY checker.py .
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -1,6 +1,6 @@
services:
server:
image: docker.gitea.com/gitea:1.26
image: docker.gitea.com/gitea:1.27.3
container_name: gitea
restart: always
environment:
@@ -61,7 +61,7 @@ services:
depends_on:
- db
db:
image: docker.io/library/postgres:14
image: docker.io/library/postgres:14.24-alpine
restart: always
environment:
- POSTGRES_USER=gitea
View File
Whitespace-only changes.
+2
View File
@@ -16,5 +16,7 @@ data:
GITEA__security__REVERSE_PROXY_TRUSTED_PROXIES: "*"
GITEA__mailer__ENABLED: "false"
GITEA__log__logger__access__MODE: "console, file"
USER_UID: "1000"
USER_GID: "1000"
+1 -1
View File
@@ -31,7 +31,7 @@ spec:
spec:
containers:
- name: gitea
image: docker.gitea.com/gitea:1.26
image: docker.gitea.com/gitea:1.27.3
envFrom:
- configMapRef:
name: gitea-config
+11 -14
View File
@@ -10,7 +10,16 @@ spec:
- match: Host(`gitea.forust.xyz`)
kind: Rule
middlewares:
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
- name: crowdsec-bouncer
namespace: crowdsec
services:
- name: gitea-service
port: 3000
- match: Host(`gcr.forust.xyz`) && PathPrefix(`/v2`)
kind: Rule
middlewares:
- name: crowdsec-bouncer
namespace: crowdsec
services:
- name: gitea-service
port: 3000
@@ -31,23 +40,11 @@ spec:
services:
- name: gitea-service
port: 3000
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: gitea-registry
namespace: gitea
spec:
entryPoints:
- websecure
routes:
- match: Host(`gcr.forust.xyz`) && PathPrefix(`/v2`)
- match: (Host(`gcr.workstation.internal`) || Host(`gcr.gigaforust.internal`)) && PathPrefix(`/v2`)
kind: Rule
services:
- name: gitea-service
port: 3000
tls:
certResolver: letsencrypt
---
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
+2 -2
View File
@@ -29,7 +29,7 @@ spec:
spec:
containers:
- name: gitea-postgres
image: postgres:14
image: postgres:14.24-alpine
env:
- name: POSTGRES_USER
valueFrom:
@@ -45,7 +45,7 @@ spec:
valueFrom:
secretKeyRef:
name: gitea-secrets
key: GITEA__database__USER
key: GITEA__database__NAME
ports:
- containerPort: 5432
name: postgres
+1
View File
@@ -7,3 +7,4 @@ type: Opaque
stringData:
GITEA__database__USER: "gitea"
GITEA__database__PASSWD: "gitea"
GITEA__database__NAME: "gitea"
+1 -1
View File
@@ -1,7 +1,7 @@
services:
glance:
container_name: glance
image: glanceapp/glance
image: glanceapp/glance:v0.8.6
restart: unless-stopped
volumes:
- ./config:/app/config:ro
View File
Whitespace-only changes.
+5 -5
View File
@@ -27,7 +27,7 @@ spec:
spec:
containers:
- name: glance
image: glanceapp/glance
image: glanceapp/glance:v0.8.6
envFrom:
- secretRef:
name: glance-secrets
@@ -56,11 +56,11 @@ spec:
readOnly: true
resources:
requests:
memory: "30Mi"
cpu: "20m"
limits:
memory: "100Mi"
cpu: "50m"
memory: "64Mi"
limits:
cpu: "200m"
memory: "256Mi"
volumes:
- name: glance-config
configMap:
+37 -3
View File
@@ -1,6 +1,6 @@
services:
headscale:
image: headscale/headscale:latest
image: headscale/headscale:0.29.3
restart: unless-stopped
container_name: headscale-server
command: serve
@@ -53,7 +53,7 @@ services:
- "traefik.http.routers.headscale-metrics-dev.service=headscale-metrics"
- "traefik.http.routers.headscale-metrics-dev.tls=true"
headplane:
image: ghcr.io/tale/headplane:latest
image: ghcr.io/tale/headplane:0.7.1
container_name: headplane
restart: unless-stopped
ports:
@@ -65,8 +65,42 @@ services:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.services.headplane.loadbalancer.server.port=3000"
# Middleware: add /admin prefix for root requests
- "traefik.http.middlewares.headplane-prefix.addPrefix.prefix=/admin"
# Prod Root Router
- "traefik.http.routers.headplane-root.rule=Host(`hp.forust.xyz`)"
- "traefik.http.routers.headplane-root.entrypoints=websecure"
- "traefik.http.routers.headplane-root.middlewares=headplane-prefix"
- "traefik.http.routers.headplane-root.tls.certresolver=letsencrypt"
# Prod Router
- "traefik.http.routers.headplane.rule=Host(`hp.forust.xyz`) && PathPrefix(`/admin`)"
- "traefik.http.routers.headplane.entrypoints=websecure"
- "traefik.http.routers.headplane.tls.certresolver=letsencrypt"
# Local Root Router
- "traefik.http.routers.headplane-root-local.rule=Host(`hp.workstation.internal`)"
- "traefik.http.routers.headplane-root-local.entrypoints=websecure"
- "traefik.http.routers.headplane-root-local.middlewares=headplane-prefix"
- "traefik.http.routers.headplane-root-local.tls=true"
# Local Router
- "traefik.http.routers.headplane-local.rule=Host(`hp.workstation.internal`) && PathPrefix(`/admin`)"
- "traefik.http.routers.headplane-local.entrypoints=websecure"
- "traefik.http.routers.headplane-local.tls=true"
# Dev Root Router
- "traefik.http.routers.headplane-root-dev.rule=Host(`hp.gigaforust.internal`)"
- "traefik.http.routers.headplane-root-dev.entrypoints=websecure"
- "traefik.http.routers.headplane-root-dev.middlewares=headplane-prefix"
- "traefik.http.routers.headplane-root-dev.tls=true"
# Dev Router
- "traefik.http.routers.headplane-dev.rule=Host(`hp.gigaforust.internal`) && PathPrefix(`/admin`)"
- "traefik.http.routers.headplane-dev.entrypoints=websecure"
- "traefik.http.routers.headplane-dev.tls=true"
web:
image: goodieshq/headscale-admin:latest
image: goodieshq/headscale-admin:0.28.0
restart: unless-stopped
ports:
- 10080:80
+20 -26
View File
@@ -1,27 +1,21 @@
{
"groups": {
"group:admin": [
"admin@"
],
"group:users": []
},
"tagOwners": {},
"hosts": {},
"acls": [
{
"randomizeClientPort": false,
"#ha-meta": {
"name": "users",
"open": true
},
"action": "accept",
"src": [
"autogroup:member"
],
"dst": [
"autogroup:self:*"
]
}
],
"ssh": []
}
"groups": {
"group:admin": ["admin@"],
"group:users": []
},
"tagOwners": {},
"hosts": {},
"acls": [
{
"randomizeClientPort": false,
"#ha-meta": {
"name": "users",
"open": true
},
"action": "accept",
"src": ["autogroup:member"],
"dst": ["autogroup:self:*"]
}
],
"ssh": []
}
@@ -57,3 +57,30 @@ ports:
endpoints:
- addresses:
- "192.168.88.100"
---
apiVersion: v1
kind: Service
metadata:
name: headplane-external
namespace: headscale
spec:
ports:
- port: 3000
targetPort: 13000
name: http
---
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: headplane-external
namespace: headscale
labels:
kubernetes.io/service-name: headplane-external
addressType: IPv4
ports:
- port: 13000
protocol: TCP
name: http
endpoints:
- addresses:
- "192.168.88.100"
@@ -1,7 +1,16 @@
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: headplane-prefix
namespace: headscale
spec:
addPrefix:
prefix: "/admin"
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: headscale-server-prod
name: headscale-prod
namespace: headscale
spec:
entryPoints:
@@ -9,18 +18,58 @@ spec:
routes:
- match: Host(`hs.forust.xyz`)
kind: Rule
middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd
services:
- name: headscale-server-external
port: 8080
- match: Host(`hs.forust.xyz`) && PathPrefix(`/admin`)
kind: Rule
middlewares:
- name: crowdsec-bouncer
namespace: crowdsec
services:
- name: headscale-ui-external
port: 80
- match: Host(`hs.forust.xyz`) && PathPrefix(`/metrics`)
kind: Rule
middlewares:
- name: crowdsec-bouncer
namespace: crowdsec
services:
- name: headscale-server-external
port: 9090
tls:
certResolver: letsencrypt
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: headscale-server-local
name: headplane-prod
namespace: headscale
spec:
entryPoints:
- websecure
routes:
- match: Host(`hp.forust.xyz`)
kind: Rule
middlewares:
- name: headplane-prefix
- name: crowdsec-bouncer
namespace: crowdsec
services:
- name: headplane-external
port: 3000
- match: Host(`hp.forust.xyz`) && PathPrefix(`/admin`)
kind: Rule
services:
- name: headplane-external
port: 3000
tls:
certResolver: letsencrypt
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: headscale-local
namespace: headscale
spec:
entryPoints:
@@ -31,76 +80,33 @@ spec:
services:
- name: headscale-server-external
port: 8080
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: headscale-ui-prod
namespace: headscale
spec:
entryPoints:
- websecure
routes:
- match: Host(`hs.forust.xyz`) && PathPrefix(`/admin`)
kind: Rule
middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd
- name: security-chain@file
services:
- name: headscale-ui-external
port: 80
tls:
certResolver: letsencrypt
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: headscale-ui-local
namespace: headscale
spec:
entryPoints:
- websecure
routes:
- match: HostRegexp(`^hs\.(workstation|gigaforust)\.internal$`) && PathPrefix(`/admin`)
- match: (Host(`hs.workstation.internal`) || Host(`hs.gigaforust.internal`)) && PathPrefix(`/admin`)
kind: Rule
services:
- name: headscale-ui-external
port: 80
- match: (Host(`hs.workstation.internal`) || Host(`hs.gigaforust.internal`)) && PathPrefix(`/metrics`)
kind: Rule
services:
- name: headscale-server-external
port: 9090
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: headscale-metrics-prod
name: headplane-local
namespace: headscale
spec:
entryPoints:
- websecure
routes:
- match: Host(`hs.forust.xyz`) && PathPrefix(`/metrics`)
- match: Host(`hp.workstation.internal`) || Host(`hp.gigaforust.internal`)
kind: Rule
middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd
- name: headplane-prefix
services:
- name: headscale-server-external
port: 9090
tls:
certResolver: letsencrypt
domains:
- main: hs.forust.xyz
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: headscale-metrics-local
namespace: headscale
spec:
entryPoints:
- websecure
routes:
- match: HostRegexp(`^hs\.(workstation|gigaforust)\.internal$`) && PathPrefix(`/metrics`)
- name: headplane-external
port: 3000
- match: (Host(`hp.workstation.internal`) || Host(`hp.gigaforust.internal`)) && PathPrefix(`/admin`)
kind: Rule
services:
- name: headscale-server-external
port: 9090
- name: headplane-external
port: 3000
Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 53 KiB

+206 -193
View File
@@ -1,212 +1,225 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MrForust // XRock</title>
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
<link rel="stylesheet" href="assets/css/style.css" />
</head>
<body>
<div class="container">
<header>
<h1 class="glitch" data-text="MrForust">Mr-Forust</h1>
<p class="subtitle">> CTF Player / XRock_Team / Just Signal.</p>
</header>
<header>
<h1 class="glitch" data-text="MrForust">Mr-Forust</h1>
<p class="subtitle">> CTF Player / XRock_Team / Just Signal.</p>
</header>
<hr>
<hr />
<div class="grid-2">
<section id="contacts">
<h2>./contacts</h2>
<ul class="link-list">
<li>
<i class="fab fa-github"></i>
<a href="https://github.com/mr-forust" target="_blank">github/mr-forust</a>
</li>
<li>
<i class="fas fa-flag"></i>
<a href="https://tryhackme.com/p/MrForust" target="_blank">tryhackme/MrForust</a>
</li>
<li>
<i class="fab fa-telegram-plane"></i>
<a href="https://t.me/MrForust" target="_blank">telegram/MrForust</a>
</li>
<li>
<i class="fab fa-discord"></i>
<span>discord/mr.forust</span>
</li>
<li>
<i class="fas fa-envelope"></i>
<a href="mailto:contact@forust.xyz">mail/contact@forust.xyz</a>
</li>
<li>
<i class="fa-solid fa-key"></i>
<a href=".well-known/pgp-key.asc">security/PGP Key</a>
</li>
<li>
<p class="comment"># PGP Key Fingerprint: A777 7CB7 D9C4 0A97 443D CCF0 7A3D A455 F820 5B82</p>
</li>
</ul>
</section>
<section id="tools">
<h2>./tools</h2>
<ul class="link-list">
<li>
<i class="fa fa-pie-chart"></i>
<a href="https://forust.xyz/glance" target="_blank">forust/dashboard</a>
<p class="comment"># Glance dashboard</p>
</li>
<li>
<i class="fa fa-refresh"></i>
<a href="https://forust.xyz/convert" target="_blank">forust/converter</a>
<p class="comment"># ConvertX instance</p>
</li>
<li>
<i class="fa-solid fa-file-pdf"></i>
<a href="https://pdf.forust.xyz" target="_blank">pdf.forust.xyz</a>
<p class="comment"># BentoPDF instance</p>
</li>
</ul>
</section>
</div>
<section id="stack">
<h2>./skills</h2>
<div class="grid-2">
<section id="socials">
<h2>./socials</h2>
<ul class="link-list">
<li>
<i class="fab fa-github"></i>
<a href="https://github.com/mr-forust" target="_blank">github/mr-forust</a>
</li>
<li>
<i class="fas fa-flag"></i>
<a href="https://tryhackme.com/p/MrForust" target="_blank">tryhackme/MrForust</a>
</li>
<li>
<i class="fab fa-telegram-plane"></i>
<a href="https://t.me/MrForust" target="_blank">telegram/MrForust</a>
</li>
<li>
<i class="fab fa-discord"></i>
<span>discord/mr.forust</span>
</li>
<li>
<i class="fas fa-envelope"></i>
<a href="mailto:contact@forust.xyz">mail/contact@forust.xyz</a>
</li>
<li>
<i class="fa-solid fa-key"></i>
<a href=".well-known/pgp-key.asc">security/PGP Key</a>
</li>
<li>
<p class="comment"># PGP Key Fingerprint: A777 7CB7 D9C4 0A97 443D CCF0 7A3D A455 F820 5B82</p>
</li>
</ul>
</section>
<section id="tools">
<h2>./tools</h2>
<ul class="link-list">
<li>
<i class="fa fa-pie-chart"></i>
<a href="https://forust.xyz/glance" target="_blank">forust/dashboard</a>
</li>
<li>
<i class="fa fa-refresh"></i>
<a href="https://forust.xyz/convert" target="_blank">forust/converter</a>
</li>
<li>
<i class="fa-solid fa-file-pdf"></i>
<a href="https://pdf.forust.xyz" target="_blank">pdf.forust.xyz</a>
</li>
</ul>
</section>
<div>
<div class="skill-item">
<span>ArchLinux # btw</span>
<span class="level">[#######...]</span>
</div>
<div class="skill-item"><span>Kubernetes</span> <span class="level">[###.......]</span></div>
<div class="skill-item"><span>Docker</span> <span class="level">[####......]</span></div>
<div class="skill-item">
<span>Docker Compose</span>
<span class="level">[#####.....]</span>
</div>
<div class="skill-item"><span>Web Pentest</span> <span class="level">[#####.....]</span></div>
<div class="skill-item"><span>Burpsuite</span> <span class="level">[#####.....]</span></div>
<div class="skill-item"><span>Steganography</span> <span class="level">[####......]</span></div>
</div>
<div>
<div class="skill-item"><span>Cryptography</span> <span class="level">[####......]</span></div>
<div class="skill-item"><span>OSINT</span> <span class="level">[####......]</span></div>
<div class="skill-item"><span>Python</span> <span class="level">[###.......]</span></div>
<div class="skill-item"><span>HTML</span> <span class="level">[###.......]</span></div>
<div class="skill-item"><span>Bash</span> <span class="level">[##........]</span></div>
<div class="skill-item"><span>Golang</span> <span class="level">[#.........]</span></div>
</div>
</div>
</section>
<section id="stack">
<h2>./skills</h2>
<div class="grid-2">
<div>
<div class="skill-item">
<span>ArchLinux # btw</span>
<span class="level">[#######...]</span>
</div>
<div class="skill-item">
<span>Kubernetes</span> <span class="level">[###.......]</span>
</div>
<div class="skill-item">
<span>Docker</span> <span class="level">[####......]</span>
</div>
<div class="skill-item">
<span>Docker Compose</span> <span class="level">[#####.....]</span>
</div>
<div class="skill-item">
<span>Web Pentest</span> <span class="level">[#####.....]</span>
</div>
<div class="skill-item">
<span>Burpsuite</span> <span class="level">[#####.....]</span>
</div>
<div class="skill-item">
<span>Steganography</span> <span class="level">[####......]</span>
</div>
</div>
<div>
<div class="skill-item">
<span>Cryptography</span> <span class="level">[####......]</span>
</div>
<div class="skill-item">
<span>OSINT</span> <span class="level">[####......]</span>
</div>
<div class="skill-item">
<span>Python</span> <span class="level">[###.......]</span>
</div>
<div class="skill-item">
<span>HTML</span> <span class="level">[###.......]</span>
</div>
<div class="skill-item">
<span>Bash</span> <span class="level">[##........]</span>
</div>
<div class="skill-item">
<span>Golang</span> <span class="level">[#.........]</span>
</div>
</div>
</div>
</section>
<section id="team">
<h2>./xrock_team</h2>
<p class="comment"># It's a select caste. Cybershamans. Cryptoanarchists. Shadows on the net..</p>
<section id="team">
<h2>./xrock_team</h2>
<p class="comment"># It's a select caste. Cybershamans. Cryptoanarchists. Shadows on the net..</p>
<div class="team-grid">
<div class="member">
<div
class="avatar"
style="
background-image: url(&quot;assets/images/team/mrforust.jpg&quot;);
background-size: cover;
background-position: center;
"
></div>
<a href="https://github.com/mr-forust" target="_blank">MrForust</a>
</div>
<div class="team-grid">
<div class="member">
<div class="avatar"
style="background-image: url('assets/images/team/mrforust.jpg'); background-size: cover; background-position: center;">
</div>
<a href="https://github.com/mr-forust" target="_blank">MrForust</a>
</div>
<div class="member">
<div
class="avatar"
style="
background-image: url(&quot;assets/images/team/anna.jpg&quot;);
background-size: cover;
background-position: center;
"
></div>
<a href="./assets/images/love.png" target="_blank">Anna~</a>
</div>
<div class="member">
<div class="avatar"
style="background-image: url('assets/images/team/anna.jpg'); background-size: cover; background-position: center;">
</div>
<a href="./assets/images/love.png" target="_blank">Anna~</a>
</div>
<div class="member">
<div
class="avatar"
style="
background-image: url(&quot;assets/images/team/chernuha.jpg&quot;);
background-size: cover;
background-position: center;
"
></div>
<a href="https://chernuha.space" target="_blank">Chernuha</a>
</div>
<div class="member">
<div class="avatar"
style="background-image: url('assets/images/team/chernuha.jpg'); background-size: cover; background-position: center;">
</div>
<a href="https://chernuha.space" target="_blank">Chernuha</a>
</div>
<div class="member">
<div
class="avatar"
style="
background-image: url(&quot;assets/images/team/hudan.jpg&quot;);
background-size: cover;
background-position: center;
"
></div>
<a href="https://hudan.xyz" target="_blank">p1ngvi</a>
</div>
<div class="member">
<div class="avatar"
style="background-image: url('assets/images/team/hudan.jpg'); background-size: cover; background-position: center;">
</div>
<a href="https://hudan.xyz" target="_blank">p1ngvi</a>
</div>
<div class="member">
<div
class="avatar"
style="
background-image: url(&quot;assets/images/team/xdfnx.jpg&quot;);
background-size: cover;
background-position: center;
"
></div>
<a href="https://xdfnx.cfd" target="_blank">xdfnx</a>
</div>
</div>
</section>
<div class="member">
<div class="avatar"
style="background-image: url('assets/images/team/xdfnx.jpg'); background-size: cover; background-position: center;">
</div>
<a href="https://xdfnx.cfd" target="_blank">xdfnx</a>
</div>
</div>
</section>
<section id="projects">
<h2>./projects</h2>
<ul class="repo-list">
<li>
<a href="https://gitea.forust.xyz/forust/gosleep" target="_blank">forust/gosleep</a>
<span class="comment">// linux sleep timer written in rust (originally in go)</span>
</li>
</ul>
</section>
<section id="repos">
<h2>./favorite_repos</h2>
<ul class="repo-list">
<li>
<a href="https://github.com/TDesktop-x64/tdesktop" target="_blank">TDesktop-x64/tdesktop</a>
<span class="comment">// unofficial telegram client with some additions</span>
</li>
<li>
<a href="https://github.com/traefik/traefik" target="_blank">traefik/traefik</a>
<span class="comment">// beloved reverse-proxy</span>
</li>
<li>
<a href="https://github.com/unhappychoice/gitlogue" target="_blank">unhappychoice/gitlogue</a>
<span class="comment">// nice git log visualizer</span>
</li>
<li>
<a href="https://github.com/tstack/lnav" target="_blank">tstack/lnav</a>
<span class="comment">// powerful log reader</span>
</li>
<li>
<a href="https://github.com/mountain-loop/yaak" target="_blank">mountain-loop/yaak</a>
<span class="comment">// modern, fancy api client</span>
</li>
<li>
<a href="https://github.com/pear-devs/pear-desktop" target="_blank">pear-devs/pear-desktop</a>
<span class="comment">// music client with a lot of features</span>
</li>
<li>
<a href="https://github.com/epi052/feroxbuster" target="_blank">epi052/feroxbuster</a>
<span class="comment">// directory discovery</span>
</li>
</ul>
</section>
<section id="repos">
<h2>./favorite_repos</h2>
<ul class="repo-list">
<li>
<a href="https://github.com/TDesktop-x64/tdesktop" target="_blank">TDesktop-x64/tdesktop</a>
<span class="comment">// unofficial telegram client with some additions</span>
</li>
<li>
<a href="https://github.com/traefik/traefik" target="_blank">traefik/traefik</a>
<span class="comment">// beloved reverse-proxy</span>
</li>
<li>
<a href="https://github.com/unhappychoice/gitlogue" target="_blank">unhappychoice/gitlogue</a>
<span class="comment">// nice git log visualizer</span>
</li>
<li>
<a href="https://github.com/tstack/lnav" target="_blank">tstack/lnav</a>
<span class="comment">// powerful log reader</span>
</li>
<li>
<a href="https://github.com/mountain-loop/yaak" target="_blank">mountain-loop/yaak</a>
<span class="comment">// modern, fancy api client</span>
</li>
<li>
<a href="https://github.com/pear-devs/pear-desktop" target="_blank">pear-devs/pear-desktop</a>
<span class="comment">// music client with a lot of features</span>
</li>
<li>
<a href="https://github.com/epi052/feroxbuster" target="_blank">epi052/feroxbuster</a>
<span class="comment">// directory discovery</span>
</li>
</ul>
</section>
<footer>
<p>root@xrock:~$ cat <a href="miku.html">./miku</a></p>
<p>miku?</p>
<p>root@xrock:~$ shutdown -h now</p>
<p>&copy; XRock - Just Signal.</p>
</footer>
<footer>
<p>root@xrock:~$ cat <a href="miku.html">./miku</a></p>
<p>miku?</p>
<p>root@xrock:~$ shutdown -h now</p>
<p>&copy; XRock - Just Signal.</p>
</footer>
</div>
</body>
</html>
</body>
</html>
View File
Whitespace-only changes.
+5 -3
View File
@@ -10,7 +10,8 @@ spec:
- match: Host(`forust.xyz`) || Host(`www.forust.xyz`)
kind: Rule
middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd
- name: crowdsec-bouncer
namespace: crowdsec
priority: 10
services:
- name: forust-homepage-service
@@ -43,10 +44,11 @@ spec:
entryPoints:
- websecure
routes:
- match: Host(`xdfnx.cfd`) || Host(`www.xdfnx.cfd`)
- match: Host(`xdfnx.cfd`)
kind: Rule
middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd
- name: crowdsec-bouncer
namespace: crowdsec
services:
- name: xdfnx-homepage-service
port: 80
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -1,6 +1,6 @@
services:
kener:
image: rajnandan1/kener:4.0.23
image: rajnandan1/kener:4.1.5
container_name: kener
restart: unless-stopped
# ports:
@@ -36,7 +36,7 @@ services:
- proxy
- kener
redis:
image: redis:7-alpine
image: redis:8.10.1-alpine
container_name: kener-redis
restart: unless-stopped
volumes:
+2 -1
View File
@@ -10,7 +10,8 @@ spec:
- match: Host(`status.forust.xyz`)
kind: Rule
middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd
- name: crowdsec-bouncer
namespace: crowdsec
services:
- name: kener-service
port: 3000
+1 -1
View File
@@ -27,7 +27,7 @@ spec:
spec:
containers:
- name: kener
image: rajnandan1/kener:4.1.0
image: rajnandan1/kener:4.1.5
envFrom:
- configMapRef:
name: kener-config
+1 -1
View File
@@ -30,7 +30,7 @@ spec:
spec:
containers:
- name: redis
image: redis:7-alpine
image: redis:8.10.1-alpine
ports:
- containerPort: 6379
volumeMounts:
+1 -1
View File
@@ -1,6 +1,6 @@
services:
metube:
image: ghcr.io/alexta69/metube
image: ghcr.io/alexta69/metube:2026.08.28
container_name: metube
restart: unless-stopped
# ports:
View File
Whitespace-only changes.
+1 -1
View File
@@ -27,7 +27,7 @@ spec:
spec:
containers:
- name: metube
image: ghcr.io/alexta69/metube
image: ghcr.io/alexta69/metube:2026.08.28
envFrom:
- configMapRef:
name: metube-config
+1 -1
View File
@@ -1,6 +1,6 @@
services:
n8n:
image: docker.n8n.io/n8nio/n8n
image: docker.n8n.io/n8nio/n8n:2.39.5
container_name: n8n
restart: unless-stopped
environment:
View File
Whitespace-only changes.
+2 -1
View File
@@ -10,7 +10,8 @@ spec:
- match: Host(`n8n.forust.xyz`)
kind: Rule
middlewares:
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
- name: crowdsec-bouncer
namespace: crowdsec
services:
- name: n8n-service
port: 5678
+1 -1
View File
@@ -27,7 +27,7 @@ spec:
spec:
containers:
- name: n8n
image: docker.n8n.io/n8nio/n8n
image: docker.n8n.io/n8nio/n8n:2.39.5
envFrom:
- configMapRef:
name: n8n-config
+2 -2
View File
@@ -1,6 +1,6 @@
services:
netronome:
image: ghcr.io/autobrr/netronome:latest
image: ghcr.io/autobrr/netronome:v0.14.0
restart: unless-stopped
container_name: netronome
ports:
@@ -33,7 +33,7 @@ services:
condition: service_healthy
postgres:
container_name: netronome-postgres
image: postgres:17-alpine
image: postgres:17.11-alpine
environment:
- POSTGRES_USER=netronome
- POSTGRES_PASSWORD=netronome
View File
Whitespace-only changes.
+2 -1
View File
@@ -10,7 +10,8 @@ spec:
- match: Host(`nm.forust.xyz`)
kind: Rule
middlewares:
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
- name: crowdsec-bouncer
namespace: crowdsec
services:
- name: netronome-service
port: 7575
+1 -1
View File
@@ -30,7 +30,7 @@ spec:
spec:
containers:
- name: netronome
image: ghcr.io/autobrr/netronome:latest
image: ghcr.io/autobrr/netronome:v0.14.0
ports:
- name: netronome-port
protocol: TCP
+1 -1
View File
@@ -29,7 +29,7 @@ spec:
spec:
containers:
- name: netronome-postgres
image: postgres:17-alpine
image: postgres:17.11-alpine
ports:
- name: postgres-port
protocol: TCP
@@ -12,7 +12,8 @@ spec:
kind: Rule
middlewares:
- name: nextcloud-chain@file
- name: crowdsec-crowdsec-bouncer@kubernetescrd
- name: crowdsec-bouncer
namespace: crowdsec
services:
- name: nextcloud-apache
port: 11000
@@ -31,7 +32,8 @@ spec:
- match: Host(`nextcloud.workstation.internal`) || Host(`nextcloud.gigaforust.internal`)
kind: Rule
middlewares:
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
- name: crowdsec-bouncer
namespace: crowdsec
- name: nextcloud-chain@file
services:
- name: nextcloud-apache
+5 -5
View File
@@ -84,7 +84,7 @@ services:
# - "443:443"
penpot-frontend:
image: "penpotapp/frontend:${PENPOT_VERSION:-latest}"
image: "penpotapp/frontend:${PENPOT_VERSION:-2.17.2}"
restart: always
# ports:
# - 9001:8080
@@ -119,7 +119,7 @@ services:
environment:
<<: [*penpot-flags, *penpot-http-body-size]
penpot-backend:
image: "penpotapp/backend:${PENPOT_VERSION:-latest}"
image: "penpotapp/backend:${PENPOT_VERSION:-2.17.2}"
restart: always
volumes:
@@ -189,7 +189,7 @@ services:
# PENPOT_SMTP_SSL: false
penpot-exporter:
image: "penpotapp/exporter:${PENPOT_VERSION:-latest}"
image: "penpotapp/exporter:${PENPOT_VERSION:-2.17.2}"
restart: always
depends_on:
@@ -209,7 +209,7 @@ services:
PENPOT_REDIS_URI: redis://penpot-valkey/0
penpot-postgres:
image: "postgres:15"
image: "postgres:15.19-alpine"
restart: always
stop_signal: SIGINT
@@ -233,7 +233,7 @@ services:
- POSTGRES_PASSWORD=penpot
penpot-valkey:
image: valkey/valkey:8.1
image: valkey/valkey:9.1.2
restart: always
healthcheck:
+1 -1
View File
@@ -1,6 +1,6 @@
services:
portainer:
image: portainer/portainer-ce:2.41.0
image: portainer/portainer-ce:2.45.0
container_name: portainer
restart: always
volumes:
View File
Whitespace-only changes.
+2 -1
View File
@@ -10,7 +10,8 @@ spec:
- match: Host(`portainer.forust.xyz`)
kind: Rule
middlewares:
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
- name: crowdsec-bouncer
namespace: crowdsec
services:
- name: portainer-service
port: 9000
+1 -1
View File
@@ -27,7 +27,7 @@ spec:
spec:
containers:
- name: portainer
image: portainer/portainer-ce:2.41.0
image: portainer/portainer-ce:2.45.0
ports:
- containerPort: 9000
volumeMounts:
+3 -3
View File
@@ -1,6 +1,6 @@
services:
grafana:
image: grafana/grafana:11.6.0
image: grafana/grafana:13.2.1
container_name: prometheus-grafana
restart: unless-stopped
env_file:
@@ -30,7 +30,7 @@ services:
- proxy
prometheus:
image: prom/prometheus:v3.2.1
image: prom/prometheus:v3.14.0
container_name: prometheus-prometheus
restart: unless-stopped
command:
@@ -44,7 +44,7 @@ services:
- proxy
alertmanager:
image: prom/alertmanager:v0.28.1
image: prom/alertmanager:v0.34.0
container_name: prometheus-alertmanager
restart: unless-stopped
command:
Loaded 100 of 256 files, more files were not shown because too many files have changed in this diff. Show more