Compare commits

..
118 Commits
Author SHA1 Message Date
renovate-bot ef86ff286a chore(deps): update renovate/renovate docker tag to v44.90.2
ci / lint-prettier (push) Successful in 12s
ci / lint-ruff (push) Successful in 5s
ci / lint-yaml (push) Successful in 8s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 6s
ci / lint-prettier (pull_request) Successful in 9s
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 6s
renovate-ci / validate-renovate (pull_request) Successful in 14s
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-15 04:18:03 +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 13747 additions and 2466 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
+2 -2
View File
@@ -40,7 +40,8 @@ traefik/letsencrypt/acme.json
traefik/dynamic/fileservers.yml traefik/dynamic/fileservers.yml
traefik/dynamic/*.local.y*ml.* traefik/dynamic/*.local.y*ml.*
traefik/dynamic/*.external.y*ml traefik/dynamic/*.external.y*ml
traefik/k8s/fileservers.y*ml
traefik/k8s/aliasHeadersStrategy.md
traefik/logs/* traefik/logs/*
@@ -106,4 +107,3 @@ temp/*
traefik/k8s/local-tls.yaml traefik/k8s/local-tls.yaml
converters/k8s/config.yaml converters/k8s/config.yaml
convertx/k8s/config.yaml convertx/k8s/config.yaml
traefik/k8s/crowdsec-middleware.yaml
+13 -12
View File
@@ -9,31 +9,32 @@
"hard_tabs": false, "hard_tabs": false,
"format_on_save": "on", "format_on_save": "on",
"formatter": { "formatter": {
"language_server": { "name": "yaml-language-server" } "language_server": { "name": "yaml-language-server" },
} },
}, },
"Python": { "Python": {
"tab_size": 4, "tab_size": 4,
"format_on_save": "on", "format_on_save": "on",
"language_servers": ["pyright", "ruff"],
"formatter": { "formatter": {
"language_server": { "name": "ruff" } "language_server": { "name": "ruff" },
} },
} },
}, },
"lsp": { "lsp": {
"yaml-language-server": { "yaml-language-server": {
"settings": { "settings": {
"yaml": { "yaml": {
"schemas": { "schemas": {
"kubernetes": ["**/k8s/*.yaml", "**/k8s/*.yml"] "kubernetes": ["**/k8s/*.yaml", "**/k8s/*.yml"],
}, },
"validate": true, "validate": true,
"completion": true, "completion": true,
"format": { "format": {
"enable": true "enable": true,
} },
} },
} },
} },
} },
} }
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
adguard: adguard:
image: adguard/adguardhome:latest image: adguard/adguardhome:v0.107.79
container_name: adguardhome container_name: adguardhome
restart: unless-stopped restart: unless-stopped
ports: ports:
View File
Whitespace-only changes.
+1 -1
View File
@@ -65,7 +65,7 @@ spec:
spec: spec:
containers: containers:
- name: adguard - name: adguard
image: adguard/adguardhome:latest image: adguard/adguardhome:v0.107.79
resources: resources:
limits: limits:
memory: "1.5Gi" memory: "1.5Gi"
+8 -16
View File
@@ -10,7 +10,13 @@ spec:
- match: Host(`adguard.forust.xyz`) || Host(`dns.forust.xyz`) - match: Host(`adguard.forust.xyz`) || Host(`dns.forust.xyz`)
kind: Rule kind: Rule
middlewares: 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: services:
- name: adguard-service - name: adguard-service
port: 3000 port: 3000
@@ -31,22 +37,8 @@ spec:
services: services:
- name: adguard-service - name: adguard-service
port: 3000 port: 3000
--- - match: (Host(`adguard.workstation.internal`) || Host(`dns.workstation.internal`) || Host(`adguard.gigaforust.internal`) || Host(`dns.gigaforust.internal`)) && PathPrefix(`/dns-query`)
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`)
kind: Rule kind: Rule
middlewares:
- name: "crowdsec-crowdsec-bouncer@kubernetescrd"
services: services:
- name: adguard-service - name: adguard-service
port: 3000 port: 3000
tls:
certResolver: letsencrypt
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
postgresql: postgresql:
image: docker.io/library/postgres:15-alpine image: docker.io/library/postgres:15.19-alpine
restart: unless-stopped restart: unless-stopped
env_file: env_file:
- .env - .env
View File
Whitespace-only changes.
+2 -2
View File
@@ -41,7 +41,7 @@ spec:
spec: spec:
containers: containers:
- name: authentik-server - name: authentik-server
image: ghcr.io/goauthentik/server:2025.10.2 image: ghcr.io/goauthentik/server:2026.8.2
args: ["server"] args: ["server"]
envFrom: envFrom:
- configMapRef: - configMapRef:
@@ -75,7 +75,7 @@ spec:
spec: spec:
containers: containers:
- name: authentik-worker - name: authentik-worker
image: ghcr.io/goauthentik/server:2025.10.2 image: ghcr.io/goauthentik/server:2026.8.2
args: ["worker"] args: ["worker"]
securityContext: securityContext:
runAsUser: 0 runAsUser: 0
+2 -1
View File
@@ -10,7 +10,8 @@ spec:
- match: Host(`auth.forust.xyz`) - match: Host(`auth.forust.xyz`)
kind: Rule kind: Rule
middlewares: middlewares:
- name: "crowdsec-crowdsec-bouncer@kubernetescrd" - name: crowdsec-bouncer
namespace: crowdsec
services: services:
- name: authentik-server-service - name: authentik-server-service
port: 9000 port: 9000
+1 -1
View File
@@ -29,7 +29,7 @@ spec:
spec: spec:
containers: containers:
- name: postgres - name: postgres
image: docker.io/library/postgres:15-alpine image: docker.io/library/postgres:15.19-alpine
env: env:
- name: POSTGRES_DB - name: POSTGRES_DB
value: authentik value: authentik
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
cloudflare-ddns: cloudflare-ddns:
image: timothyjmiller/cloudflare-ddns:latest image: timothyjmiller/cloudflare-ddns:2.2.0
container_name: cloudflare-ddns container_name: cloudflare-ddns
restart: unless-stopped restart: unless-stopped
security_opt: security_opt:
+1 -1
View File
@@ -7,7 +7,7 @@
"api_key": { "api_key": {
"api_key": "api_key_here", "api_key": "api_key_here",
"account_email": "your_email_here" "account_email": "your_email_here"
} },
"zone_id": "your_zone-id", "zone_id": "your_zone-id",
"subdomains": [ "subdomains": [
{ "name": "", "proxied": true }, { "name": "", "proxied": true },
View File
Whitespace-only changes.
+1 -1
View File
@@ -18,7 +18,7 @@ spec:
dnsPolicy: ClusterFirstWithHostNet dnsPolicy: ClusterFirstWithHostNet
containers: containers:
- name: cloudflare-ddns - name: cloudflare-ddns
image: timothyjmiller/cloudflare-ddns:latest image: timothyjmiller/cloudflare-ddns:2.2.0
imagePullPolicy: Always imagePullPolicy: Always
resources: resources:
requests: requests:
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
checkmk: checkmk:
image: "checkmk/check-mk-raw:2.4.0-latest" image: "checkmk/check-mk-raw:2.4.0-2026.09.14"
container_name: "checkmk" container_name: "checkmk"
restart: unless-stopped restart: unless-stopped
# ports: # ports:
View File
Whitespace-only changes.
+10 -3
View File
@@ -7,8 +7,12 @@ spec:
selector: selector:
app: checkmk app: checkmk
ports: ports:
- port: 5000 - name: web
port: 5000
targetPort: 5000 targetPort: 5000
- name: agent-receiver
port: 8000
targetPort: 8000
--- ---
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
@@ -27,14 +31,17 @@ spec:
spec: spec:
containers: containers:
- name: checkmk - name: checkmk
image: checkmk/check-mk-raw:2.4.0-latest image: checkmk/check-mk-raw:2.4.0-2026.09.14
envFrom: envFrom:
- secretRef: - secretRef:
name: checkmk-secrets name: checkmk-secrets
- configMapRef: - configMapRef:
name: checkmk-config name: checkmk-config
ports: ports:
- containerPort: 5000 - name: web
containerPort: 5000
- name: agent-receiver
containerPort: 8000
volumeMounts: volumeMounts:
- name: sites - name: sites
mountPath: /omd/sites mountPath: /omd/sites
+18 -1
View File
@@ -10,7 +10,8 @@ spec:
- match: Host(`cmk.forust.xyz`) - match: Host(`cmk.forust.xyz`)
kind: Rule kind: Rule
middlewares: middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd - name: crowdsec-bouncer
namespace: crowdsec
services: services:
- name: checkmk-service - name: checkmk-service
port: 5000 port: 5000
@@ -18,6 +19,22 @@ spec:
certResolver: letsencrypt certResolver: letsencrypt
--- ---
apiVersion: traefik.io/v1alpha1 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 kind: IngressRoute
metadata: metadata:
name: checkmk-local name: checkmk-local
+2 -2
View File
@@ -1,7 +1,7 @@
services: services:
convertx: convertx:
container_name: convertx container_name: convertx
image: ghcr.io/c4illin/convertx:latest image: ghcr.io/c4illin/convertx:v0.18.0
restart: unless-stopped restart: unless-stopped
ports: ports:
- "9992:3000" - "9992:3000"
@@ -42,7 +42,7 @@ services:
bentopdf: bentopdf:
container_name: bentopdf container_name: bentopdf
image: bentopdf/bentopdf:latest image: bentopdf/bentopdf@sha256:4eb4ec8f5030faf87c29a73d3d5a2781f28a597cf440c3ab111eb96aee550871
restart: unless-stopped restart: unless-stopped
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
View File
Whitespace-only changes.
+1 -1
View File
@@ -26,7 +26,7 @@ spec:
app: bentopdf app: bentopdf
spec: spec:
containers: containers:
- image: bentopdf/bentopdf:latest - image: bentopdf/bentopdf@sha256:4eb4ec8f5030faf87c29a73d3d5a2781f28a597cf440c3ab111eb96aee550871
imagePullPolicy: Always imagePullPolicy: Always
name: bentopdf name: bentopdf
ports: ports:
+1 -1
View File
@@ -26,7 +26,7 @@ spec:
app: convertx app: convertx
spec: spec:
containers: containers:
- image: ghcr.io/c4illin/convertx:latest - image: ghcr.io/c4illin/convertx:v0.18.0
name: convertx name: convertx
envFrom: envFrom:
- configMapRef: - configMapRef:
+28 -28
View File
@@ -2,23 +2,9 @@ container_runtime: containerd
agent: agent:
env: env:
- name: COLLECTIONS - name: COLLECTIONS
value: "crowdsecurity/traefik crowdsecurity/base-http-scenarios crowdsecurity/sshd" value: "crowdsecurity/traefik crowdsecurity/base-http-scenarios"
extraVolumes: - name: DISABLE_COLLECTIONS
- name: journal-dir value: "crowdsecurity/linux crowdsecurity/sshd"
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
acquisition: acquisition:
- namespace: traefik - namespace: traefik
@@ -26,13 +12,6 @@ agent:
program: traefik program: traefik
poll_without_inotify: true poll_without_inotify: true
acquisitionCustom: |
- source: journalctl
journalctl_filter:
- _SYSTEMD_UNIT=sshd.service
labels:
type: syslog
resources: resources:
requests: requests:
cpu: 50m cpu: 50m
@@ -44,14 +23,35 @@ agent:
lapi: lapi:
env: env:
- name: COLLECTIONS - 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: service:
type: NodePort type: ClusterIP
nodePort: 30011 persistentVolume:
data:
enabled: true
storageClassName: local-path-retain
size: 1Gi
config:
enabled: true
storageClassName: local-path-retain
size: 100Mi
storeLAPICscliCredentialsInSecret: true
resources: resources:
requests: requests:
cpu: 50m cpu: 50m
memory: 150Mi memory: 150Mi
limits: limits:
cpu: 200m cpu: 400m
memory: 500Mi 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: services:
dockmon: dockmon:
image: darthnorse/dockmon:latest image: darthnorse/dockmon:2.4.5
container_name: dockmon container_name: dockmon
restart: unless-stopped restart: unless-stopped
# ports: # ports:
View File
Whitespace-only changes.
+1 -1
View File
@@ -29,7 +29,7 @@ spec:
spec: spec:
containers: containers:
- name: dockmon - name: dockmon
image: darthnorse/dockmon:latest image: darthnorse/dockmon:2.4.5
ports: ports:
- containerPort: 443 - containerPort: 443
volumeMounts: volumeMounts:
+2 -1
View File
@@ -18,7 +18,8 @@ spec:
- match: Host(`dockmon.forust.xyz`) - match: Host(`dockmon.forust.xyz`)
kind: Rule kind: Rule
middlewares: middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd - name: crowdsec-bouncer
namespace: crowdsec
- name: security-headers@file - name: security-headers@file
services: services:
- name: dockmon-service - name: dockmon-service
+1 -1
View File
@@ -1,7 +1,7 @@
services: services:
downtify: downtify:
container_name: downtify container_name: downtify
image: ghcr.io/henriquesebastiao/downtify:latest image: ghcr.io/henriquesebastiao/downtify:2.12.0
restart: unless-stopped restart: unless-stopped
# ports: # ports:
# - '7077:8000' # - '7077:8000'
+1 -1
View File
@@ -27,7 +27,7 @@ spec:
spec: spec:
containers: containers:
- name: downtify - name: downtify
image: ghcr.io/henriquesebastiao/downtify:latest image: ghcr.io/henriquesebastiao/downtify:2.12.0
ports: ports:
- containerPort: 8000 - containerPort: 8000
volumeMounts: volumeMounts:
+2 -1
View File
@@ -10,7 +10,8 @@ spec:
- match: Host(`downtify.forust.xyz`) - match: Host(`downtify.forust.xyz`)
kind: Rule kind: Rule
middlewares: middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd - name: crowdsec-bouncer
namespace: crowdsec
- name: security-chain@file - name: security-chain@file
services: services:
- name: downtify-service - name: downtify-service
+284 -314
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_HOST=redis
REDIS_PORT=6379 REDIS_PORT=6379
PLAYWRIGHT_WS=ws://playwright-service:3000/ws PLAYWRIGHT_WS=ws://playwright-service:3000/ws
TZ=Europe/Kyiv
WEBINAR_TELEGRAM_TOKEN=your_telegram_bot_token_here WEBINAR_TELEGRAM_TOKEN=your_telegram_bot_token_here
WEBINAR_ADMIN_ID=123456789 WEBINAR_ADMIN_ID=123456789
+2 -2
View File
@@ -1,6 +1,6 @@
services: services:
redis: redis:
image: redis:alpine image: redis:8.10.1-alpine
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- redis-data:/data - redis-data:/data
@@ -11,7 +11,7 @@ services:
retries: 5 retries: 5
playwright-service: playwright-service:
image: mcr.microsoft.com/playwright:v1.56.0-jammy image: mcr.microsoft.com/playwright:v1.63.0-jammy
restart: unless-stopped restart: unless-stopped
command: npx -y playwright@1.56.0 run-server --port 3000 --path /ws 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 WORKDIR /app
# Install system dependencies # 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 # 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 application code
COPY . . COPY . .
+36 -33
View File
@@ -1,17 +1,16 @@
import logging
import os import os
import time import time
import requests
import logging
import redis
from datetime import datetime from datetime import datetime
import redis
import requests
# Configure logging # Configure logging
logging.basicConfig( logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Load configuration (adapted to .env keys) # Load configuration (adapted to .env keys)
def _env(key, default=None): def _env(key, default=None):
v = os.getenv(key, default) v = os.getenv(key, default)
@@ -19,21 +18,26 @@ def _env(key, default=None):
return v[1:-1] return v[1:-1]
return v return v
LOGIN = _env('KEEPER_LOGIN') LOGIN = _env('KEEPER_LOGIN')
PASSWORD = _env('KEEPER_PASSWORD') PASSWORD = _env('KEEPER_PASSWORD')
EDU_BASE = _env('EDU_URL_BASE', 'https://edu.edu.vn.ua') EDU_BASE = _env('EDU_URL_BASE', 'https://edu.edu.vn.ua')
EDU_LOGIN_PATH = _env('EDU_URL_LOGIN', '/user/login') EDU_LOGIN_PATH = _env('EDU_URL_LOGIN', '/user/login')
EDU_COURSES_PATH = _env('EDU_URL_COURSES', '/course/userlist') EDU_COURSES_PATH = _env('EDU_URL_COURSES', '/course/userlist')
URL_LOGIN = f"{EDU_BASE.rstrip('/')}/{EDU_LOGIN_PATH.lstrip('/')}" URL_LOGIN = f'{EDU_BASE.rstrip("/")}/{EDU_LOGIN_PATH.lstrip("/")}'
URL_VERIFY = f"{EDU_BASE.rstrip('/')}/{EDU_COURSES_PATH.lstrip('/')}" URL_VERIFY = f'{EDU_BASE.rstrip("/")}/{EDU_COURSES_PATH.lstrip("/")}'
INTERVAL = int(_env('KEEPER_INTERVAL', 10)) 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_HOST = _env('REDIS_HOST', 'redis')
REDIS_PORT = int(_env('REDIS_PORT', 6379)) REDIS_PORT = int(_env('REDIS_PORT', 6379))
SUCCESS_FILE = '/tmp/last_success' SUCCESS_FILE = '/tmp/last_success' # noqa: S108
def touch_success_file(): def touch_success_file():
"""Updates the timestamp of the success file for healthchecks.""" """Updates the timestamp of the success file for healthchecks."""
@@ -41,18 +45,19 @@ def touch_success_file():
with open(SUCCESS_FILE, 'w') as f: with open(SUCCESS_FILE, 'w') as f:
f.write(str(datetime.now().timestamp())) f.write(str(datetime.now().timestamp()))
except Exception as e: except Exception as e:
logger.error(f"Failed to touch success file: {e}") logger.error(f'Failed to touch success file: {e}')
def main(): def main():
logger.info("Starting Session Keeper Bot") logger.info('Starting Session Keeper Bot')
# Connect to Redis # Connect to Redis
try: try:
redis_client = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, decode_responses=True) redis_client = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, decode_responses=True)
redis_client.ping() 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: except Exception as e:
logger.error(f"Failed to connect to Redis: {e}") logger.error(f'Failed to connect to Redis: {e}')
return return
session = requests.Session() session = requests.Session()
@@ -72,19 +77,16 @@ def main():
'Sec-Ch-Ua-Mobile': '?0', 'Sec-Ch-Ua-Mobile': '?0',
'Sec-Ch-Ua-Platform': '"Linux"', 'Sec-Ch-Ua-Platform': '"Linux"',
'Accept-Encoding': 'gzip, deflate, br', 'Accept-Encoding': 'gzip, deflate, br',
'Priority': 'u=0, i' 'Priority': 'u=0, i',
} }
session.headers.update(headers) session.headers.update(headers)
while True: while True:
try: try:
logger.info("Attempting login...") logger.info('Attempting login...')
# Login payload # Login payload
payload = { payload = {'login': LOGIN, 'password': PASSWORD}
'login': LOGIN,
'password': PASSWORD
}
# Perform Login # Perform Login
# Note: The user request shows a POST to /user/login with form data # Note: The user request shows a POST to /user/login with form data
@@ -93,17 +95,17 @@ def main():
login_response = session.post(URL_LOGIN, data=payload, allow_redirects=True) login_response = session.post(URL_LOGIN, data=payload, allow_redirects=True)
logger.info(f"Login Response Status: {login_response.status_code}") logger.info(f'Login Response Status: {login_response.status_code}')
logger.info(f"Cookies after login: {session.cookies.get_dict()}") logger.info(f'Cookies after login: {session.cookies.get_dict()}')
# Verify Session # Verify Session
logger.info("Verifying session...") logger.info('Verifying session...')
verify_response = session.get(URL_VERIFY, allow_redirects=False) 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: if verify_response.status_code == 200:
logger.info("Session verification SUCCESS (200 OK).") logger.info('Session verification SUCCESS (200 OK).')
touch_success_file() touch_success_file()
# Save PHPSESSID to Redis # Save PHPSESSID to Redis
@@ -111,19 +113,20 @@ def main():
if phpsessid: if phpsessid:
try: try:
redis_client.set('EDU_PHPSESSID', phpsessid) 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: 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: 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: 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: 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) time.sleep(INTERVAL * 60)
if __name__ == "__main__":
if __name__ == '__main__':
main() main()
+1 -1
View File
@@ -3,7 +3,7 @@ FROM python:3.11-slim
WORKDIR /app WORKDIR /app
# Install dependencies # 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 . COPY checker.py .
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -1,6 +1,6 @@
services: services:
server: server:
image: docker.gitea.com/gitea:1.26 image: docker.gitea.com/gitea:1.27.3
container_name: gitea container_name: gitea
restart: always restart: always
environment: environment:
@@ -61,7 +61,7 @@ services:
depends_on: depends_on:
- db - db
db: db:
image: docker.io/library/postgres:14 image: docker.io/library/postgres:14.24-alpine
restart: always restart: always
environment: environment:
- POSTGRES_USER=gitea - POSTGRES_USER=gitea
View File
Whitespace-only changes.
+2
View File
@@ -16,5 +16,7 @@ data:
GITEA__security__REVERSE_PROXY_TRUSTED_PROXIES: "*" GITEA__security__REVERSE_PROXY_TRUSTED_PROXIES: "*"
GITEA__mailer__ENABLED: "false" GITEA__mailer__ENABLED: "false"
GITEA__log__logger__access__MODE: "console, file"
USER_UID: "1000" USER_UID: "1000"
USER_GID: "1000" USER_GID: "1000"
+1 -1
View File
@@ -31,7 +31,7 @@ spec:
spec: spec:
containers: containers:
- name: gitea - name: gitea
image: docker.gitea.com/gitea:1.26 image: docker.gitea.com/gitea:1.27.3
envFrom: envFrom:
- configMapRef: - configMapRef:
name: gitea-config name: gitea-config
+11 -14
View File
@@ -10,7 +10,16 @@ spec:
- match: Host(`gitea.forust.xyz`) - match: Host(`gitea.forust.xyz`)
kind: Rule kind: Rule
middlewares: 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: services:
- name: gitea-service - name: gitea-service
port: 3000 port: 3000
@@ -31,23 +40,11 @@ spec:
services: services:
- name: gitea-service - name: gitea-service
port: 3000 port: 3000
--- - match: (Host(`gcr.workstation.internal`) || Host(`gcr.gigaforust.internal`)) && PathPrefix(`/v2`)
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: gitea-registry
namespace: gitea
spec:
entryPoints:
- websecure
routes:
- match: Host(`gcr.forust.xyz`) && PathPrefix(`/v2`)
kind: Rule kind: Rule
services: services:
- name: gitea-service - name: gitea-service
port: 3000 port: 3000
tls:
certResolver: letsencrypt
--- ---
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP kind: IngressRouteTCP
+2 -2
View File
@@ -29,7 +29,7 @@ spec:
spec: spec:
containers: containers:
- name: gitea-postgres - name: gitea-postgres
image: postgres:14 image: postgres:14.24-alpine
env: env:
- name: POSTGRES_USER - name: POSTGRES_USER
valueFrom: valueFrom:
@@ -45,7 +45,7 @@ spec:
valueFrom: valueFrom:
secretKeyRef: secretKeyRef:
name: gitea-secrets name: gitea-secrets
key: GITEA__database__USER key: GITEA__database__NAME
ports: ports:
- containerPort: 5432 - containerPort: 5432
name: postgres name: postgres
+1
View File
@@ -7,3 +7,4 @@ type: Opaque
stringData: stringData:
GITEA__database__USER: "gitea" GITEA__database__USER: "gitea"
GITEA__database__PASSWD: "gitea" GITEA__database__PASSWD: "gitea"
GITEA__database__NAME: "gitea"
+1 -1
View File
@@ -1,7 +1,7 @@
services: services:
glance: glance:
container_name: glance container_name: glance
image: glanceapp/glance image: glanceapp/glance:v0.8.6
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- ./config:/app/config:ro - ./config:/app/config:ro
View File
Whitespace-only changes.
+5 -5
View File
@@ -27,7 +27,7 @@ spec:
spec: spec:
containers: containers:
- name: glance - name: glance
image: glanceapp/glance image: glanceapp/glance:v0.8.6
envFrom: envFrom:
- secretRef: - secretRef:
name: glance-secrets name: glance-secrets
@@ -56,11 +56,11 @@ spec:
readOnly: true readOnly: true
resources: resources:
requests: requests:
memory: "30Mi"
cpu: "20m"
limits:
memory: "100Mi"
cpu: "50m" cpu: "50m"
memory: "64Mi"
limits:
cpu: "200m"
memory: "256Mi"
volumes: volumes:
- name: glance-config - name: glance-config
configMap: configMap:
+37 -3
View File
@@ -1,6 +1,6 @@
services: services:
headscale: headscale:
image: headscale/headscale:latest image: headscale/headscale:0.29.3
restart: unless-stopped restart: unless-stopped
container_name: headscale-server container_name: headscale-server
command: serve command: serve
@@ -53,7 +53,7 @@ services:
- "traefik.http.routers.headscale-metrics-dev.service=headscale-metrics" - "traefik.http.routers.headscale-metrics-dev.service=headscale-metrics"
- "traefik.http.routers.headscale-metrics-dev.tls=true" - "traefik.http.routers.headscale-metrics-dev.tls=true"
headplane: headplane:
image: ghcr.io/tale/headplane:latest image: ghcr.io/tale/headplane:0.7.1
container_name: headplane container_name: headplane
restart: unless-stopped restart: unless-stopped
ports: ports:
@@ -65,8 +65,42 @@ services:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
networks: networks:
- proxy - 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: web:
image: goodieshq/headscale-admin:latest image: goodieshq/headscale-admin:0.28.0
restart: unless-stopped restart: unless-stopped
ports: ports:
- 10080:80 - 10080:80
+4 -10
View File
@@ -1,26 +1,20 @@
{ {
"groups": { "groups": {
"group:admin": [ "group:admin": ["admin@"],
"admin@"
],
"group:users": [] "group:users": []
}, },
"tagOwners": {}, "tagOwners": {},
"hosts": {}, "hosts": {},
"acls": [ "acls": [
{ {
"randomizeClientPort": false, "randomizeClientPort": false,
"#ha-meta": { "#ha-meta": {
"name": "users", "name": "users",
"open": true "open": true
}, },
"action": "accept", "action": "accept",
"src": [ "src": ["autogroup:member"],
"autogroup:member" "dst": ["autogroup:self:*"]
],
"dst": [
"autogroup:self:*"
]
} }
], ],
"ssh": [] "ssh": []
@@ -57,3 +57,30 @@ ports:
endpoints: endpoints:
- addresses: - addresses:
- "192.168.88.100" - "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 apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: headplane-prefix
namespace: headscale
spec:
addPrefix:
prefix: "/admin"
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: headscale-server-prod name: headscale-prod
namespace: headscale namespace: headscale
spec: spec:
entryPoints: entryPoints:
@@ -9,18 +18,58 @@ spec:
routes: routes:
- match: Host(`hs.forust.xyz`) - match: Host(`hs.forust.xyz`)
kind: Rule kind: Rule
middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd
services: services:
- name: headscale-server-external - name: headscale-server-external
port: 8080 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: tls:
certResolver: letsencrypt certResolver: letsencrypt
--- ---
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: 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 namespace: headscale
spec: spec:
entryPoints: entryPoints:
@@ -31,76 +80,33 @@ spec:
services: services:
- name: headscale-server-external - name: headscale-server-external
port: 8080 port: 8080
- match: (Host(`hs.workstation.internal`) || Host(`hs.gigaforust.internal`)) && PathPrefix(`/admin`)
---
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`)
kind: Rule kind: Rule
services: services:
- name: headscale-ui-external - name: headscale-ui-external
port: 80 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 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: headscale-metrics-prod name: headplane-local
namespace: headscale namespace: headscale
spec: spec:
entryPoints:
- websecure
routes: routes:
- match: Host(`hs.forust.xyz`) && PathPrefix(`/metrics`) - match: Host(`hp.workstation.internal`) || Host(`hp.gigaforust.internal`)
kind: Rule kind: Rule
middlewares: middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd - name: headplane-prefix
services: services:
- name: headscale-server-external - name: headplane-external
port: 9090 port: 3000
tls: - match: (Host(`hp.workstation.internal`) || Host(`hp.gigaforust.internal`)) && PathPrefix(`/admin`)
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`)
kind: Rule kind: Rule
services: services:
- name: headscale-server-external - name: headplane-external
port: 9090 port: 3000
Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 53 KiB

+78 -65
View File
@@ -1,28 +1,26 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<head> <meta charset="UTF-8" />
<meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MrForust // XRock</title> <title>MrForust // XRock</title>
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script> <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="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
<link rel="stylesheet" href="assets/css/style.css"> <link rel="stylesheet" href="assets/css/style.css" />
</head> </head>
<body>
<body>
<div class="container"> <div class="container">
<header> <header>
<h1 class="glitch" data-text="MrForust">Mr-Forust</h1> <h1 class="glitch" data-text="MrForust">Mr-Forust</h1>
<p class="subtitle">> CTF Player / XRock_Team / Just Signal.</p> <p class="subtitle">> CTF Player / XRock_Team / Just Signal.</p>
</header> </header>
<hr> <hr />
<div class="grid-2"> <div class="grid-2">
<section id="socials"> <section id="contacts">
<h2>./socials</h2> <h2>./contacts</h2>
<ul class="link-list"> <ul class="link-list">
<li> <li>
<i class="fab fa-github"></i> <i class="fab fa-github"></i>
@@ -60,14 +58,17 @@
<li> <li>
<i class="fa fa-pie-chart"></i> <i class="fa fa-pie-chart"></i>
<a href="https://forust.xyz/glance" target="_blank">forust/dashboard</a> <a href="https://forust.xyz/glance" target="_blank">forust/dashboard</a>
<p class="comment"># Glance dashboard</p>
</li> </li>
<li> <li>
<i class="fa fa-refresh"></i> <i class="fa fa-refresh"></i>
<a href="https://forust.xyz/convert" target="_blank">forust/converter</a> <a href="https://forust.xyz/convert" target="_blank">forust/converter</a>
<p class="comment"># ConvertX instance</p>
</li> </li>
<li> <li>
<i class="fa-solid fa-file-pdf"></i> <i class="fa-solid fa-file-pdf"></i>
<a href="https://pdf.forust.xyz" target="_blank">pdf.forust.xyz</a> <a href="https://pdf.forust.xyz" target="_blank">pdf.forust.xyz</a>
<p class="comment"># BentoPDF instance</p>
</li> </li>
</ul> </ul>
</section> </section>
@@ -81,44 +82,23 @@
<span>ArchLinux # btw</span> <span>ArchLinux # btw</span>
<span class="level">[#######...]</span> <span class="level">[#######...]</span>
</div> </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"> <div class="skill-item">
<span>Kubernetes</span> <span class="level">[###.......]</span> <span>Docker Compose</span>
</div> <span class="level">[#####.....]</span>
<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 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> <div>
<div class="skill-item"> <div class="skill-item"><span>Cryptography</span> <span class="level">[####......]</span></div>
<span>Cryptography</span> <span class="level">[####......]</span> <div class="skill-item"><span>OSINT</span> <span class="level">[####......]</span></div>
</div> <div class="skill-item"><span>Python</span> <span class="level">[###.......]</span></div>
<div class="skill-item"> <div class="skill-item"><span>HTML</span> <span class="level">[###.......]</span></div>
<span>OSINT</span> <span class="level">[####......]</span> <div class="skill-item"><span>Bash</span> <span class="level">[##........]</span></div>
</div> <div class="skill-item"><span>Golang</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>
</div> </div>
</section> </section>
@@ -129,42 +109,77 @@
<div class="team-grid"> <div class="team-grid">
<div class="member"> <div class="member">
<div class="avatar" <div
style="background-image: url('assets/images/team/mrforust.jpg'); background-size: cover; background-position: center;"> class="avatar"
</div> 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> <a href="https://github.com/mr-forust" target="_blank">MrForust</a>
</div> </div>
<div class="member"> <div class="member">
<div class="avatar" <div
style="background-image: url('assets/images/team/anna.jpg'); background-size: cover; background-position: center;"> class="avatar"
</div> 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> <a href="./assets/images/love.png" target="_blank">Anna~</a>
</div> </div>
<div class="member"> <div class="member">
<div class="avatar" <div
style="background-image: url('assets/images/team/chernuha.jpg'); background-size: cover; background-position: center;"> class="avatar"
</div> 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> <a href="https://chernuha.space" target="_blank">Chernuha</a>
</div> </div>
<div class="member"> <div class="member">
<div class="avatar" <div
style="background-image: url('assets/images/team/hudan.jpg'); background-size: cover; background-position: center;"> class="avatar"
</div> 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> <a href="https://hudan.xyz" target="_blank">p1ngvi</a>
</div> </div>
<div class="member"> <div class="member">
<div class="avatar" <div
style="background-image: url('assets/images/team/xdfnx.jpg'); background-size: cover; background-position: center;"> class="avatar"
</div> 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> <a href="https://xdfnx.cfd" target="_blank">xdfnx</a>
</div> </div>
</div> </div>
</section> </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"> <section id="repos">
<h2>./favorite_repos</h2> <h2>./favorite_repos</h2>
<ul class="repo-list"> <ul class="repo-list">
@@ -206,7 +221,5 @@
<p>&copy; XRock - Just Signal.</p> <p>&copy; XRock - Just Signal.</p>
</footer> </footer>
</div> </div>
</body>
</body>
</html> </html>
View File
Whitespace-only changes.
+5 -3
View File
@@ -10,7 +10,8 @@ spec:
- match: Host(`forust.xyz`) || Host(`www.forust.xyz`) - match: Host(`forust.xyz`) || Host(`www.forust.xyz`)
kind: Rule kind: Rule
middlewares: middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd - name: crowdsec-bouncer
namespace: crowdsec
priority: 10 priority: 10
services: services:
- name: forust-homepage-service - name: forust-homepage-service
@@ -43,10 +44,11 @@ spec:
entryPoints: entryPoints:
- websecure - websecure
routes: routes:
- match: Host(`xdfnx.cfd`) || Host(`www.xdfnx.cfd`) - match: Host(`xdfnx.cfd`)
kind: Rule kind: Rule
middlewares: middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd - name: crowdsec-bouncer
namespace: crowdsec
services: services:
- name: xdfnx-homepage-service - name: xdfnx-homepage-service
port: 80 port: 80
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -1,6 +1,6 @@
services: services:
kener: kener:
image: rajnandan1/kener:4.0.23 image: rajnandan1/kener:4.1.5
container_name: kener container_name: kener
restart: unless-stopped restart: unless-stopped
# ports: # ports:
@@ -36,7 +36,7 @@ services:
- proxy - proxy
- kener - kener
redis: redis:
image: redis:7-alpine image: redis:8.10.1-alpine
container_name: kener-redis container_name: kener-redis
restart: unless-stopped restart: unless-stopped
volumes: volumes:
+2 -1
View File
@@ -10,7 +10,8 @@ spec:
- match: Host(`status.forust.xyz`) - match: Host(`status.forust.xyz`)
kind: Rule kind: Rule
middlewares: middlewares:
- name: crowdsec-crowdsec-bouncer@kubernetescrd - name: crowdsec-bouncer
namespace: crowdsec
services: services:
- name: kener-service - name: kener-service
port: 3000 port: 3000
+1 -1
View File
@@ -27,7 +27,7 @@ spec:
spec: spec:
containers: containers:
- name: kener - name: kener
image: rajnandan1/kener:4.1.0 image: rajnandan1/kener:4.1.5
envFrom: envFrom:
- configMapRef: - configMapRef:
name: kener-config name: kener-config
+1 -1
View File
@@ -30,7 +30,7 @@ spec:
spec: spec:
containers: containers:
- name: redis - name: redis
image: redis:7-alpine image: redis:8.10.1-alpine
ports: ports:
- containerPort: 6379 - containerPort: 6379
volumeMounts: volumeMounts:
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
metube: metube:
image: ghcr.io/alexta69/metube image: ghcr.io/alexta69/metube:2026.08.28
container_name: metube container_name: metube
restart: unless-stopped restart: unless-stopped
# ports: # ports:
View File
Whitespace-only changes.
+1 -1
View File
@@ -27,7 +27,7 @@ spec:
spec: spec:
containers: containers:
- name: metube - name: metube
image: ghcr.io/alexta69/metube image: ghcr.io/alexta69/metube:2026.08.28
envFrom: envFrom:
- configMapRef: - configMapRef:
name: metube-config name: metube-config
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
n8n: n8n:
image: docker.n8n.io/n8nio/n8n image: docker.n8n.io/n8nio/n8n:2.39.5
container_name: n8n container_name: n8n
restart: unless-stopped restart: unless-stopped
environment: environment:
View File
Whitespace-only changes.
+2 -1
View File
@@ -10,7 +10,8 @@ spec:
- match: Host(`n8n.forust.xyz`) - match: Host(`n8n.forust.xyz`)
kind: Rule kind: Rule
middlewares: middlewares:
- name: "crowdsec-crowdsec-bouncer@kubernetescrd" - name: crowdsec-bouncer
namespace: crowdsec
services: services:
- name: n8n-service - name: n8n-service
port: 5678 port: 5678
+1 -1
View File
@@ -27,7 +27,7 @@ spec:
spec: spec:
containers: containers:
- name: n8n - name: n8n
image: docker.n8n.io/n8nio/n8n image: docker.n8n.io/n8nio/n8n:2.39.5
envFrom: envFrom:
- configMapRef: - configMapRef:
name: n8n-config name: n8n-config
+2 -2
View File
@@ -1,6 +1,6 @@
services: services:
netronome: netronome:
image: ghcr.io/autobrr/netronome:latest image: ghcr.io/autobrr/netronome:v0.14.0
restart: unless-stopped restart: unless-stopped
container_name: netronome container_name: netronome
ports: ports:
@@ -33,7 +33,7 @@ services:
condition: service_healthy condition: service_healthy
postgres: postgres:
container_name: netronome-postgres container_name: netronome-postgres
image: postgres:17-alpine image: postgres:17.11-alpine
environment: environment:
- POSTGRES_USER=netronome - POSTGRES_USER=netronome
- POSTGRES_PASSWORD=netronome - POSTGRES_PASSWORD=netronome
View File
Whitespace-only changes.
+2 -1
View File
@@ -10,7 +10,8 @@ spec:
- match: Host(`nm.forust.xyz`) - match: Host(`nm.forust.xyz`)
kind: Rule kind: Rule
middlewares: middlewares:
- name: "crowdsec-crowdsec-bouncer@kubernetescrd" - name: crowdsec-bouncer
namespace: crowdsec
services: services:
- name: netronome-service - name: netronome-service
port: 7575 port: 7575
+1 -1
View File
@@ -30,7 +30,7 @@ spec:
spec: spec:
containers: containers:
- name: netronome - name: netronome
image: ghcr.io/autobrr/netronome:latest image: ghcr.io/autobrr/netronome:v0.14.0
ports: ports:
- name: netronome-port - name: netronome-port
protocol: TCP protocol: TCP
+1 -1
View File
@@ -29,7 +29,7 @@ spec:
spec: spec:
containers: containers:
- name: netronome-postgres - name: netronome-postgres
image: postgres:17-alpine image: postgres:17.11-alpine
ports: ports:
- name: postgres-port - name: postgres-port
protocol: TCP protocol: TCP
@@ -12,7 +12,8 @@ spec:
kind: Rule kind: Rule
middlewares: middlewares:
- name: nextcloud-chain@file - name: nextcloud-chain@file
- name: crowdsec-crowdsec-bouncer@kubernetescrd - name: crowdsec-bouncer
namespace: crowdsec
services: services:
- name: nextcloud-apache - name: nextcloud-apache
port: 11000 port: 11000
@@ -31,7 +32,8 @@ spec:
- match: Host(`nextcloud.workstation.internal`) || Host(`nextcloud.gigaforust.internal`) - match: Host(`nextcloud.workstation.internal`) || Host(`nextcloud.gigaforust.internal`)
kind: Rule kind: Rule
middlewares: middlewares:
- name: "crowdsec-crowdsec-bouncer@kubernetescrd" - name: crowdsec-bouncer
namespace: crowdsec
- name: nextcloud-chain@file - name: nextcloud-chain@file
services: services:
- name: nextcloud-apache - name: nextcloud-apache
+5 -5
View File
@@ -84,7 +84,7 @@ services:
# - "443:443" # - "443:443"
penpot-frontend: penpot-frontend:
image: "penpotapp/frontend:${PENPOT_VERSION:-latest}" image: "penpotapp/frontend:${PENPOT_VERSION:-2.17.2}"
restart: always restart: always
# ports: # ports:
# - 9001:8080 # - 9001:8080
@@ -119,7 +119,7 @@ services:
environment: environment:
<<: [*penpot-flags, *penpot-http-body-size] <<: [*penpot-flags, *penpot-http-body-size]
penpot-backend: penpot-backend:
image: "penpotapp/backend:${PENPOT_VERSION:-latest}" image: "penpotapp/backend:${PENPOT_VERSION:-2.17.2}"
restart: always restart: always
volumes: volumes:
@@ -189,7 +189,7 @@ services:
# PENPOT_SMTP_SSL: false # PENPOT_SMTP_SSL: false
penpot-exporter: penpot-exporter:
image: "penpotapp/exporter:${PENPOT_VERSION:-latest}" image: "penpotapp/exporter:${PENPOT_VERSION:-2.17.2}"
restart: always restart: always
depends_on: depends_on:
@@ -209,7 +209,7 @@ services:
PENPOT_REDIS_URI: redis://penpot-valkey/0 PENPOT_REDIS_URI: redis://penpot-valkey/0
penpot-postgres: penpot-postgres:
image: "postgres:15" image: "postgres:15.19-alpine"
restart: always restart: always
stop_signal: SIGINT stop_signal: SIGINT
@@ -233,7 +233,7 @@ services:
- POSTGRES_PASSWORD=penpot - POSTGRES_PASSWORD=penpot
penpot-valkey: penpot-valkey:
image: valkey/valkey:8.1 image: valkey/valkey:9.1.2
restart: always restart: always
healthcheck: healthcheck:
+1 -1
View File
@@ -1,6 +1,6 @@
services: services:
portainer: portainer:
image: portainer/portainer-ce:2.41.0 image: portainer/portainer-ce:2.45.0
container_name: portainer container_name: portainer
restart: always restart: always
volumes: volumes:
View File
Whitespace-only changes.
+2 -1
View File
@@ -10,7 +10,8 @@ spec:
- match: Host(`portainer.forust.xyz`) - match: Host(`portainer.forust.xyz`)
kind: Rule kind: Rule
middlewares: middlewares:
- name: "crowdsec-crowdsec-bouncer@kubernetescrd" - name: crowdsec-bouncer
namespace: crowdsec
services: services:
- name: portainer-service - name: portainer-service
port: 9000 port: 9000
+1 -1
View File
@@ -27,7 +27,7 @@ spec:
spec: spec:
containers: containers:
- name: portainer - name: portainer
image: portainer/portainer-ce:2.41.0 image: portainer/portainer-ce:2.45.0
ports: ports:
- containerPort: 9000 - containerPort: 9000
volumeMounts: volumeMounts:
+3 -3
View File
@@ -1,6 +1,6 @@
services: services:
grafana: grafana:
image: grafana/grafana:11.6.0 image: grafana/grafana:13.2.1
container_name: prometheus-grafana container_name: prometheus-grafana
restart: unless-stopped restart: unless-stopped
env_file: env_file:
@@ -30,7 +30,7 @@ services:
- proxy - proxy
prometheus: prometheus:
image: prom/prometheus:v3.2.1 image: prom/prometheus:v3.14.0
container_name: prometheus-prometheus container_name: prometheus-prometheus
restart: unless-stopped restart: unless-stopped
command: command:
@@ -44,7 +44,7 @@ services:
- proxy - proxy
alertmanager: alertmanager:
image: prom/alertmanager:v0.28.1 image: prom/alertmanager:v0.34.0
container_name: prometheus-alertmanager container_name: prometheus-alertmanager
restart: unless-stopped restart: unless-stopped
command: command:
Loaded 100 of 256 files, more files were not shown because too many files have changed in this diff. Show more