From 38be6c13f76e893cf47636257071b440dec0c5b2 Mon Sep 17 00:00:00 2001 From: Robert Günzler Date: Sun, 7 Sep 2025 17:32:13 +0200 Subject: initial commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Robert Günzler --- bundles/alpine-base/items.py | 11 + bundles/apk/items.py | 15 + bundles/apk/metadata.py | 9 + bundles/apt/items.py | 15 + bundles/apt/metadata.py | 7 + bundles/authelia/files/configuration.yml | 59 +++ bundles/authelia/files/configuration_oidc.yml | 39 ++ bundles/authelia/files/kube.yaml | 72 ++++ bundles/authelia/files/secret.yaml | 9 + bundles/authelia/files/users.yml | 19 + bundles/authelia/items.py | 29 ++ bundles/authelia/metadata.py | 13 + bundles/books/files/books.run | 6 + bundles/books/items.py | 15 + bundles/books/metadata.py | 6 + bundles/cal/files/kube.yaml | 115 ++++++ bundles/cal/files/radicale.conf | 7 + bundles/cal/files/secret.yaml | 9 + bundles/cal/items.py | 20 + bundles/cal/metadata.py | 11 + .../containers.conf.d/10-firewall-driver.conf | 2 + .../containers/files/etc/init.d/podman-healthcheck | 11 + .../files/etc/periodic/15min/podman-healthcheck | 4 + .../files/etc/periodic/weekly/podman-prune | 4 + .../templates/container/+service-log/consumer-for | 1 + .../container/+service-log/notification-fd | 1 + .../templates/container/+service-log/pipeline-name | 1 + .../etc/s6-rc/templates/container/+service-log/run | 10 + .../s6-rc/templates/container/+service-log/type | 1 + .../templates/container/+service-srv/producer-for | 1 + .../etc/s6-rc/templates/container/+service-srv/run | 23 ++ .../s6-rc/templates/container/+service-srv/type | 1 + bundles/containers/items.py | 63 ++++ bundles/containers/metadata.py | 8 + bundles/coredns/files/Corefile | 6 + bundles/coredns/files/zones/gzr.im | 21 ++ bundles/doyouhavewifi/files/Caddyfile | 46 +++ bundles/doyouhavewifi/files/doyouhavewifi.conf | 1 + bundles/doyouhavewifi/files/traefik.yaml | 18 + bundles/doyouhavewifi/items.py | 29 ++ bundles/doyouhavewifi/metadata.py | 5 + .../etc/fail2ban/action.d/telegram-webhook.conf | 24 ++ .../etc/fail2ban/filter.d/traefik-badbots.conf | 12 + .../files/etc/fail2ban/filter.d/traefik-spam.conf | 13 + .../fail2ban/files/etc/fail2ban/jail.d/sshd.conf | 5 + .../files/etc/fail2ban/jail.d/traefik.conf | 10 + bundles/fail2ban/files/etc/fail2ban/jail.local | 22 ++ bundles/fail2ban/items.py | 31 ++ bundles/fail2ban/metadata.py | 7 + bundles/firewall/.editorconfig | 4 + bundles/firewall/files/etc/nftables.d/00-basic.nft | 59 +++ .../firewall/files/etc/nftables.d/01-my-filter.nft | 18 + .../firewall/files/etc/nftables.d/10-tailscale.nft | 16 + bundles/firewall/files/etc/nftables.d/22-ssh.nft | 10 + bundles/firewall/files/etc/nftables.d/50-www.nft | 10 + .../firewall/files/etc/nftables.d/80-podman.nft | 15 + bundles/firewall/files/etc/nftables.nft | 21 ++ bundles/firewall/items.py | 43 +++ bundles/firewall/metadata.py | 7 + bundles/fstab/items.py | 12 + bundles/fstab/metadata.py | 11 + bundles/glance/files/config.yml | 133 +++++++ bundles/glance/files/glance.run | 4 + bundles/glance/files/hex.png | Bin 0 -> 128583 bytes bundles/glance/files/hex.svg | 411 +++++++++++++++++++++ bundles/glance/files/home-assistant.svg | 4 + bundles/glance/files/immich.svg | 29 ++ bundles/glance/files/jellyfin.svg | 24 ++ bundles/glance/files/miniflux.svg | 1 + bundles/glance/files/paperless.svg | 82 ++++ bundles/glance/files/traefik.yaml | 18 + bundles/glance/files/vaultwarden.svg | 74 ++++ bundles/glance/items.py | 42 +++ bundles/glance/metadata.py | 6 + bundles/go-away/files/go-away.run | 18 + bundles/go-away/files/traefik.yaml | 28 ++ bundles/go-away/items.py | 41 ++ bundles/go-away/metadata.py | 5 + bundles/golink/files/kube.yaml | 30 ++ bundles/golink/items.py | 7 + bundles/golink/metadata.py | 5 + bundles/gonca-me/files/gonca-me.conf | 1 + bundles/gonca-me/files/traefik.yaml | 18 + bundles/gonca-me/items.py | 20 + bundles/gonca-me/metadata.py | 5 + bundles/gzr-im-preview/files/gzr-im-preview.conf | 1 + bundles/gzr-im-preview/files/traefik.yaml | 20 + bundles/gzr-im-preview/items.py | 24 ++ bundles/gzr-im-preview/metadata.py | 5 + bundles/gzr-im/files/gzr-im.conf | 1 + bundles/gzr-im/files/traefik.yaml | 18 + bundles/gzr-im/items.py | 20 + bundles/gzr-im/metadata.py | 5 + bundles/hdidle/files/cron_job | 5 + bundles/hdidle/files/hdidle | 17 + bundles/hdidle/items.py | 16 + bundles/hdidle/metadata.py | 7 + bundles/immich/files/immich-api-tool | 49 +++ bundles/immich/files/immich.yaml | 27 ++ bundles/immich/files/kube.yaml | 104 ++++++ bundles/immich/files/secret.yaml | 12 + bundles/immich/items.py | 15 + bundles/immich/metadata.py | 17 + bundles/jellyfin/bcast.py | 20 + bundles/jellyfin/files/SSO-Auth.xml | 71 ++++ bundles/jellyfin/files/branding.xml | 17 + bundles/jellyfin/files/kube.yaml | 72 ++++ bundles/jellyfin/files/logging.json | 11 + bundles/jellyfin/files/system.xml | 202 ++++++++++ bundles/jellyfin/items.py | 33 ++ bundles/jellyfin/metadata.py | 16 + bundles/langtool/files/kube.yaml | 24 ++ bundles/langtool/items.py | 5 + bundles/langtool/metadata.py | 5 + bundles/lhost/files/Caddyfile | 45 +++ bundles/lhost/files/lhost-prune | 20 + bundles/lhost/files/lhost.conf | 1 + bundles/lhost/files/traefik.yaml | 18 + bundles/lhost/items.py | 24 ++ bundles/lhost/metadata.py | 5 + bundles/minecraft/README | 5 + bundles/minecraft/files/kube.yaml | 43 +++ bundles/minecraft/items.py | 7 + bundles/minecraft/metadata.py | 16 + bundles/miniflux/files/kube.yaml | 84 +++++ bundles/miniflux/files/secret.yaml | 10 + bundles/miniflux/items.py | 11 + bundles/miniflux/metadata.py | 13 + bundles/motd/items.py | 5 + bundles/nfs/files/exports | 5 + bundles/nfs/files/fstab | 17 + bundles/nfs/files/nfs.conf.d | 38 ++ bundles/nfs/items.py | 45 +++ bundles/nfs/metadata.py | 7 + bundles/node-exporter/files/kube.yaml | 63 ++++ bundles/node-exporter/items.py | 7 + bundles/node-exporter/metadata.py | 8 + bundles/paperless/README.md | 1 + bundles/paperless/files/kube.yaml | 95 +++++ bundles/paperless/files/secret.yaml | 10 + bundles/paperless/items.py | 13 + bundles/paperless/metadata.py | 11 + .../prometheus/files/console_libraries/menu.lib | 64 ++++ .../prometheus/files/console_libraries/prom.lib | 139 +++++++ bundles/prometheus/files/consoles/index.html | 82 ++++ bundles/prometheus/files/consoles/node-cpu.html | 60 +++ bundles/prometheus/files/consoles/node-disk.html | 78 ++++ .../prometheus/files/consoles/node-overview.html | 121 ++++++ .../files/consoles/prometheus-overview.html | 96 +++++ bundles/prometheus/files/kube.yaml | 48 +++ bundles/prometheus/files/prometheus.yml | 28 ++ bundles/prometheus/files/service.json | 12 + bundles/prometheus/items.py | 47 +++ bundles/prometheus/metadata.py | 5 + bundles/quad9-demo/files/quad9-demo.conf | 1 + bundles/quad9-demo/files/traefik.yaml | 20 + bundles/quad9-demo/items.py | 20 + bundles/quad9-demo/metadata.py | 5 + bundles/restic-backup/files/cron_job | 5 + bundles/restic-backup/files/excludes | 3 + bundles/restic-backup/files/includes | 3 + bundles/restic-backup/files/restic.conf | 4 + bundles/restic-backup/files/resticbackup | 36 ++ bundles/restic-backup/files/resticrestore | 29 ++ bundles/restic-backup/files/resticw | 21 ++ bundles/restic-backup/items.py | 62 ++++ bundles/restic-backup/metadata.py | 7 + bundles/s6/files/etc/conf.d/s6-rc-common | 8 + bundles/s6/files/etc/init.d/s6-rc-init | 56 +++ bundles/s6/files/etc/init.d/s6-rc-up | 37 ++ bundles/s6/files/etc/periodic/15min/s6-rc-metrics | 61 +++ bundles/s6/files/etc/s6-rc/README | 18 + bundles/s6/files/etc/s6-rc/services/default/type | 1 + bundles/s6/files/etc/s6-rc/system.conf | 3 + .../templates/service/+service-log/consumer-for | 1 + .../templates/service/+service-log/notification-fd | 1 + .../templates/service/+service-log/pipeline-name | 1 + .../etc/s6-rc/templates/service/+service-log/run | 10 + .../etc/s6-rc/templates/service/+service-log/type | 1 + .../templates/service/+service-srv/producer-for | 1 + .../etc/s6-rc/templates/service/+service-srv/run | 6 + .../etc/s6-rc/templates/service/+service-srv/type | 1 + bundles/s6/items.py | 62 ++++ bundles/s6/metadata.py | 9 + bundles/smb/files/kube.yaml | 46 +++ bundles/smb/files/smb.conf | 39 ++ bundles/smb/files/smb.nft | 10 + bundles/smb/files/users.conf | 3 + bundles/smb/items.py | 18 + bundles/smb/metadata.py | 5 + bundles/stash/files/kube.yaml | 58 +++ bundles/stash/items.py | 7 + bundles/stash/metadata.py | 5 + bundles/syncthing/files/traefik.yaml | 22 ++ bundles/syncthing/items.py | 44 +++ bundles/syncthing/metadata.py | 8 + bundles/tailscale/items.py | 39 ++ bundles/tailscale/metadata.py | 7 + bundles/tgnotify/files/tgnotify | 33 ++ bundles/tgnotify/files/tgnotify.conf | 2 + bundles/tgnotify/items.py | 17 + bundles/tgnotify/metadata.py | 7 + bundles/timelinize/files/kube.yaml | 116 ++++++ bundles/timelinize/items.py | 5 + bundles/timelinize/metadata.py | 16 + bundles/traefik/files/kube.yaml | 146 ++++++++ bundles/traefik/files/logrotate.conf | 10 + bundles/traefik/files/secret.yaml | 6 + bundles/traefik/files/tls-redirect.yaml | 6 + bundles/traefik/items.py | 15 + bundles/traefik/metadata.py | 8 + bundles/vast-base/items.py | 32 ++ bundles/vast-base/metadata.py | 24 ++ bundles/vaultwarden/files/kube.yaml | 41 ++ .../vaultwarden/files/user.vaultwarden.scss.hbs | 42 +++ bundles/vaultwarden/items.py | 10 + bundles/vaultwarden/metadata.py | 10 + .../s6-rc/templates/www/+service-log/consumer-for | 1 + .../templates/www/+service-log/notification-fd | 1 + .../s6-rc/templates/www/+service-log/pipeline-name | 1 + .../files/etc/s6-rc/templates/www/+service-log/run | 10 + .../etc/s6-rc/templates/www/+service-log/type | 1 + .../templates/www/+service-srv/notification-fd | 1 + .../s6-rc/templates/www/+service-srv/producer-for | 1 + .../files/etc/s6-rc/templates/www/+service-srv/run | 31 ++ .../etc/s6-rc/templates/www/+service-srv/type | 1 + bundles/www/items.py | 40 ++ bundles/www/metadata.py | 8 + 228 files changed, 5791 insertions(+) create mode 100644 bundles/alpine-base/items.py create mode 100644 bundles/apk/items.py create mode 100644 bundles/apk/metadata.py create mode 100644 bundles/apt/items.py create mode 100644 bundles/apt/metadata.py create mode 100644 bundles/authelia/files/configuration.yml create mode 100644 bundles/authelia/files/configuration_oidc.yml create mode 100644 bundles/authelia/files/kube.yaml create mode 100644 bundles/authelia/files/secret.yaml create mode 100644 bundles/authelia/files/users.yml create mode 100644 bundles/authelia/items.py create mode 100644 bundles/authelia/metadata.py create mode 100644 bundles/books/files/books.run create mode 100644 bundles/books/items.py create mode 100644 bundles/books/metadata.py create mode 100644 bundles/cal/files/kube.yaml create mode 100644 bundles/cal/files/radicale.conf create mode 100644 bundles/cal/files/secret.yaml create mode 100644 bundles/cal/items.py create mode 100644 bundles/cal/metadata.py create mode 100644 bundles/containers/files/etc/containers/containers.conf.d/10-firewall-driver.conf create mode 100644 bundles/containers/files/etc/init.d/podman-healthcheck create mode 100755 bundles/containers/files/etc/periodic/15min/podman-healthcheck create mode 100755 bundles/containers/files/etc/periodic/weekly/podman-prune create mode 100644 bundles/containers/files/etc/s6-rc/templates/container/+service-log/consumer-for create mode 100644 bundles/containers/files/etc/s6-rc/templates/container/+service-log/notification-fd create mode 100644 bundles/containers/files/etc/s6-rc/templates/container/+service-log/pipeline-name create mode 100644 bundles/containers/files/etc/s6-rc/templates/container/+service-log/run create mode 100644 bundles/containers/files/etc/s6-rc/templates/container/+service-log/type create mode 100644 bundles/containers/files/etc/s6-rc/templates/container/+service-srv/producer-for create mode 100755 bundles/containers/files/etc/s6-rc/templates/container/+service-srv/run create mode 100644 bundles/containers/files/etc/s6-rc/templates/container/+service-srv/type create mode 100644 bundles/containers/items.py create mode 100644 bundles/containers/metadata.py create mode 100644 bundles/coredns/files/Corefile create mode 100644 bundles/coredns/files/zones/gzr.im create mode 100644 bundles/doyouhavewifi/files/Caddyfile create mode 100644 bundles/doyouhavewifi/files/doyouhavewifi.conf create mode 100644 bundles/doyouhavewifi/files/traefik.yaml create mode 100644 bundles/doyouhavewifi/items.py create mode 100644 bundles/doyouhavewifi/metadata.py create mode 100644 bundles/fail2ban/files/etc/fail2ban/action.d/telegram-webhook.conf create mode 100644 bundles/fail2ban/files/etc/fail2ban/filter.d/traefik-badbots.conf create mode 100644 bundles/fail2ban/files/etc/fail2ban/filter.d/traefik-spam.conf create mode 100644 bundles/fail2ban/files/etc/fail2ban/jail.d/sshd.conf create mode 100644 bundles/fail2ban/files/etc/fail2ban/jail.d/traefik.conf create mode 100644 bundles/fail2ban/files/etc/fail2ban/jail.local create mode 100644 bundles/fail2ban/items.py create mode 100644 bundles/fail2ban/metadata.py create mode 100644 bundles/firewall/.editorconfig create mode 100644 bundles/firewall/files/etc/nftables.d/00-basic.nft create mode 100644 bundles/firewall/files/etc/nftables.d/01-my-filter.nft create mode 100644 bundles/firewall/files/etc/nftables.d/10-tailscale.nft create mode 100644 bundles/firewall/files/etc/nftables.d/22-ssh.nft create mode 100644 bundles/firewall/files/etc/nftables.d/50-www.nft create mode 100644 bundles/firewall/files/etc/nftables.d/80-podman.nft create mode 100644 bundles/firewall/files/etc/nftables.nft create mode 100644 bundles/firewall/items.py create mode 100644 bundles/firewall/metadata.py create mode 100644 bundles/fstab/items.py create mode 100644 bundles/fstab/metadata.py create mode 100644 bundles/glance/files/config.yml create mode 100755 bundles/glance/files/glance.run create mode 100644 bundles/glance/files/hex.png create mode 100644 bundles/glance/files/hex.svg create mode 100644 bundles/glance/files/home-assistant.svg create mode 100644 bundles/glance/files/immich.svg create mode 100644 bundles/glance/files/jellyfin.svg create mode 100644 bundles/glance/files/miniflux.svg create mode 100644 bundles/glance/files/paperless.svg create mode 100644 bundles/glance/files/traefik.yaml create mode 100644 bundles/glance/files/vaultwarden.svg create mode 100644 bundles/glance/items.py create mode 100644 bundles/glance/metadata.py create mode 100755 bundles/go-away/files/go-away.run create mode 100644 bundles/go-away/files/traefik.yaml create mode 100644 bundles/go-away/items.py create mode 100644 bundles/go-away/metadata.py create mode 100644 bundles/golink/files/kube.yaml create mode 100644 bundles/golink/items.py create mode 100644 bundles/golink/metadata.py create mode 100644 bundles/gonca-me/files/gonca-me.conf create mode 100644 bundles/gonca-me/files/traefik.yaml create mode 100644 bundles/gonca-me/items.py create mode 100644 bundles/gonca-me/metadata.py create mode 100644 bundles/gzr-im-preview/files/gzr-im-preview.conf create mode 100644 bundles/gzr-im-preview/files/traefik.yaml create mode 100644 bundles/gzr-im-preview/items.py create mode 100644 bundles/gzr-im-preview/metadata.py create mode 100644 bundles/gzr-im/files/gzr-im.conf create mode 100644 bundles/gzr-im/files/traefik.yaml create mode 100644 bundles/gzr-im/items.py create mode 100644 bundles/gzr-im/metadata.py create mode 100755 bundles/hdidle/files/cron_job create mode 100755 bundles/hdidle/files/hdidle create mode 100644 bundles/hdidle/items.py create mode 100644 bundles/hdidle/metadata.py create mode 100755 bundles/immich/files/immich-api-tool create mode 100644 bundles/immich/files/immich.yaml create mode 100644 bundles/immich/files/kube.yaml create mode 100644 bundles/immich/files/secret.yaml create mode 100644 bundles/immich/items.py create mode 100644 bundles/immich/metadata.py create mode 100644 bundles/jellyfin/bcast.py create mode 100644 bundles/jellyfin/files/SSO-Auth.xml create mode 100644 bundles/jellyfin/files/branding.xml create mode 100644 bundles/jellyfin/files/kube.yaml create mode 100644 bundles/jellyfin/files/logging.json create mode 100644 bundles/jellyfin/files/system.xml create mode 100644 bundles/jellyfin/items.py create mode 100644 bundles/jellyfin/metadata.py create mode 100644 bundles/langtool/files/kube.yaml create mode 100644 bundles/langtool/items.py create mode 100644 bundles/langtool/metadata.py create mode 100644 bundles/lhost/files/Caddyfile create mode 100755 bundles/lhost/files/lhost-prune create mode 100644 bundles/lhost/files/lhost.conf create mode 100644 bundles/lhost/files/traefik.yaml create mode 100644 bundles/lhost/items.py create mode 100644 bundles/lhost/metadata.py create mode 100644 bundles/minecraft/README create mode 100644 bundles/minecraft/files/kube.yaml create mode 100644 bundles/minecraft/items.py create mode 100644 bundles/minecraft/metadata.py create mode 100644 bundles/miniflux/files/kube.yaml create mode 100644 bundles/miniflux/files/secret.yaml create mode 100644 bundles/miniflux/items.py create mode 100644 bundles/miniflux/metadata.py create mode 100644 bundles/motd/items.py create mode 100644 bundles/nfs/files/exports create mode 100644 bundles/nfs/files/fstab create mode 100644 bundles/nfs/files/nfs.conf.d create mode 100644 bundles/nfs/items.py create mode 100644 bundles/nfs/metadata.py create mode 100644 bundles/node-exporter/files/kube.yaml create mode 100644 bundles/node-exporter/items.py create mode 100644 bundles/node-exporter/metadata.py create mode 100644 bundles/paperless/README.md create mode 100644 bundles/paperless/files/kube.yaml create mode 100644 bundles/paperless/files/secret.yaml create mode 100644 bundles/paperless/items.py create mode 100644 bundles/paperless/metadata.py create mode 100644 bundles/prometheus/files/console_libraries/menu.lib create mode 100644 bundles/prometheus/files/console_libraries/prom.lib create mode 100644 bundles/prometheus/files/consoles/index.html create mode 100644 bundles/prometheus/files/consoles/node-cpu.html create mode 100644 bundles/prometheus/files/consoles/node-disk.html create mode 100644 bundles/prometheus/files/consoles/node-overview.html create mode 100644 bundles/prometheus/files/consoles/prometheus-overview.html create mode 100644 bundles/prometheus/files/kube.yaml create mode 100644 bundles/prometheus/files/prometheus.yml create mode 100644 bundles/prometheus/files/service.json create mode 100644 bundles/prometheus/items.py create mode 100644 bundles/prometheus/metadata.py create mode 100644 bundles/quad9-demo/files/quad9-demo.conf create mode 100644 bundles/quad9-demo/files/traefik.yaml create mode 100644 bundles/quad9-demo/items.py create mode 100644 bundles/quad9-demo/metadata.py create mode 100644 bundles/restic-backup/files/cron_job create mode 100644 bundles/restic-backup/files/excludes create mode 100644 bundles/restic-backup/files/includes create mode 100644 bundles/restic-backup/files/restic.conf create mode 100644 bundles/restic-backup/files/resticbackup create mode 100644 bundles/restic-backup/files/resticrestore create mode 100644 bundles/restic-backup/files/resticw create mode 100644 bundles/restic-backup/items.py create mode 100644 bundles/restic-backup/metadata.py create mode 100644 bundles/s6/files/etc/conf.d/s6-rc-common create mode 100755 bundles/s6/files/etc/init.d/s6-rc-init create mode 100755 bundles/s6/files/etc/init.d/s6-rc-up create mode 100755 bundles/s6/files/etc/periodic/15min/s6-rc-metrics create mode 100644 bundles/s6/files/etc/s6-rc/README create mode 100644 bundles/s6/files/etc/s6-rc/services/default/type create mode 100644 bundles/s6/files/etc/s6-rc/system.conf create mode 100644 bundles/s6/files/etc/s6-rc/templates/service/+service-log/consumer-for create mode 100644 bundles/s6/files/etc/s6-rc/templates/service/+service-log/notification-fd create mode 100644 bundles/s6/files/etc/s6-rc/templates/service/+service-log/pipeline-name create mode 100644 bundles/s6/files/etc/s6-rc/templates/service/+service-log/run create mode 100644 bundles/s6/files/etc/s6-rc/templates/service/+service-log/type create mode 100644 bundles/s6/files/etc/s6-rc/templates/service/+service-srv/producer-for create mode 100755 bundles/s6/files/etc/s6-rc/templates/service/+service-srv/run create mode 100644 bundles/s6/files/etc/s6-rc/templates/service/+service-srv/type create mode 100644 bundles/s6/items.py create mode 100644 bundles/s6/metadata.py create mode 100644 bundles/smb/files/kube.yaml create mode 100644 bundles/smb/files/smb.conf create mode 100644 bundles/smb/files/smb.nft create mode 100644 bundles/smb/files/users.conf create mode 100644 bundles/smb/items.py create mode 100644 bundles/smb/metadata.py create mode 100644 bundles/stash/files/kube.yaml create mode 100644 bundles/stash/items.py create mode 100644 bundles/stash/metadata.py create mode 100644 bundles/syncthing/files/traefik.yaml create mode 100644 bundles/syncthing/items.py create mode 100644 bundles/syncthing/metadata.py create mode 100644 bundles/tailscale/items.py create mode 100644 bundles/tailscale/metadata.py create mode 100755 bundles/tgnotify/files/tgnotify create mode 100644 bundles/tgnotify/files/tgnotify.conf create mode 100644 bundles/tgnotify/items.py create mode 100644 bundles/tgnotify/metadata.py create mode 100644 bundles/timelinize/files/kube.yaml create mode 100644 bundles/timelinize/items.py create mode 100644 bundles/timelinize/metadata.py create mode 100644 bundles/traefik/files/kube.yaml create mode 100644 bundles/traefik/files/logrotate.conf create mode 100644 bundles/traefik/files/secret.yaml create mode 100644 bundles/traefik/files/tls-redirect.yaml create mode 100644 bundles/traefik/items.py create mode 100644 bundles/traefik/metadata.py create mode 100644 bundles/vast-base/items.py create mode 100644 bundles/vast-base/metadata.py create mode 100644 bundles/vaultwarden/files/kube.yaml create mode 100644 bundles/vaultwarden/files/user.vaultwarden.scss.hbs create mode 100644 bundles/vaultwarden/items.py create mode 100644 bundles/vaultwarden/metadata.py create mode 100644 bundles/www/files/etc/s6-rc/templates/www/+service-log/consumer-for create mode 100644 bundles/www/files/etc/s6-rc/templates/www/+service-log/notification-fd create mode 100644 bundles/www/files/etc/s6-rc/templates/www/+service-log/pipeline-name create mode 100644 bundles/www/files/etc/s6-rc/templates/www/+service-log/run create mode 100644 bundles/www/files/etc/s6-rc/templates/www/+service-log/type create mode 100644 bundles/www/files/etc/s6-rc/templates/www/+service-srv/notification-fd create mode 100644 bundles/www/files/etc/s6-rc/templates/www/+service-srv/producer-for create mode 100755 bundles/www/files/etc/s6-rc/templates/www/+service-srv/run create mode 100644 bundles/www/files/etc/s6-rc/templates/www/+service-srv/type create mode 100644 bundles/www/items.py create mode 100644 bundles/www/metadata.py (limited to 'bundles') diff --git a/bundles/alpine-base/items.py b/bundles/alpine-base/items.py new file mode 100644 index 0000000..459f348 --- /dev/null +++ b/bundles/alpine-base/items.py @@ -0,0 +1,11 @@ +if node.os != "alpine": + raise BundleError(f"{node.name}: OS {node.os} is not supported.") + +actions = { + "enable_etc_profiled_color_prompt": { + "command": "rename -v '.disabled' '' /etc/profile.d/color_prompt.sh.disabled", + "unless": "test -e /etc/profile.d/color_prompt.sh", + } +} + +# TODO: weekly apk update cronjob? diff --git a/bundles/apk/items.py b/bundles/apk/items.py new file mode 100644 index 0000000..1ee7195 --- /dev/null +++ b/bundles/apk/items.py @@ -0,0 +1,15 @@ +if node.os != "alpine": + raise BundleError(f"{node.name}: OS {node.os} is not supported.") + +actions = { + "apk_update": { + "command": "apk update", + "before": { + "pkg_apk:", + }, + "triggered": True, + }, +} + +for package, options in node.metadata.get("apk/packages", {}).items(): + pkg_apk[package] = options diff --git a/bundles/apk/metadata.py b/bundles/apk/metadata.py new file mode 100644 index 0000000..c7a1a18 --- /dev/null +++ b/bundles/apk/metadata.py @@ -0,0 +1,9 @@ +defaults = { + "apk": { + "packages": { + "file": {}, # support bw file checks + "doas": {}, + "curl": {}, + }, + }, +} diff --git a/bundles/apt/items.py b/bundles/apt/items.py new file mode 100644 index 0000000..9792330 --- /dev/null +++ b/bundles/apt/items.py @@ -0,0 +1,15 @@ +if node.os != "debian": + raise BundleError(f"{node.name}: OS {node.os} is not supported.") + +actions = { + "apt_update": { + "command": "apt-get update", + "before": { + "pkg_apt:", + }, + "triggered": True, + }, +} + +for package, options in node.metadata.get("apt/packages", {}).items(): + pkg_apt[package] = options diff --git a/bundles/apt/metadata.py b/bundles/apt/metadata.py new file mode 100644 index 0000000..f14b955 --- /dev/null +++ b/bundles/apt/metadata.py @@ -0,0 +1,7 @@ +defaults = { + "apt": { + "packages": { + "file": {}, # support bw file checks + }, + }, +} diff --git a/bundles/authelia/files/configuration.yml b/bundles/authelia/files/configuration.yml new file mode 100644 index 0000000..39cf40f --- /dev/null +++ b/bundles/authelia/files/configuration.yml @@ -0,0 +1,59 @@ + +log: + level: debug + +telemetry: + metrics: + enabled: true + +access_control: + default_policy: 'deny' + rules: + # bypass api endpoints + - domain: 'rss.gzr.im' + resources: ['^/v1/'] + policy: 'bypass' + - domain: 'paper.gzr.im' + resources: ['^/api/'] + policy: 'bypass' + # 1factor is fine + - domain: + - 'rss.gzr.im' + - 'paper.gzr.im' + - 'tv.gzr.im' + - 'photos.gzr.im' + policy: 'one_factor' + # 2factor is a sane default + - domain: + - 'gzr.im' + - '*.gzr.im' + policy: 'two_factor' + +# regulation: +# max_retries: 3 +# find_time: '2 minutes' +# ban_time: '5 minutes' + +session: + cookies: + - name: 'authelia_session' + domain: 'gzr.im' # Should match whatever your root protected domain is + authelia_url: 'https://login.gzr.im' + # relaxed expiry and inactivity + expiration: '3w' + inactivity: '3w' + # blank auth should go back to account page + default_redirection_url: 'https://login.gzr.im/authenticated' + +notifier: + filesystem: + filename: /notifier/message.txt + +totp: + issuer: 'login.gzr.im' + +webauthn: + enable_passkey_login: true + +duo_api: + disable: true diff --git a/bundles/authelia/files/configuration_oidc.yml b/bundles/authelia/files/configuration_oidc.yml new file mode 100644 index 0000000..34280d3 --- /dev/null +++ b/bundles/authelia/files/configuration_oidc.yml @@ -0,0 +1,39 @@ + +identity_providers: + oidc: + jwks: + - key: |- + ${repo.libs.util.indent(repo.vault.decrypt("encrypt$gAAAAABnOUxLHGPI8B_65SL7obl2v4_3E8J1i271tq-JfX2ohlz8PLydbQRI-wJlg-4kqm3Bp1jkH5BXa92KP9ZEseqadX7uLWU-9cVKj8WAUNlC3VoBFubNRb_crVKOavI3zCJd_9--CpTVCdR062EaR8AJUeiTLkTx8DU2CcaMFKLQ1nuUA4tpEk8dyVhvwtlbm8ItjCdvfl-CTBVE3LoczY6E_I35bKEcDvT_fS1Y6fDFV2S6PxRQSWLByfwsVFMkkHza1gNqvejaNggHxI-hjzlCHPdjlgpRBhd9Sd-Zs3u8PU5GhShhc2SlRv640QBzfrAcxckMDlYUcgvkcp38AiDF9rKgQiUfYSoHIg8dJJMP8pYHGYSIdCZQY7tw5Pdge_xVAEM3KIiIh6IPRm3iJ8ZG4HFq0Xjh42T86BCcYUsY_RGBBKaygf4UvDotlb3Np5v9mYc7jun8Z-DrGmEt6a5fHw9JlmFpbDaqp-pLzv9aq9TD9yGB-hisLTJbEjp3VzOc5GPhROH5qYxbeHqoisGeETNI575tOnS_MN-skJlWU2Uaglep1wH4gWyWRQqA-Xb5d2LZYenMldJ7DJhFCO8XgAcFgQrdma1MlmumEUmQYuM0IeOhq862rnO1UgQQ_gXOVyT4yYUSK3C0-s28slkN3b82tyyl7YzNKQpyUHGTIsKlDIpntX_TTy78wBJLjmF8C8RphZCixOFgrEvt8puBDvBLlJDkq4xgJTkExYsTsZaG-TJxXYtIR-yfWgTiMKnflFYK6um-JsaaIVfeKtcRdVao70V9vRaDER5DWE1XCCXWQru0BBMBbv_qpDeHYrjgZrkEhZZeXgveS-2f5PMlFFRhTkXRj-8qe9cO3qBsoZOBxLCCs_-ueyNH7jqw8_Yn8H-qrt5KI8MuKXTqwjQpbeW2JXiqusiqrVQElx3efNoCohdXtvSyTD7D-UriPij85he8sDJv_YouScHb3ItGbJrv0mPiAUO0iX_XA0I6Ck2jETlc6brzzmyKc23pdj-clkWZENouaiitoiYKECIlergumMmId0WjcMiXpA0Jwe8rfPT4S2QiemUsiM1u5SMkI6eSL8uQTUr_kavlUFV3r3yZqeQk1SpGT2hB_iMkqcNzliwk6J6n_ohqEzC8IP1yM4EzFKh2P2Q1n_Ro2MZsk7wjjLdm32895b_0o3FF-4pA4NSm0UZ-kvibebn-8TVhv2eRn-DsWgtzXDxWtAwBUtHKtQX6MZQVuswTZTSSuh6piQV4AnKaGpr7Gt1G0-VUG8QI9uuc-hBdwuZbS-q_d19UGabJxAw1Ssh3UOA__Bfhc9_2TZ2a6VKfzXih1-mxFQfA0EQJ4E4cktFfv6YEOlFsJ6ayfaCEJM4UhmuArKWLidaOnRVgO3qn-GG-p-q294O4Z1GSD8gddVrfdOymHRBjPbVMRvvQCUzvEdrt_oAqUWhG5ZenS6jF2aLZtDMVu-E87GKYKEDFSwybMRlEfiAS6GEySUB9MEGqrskf-u_esVv2U-luDV1GBhXi08n8RsLjOScEc59iZMC1CEFM0tG1kAPIE-57XK-Ja0tw3TofMXa6xauL8CnFMqYhdPTwUHeTwv7LM9n3kFksjAFcYAp8TeI-NPktswjFBsRaBc1-55BUJfhzKzL-DPJLHI_O7H9GhaSKlO0HGKcR7iUayR_GHfhdkAEZIdd5qXKOUIvd5o4n1klXrN1svBpRODlN3bmC6t9EmFYTSGSZNfNqrRrS7JNBioM0INuVQyqFXBqeavlTvSStEAaJxtCegt5bsqWNpuvQA06b7xyTcqiaQ8CaB9RzOx-xI7Vii0y-1gTjq35_rpndvNqVZC_hpPpTt-4ZQVJAmzYPZ6TIsiBcUTnOc1fRUEV1PtVJm0KhHWV_GNrHdkiHsmNtb759gKHo16-k2eyvOTUEXNwMRgQ3_U9PV2qXxoiAfiKFbgSDIy4qSdhsm2C4VREmlspkNEMgy6JB2QGygzrOs4uOS1p4lcbKvmhVllqqLyQ9nW_FkjkeibVQHvlKGX7V-NoFlWeyJ15XOupRb-3Wh8ibjOMD8IlX9vIhTJSJh54fy9DbrCo-UR8BIpP7JT0FeTKcbmjtmS7TuyRO8jpSqdiRys_rT-Ebgc-sq77uLpNBN1YBZU0VB848tSKM30Nl2wl-o9NBVh8tjGF8NsJyCoF0lR4w1rvVURNPj7dKsn6w_Qz4QKrTtUIo8jYkBy_VwbySJz6aN2CUM66_2h6_wxwopGaCYV18p3lhBe7u5WwqrCJ563b2VW7NBE5MD1AZbOCKmOWBSdnZud7Ee2U6nNr9jeAyDTCaut4NSS-ecDqnrDYxp9SSXVY="), " "*10, "not_first")} + + clients: + - client_id: jellyfin + client_name: 'Jellyfin' + client_secret: '${repo.libs.util.hash_bcrypt(repo.vault.cmd(f"rbw get authelia/client/jellyfin"), repo.vault.decrypt("encrypt$gAAAAABnOVCVoszwYPX_uTEIrMSyX94T_Mi8965InBlhKjkNBHMnN96ihiIt4yi8uYRl9DM70aPIwzXm2XVa_AXEEuIzbMJHBum6LTVDDE9CUYUErSFskMw="))}' + authorization_policy: one_factor + redirect_uris: + - https://tv.gzr.im/sso/OID/redirect/authelia + - https://tv.gzr.im/sso/OID/r/authelia + token_endpoint_auth_method: client_secret_post + + - client_id: paperless + client_name: 'Paperless-ngx' + client_secret: '${repo.libs.util.hash_bcrypt(repo.vault.cmd(f"rbw get authelia/client/paperless"), repo.vault.decrypt("encrypt$gAAAAABnOVCVoszwYPX_uTEIrMSyX94T_Mi8965InBlhKjkNBHMnN96ihiIt4yi8uYRl9DM70aPIwzXm2XVa_AXEEuIzbMJHBum6LTVDDE9CUYUErSFskMw="))}' + pkce_challenge_method: 'S256' + authorization_policy: one_factor + # consent_mode: implicit + redirect_uris: + - https://paper.gzr.im/accounts/oidc/authelia/login/callback/ + + - client_id: immich + client_name: 'Immich' + client_secret: '${repo.libs.util.hash_bcrypt(repo.vault.password_for("authelia_client_secret_immich"), repo.vault.decrypt("encrypt$gAAAAABnOVCVoszwYPX_uTEIrMSyX94T_Mi8965InBlhKjkNBHMnN96ihiIt4yi8uYRl9DM70aPIwzXm2XVa_AXEEuIzbMJHBum6LTVDDE9CUYUErSFskMw="))}' + authorization_policy: one_factor + token_endpoint_auth_method: client_secret_post + redirect_uris: + - https://photos.gzr.im/auth/login + - https://photos.gzr.im/user-settings + - https://photos.gzr.im/api/oauth/mobile-redirect + - app.immich:///oauth-callback # required since immich v1.113.0 + + # consent_mode: pre-configured + # pre_configured_consent_duration: 1w diff --git a/bundles/authelia/files/kube.yaml b/bundles/authelia/files/kube.yaml new file mode 100644 index 0000000..cda7d2c --- /dev/null +++ b/bundles/authelia/files/kube.yaml @@ -0,0 +1,72 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: authelia +data: + X_AUTHELIA_CONFIG: /config/configuration.yml,/config/configuration_oidc.yml + X_AUTHELIA_CONFIG_FILTERS: template + AUTHELIA_AUTHENTICATION_BACKEND_FILE_PATH: /config/users.yml + AUTHELIA_STORAGE_LOCAL_PATH: /data/db.sqlite3 +--- +apiVersion: v1 +kind: Pod +metadata: + name: authelia + labels: + traefik.enable: true + traefik.http.routers.authelia.tls: true + traefik.http.routers.authelia.tls.certresolver: le + traefik.http.routers.authelia.entrypoints: http,https + traefik.http.routers.authelia.rule: Host(`login.gzr.im`) + traefik.http.routers.authelia.service: authelia + traefik.http.services.authelia.loadbalancer.server.port: 9091 + # metrics + traefik.http.routers.authelia-metrics.entrypoints: metrics + traefik.http.routers.authelia-metrics.rule: Path(`/metrics/authelia`) + traefik.http.routers.authelia-metrics.service: authelia-metrics + traefik.http.routers.authelia-metrics.middlewares: replacepath-metrics + traefik.http.services.authelia-metrics.loadbalancer.server.port: 9959 +spec: + restartPolicy: Never + dnsPolicy: Default + imagePullPolicy: Always + containers: + - name: authelia + image: ghcr.io/authelia/authelia:4.39.4 + envFrom: + - configMapRef: + name: authelia + - secretRef: + name: authelia + ports: + - containerPort: 9091 + protocol: TCP + - containerPort: 9959 + protocol: TCP + volumeMounts: + - name: config + mountPath: /config + - name: data + mountPath: /data + - name: notifier + mountPath: /notifier + livenessProbe: null + resources: + requests: + cpu: 50m + memory: 30Mi + limits: + cpu: 100m + memory: 250Mi + + volumes: + - name: config + hostPath: + path: /var/lib/authelia/config + type: DirectoryOrCreate + - name: data + hostPath: + path: /var/lib/authelia/data + type: DirectoryOrCreate + - name: notifier # TODO + emptyDir: {} diff --git a/bundles/authelia/files/secret.yaml b/bundles/authelia/files/secret.yaml new file mode 100644 index 0000000..aef62c7 --- /dev/null +++ b/bundles/authelia/files/secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: authelia +data: + AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_SECRET: ${repo.vault.password_for("authelia_jwt").b64encode().value} + AUTHELIA_SESSION_SECRET: ${repo.vault.password_for("authelia_session_sec").b64encode().value} + AUTHELIA_STORAGE_ENCRYPTION_KEY: ${repo.vault.password_for("authelia_storage_encryption_key").b64encode().value} + AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET: ${repo.vault.password_for("authelia_oidc_hmac").b64encode().value} diff --git a/bundles/authelia/files/users.yml b/bundles/authelia/files/users.yml new file mode 100644 index 0000000..5788d6b --- /dev/null +++ b/bundles/authelia/files/users.yml @@ -0,0 +1,19 @@ +users: + robert: + disabled: false + displayname: "Robert" + password: '${repo.libs.util.hash_bcrypt(repo.vault.cmd(f"rbw get authelia/robert/pw"), repo.vault.decrypt("encrypt$gAAAAABnOVCVoszwYPX_uTEIrMSyX94T_Mi8965InBlhKjkNBHMnN96ihiIt4yi8uYRl9DM70aPIwzXm2XVa_AXEEuIzbMJHBum6LTVDDE9CUYUErSFskMw="))}' + email: robert@gnzler.de + groups: + - admins + - jellyfin + - jellyfin-anime + - jellyfin-misc + + gonca: + disabled: false + displayname: "Gonca" + password: '${repo.libs.util.hash_bcrypt(repo.vault.cmd(f"rbw get authelia/gonca/pw"), repo.vault.decrypt("encrypt$gAAAAABnOVCVoszwYPX_uTEIrMSyX94T_Mi8965InBlhKjkNBHMnN96ihiIt4yi8uYRl9DM70aPIwzXm2XVa_AXEEuIzbMJHBum6LTVDDE9CUYUErSFskMw="))}' + email: hi@gonca.me + groups: + - jellyfin diff --git a/bundles/authelia/items.py b/bundles/authelia/items.py new file mode 100644 index 0000000..0ac3a71 --- /dev/null +++ b/bundles/authelia/items.py @@ -0,0 +1,29 @@ +files = { + "/var/lib/authelia/config/configuration.yml": { + "content_type": "text", + "mode": "0600", + "triggers": {"svc_s6rc:authelia:restart"}, + }, + "/var/lib/authelia/config/configuration_oidc.yml": { + "content_type": "mako", + "mode": "0600", + "triggers": {"svc_s6rc:authelia:restart"}, + }, + "/var/lib/authelia/config/users.yml": { + "content_type": "mako", + "mode": "0600", + "triggers": {"svc_s6rc:authelia:restart"}, + }, + + "/etc/deployments/authelia/kube.yaml": { + "content_type": "mako", + "source": "kube.yaml", + "triggers": {"svc_s6rc:authelia:restart"}, + }, + "/etc/deployments/authelia/secret.yaml": { + "content_type": "mako", + "source": "secret.yaml", + "mode": "0600", + "triggers": {"svc_s6rc:authelia:restart"}, + }, +} diff --git a/bundles/authelia/metadata.py b/bundles/authelia/metadata.py new file mode 100644 index 0000000..294e295 --- /dev/null +++ b/bundles/authelia/metadata.py @@ -0,0 +1,13 @@ +defaults = { + "backup": { + "includes": { + "/var/lib/authelia/data", + } + }, + "containers": { + "authelia": {}, + }, + "metrics": { + "authelia": {}, + }, +} diff --git a/bundles/books/files/books.run b/bundles/books/files/books.run new file mode 100644 index 0000000..c87df82 --- /dev/null +++ b/bundles/books/files/books.run @@ -0,0 +1,6 @@ +#!/bin/sh +set -eu + +cd /var/lib/books || exit 1 + +exec /usr/bin/books -debug -addr :20103 -root . diff --git a/bundles/books/items.py b/bundles/books/items.py new file mode 100644 index 0000000..0854717 --- /dev/null +++ b/bundles/books/items.py @@ -0,0 +1,15 @@ + +directories = { + "/var/lib/books": {}, +} + +files = { + "/etc/service/books.run": {"mode": "0755"}, +} + +svc_s6rc = { + "books": { + "bundle": "services", + "generate": "service", + } +} diff --git a/bundles/books/metadata.py b/bundles/books/metadata.py new file mode 100644 index 0000000..9b16788 --- /dev/null +++ b/bundles/books/metadata.py @@ -0,0 +1,6 @@ +defaults = { + "apk": { + "packages": { + }, + }, +} diff --git a/bundles/cal/files/kube.yaml b/bundles/cal/files/kube.yaml new file mode 100644 index 0000000..eece518 --- /dev/null +++ b/bundles/cal/files/kube.yaml @@ -0,0 +1,115 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: cal +data: + NEXTAUTH_URL: https://cal.gzr.im/web/ + NEXT_PUBLIC_APP_URL: https://cal.gzr.im/web/ + NEXT_PUBLIC_SITE_URL: https://cal.gzr.im/web/ + NEXT_PUBLIC_ENABLE_SAAS_FEATURES: false +--- +apiVersion: v1 +kind: Pod +metadata: + name: cal + labels: + traefik.enable: true + # radicale + traefik.http.routers.radicale.tls: true + traefik.http.routers.radicale.tls.certresolver: le + traefik.http.routers.radicale.entrypoints: http,https + traefik.http.routers.radicale.rule: Host(`cal.gzr.im`) && PathPrefix(`/dav`) + traefik.http.middlewares.radicale-headers.headers.customrequestheaders.X-Script-Name: /dav + traefik.http.middlewares.radicale-stripprefix.stripprefix.prefixes: /dav + traefik.http.routers.radicale.middlewares: radicale-stripprefix@docker + traefik.http.routers.radicale.service: radicale + traefik.http.services.radicale.loadbalancer.server.port: 5232 + # fluidcal + traefik.http.routers.fluidcal.tls: true + traefik.http.routers.fluidcal.tls.certresolver: le + traefik.http.routers.fluidcal.entrypoints: http,https + traefik.http.routers.fluidcal.rule: Host(`cal.gzr.im`) && PathPrefix(`/`) + traefik.http.routers.fluidcal.service: fluidcal + traefik.http.services.fluidcal.loadbalancer.server.port: 3000 + # well-known + traefik.http.middlewares.wellknowncarddav-redirect.redirectregex.regex: ^https://cal.gzr.im/.well-known/carddav + traefik.http.middlewares.wellknowncarddav-redirect.redirectregex.replacement: https://cal.gzr.im/dav/ + traefik.http.middlewares.wellknowncaldav-redirect.redirectregex.regex: ^https://cal.gzr.im/.well-known/caldav + traefik.http.middlewares.wellknowncaldav-redirect.redirectregex.replacement: https://cal.gzr.im/dav/ + traefik.http.routers.fluidcal.middlewares: wellknowncarddav-redirect,wellknowncaldav-redirect +spec: + restartPolicy: Never + dnsPolicy: Default + containers: + - name: radicale + image: index.docker.io/grepular/radicale:3.5 + volumeMounts: + - name: radicale-config + mountPath: /etc/radicale + readOnly: true + - name: radicale-data + mountPath: /var/lib/radicale + ports: + - containerPort: 5232 + protocol: TCP + # livenessProbe: + # httpGet: + # path: / + # port: 5232 + resources: + requests: + cpu: 10m + memory: 25Mi + limits: + cpu: 100m + memory: 128Mi + + # - name: fluidcal + # image: index.docker.io/eibrahim/fluid-calendar:1.4 + # ports: + # - containerPort: 3000 + # protocol: TCP + # envFrom: + # - configMapRef: + # name: cal + # - secretRef: + # name: cal + # livenessProbe: + # httpGet: + # path: / + # port: 3000 + # # resources: + # # limits: + # # cpu: 100m + # # memory: 128Mi + # + # - name: postgres + # image: docker.io/library/postgres:15-alpine + # volumeMounts: + # - name: pg-data + # mountPath: /var/lib/postgresql/data + # envFrom: + # - secretRef: + # name: cal + # livenessProbe: + # exec: + # command: ["pg_isready", "-U", "cal"] + # initialDelaySeconds: 5 + # # resources: + # # limits: + # # cpu: 100m + # # memory: 128Mi + + volumes: + - name: radicale-config + hostPath: + path: /etc/radicale + type: DirectoryOrCreate + - name: radicale-data + hostPath: + path: /var/lib/radicale + type: DirectoryOrCreate + - name: pg-data + hostPath: + path: /var/lib/fluidcal + type: DirectoryOrCreate diff --git a/bundles/cal/files/radicale.conf b/bundles/cal/files/radicale.conf new file mode 100644 index 0000000..e5933ee --- /dev/null +++ b/bundles/cal/files/radicale.conf @@ -0,0 +1,7 @@ +[server] +# Bind all addresses +hosts = 0.0.0.0:5232, [::]:5232 +script_name = /dav + +[auth] +type = none diff --git a/bundles/cal/files/secret.yaml b/bundles/cal/files/secret.yaml new file mode 100644 index 0000000..ddf91fe --- /dev/null +++ b/bundles/cal/files/secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: cal +data: + NEXTAUTH_SECRET: ${repo.vault.password_for("cal_fluidcalendar_secret").b64encode().value} + POSTGRES_USER: ${repo.libs.util.base64("cal")} + POSTGRES_PASSWORD: ${repo.vault.password_for("cal_db_pw").b64encode().value} + DATABASE_URL: ${repo.libs.util.base64(f"postgresql://cal:{repo.vault.password_for("cal_db_pw")}@localhost/fluid_calendar")} diff --git a/bundles/cal/items.py b/bundles/cal/items.py new file mode 100644 index 0000000..5836213 --- /dev/null +++ b/bundles/cal/items.py @@ -0,0 +1,20 @@ +directories = { + "/var/lib/radicale/collections": { + "owner": "1000", + "group": "1000", + }, +} +files = { + "/etc/deployments/cal/kube.yaml": { + "triggers": {"svc_s6rc:cal:restart"}, + }, + "/etc/deployments/cal/secret.yaml": { + "content_type": "mako", + "mode": "0600", + "triggers": {"svc_s6rc:cal:restart"}, + }, + "/etc/radicale/config": { + "source": "radicale.conf", + "triggers": {"svc_s6rc:cal:restart"}, + }, +} diff --git a/bundles/cal/metadata.py b/bundles/cal/metadata.py new file mode 100644 index 0000000..7021b95 --- /dev/null +++ b/bundles/cal/metadata.py @@ -0,0 +1,11 @@ +defaults = { + "backup": { + "includes": { + "/var/lib/radicale", + # "/var/lib/fluidcal", + } + }, + "containers": { + "cal": {}, + }, +} diff --git a/bundles/containers/files/etc/containers/containers.conf.d/10-firewall-driver.conf b/bundles/containers/files/etc/containers/containers.conf.d/10-firewall-driver.conf new file mode 100644 index 0000000..be33d99 --- /dev/null +++ b/bundles/containers/files/etc/containers/containers.conf.d/10-firewall-driver.conf @@ -0,0 +1,2 @@ +[network] +firewall_driver = "nftables" diff --git a/bundles/containers/files/etc/init.d/podman-healthcheck b/bundles/containers/files/etc/init.d/podman-healthcheck new file mode 100644 index 0000000..8e12fa4 --- /dev/null +++ b/bundles/containers/files/etc/init.d/podman-healthcheck @@ -0,0 +1,11 @@ +#!/sbin/openrc-run + +description="Run podman healthchecks when containers start." + +supervisor=supervise-daemon +command="/bin/podman-healthcheckd" + +depend() +{ + need localmount +} diff --git a/bundles/containers/files/etc/periodic/15min/podman-healthcheck b/bundles/containers/files/etc/periodic/15min/podman-healthcheck new file mode 100755 index 0000000..7aa6d41 --- /dev/null +++ b/bundles/containers/files/etc/periodic/15min/podman-healthcheck @@ -0,0 +1,4 @@ +#!/bin/sh +set -eu +/bin/podman-healthcheck >/dev/null 2>&1 +exit 0 diff --git a/bundles/containers/files/etc/periodic/weekly/podman-prune b/bundles/containers/files/etc/periodic/weekly/podman-prune new file mode 100755 index 0000000..5715b95 --- /dev/null +++ b/bundles/containers/files/etc/periodic/weekly/podman-prune @@ -0,0 +1,4 @@ +#!/bin/sh +set -eu +/usr/bin/podman system prune --force --all --volumes >/dev/null 2>&1 +exit 0 diff --git a/bundles/containers/files/etc/s6-rc/templates/container/+service-log/consumer-for b/bundles/containers/files/etc/s6-rc/templates/container/+service-log/consumer-for new file mode 100644 index 0000000..83c1762 --- /dev/null +++ b/bundles/containers/files/etc/s6-rc/templates/container/+service-log/consumer-for @@ -0,0 +1 @@ ++service-srv diff --git a/bundles/containers/files/etc/s6-rc/templates/container/+service-log/notification-fd b/bundles/containers/files/etc/s6-rc/templates/container/+service-log/notification-fd new file mode 100644 index 0000000..00750ed --- /dev/null +++ b/bundles/containers/files/etc/s6-rc/templates/container/+service-log/notification-fd @@ -0,0 +1 @@ +3 diff --git a/bundles/containers/files/etc/s6-rc/templates/container/+service-log/pipeline-name b/bundles/containers/files/etc/s6-rc/templates/container/+service-log/pipeline-name new file mode 100644 index 0000000..0db7d83 --- /dev/null +++ b/bundles/containers/files/etc/s6-rc/templates/container/+service-log/pipeline-name @@ -0,0 +1 @@ ++service diff --git a/bundles/containers/files/etc/s6-rc/templates/container/+service-log/run b/bundles/containers/files/etc/s6-rc/templates/container/+service-log/run new file mode 100644 index 0000000..d558f3c --- /dev/null +++ b/bundles/containers/files/etc/s6-rc/templates/container/+service-log/run @@ -0,0 +1,10 @@ +#!/bin/execlineb -P + +envfile -I /etc/s6-rc/system.conf +importas -sCuD "/run/log" s6_log_dir s6_log_dir + +envfile -I /etc/s6-rc/+service.conf +importas -sCuD "n3 s2000000 T" s6_log_directives s6_log_directives + +foreground { install -d ${s6_log_dir}/+service } +exec -c s6-log -d3 -b -- ${s6_log_directives} ${s6_log_dir}/+service diff --git a/bundles/containers/files/etc/s6-rc/templates/container/+service-log/type b/bundles/containers/files/etc/s6-rc/templates/container/+service-log/type new file mode 100644 index 0000000..5883cff --- /dev/null +++ b/bundles/containers/files/etc/s6-rc/templates/container/+service-log/type @@ -0,0 +1 @@ +longrun diff --git a/bundles/containers/files/etc/s6-rc/templates/container/+service-srv/producer-for b/bundles/containers/files/etc/s6-rc/templates/container/+service-srv/producer-for new file mode 100644 index 0000000..cba6eea --- /dev/null +++ b/bundles/containers/files/etc/s6-rc/templates/container/+service-srv/producer-for @@ -0,0 +1 @@ ++service-log diff --git a/bundles/containers/files/etc/s6-rc/templates/container/+service-srv/run b/bundles/containers/files/etc/s6-rc/templates/container/+service-srv/run new file mode 100755 index 0000000..3ca06e2 --- /dev/null +++ b/bundles/containers/files/etc/s6-rc/templates/container/+service-srv/run @@ -0,0 +1,23 @@ +#!/bin/execlineb -P + +# load location of deployments from system config +envfile /etc/s6-rc/system.conf +importas -iu deployments_dir deployments_dir + +# route all logs to stdout +fdmove -c 2 1 + +ifthenelse { eltest -e ${deployments_dir}/+service/secret.yaml } +{ + foreground { podman secret rm -i +service } + foreground { podman secret create +service ${deployments_dir}/+service/secret.yaml } +} +{ + # noop +} + +exec podman play kube \ + --log-driver=passthrough \ + --replace \ + --wait \ + ${deployments_dir}/+service/kube.yaml diff --git a/bundles/containers/files/etc/s6-rc/templates/container/+service-srv/type b/bundles/containers/files/etc/s6-rc/templates/container/+service-srv/type new file mode 100644 index 0000000..5883cff --- /dev/null +++ b/bundles/containers/files/etc/s6-rc/templates/container/+service-srv/type @@ -0,0 +1 @@ +longrun diff --git a/bundles/containers/items.py b/bundles/containers/items.py new file mode 100644 index 0000000..eb28c44 --- /dev/null +++ b/bundles/containers/items.py @@ -0,0 +1,63 @@ +if node.os != "alpine": + raise BundleError(f"{node.name}: OS {node.os} is not supported.") + +from os.path import join + +# TODO: healthcheck metrics? +svc_openrc = { + "podman-healthcheck": { + "runlevel": "default", + "enabled": True, + "needs": {"file:/etc/init.d/podman-healthcheck"}, + }, +} + +files = { + "/etc/containers/containers.conf.d/10-firewall-driver.conf": { + "needs": { + "bundle:firewall", + }, + }, + + "/etc/s6-rc/templates/container/+service-srv/run": { "mode": "0755" }, + "/etc/s6-rc/templates/container/+service-log/run": { "mode": "0755" }, + "/etc/s6-rc/services/default/contents.d/containers": { + "content_type": "text", + "content": "", + "triggers": { + "svc_openrc:s6-rc-up:restart", + }, + }, + "/etc/s6-rc/services/containers/type": { + "content_type": "text", + "content": "bundle", + "triggers": { + "svc_openrc:s6-rc-up:restart", + }, + }, + + "/bin/podman-healthcheck": { "mode": "0755" }, + "/etc/periodic/15min/podman-healthcheck": { "mode": "0755" }, + "/etc/periodic/weekly/podman-prune": { "mode": "0755" }, + "/bin/podman-healthcheckd": { "mode": "0755" }, + "/etc/init.d/podman-healthcheck": { "mode": "0755" }, +} + +repo.libs.gen.add_files_recursive( + files, + join(repo.path, "bundles", "containers", "files"), +) + +directories = { + "/etc/s6-rc/services/containers/contents.d": {} +} + +for container, _ in node.metadata.get("containers", {}).items(): + svc_s6rc[container] = { + "bundle": "containers", + "generate": "container", + "triggers": { + "svc_openrc:s6-rc-init:reload", + "svc_openrc:s6-rc-up:reload", + }, + } diff --git a/bundles/containers/metadata.py b/bundles/containers/metadata.py new file mode 100644 index 0000000..dc5832c --- /dev/null +++ b/bundles/containers/metadata.py @@ -0,0 +1,8 @@ +defaults = { + "apk": { + "packages": { + "crun": {}, + "podman": {}, + }, + }, +} diff --git a/bundles/coredns/files/Corefile b/bundles/coredns/files/Corefile new file mode 100644 index 0000000..5e4ec4c --- /dev/null +++ b/bundles/coredns/files/Corefile @@ -0,0 +1,6 @@ +.:65353 { + file zones/gzr.im gzr.im + log + errors + #forward . 9.9.9.9:53 +} diff --git a/bundles/coredns/files/zones/gzr.im b/bundles/coredns/files/zones/gzr.im new file mode 100644 index 0000000..eeb5a37 --- /dev/null +++ b/bundles/coredns/files/zones/gzr.im @@ -0,0 +1,21 @@ +$ORIGIN gzr.im. +@ 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. ( + 2017042745 ; serial + 7200 ; refresh (2 hours) + 3600 ; retry (1 hour) + 1209600 ; expire (2 weeks) + 3600 ; minimum (1 hour) + ) + + 3600 IN NS a.iana-servers.net. + 3600 IN NS b.iana-servers.net. + +;; A records +iz.ts.gzr.im. IN A 100.82.12.64 ; inazuma +sk.ts.gzr.im. IN A 100.116.153.10 ; shimakaze + +;; CNAME records - inazuma +rss.gzr.im. IN CNAME iz.ts.gzr.im. + +;; CNAME records - shimakaze +tv.gzr.im. IN CNAME sk.ts.gzr.im. diff --git a/bundles/doyouhavewifi/files/Caddyfile b/bundles/doyouhavewifi/files/Caddyfile new file mode 100644 index 0000000..b7a57fe --- /dev/null +++ b/bundles/doyouhavewifi/files/Caddyfile @@ -0,0 +1,46 @@ +{ + admin off + auto_https off + http_port {$CADDY_HTTP_PORT:1414} +} + +(external_redirects) { +} + +(internal_redirects) { + redir /.well-known/security.txt /security.txt + redir /.well-known/humans.txt /humans.txt + redir /.well-known/robots.txt /robots.txt +} + +http:// { + root * ./public + + encode zstd gzip + file_server + + import external_redirects + import internal_redirects + + respond /healthz 200 + + log { + format filter { + wrap console { + time_format wall_milli + level_format color + } + fields { + common_log delete + request>headers>Accept-Encoding delete + request>headers>Accept-Language delete + request>headers>Connection delete + request>headers>Authorization delete + request>remote_addr ip_mask { + ipv4 24 + ipv6 32 + } + } + } + } +} diff --git a/bundles/doyouhavewifi/files/doyouhavewifi.conf b/bundles/doyouhavewifi/files/doyouhavewifi.conf new file mode 100644 index 0000000..66fea31 --- /dev/null +++ b/bundles/doyouhavewifi/files/doyouhavewifi.conf @@ -0,0 +1 @@ +www_port=${node.metadata.get("www/doyouhavewifi/port")} diff --git a/bundles/doyouhavewifi/files/traefik.yaml b/bundles/doyouhavewifi/files/traefik.yaml new file mode 100644 index 0000000..f9a2850 --- /dev/null +++ b/bundles/doyouhavewifi/files/traefik.yaml @@ -0,0 +1,18 @@ +http: + services: + doyouhavewifi: + loadBalancer: + servers: + - url: http://${node.metadata.get("container_gateway", "10.89.0.1")}:${node.metadata.get("www/doyouhavewifi/port")} + + routers: + doyouhavewifi: + rule: Host(`doyouhavewifi.gzr.im`) + entryPoints: + - http + - https + service: doyouhavewifi + middlewares: + - tls-redirect@file + tls: + certResolver: le diff --git a/bundles/doyouhavewifi/items.py b/bundles/doyouhavewifi/items.py new file mode 100644 index 0000000..460693a --- /dev/null +++ b/bundles/doyouhavewifi/items.py @@ -0,0 +1,29 @@ +directories = { + "/var/www/doyouhavewifi": { + "mode": "0775", + "group": "www-data", + "owner": "webdeploy", + }, + "/var/www/doyouhavewifi/public": { + "mode": "0775", + "group": "www-data", + "owner": "webdeploy", + }, +} + +files = { + "/etc/traefik/doyouhavewifi.yaml": { + "content_type": "mako", + "source": "traefik.yaml", + "needs": { + "bundle:traefik", + }, + }, + "/var/www/doyouhavewifi/Caddyfile": { + "content_type": "mako", + "source": "Caddyfile", + }, + "/etc/s6-rc/doyouhavewifi.conf": { + "content_type": "mako", + }, +} diff --git a/bundles/doyouhavewifi/metadata.py b/bundles/doyouhavewifi/metadata.py new file mode 100644 index 0000000..402491e --- /dev/null +++ b/bundles/doyouhavewifi/metadata.py @@ -0,0 +1,5 @@ +defaults = { + "www": { + "doyouhavewifi": {}, + }, +} diff --git a/bundles/fail2ban/files/etc/fail2ban/action.d/telegram-webhook.conf b/bundles/fail2ban/files/etc/fail2ban/action.d/telegram-webhook.conf new file mode 100644 index 0000000..34b2cb3 --- /dev/null +++ b/bundles/fail2ban/files/etc/fail2ban/action.d/telegram-webhook.conf @@ -0,0 +1,24 @@ +[Definition] + +# Notify on Startup +# actionstart = tgnotify "The **[]** jail has started" +actionstart = + +# Notify on Shutdown +# actionstop = tgnotify "The **[]** jail has been stopped" +actionstop = + +actioncheck = + +# Notify on Banned +# TODO: template node.name +actionban = tgnotify '💥*inazuma/fail2ban/*\nBanned []() for seconds after failure(s).\nIf you want to unban the IP run: `fail2ban-client unban `' + +# Notify on Unbanned +# TODO: template node.name +actionunban = tgnotify '🕊️*inazuma/fail2ban/*\nUnbanned []()' + +[Init] +# URL prefix for an IP checking website +# abuseipdb is used by default since there is also an action to report an IP to their API +url_check_ip = https://www.abuseipdb.com/check/ diff --git a/bundles/fail2ban/files/etc/fail2ban/filter.d/traefik-badbots.conf b/bundles/fail2ban/files/etc/fail2ban/filter.d/traefik-badbots.conf new file mode 100644 index 0000000..65ea993 --- /dev/null +++ b/bundles/fail2ban/files/etc/fail2ban/filter.d/traefik-badbots.conf @@ -0,0 +1,12 @@ +[Definition] + +badbots = 360Spider|404checker|404enemy|80legs|Abonti|Aboundex|Acunetix|ADmantX|AfD-Verbotsverfahren|AhrefsBot|AIBOT|AiHitBot|Aipbot|Alexibot|Alligator|AllSubmitter|AlphaBot|Anarchie|Apexoo|ASPSeek|Asterias|Attach|autoemailspider|BackDoorBot|Backlink-Ceck|backlink-check|BacklinkCrawler|BackStreet|BackWeb|Badass|Bandit|Barkrowler|BatchFTP|Battleztar Bazinga|BBBike|BDCbot|BDFetch|BetaBot|Bigfoot|Bitacle|Blackboard|Black Hole|BlackWidow|BLEXBot|Blow|BlowFish|Boardreader|Bolt|BotALot|Brandprotect|Brandwatch|Bubing|Buddy|BuiltBotTough|BuiltWith|Bullseye|BunnySlippers|BuzzSumo|Calculon|CATExplorador|CazoodleBot|CCBot|Cegbfeieh|CheeseBot|CherryPicker|ChinaClaw|Chlooe|Claritybot|Cliqzbot|Cloud mapping|coccocbot-web|Cogentbot|cognitiveseo|Collector|com\.plumanalytics|Copier|CopyRightCheck|Copyscape|Cosmos|Craftbot|crawler4j|crawler\.feedback|CrazyWebCrawler|Crescent|CSHttp|Curious|Custo|DatabaseDriverMysqli|DataCha0s|DBLBot|demandbase-bot|Demon|Deusu|Devil|Digincore|DigitalPebble|DIIbot|Dirbuster|Disco|Discobot|Discoverybot|DittoSpyder|DnyzBot|DomainAppender|DomainCrawler|DomainSigmaCrawler|DomainStatsBot|Dotbot|Download Wonder|Dragonfly|Drip|DTS Agent|EasyDL|Ebingbong|eCatch|ECCP/1\.0|Ecxi|EirGrabber|EMail Siphon|EMail Wolf|EroCrawler|evc-batch|Evil|Exabot|Express WebPictures|ExtLinksBot|Extractor|ExtractorPro|Extreme Picture Finder|Expanse|EyeNetIE|Ezooms|FDM|FemtosearchBot|FHscan|Fimap|Firefox/7\.0|FlashGet|Flunky|Foobot|Freeuploader|FrontPage|Fyrebot|GalaxyBot|Genieo|GermCrawler|Getintent|GetRight|GetWeb|Gigablast|Gigabot|G-i-g-a-b-o-t|Go-Ahead-Got-It|Gotit|GoZilla|Go!Zilla|Grabber|GrabNet|Grafula|GrapeFX|GrapeshotCrawler|GridBot|GT\:\:WWW|Haansoft|HaosouSpider|Harvest|Havij|HEADMasterSEO|Heritrix|Hloader|HMView|HTMLparser|HTTP\:\:Lite|HTTrack|Humanlinks|HybridBot|Iblog|IDBot|Id-search|IlseBot|Image Fetch|Image Sucker|IndeedBot|Indy Library|InfoNaviRobot|InfoTekies|instabid|Intelliseek|InterGET|Internet Ninja|InternetSeer|internetVista monitor|ips-agent|Iria|IRLbot|Iskanie|IstellaBot|JamesBOT|Jbrofuzz|JennyBot|JetCar|JikeSpider|JOC Web Spider|Joomla|Jorgee|JustView|Jyxobot|Kenjin Spider|Keyword Density|Kozmosbot|Lanshanbot|Larbin|LeechFTP|LeechGet|LexiBot|Lftp|LibWeb|Libwhisker|Lightspeedsystems|Likse|Linkdexbot|LinkextractorPro|LinkpadBot|LinkScan|LinksManager|LinkWalker|LinqiaMetadataDownloaderBot|LinqiaRSSBot|LinqiaScrapeBot|Lipperhey|Litemage_walker|Lmspider|LNSpiderguy|Ltx71|lwp-request|LWP\:\:Simple|lwp-trivial|Magnet|Mag-Net|magpie-crawler|Mail\.RU_Bot|Majestic12|MarkMonitor|MarkWatch|Masscan|masscan|Mass Downloader|Mata Hari|MauiBot|Meanpathbot|mediawords|MegaIndex\.ru|Metauri|MFC_Tear_Sample|Microsoft Data Access|Microsoft URL Control|MIDown tool|MIIxpc|Mister PiX|MJ12bot|Mojeek|Morfeus Fucking Scanner|Mr\.4x3|MSFrontPage|MSIECrawler|Msrabot|MS Web Services Client Protocol|muhstik-scan|Musobot|Name Intelligence|Nameprotect|Navroad|NearSite|Needle|Nessus|NetAnts|Netcraft|netEstate NE Crawler|NetLyzer|NetMechanic|NetSpider|Nettrack|Net Vampire|Netvibes|NetZIP|NextGenSearchBot|Nibbler|NICErsPRO|Niki-bot|Nikto|NimbleCrawler|Ninja|Nmap|NPbot|Nutch|oBot|Octopus|Offline Explorer|Offline Navigator|Openfind|OpenLinkProfiler|Openvas|OrangeBot|OrangeSpider|OutclicksBot|OutfoxBot|PageAnalyzer|Page Analyzer|PageGrabber|page scorer|PageScorer|Panscient|Papa Foto|Pavuk|pcBrowser|PECL\:\:HTTP|PeoplePal|PHPCrawl|Picscout|Picsearch|PictureFinder|Pimonster|Pi-Monster|Pixray|PleaseCrawl|plumanalytics|Pockey|POE-Component-Client-HTTP|Probethenet|ProPowerBot|ProWebWalker|Psbot|Pump|PxBroker|PyCurl|QueryN Metasearch|Quick-Crawler|RankActive|RankActiveLinkBot|RankFlex|RankingBot|RankingBot2|Rankivabot|RankurBot|RealDownload|Reaper|RebelMouse|Recorder|RedesScrapy|ReGet|RepoMonkey|Ripper|RocketCrawler|Rogerbot|SalesIntelligent|SBIder|ScanAlert|Scanbot|scan\.lol|Scrapy|Screaming|ScreenerBot|Searchestate|SearchmetricsBot|Semrush|SemrushBot|SEOkicks|SEOlyticsCrawler|Seomoz|SEOprofiler|seoscanners|SEOstats|sexsearcher|Seznam|SeznamBot|Shodan|Siphon|SISTRIX|Sitebeam|SiteExplorer|Siteimprove|SiteLockSpider|SiteSnagger|SiteSucker|Site Sucker|Sitevigil|Slackbot-LinkExpanding|SlySearch|SmartDownload|SMTBot|Snake|Snapbot|Snoopy|SocialRankIOBot|Sogou web spider|Sosospider|Sottopop|SpaceBison|Spammen|SpankBot|Spanner|Spbot|Spinn3r|SputnikBot|Sqlmap|Sqlworm|Sqworm|Steeler|Stripper|Sucker|Sucuri|SuperBot|SuperHTTP|Surfbot|SurveyBot|Suzuran|Swiftbot|sysscan|Szukacz|T0PHackTeam|T8Abot|tAkeOut|Teleport|TeleportPro|Telesoft|Telesphoreo|Telesphorep|The Intraformant|TheNomad|TightTwatBot|Titan|Toata|Toweyabot|Trendiction|Trendictionbot|trendiction\.com|trendiction\.de|True_Robot|Turingos|Turnitin|TurnitinBot|TwengaBot|Twice|Typhoeus|UnisterBot|URLy\.Warning|URLy Warning|Vacuum|Vagabondo|VB Project|VCI|VeriCiteCrawler|VidibleScraper|Virusdie|VoidEYE|Voil|Voltron|Wallpapers/3\.0|WallpapersHD|WASALive-Bot|WBSearchBot|Webalta|WebAuto|Web Auto|WebBandit|WebCollage|Web Collage|WebCopier|WEBDAV|WebEnhancer|Web Enhancer|WebFetch|Web Fetch|WebFuck|Web Fuck|WebGo IS|WebImageCollector|WebLeacher|WebmasterWorldForumBot|webmeup-crawler|WebPix|Web Pix|WebReaper|WebSauger|Web Sauger|Webshag|WebsiteExtractor|WebsiteQuester|Website Quester|Webster|WebStripper|WebSucker|Web Sucker|WebWhacker|WebZIP|WeSEE|Whack|Whacker|Whatweb|Who\.is Bot|Widow|WinHTTrack|WiseGuys Robot|WISENutbot|Wonderbot|Woobot|Wotbox|Wprecon|WPScan|WWW-Collector-E|WWW-Mechanize|WWW\:\:Mechanize|WWWOFFLE|x09Mozilla|x22Mozilla|Xaldon_WebSpider|Xaldon WebSpider|Xenu|xpymep1\.exe|YoudaoBot|Zade|Zauba|zauba\.io|Zermelo|Zeus|zgrab|Zitebot|ZmEu|ZumBot|ZyBorg|CensysInspect|Nmap Scripting Engine + +# fail regex based on traefik JSON access logs with enabled user agent logging +failregex = ^{"ClientAddr":".*","ClientHost":"","ClientPort":".*","ClientUsername":".*","DownstreamContentSize":.*,"DownstreamStatus":.*,"Duration":.*,"OriginContentSize":.*,"OriginDuration":.*,"OriginStatus":.*,"Overhead":.*,"RequestAddr":".*","RequestContentSize":.*,"RequestCount":.*,"RequestHost":".*","RequestMethod":".*","RequestPath":".*","RequestPort":".*","RequestProtocol":".*","RequestScheme":".*","RetryAttempts":.*,.*"StartLocal":".*","StartUTC":".*","TLSCipher":".*","TLSVersion":".*","entryPointName":".*","level":".*","msg":".*","request_User-Agent":".*(?:%(badbots)s).*","time":".*"}$ + +# custom date pattern for traefik JSON access logs +# based on https://github.com/fail2ban/fail2ban/issues/2558#issuecomment-546738270 +datepattern = "StartLocal"\s*:\s*"%%Y-%%m-%%d[T]%%H:%%M:%%S\.%%f\d*(%%z)?", + +ignoreregex = diff --git a/bundles/fail2ban/files/etc/fail2ban/filter.d/traefik-spam.conf b/bundles/fail2ban/files/etc/fail2ban/filter.d/traefik-spam.conf new file mode 100644 index 0000000..7abad3e --- /dev/null +++ b/bundles/fail2ban/files/etc/fail2ban/filter.d/traefik-spam.conf @@ -0,0 +1,13 @@ +[INCLUDES] + +[Definition] + +# fail regex based on traefik JSON access logs with enabled user agent logging +failregex = ^{"ClientAddr":".*","ClientHost":"","ClientPort":".*","ClientUsername":".*","DownstreamContentSize":.*,"DownstreamStatus":.*,"Duration":.*,"OriginContentSize":.*,"OriginDuration":.*,"OriginStatus":(405|404|403|402|401),"Overhead":.*,"RequestAddr":".*","RequestContentSize":.*,"RequestCount":.*,"RequestHost":".*","RequestMethod":".*","RequestPath":".*","RequestPort":".*","RequestProtocol":".*","RequestScheme":".*","RetryAttempts":.*,.*"StartLocal":".*","StartUTC":".*","TLSCipher":".*","TLSVersion":".*","entryPointName":".*","level":".*","msg":".*",("request_User-Agent":".*",){0,1}?"time":".*"}$ + +# custom date pattern for traefik JSON access logs +# based on https://github.com/fail2ban/fail2ban/issues/2558#issuecomment-546738270 +datepattern = "StartLocal"\s*:\s*"%%Y-%%m-%%d[T]%%H:%%M:%%S\.%%f\d*(%%z)?", + +# ignore common errors like missing media files or JS/CSS/TXT/ICO stuff +ignoreregex = ^{"ClientAddr":".*","ClientHost":"","ClientPort":".*","ClientUsername":".*","DownstreamContentSize":.*,"DownstreamStatus":.*,"Duration":.*,"OriginContentSize":.*,"OriginDuration":.*,"OriginStatus":(405|404|403|402|401),"Overhead":.*,"RequestAddr":".*","RequestContentSize":.*,"RequestCount":.*,"RequestHost":".*","RequestMethod":".*","RequestPath":".*(\.png|\.txt|\.jpg|\.ico|\.js|\.css|\.ttf|\.woff|\.woff2)(/)*?","RequestPort":".*","RequestProtocol":".*","RequestScheme":".*","RetryAttempts":.*,.*"StartLocal":".*","StartUTC":".*","TLSCipher":".*","TLSVersion":".*","entryPointName":".*","level":".*","msg":".*",("request_User-Agent":".*",){0,1}?"time":".*"}$ diff --git a/bundles/fail2ban/files/etc/fail2ban/jail.d/sshd.conf b/bundles/fail2ban/files/etc/fail2ban/jail.d/sshd.conf new file mode 100644 index 0000000..5eb1c14 --- /dev/null +++ b/bundles/fail2ban/files/etc/fail2ban/jail.d/sshd.conf @@ -0,0 +1,5 @@ +[sshd] +enabled = false + +[sshd-ddos] +enabled = false diff --git a/bundles/fail2ban/files/etc/fail2ban/jail.d/traefik.conf b/bundles/fail2ban/files/etc/fail2ban/jail.d/traefik.conf new file mode 100644 index 0000000..a46a1d3 --- /dev/null +++ b/bundles/fail2ban/files/etc/fail2ban/jail.d/traefik.conf @@ -0,0 +1,10 @@ +[traefik-spam] +enabled = true +filter = traefik-spam +logpath = /var/lib/traefik/access.log* + +[traefik-badbots] +enabled = true +filter = traefik-badbots +logpath = /var/lib/traefik/access.log* +maxretry = 1 diff --git a/bundles/fail2ban/files/etc/fail2ban/jail.local b/bundles/fail2ban/files/etc/fail2ban/jail.local new file mode 100644 index 0000000..45fcdc5 --- /dev/null +++ b/bundles/fail2ban/files/etc/fail2ban/jail.local @@ -0,0 +1,22 @@ +[DEFAULT] +backend = polling +banaction = nftables +banaction_allports = nftables[type=allports] +bantime = 31m +bantime.increment = true +bantime.maxtime = 6d +bantime.overalljails = true +findtime = 20m +ignoreip = 127.0.0.0/8 ::1 + 172.16.0.0/12 + 192.168.0.0/16 + 10.0.0.0/8 + 100.64.0.0/10 +maxretry = 3 +usedns = false + +# make sure no jails are enabled by default +enabled = false + +action = %(action_)s + telegram-webhook diff --git a/bundles/fail2ban/items.py b/bundles/fail2ban/items.py new file mode 100644 index 0000000..9627587 --- /dev/null +++ b/bundles/fail2ban/items.py @@ -0,0 +1,31 @@ +if node.os != "alpine": + raise BundleError(f"{node.name}: OS {node.os} is not supported.") + +from os.path import join + +svc_openrc = { + "fail2ban": { + "enabled": True, + "running": True, + "needs": { + "pkg_apk:fail2ban", + "bundle:firewall", + "bundle:tgnotify", + }, + }, +} + +files = { +} + +repo.libs.gen.add_files_recursive( + files, + join(repo.path, "bundles", "fail2ban", "files"), + { + "triggers": { + "svc_openrc:fail2ban:restart", + }, + }, +) + +# TODO: metrics? https://github.com/hectorjsmith/fail2ban-prometheus-exporter diff --git a/bundles/fail2ban/metadata.py b/bundles/fail2ban/metadata.py new file mode 100644 index 0000000..c1a54bc --- /dev/null +++ b/bundles/fail2ban/metadata.py @@ -0,0 +1,7 @@ +defaults = { + "apk": { + "packages": { + "fail2ban": {}, + }, + }, +} diff --git a/bundles/firewall/.editorconfig b/bundles/firewall/.editorconfig new file mode 100644 index 0000000..205511f --- /dev/null +++ b/bundles/firewall/.editorconfig @@ -0,0 +1,4 @@ +root = false + +[*.nft] +indent_size = 2 diff --git a/bundles/firewall/files/etc/nftables.d/00-basic.nft b/bundles/firewall/files/etc/nftables.d/00-basic.nft new file mode 100644 index 0000000..493ab38 --- /dev/null +++ b/bundles/firewall/files/etc/nftables.d/00-basic.nft @@ -0,0 +1,59 @@ +#!/usr/sbin/nft -f +# vim: set ts=4 sw=4: +table inet filter { + chain input { + iifname lo accept \ + comment "Accept any localhost traffic" + + ct state { established, related } accept \ + comment "Accept traffic originated from us" + + ct state invalid drop \ + comment "Drop invalid connections" + + tcp dport 113 reject with icmpx type port-unreachable \ + comment "Reject AUTH to make it fail fast" + + # ICMPv4 + + ip protocol icmp icmp type { + echo-reply, # type 0 + destination-unreachable, # type 3 + echo-request, # type 8 + time-exceeded, # type 11 + parameter-problem, # type 12 + } accept \ + comment "Accept ICMP" + + # ICMPv6 + + icmpv6 type { + destination-unreachable, # type 1 + packet-too-big, # type 2 + time-exceeded, # type 3 + parameter-problem, # type 4 + echo-request, # type 128 + echo-reply, # type 129 + } accept \ + comment "Accept basic IPv6 functionality" + + icmpv6 type { + nd-router-solicit, # type 133 + nd-router-advert, # type 134 + nd-neighbor-solicit, # type 135 + nd-neighbor-advert, # type 136 + } ip6 hoplimit 255 accept \ + comment "Allow IPv6 SLAAC" + + icmpv6 type { + mld-listener-query, # type 130 + mld-listener-report, # type 131 + mld-listener-reduction, # type 132 + mld2-listener-report, # type 143 + } ip6 saddr fe80::/10 accept \ + comment "Allow IPv6 multicast listener discovery on link-local" + + ip6 saddr fe80::/10 udp sport 547 udp dport 546 accept \ + comment "Accept DHCPv6 replies from IPv6 link-local addresses" + } +} diff --git a/bundles/firewall/files/etc/nftables.d/01-my-filter.nft b/bundles/firewall/files/etc/nftables.d/01-my-filter.nft new file mode 100644 index 0000000..fffd9f2 --- /dev/null +++ b/bundles/firewall/files/etc/nftables.d/01-my-filter.nft @@ -0,0 +1,18 @@ +#!/usr/sbin/nft -f +# vim: set ts=4 sw=4: +table inet filter { + + chain my_filter { + } + + chain input { + jump my_filter + log prefix "blocked input traffic: " counter + } + + chain forward { + jump my_filter + log prefix "blocked input traffic: " counter + } + +} diff --git a/bundles/firewall/files/etc/nftables.d/10-tailscale.nft b/bundles/firewall/files/etc/nftables.d/10-tailscale.nft new file mode 100644 index 0000000..f95e142 --- /dev/null +++ b/bundles/firewall/files/etc/nftables.d/10-tailscale.nft @@ -0,0 +1,16 @@ +#!/usr/sbin/nft -f +# vim: set ts=4 sw=4: +table inet filter { + + chain my_filter { + iifname tailscale0 accept \ + comment "Accept inbount tailscale traffic" + + oifname tailscale0 accept \ + comment "Accept outbound tailscale traffic" + + udp dport { 41641 } accept \ + comment "Accept tailscale NAT traffic" + } + +} diff --git a/bundles/firewall/files/etc/nftables.d/22-ssh.nft b/bundles/firewall/files/etc/nftables.d/22-ssh.nft new file mode 100644 index 0000000..c8c91a1 --- /dev/null +++ b/bundles/firewall/files/etc/nftables.d/22-ssh.nft @@ -0,0 +1,10 @@ +#!/bin/nft -f +# vim: set ts=4 sw=4: +table inet filter { + + chain my_filter { + tcp dport { ${node.metadata.get("ssh/port", 22)} } accept \ + comment "Accept SSH traffic" + } + +} diff --git a/bundles/firewall/files/etc/nftables.d/50-www.nft b/bundles/firewall/files/etc/nftables.d/50-www.nft new file mode 100644 index 0000000..6969f6d --- /dev/null +++ b/bundles/firewall/files/etc/nftables.d/50-www.nft @@ -0,0 +1,10 @@ +#!/usr/sbin/nft -f +# vim: set ts=4 sw=4: +table inet filter { + + chain my_filter { + tcp dport { http, https } accept \ + comment "Accept HTTP traffic" + } + +} diff --git a/bundles/firewall/files/etc/nftables.d/80-podman.nft b/bundles/firewall/files/etc/nftables.d/80-podman.nft new file mode 100644 index 0000000..90142bc --- /dev/null +++ b/bundles/firewall/files/etc/nftables.d/80-podman.nft @@ -0,0 +1,15 @@ +#!/usr/sbin/nft -f +# vim: set ts=4 sw=4: +table inet filter { + + # HACK: this would not be necessary if not for the two chains being + # incompatible + chain my_filter { + ip daddr 10.89.0.0/24 ct state established,related accept \ + comment "Accept incoming podman traffic" + + ip saddr 10.89.0.0/24 accept \ + comment "Accept outgoing podman traffic" + } + +} diff --git a/bundles/firewall/files/etc/nftables.nft b/bundles/firewall/files/etc/nftables.nft new file mode 100644 index 0000000..35b10dd --- /dev/null +++ b/bundles/firewall/files/etc/nftables.nft @@ -0,0 +1,21 @@ +#!/usr/sbin/nft -f +# vim: set ts=4 sw=4: +# You can find examples in /usr/share/nftables/. + +# Clear all prior state +flush ruleset + +# Basic IPv4/IPv6 stateful firewall for server/workstation. +table inet filter { + + chain input { type filter hook input priority 0; policy drop; } + chain forward { type filter hook forward priority 0; policy drop; } + chain output { type filter hook output priority 0; policy accept; } + +} + +# The state of stateful objects saved on the nftables service stop. +include "/var/lib/nftables/*.nft" + +# Rules +include "/etc/nftables.d/*.nft" diff --git a/bundles/firewall/items.py b/bundles/firewall/items.py new file mode 100644 index 0000000..1949d13 --- /dev/null +++ b/bundles/firewall/items.py @@ -0,0 +1,43 @@ +if node.os != "alpine": + raise BundleError(f"{node.name}: OS {node.os} is not supported.") + +from os.path import join + +actions = { + "restart_container_bundle": { + "command": "s6-rc -bt 30000 stop containers && s6-rc -bt 30000 start containers", + "triggered": True, + } +} + +# TODO: fix on shimakaze, missing rules for photos./tv. +svc_openrc = { + "nftables": { + "runlevel": "boot", + "enabled": True, + "running": True, + "needs": { + "pkg_apk:nftables", + }, + "triggers": { + # NOTE: required because restart nftables drops all rules and + # podman will install rules to enable container networking + "action:restart_container_bundle", + }, + }, +} + +files = { + # overwrite content_type to allowing templating ssh port + "/etc/nftables.d/22-ssh.nft": { "content_type": "mako" } +} + +repo.libs.gen.add_files_recursive( + files, + join(repo.path, "bundles", "firewall", "files"), + { + "triggers": { + "svc_openrc:nftables:restart", + }, + } +) diff --git a/bundles/firewall/metadata.py b/bundles/firewall/metadata.py new file mode 100644 index 0000000..76ba53c --- /dev/null +++ b/bundles/firewall/metadata.py @@ -0,0 +1,7 @@ +defaults = { + "apk": { + "packages": { + "nftables": {}, + }, + }, +} diff --git a/bundles/fstab/items.py b/bundles/fstab/items.py new file mode 100644 index 0000000..e9327ab --- /dev/null +++ b/bundles/fstab/items.py @@ -0,0 +1,12 @@ +if node.os != "alpine": + raise BundleError(f"{node.name}: OS {node.os} is not supported.") + +actions = { + "mount_all": { + "command": "mount -a", + "triggered": True, + }, +} + +for file, config in node.metadata.get("fstab", {}).items(): + pkg_apk[package] = options diff --git a/bundles/fstab/metadata.py b/bundles/fstab/metadata.py new file mode 100644 index 0000000..22144c2 --- /dev/null +++ b/bundles/fstab/metadata.py @@ -0,0 +1,11 @@ +defaults = { + "fstab": { + "/tmp": { + "spec": "tmpfs", + "vfstype": "tmpfs", + "mntopts": "size=500G", + "freq": "0", + "passno": "0", + } + }, +} diff --git a/bundles/glance/files/config.yml b/bundles/glance/files/config.yml new file mode 100644 index 0000000..eda57c4 --- /dev/null +++ b/bundles/glance/files/config.yml @@ -0,0 +1,133 @@ +server: + host: 0.0.0.0 + port: ${node.metadata.get("glance/port")} + assets-path: /var/lib/glance/assets/ + +branding: + custom-footer: " " + logo-url: /assets/hex.svg + favicon-url: /assets/hex.png + +theme: # kanagawa dark + background-color: 240 13 14 + primary-color: 51 33 68 + negative-color: 358 100 68 + contrast-multiplier: 1.2 + +pages: + - name: Home + columns: + - size: small + widgets: + - type: calendar + + - type: html + source: | +
+ TODO: miniflux widget +
+ + - size: full + widgets: + - type: search + search-engine: duckduckgo + + - type: monitor + cache: 1m + title: Services + sites: + - title: Jellyfin + url: https://tv.gzr.im + icon: /assets/jellyfin.svg + - title: Vaultwarden + url: https://vault.gzr.im + icon: /assets/vaultwarden.svg + - title: Miniflux + url: https://rss.gzr.im + icon: /assets/miniflux.svg + - title: Paperless + url: https://paper.gzr.im + icon: /assets/paperless.svg + - title: Immich + url: https://photos.gzr.im + icon: /assets/immich.svg + - title: Home Assistant + url: http://home.gzr.im + icon: /assets/home-assistant.svg + + - type: bookmarks + groups: + - links: + - title: Gmail + url: https://mail.google.com/mail/u/0/ + - title: Amazon + url: https://www.amazon.com/ + - title: Github + url: https://github.com/ + - title: Wikipedia + url: https://en.wikipedia.org/ + - title: Entertainment + color: 10 70 50 + links: + - title: Netflix + url: https://www.netflix.com/ + - title: Disney+ + url: https://www.disneyplus.com/ + - title: YouTube + url: https://www.youtube.com/ + - title: Prime Video + url: https://www.primevideo.com/ + - title: Social + color: 200 50 50 + links: + - title: Reddit + url: https://www.reddit.com/ + - title: Twitter + url: https://twitter.com/ + - title: Instagram + url: https://www.instagram.com/ + + - size: small + widgets: + - type: clock + hour-format: 24h + timezones: + - timezone: UTC + label: UTC + - timezone: Europe/Berlin + label: Berlin + - timezone: Europe/Istanbul + label: Istanbul + + - type: weather + location: Berlin, Germany + + - type: markets + markets: + - symbol: OTLY + name: Oatly + + - name: News + columns: + - size: small + widgets: + - type: hacker-news + + - type: reddit + subreddit: selfhosted + + - size: full + widgets: + - type: rss + limit: 10 + collapse-after: 3 + cache: 3h + feeds: + - url: https://ciechanow.ski/atom.xml + - url: https://www.joshwcomeau.com/rss.xml + title: Josh Comeau + - url: https://samwho.dev/rss.xml + - url: https://awesomekling.github.io/feed.xml + - url: https://ishadeed.com/feed.xml + title: Ahmad Shadeed + diff --git a/bundles/glance/files/glance.run b/bundles/glance/files/glance.run new file mode 100755 index 0000000..fda6185 --- /dev/null +++ b/bundles/glance/files/glance.run @@ -0,0 +1,4 @@ +#!/bin/sh +set -eu + +exec /usr/bin/glance -config /var/lib/glance/config.yml diff --git a/bundles/glance/files/hex.png b/bundles/glance/files/hex.png new file mode 100644 index 0000000..182e36a Binary files /dev/null and b/bundles/glance/files/hex.png differ diff --git a/bundles/glance/files/hex.svg b/bundles/glance/files/hex.svg new file mode 100644 index 0000000..341407d --- /dev/null +++ b/bundles/glance/files/hex.svg @@ -0,0 +1,411 @@ + + + + + + diff --git a/bundles/glance/files/home-assistant.svg b/bundles/glance/files/home-assistant.svg new file mode 100644 index 0000000..7bce628 --- /dev/null +++ b/bundles/glance/files/home-assistant.svg @@ -0,0 +1,4 @@ + + + + diff --git a/bundles/glance/files/immich.svg b/bundles/glance/files/immich.svg new file mode 100644 index 0000000..376fa6f --- /dev/null +++ b/bundles/glance/files/immich.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + diff --git a/bundles/glance/files/jellyfin.svg b/bundles/glance/files/jellyfin.svg new file mode 100644 index 0000000..d4d7f01 --- /dev/null +++ b/bundles/glance/files/jellyfin.svg @@ -0,0 +1,24 @@ + + + + + + + + + + icon-transparent + + + + + diff --git a/bundles/glance/files/miniflux.svg b/bundles/glance/files/miniflux.svg new file mode 100644 index 0000000..33ae73a --- /dev/null +++ b/bundles/glance/files/miniflux.svg @@ -0,0 +1 @@ +icon \ No newline at end of file diff --git a/bundles/glance/files/paperless.svg b/bundles/glance/files/paperless.svg new file mode 100644 index 0000000..3144ae9 --- /dev/null +++ b/bundles/glance/files/paperless.svg @@ -0,0 +1,82 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/bundles/glance/files/traefik.yaml b/bundles/glance/files/traefik.yaml new file mode 100644 index 0000000..1f248a7 --- /dev/null +++ b/bundles/glance/files/traefik.yaml @@ -0,0 +1,18 @@ +http: + services: + glance: + loadBalancer: + servers: + - url: http://${node.metadata.get("container_gateway", "10.89.0.1")}:${node.metadata.get("glance/port")} + + routers: + glance: + rule: Host(`1.gzr.im`) + entryPoints: + - http + - https + service: glance + middlewares: + - tls-redirect@file + tls: + certResolver: le diff --git a/bundles/glance/files/vaultwarden.svg b/bundles/glance/files/vaultwarden.svg new file mode 100644 index 0000000..91abbd6 --- /dev/null +++ b/bundles/glance/files/vaultwarden.svg @@ -0,0 +1,74 @@ + + + + + Vaultwarden Icon + + + + + Vaultwarden Icon + + + Mathijs van Veluw + + + Rust Logo + + + + diff --git a/bundles/glance/items.py b/bundles/glance/items.py new file mode 100644 index 0000000..3db77c6 --- /dev/null +++ b/bundles/glance/items.py @@ -0,0 +1,42 @@ + +local_deploy = { + "/usr/bin/glance": { + "command": "env CGO_ENABLED=0 go build -o glance main.go", + "cwd": "/home/robert/src/glance", + "artifact": "glance", + "content_hash": "335fc3b7868f3610dbe8397021501c465ee2627c", + }, +} + +files = { + "/etc/service/glance.run": { + "mode": "0755", + "triggers": {"svc_s6rc:glance:restart"}, + }, + "/etc/traefik/glance.yaml": { + "content_type": "mako", + "source": "traefik.yaml", + "needs": {"bundle:traefik"}, + }, + "/var/lib/glance/config.yml": { + "content_type": "mako", + "triggers": {"svc_s6rc:glance:restart"}, + }, + + # assets + "/var/lib/glance/assets/home-assistant.svg": {}, + "/var/lib/glance/assets/immich.svg": {}, + "/var/lib/glance/assets/jellyfin.svg": {}, + "/var/lib/glance/assets/miniflux.svg": {}, + "/var/lib/glance/assets/paperless.svg": {}, + "/var/lib/glance/assets/vaultwarden.svg": {}, + "/var/lib/glance/assets/hex.svg": {}, + "/var/lib/glance/assets/hex.png": {}, +} + +svc_s6rc = { + "glance": { + "bundle": "services", + "generate": "service", + } +} diff --git a/bundles/glance/metadata.py b/bundles/glance/metadata.py new file mode 100644 index 0000000..9b16788 --- /dev/null +++ b/bundles/glance/metadata.py @@ -0,0 +1,6 @@ +defaults = { + "apk": { + "packages": { + }, + }, +} diff --git a/bundles/go-away/files/go-away.run b/bundles/go-away/files/go-away.run new file mode 100755 index 0000000..376fbb2 --- /dev/null +++ b/bundles/go-away/files/go-away.run @@ -0,0 +1,18 @@ +#!/bin/sh +set -eux + +cd /var/lib/go-away || exit 1 + +test -d examples +test -d examples/snippets +test -f examples/generic.yml + +exec ./go-away \ + --bind 0.0.0.0:${node.metadata.get("go-away/port")} \ + --backend gzr.im="http://${node.metadata.get("container_gateway", "10.89.0.1")}:${node.metadata.get("www/gzr-im/port")}" \ + --backend gnzler.de="http://${node.metadata.get("container_gateway", "10.89.0.1")}:${node.metadata.get("www/gzr-im/port")}" \ + --backend gnzler.io="http://${node.metadata.get("container_gateway", "10.89.0.1")}:${node.metadata.get("www/gzr-im/port")}" \ + --backend gonca.me="http://${node.metadata.get("container_gateway","10.89.0.1")}:${node.metadata.get("www/gonca-me/port")}" \ + --policy examples/generic.yml \ + --policy-snippets examples/snippets/ \ + --challenge-template anubis diff --git a/bundles/go-away/files/traefik.yaml b/bundles/go-away/files/traefik.yaml new file mode 100644 index 0000000..e44a842 --- /dev/null +++ b/bundles/go-away/files/traefik.yaml @@ -0,0 +1,28 @@ +http: + services: + go-away: + loadBalancer: + servers: + - url: http://${node.metadata.get("container_gateway", "10.89.0.1")}:${node.metadata.get("go-away/port")} + + routers: + gzr-im: + rule: Host(`gzr.im`) || Host(`gnzler.io`) || Host(`gnzler.de`) + entryPoints: + - http + - https + service: go-away + middlewares: + - tls-redirect@file + tls: + certResolver: le + gonca-me: + rule: Host(`gonca.me`) + entryPoints: + - http + - https + service: go-away + middlewares: + - tls-redirect@file + tls: + certResolver: le diff --git a/bundles/go-away/items.py b/bundles/go-away/items.py new file mode 100644 index 0000000..904b922 --- /dev/null +++ b/bundles/go-away/items.py @@ -0,0 +1,41 @@ + +# local_deploy = { +# "/usr/bin/go-away": { +# "command": "make build", +# "cwd": "/home/robert/src/go-away", +# "artifact": "go-away", +# "content_hash": "335fc3b7868f3610dbe8397021501c465ee2627c", +# }, +# } + +directories = { + "/var/lib/go-away": { + "mode": "0775", + "group": "wheel", + "owner": "nobody", + }, +} + +files = { + "/etc/service/go-away.run": { + "content_type": "mako", + "mode": "0755", + "triggers": {"svc_s6rc:go-away:restart"}, + }, + + "/etc/traefik/go-away.yaml": { + "content_type": "mako", + "source": "traefik.yaml", + "needs": { + "bundle:traefik", + }, + }, + +} + +svc_s6rc = { + "go-away": { + "bundle": "services", + "generate": "service", + } +} diff --git a/bundles/go-away/metadata.py b/bundles/go-away/metadata.py new file mode 100644 index 0000000..5160f64 --- /dev/null +++ b/bundles/go-away/metadata.py @@ -0,0 +1,5 @@ +defaults = { + "apk": { + "packages": {}, + }, +} diff --git a/bundles/golink/files/kube.yaml b/bundles/golink/files/kube.yaml new file mode 100644 index 0000000..8ab2789 --- /dev/null +++ b/bundles/golink/files/kube.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Pod +metadata: + name: golink + labels: + traefik.enable: false +spec: + restartPolicy: Never + dnsPolicy: Default + securityContext: + # NOTE: required unless mounted directory is owned by 65532 + runAsUser: 0 + containers: + - name: golink + image: ghcr.io/tailscale/golink:main + volumeMounts: + - name: golink-data + mountPath: /home/nonroot + resources: + limits: + cpu: 20m + memory: 100Mi + requests: + cpu: 10m + memory: 50Mi + volumes: + - name: golink-data + hostPath: + path: /var/lib/golink + type: DirectoryOrCreate diff --git a/bundles/golink/items.py b/bundles/golink/items.py new file mode 100644 index 0000000..8f79a22 --- /dev/null +++ b/bundles/golink/items.py @@ -0,0 +1,7 @@ +files = { + "/etc/deployments/golink/kube.yaml": { + "content_type": "text", + "source": "kube.yaml", + "triggers": {"svc_s6rc:golink:restart"}, + }, +} diff --git a/bundles/golink/metadata.py b/bundles/golink/metadata.py new file mode 100644 index 0000000..1fc3cef --- /dev/null +++ b/bundles/golink/metadata.py @@ -0,0 +1,5 @@ +defaults = { + "containers": { + "golink": {}, + }, +} diff --git a/bundles/gonca-me/files/gonca-me.conf b/bundles/gonca-me/files/gonca-me.conf new file mode 100644 index 0000000..f8d03e4 --- /dev/null +++ b/bundles/gonca-me/files/gonca-me.conf @@ -0,0 +1 @@ +www_port=${node.metadata.get("www/gonca-me/port")} diff --git a/bundles/gonca-me/files/traefik.yaml b/bundles/gonca-me/files/traefik.yaml new file mode 100644 index 0000000..63bfb34 --- /dev/null +++ b/bundles/gonca-me/files/traefik.yaml @@ -0,0 +1,18 @@ +# http: +# services: +# gonca-me: +# loadBalancer: +# servers: +# - url: http://${node.metadata.get("container_gateway", "10.89.0.1")}:${node.metadata.get("www/gonca-me/port")} +# +# routers: +# gonca-me: +# rule: Host(`gonca-me`) +# entryPoints: +# - http +# - https +# service: gonca-me +# middlewares: +# - tls-redirect@file +# tls: +# certResolver: le diff --git a/bundles/gonca-me/items.py b/bundles/gonca-me/items.py new file mode 100644 index 0000000..7920399 --- /dev/null +++ b/bundles/gonca-me/items.py @@ -0,0 +1,20 @@ +directories = { + "/var/www/gonca-me": { + "mode": "0775", + "group": "www-data", + "owner": "webdeploy", + }, +} + +files = { + "/etc/traefik/gonca-me.yaml": { + "content_type": "mako", + "source": "traefik.yaml", + "needs": { + "bundle:traefik", + }, + }, + "/etc/s6-rc/gonca-me.conf": { + "content_type": "mako", + }, +} diff --git a/bundles/gonca-me/metadata.py b/bundles/gonca-me/metadata.py new file mode 100644 index 0000000..159d68f --- /dev/null +++ b/bundles/gonca-me/metadata.py @@ -0,0 +1,5 @@ +defaults = { + "www": { + "gonca-me": {}, + }, +} diff --git a/bundles/gzr-im-preview/files/gzr-im-preview.conf b/bundles/gzr-im-preview/files/gzr-im-preview.conf new file mode 100644 index 0000000..e48639f --- /dev/null +++ b/bundles/gzr-im-preview/files/gzr-im-preview.conf @@ -0,0 +1 @@ +www_port=${node.metadata.get("www/gzr-im-preview/port")} diff --git a/bundles/gzr-im-preview/files/traefik.yaml b/bundles/gzr-im-preview/files/traefik.yaml new file mode 100644 index 0000000..4d7aa96 --- /dev/null +++ b/bundles/gzr-im-preview/files/traefik.yaml @@ -0,0 +1,20 @@ +http: + services: + gzr-im-preview: + loadBalancer: + servers: + - url: http://${node.metadata.get("container_gateway", "10.89.0.1")}:${node.metadata.get("www/gzr-im-preview/port")} + + routers: + gzr-im-preview: + rule: Host(`gzr-im.demo.gzr.im`) + entryPoints: + - http + - https + service: gzr-im-preview + middlewares: + - tls-redirect@file + tls: + certResolver: le + domains: + - main: '*.demo.gzr.im' diff --git a/bundles/gzr-im-preview/items.py b/bundles/gzr-im-preview/items.py new file mode 100644 index 0000000..9e91e8b --- /dev/null +++ b/bundles/gzr-im-preview/items.py @@ -0,0 +1,24 @@ +# groups = { +# "www": {} +# } + +directories = { + "/var/www/gzr-im-preview": { + "mode": "0775", + "group": "www-data", + "owner": "webdeploy", + }, +} + +files = { + "/etc/traefik/gzr-im-preview.yaml": { + "content_type": "mako", + "source": "traefik.yaml", + "needs": { + "bundle:traefik", + }, + }, + "/etc/s6-rc/gzr-im-preview.conf": { + "content_type": "mako", + }, +} diff --git a/bundles/gzr-im-preview/metadata.py b/bundles/gzr-im-preview/metadata.py new file mode 100644 index 0000000..8cd16c5 --- /dev/null +++ b/bundles/gzr-im-preview/metadata.py @@ -0,0 +1,5 @@ +defaults = { + "www": { + "gzr-im-preview": {}, + }, +} diff --git a/bundles/gzr-im/files/gzr-im.conf b/bundles/gzr-im/files/gzr-im.conf new file mode 100644 index 0000000..8a9d796 --- /dev/null +++ b/bundles/gzr-im/files/gzr-im.conf @@ -0,0 +1 @@ +www_port=${node.metadata.get("www/gzr-im/port")} diff --git a/bundles/gzr-im/files/traefik.yaml b/bundles/gzr-im/files/traefik.yaml new file mode 100644 index 0000000..36e6403 --- /dev/null +++ b/bundles/gzr-im/files/traefik.yaml @@ -0,0 +1,18 @@ +# http: +# services: +# gzr-im: +# loadBalancer: +# servers: +# - url: http://${node.metadata.get("container_gateway", "10.89.0.1")}:${node.metadata.get("www/gzr-im/port")} +# +# routers: +# gzr-im: +# rule: Host(`gzr.im`) || Host(`gnzler.io`) || Host(`gnzler.de`) +# entryPoints: +# - http +# - https +# service: gzr-im +# middlewares: +# - tls-redirect@file +# tls: +# certResolver: le diff --git a/bundles/gzr-im/items.py b/bundles/gzr-im/items.py new file mode 100644 index 0000000..1639559 --- /dev/null +++ b/bundles/gzr-im/items.py @@ -0,0 +1,20 @@ +directories = { + "/var/www/gzr-im": { + "mode": "0775", + "group": "www-data", + "owner": "webdeploy", + }, +} + +files = { + "/etc/traefik/gzr-im.yaml": { + "content_type": "mako", + "source": "traefik.yaml", + "needs": { + "bundle:traefik", + }, + }, + "/etc/s6-rc/gzr-im.conf": { + "content_type": "mako", + }, +} diff --git a/bundles/gzr-im/metadata.py b/bundles/gzr-im/metadata.py new file mode 100644 index 0000000..92592af --- /dev/null +++ b/bundles/gzr-im/metadata.py @@ -0,0 +1,5 @@ +defaults = { + "www": { + "gzr-im": {}, + }, +} diff --git a/bundles/hdidle/files/cron_job b/bundles/hdidle/files/cron_job new file mode 100755 index 0000000..c4d8209 --- /dev/null +++ b/bundles/hdidle/files/cron_job @@ -0,0 +1,5 @@ +#!/bin/sh +set -eu +/usr/bin/hdidle >/dev/null 2>&1 && + tgnotify '💤*shimakaze/hdidle*\nput drives to sleep' >/dev/null 2>&1 +exit 0 diff --git a/bundles/hdidle/files/hdidle b/bundles/hdidle/files/hdidle new file mode 100755 index 0000000..d88c992 --- /dev/null +++ b/bundles/hdidle/files/hdidle @@ -0,0 +1,17 @@ +#!/bin/sh +set -ex + +main() { + disks_were_put_to_sleep=0 + + for drv in /dev/sd*; do + if hdparm -C $drv | grep -vq 'standby'; then + hdparm -y $drv + disks_were_put_to_sleep=1 + fi + done + + test -z "$disks_were_put_to_sleep" && exit 1 || exit 0 +} + +main diff --git a/bundles/hdidle/items.py b/bundles/hdidle/items.py new file mode 100644 index 0000000..b03810e --- /dev/null +++ b/bundles/hdidle/items.py @@ -0,0 +1,16 @@ + +files = { + "/usr/bin/hdidle": { + "source": "hdidle", + "content_type": "text", + "mode": "0755", + }, + "/etc/periodic/hourly/hdidle": { + "source": "cron_job", + "content_type": "text", + "mode": "0755", + "needs": { + "bundle:tgnotify", + }, + }, +} diff --git a/bundles/hdidle/metadata.py b/bundles/hdidle/metadata.py new file mode 100644 index 0000000..19b0117 --- /dev/null +++ b/bundles/hdidle/metadata.py @@ -0,0 +1,7 @@ +defaults = { + "apk": { + "packages": { + "hdparm": {}, + }, + }, +} diff --git a/bundles/immich/files/immich-api-tool b/bundles/immich/files/immich-api-tool new file mode 100755 index 0000000..d0a1a41 --- /dev/null +++ b/bundles/immich/files/immich-api-tool @@ -0,0 +1,49 @@ +#!/bin/sh +set -eu + +API_URL=https://photos.gzr.im/api +API_KEY=zaJUJGHpFqY9OA6kAhuauTj9tC1YVfA8umtZc6ls8U + +request() { + ENDPOINT=${1:?} + shift 1 + + curl -sSfL "${API_URL}${ENDPOINT}" \ + -H "x-api-key: $API_KEY" \ + -H 'Content-Type: application/json' \ + "$@" +} + +validateAccessToken() { + request '/auth/validateToken' -X POST >/dev/null +} + +# returns full json response +searchFavorites() { + request '/search/metadata' \ + -H 'Accept: application/json' \ + -d '{"isFavorite": true}' +} + +# requires '{"assetIds":["list","of","ids",...]}' on stdin +# downloads to PWD +downloadArchive() { + request '/download/archive' \ + -H 'Accept: application/octet-stream' \ + -d @- \ + -o "archive.zip" \ + --no-silent +} + +main() { + validateAccessToken || exit 1 + + # download favorites as archive + searchFavorites | + jq '[.assets.items[].id] | {assetIds:.}' -rc | + downloadArchive +} + +test -n "${DEBUG:-}" && set -x +main +exit 0 diff --git a/bundles/immich/files/immich.yaml b/bundles/immich/files/immich.yaml new file mode 100644 index 0000000..fd2454a --- /dev/null +++ b/bundles/immich/files/immich.yaml @@ -0,0 +1,27 @@ +storageTemplate: + enabled: false + hashVerificationEnabled: true + template: '{{y}}/{{MM}}-{{MMM}}/{{filetype}}_{{y}}{{MM}}{{dd}}_{{HH}}{{mm}}{{ss}}.{{ext}}' + +machineLearning: + enabled: false + # url: + +passwordLogin: + enabled: false +oauth: + enabled: true + autoLaunch: true + autoRegister: true + clientId: immich + clientSecret: '${repo.vault.password_for("authelia_client_secret_immich")}' + issuerUrl: https://login.gzr.im/.well-known/openid-configuration + # scope: 'openid email profile' + # profileSigningAlgorithm: none + # defaultStorageQuota: 0 + # signingAlgorithm: RS256 + # storageLabelClaim: preferred_username + # storageQuotaClaim: immich_quota + buttonText: 'Login with SSO' + # mobileOverrideEnabled: false + # mobileRedirectUri: https://photos.gzr.im/api/oauth/mobile-redirect diff --git a/bundles/immich/files/kube.yaml b/bundles/immich/files/kube.yaml new file mode 100644 index 0000000..523c073 --- /dev/null +++ b/bundles/immich/files/kube.yaml @@ -0,0 +1,104 @@ +apiVersion: v1 +kind: Pod +metadata: + name: immich + labels: + traefik.enable: true + traefik.http.routers.immich.tls: true + traefik.http.routers.immich.tls.certresolver: le + traefik.http.routers.immich.entrypoints: http,https + traefik.http.routers.immich.rule: Host(`photos.gzr.im`) + traefik.http.routers.immich.service: immich + traefik.http.services.immich.loadbalancer.server.port: 2283 + + # TODO: https://immich.app/docs/features/monitoring + # https://github.com/immich-app/immich/blob/v1.121.0/docker/prometheus.yml + # + # metrics + # traefik.http.routers.immich-metrics.entrypoints: metrics + # traefik.http.routers.immich-metrics.rule: Path(`/metrics/immich`) + # traefik.http.routers.immich-metrics.middlewares: replacepath-metrics +spec: + restartPolicy: Always + dnsPolicy: Default + containers: + - name: immich + image: ghcr.io/immich-app/immich-server:v1.140.1 + envFrom: + - secretRef: + name: immich + env: + - name: IMMICH_CONFIG_FILE + value: /immich.yaml + ports: + - containerPort: 2283 + protocol: TCP + # NOTE: required to make tv app work + hostPort: 2283 + volumeMounts: + - name: config + mountPath: /immich.yaml + - name: data + mountPath: /usr/src/app/upload + - name: media + mountPath: /media + readOnly: true + # resources: + # limits: + # cpu: 300m + # memory: 512Mi + - name: redis + image: docker.io/redis:6.2-alpine@sha256:eaba718fecd1196d88533de7ba49bf903ad33664a92debb24660a922ecd9cac8 + livenessProbe: + exec: + command: ["sh", "-c", "redis-cli ping || exit 1"] + - name: postgres + image: docker.io/tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0 + envFrom: + - secretRef: + name: immich + env: + - name: POSTGRES_INITDB_ARGS + value: --data-checksums + volumeMounts: + - name: pg-data + mountPath: /var/lib/postgresql/data + livenessProbe: + exec: + command: + - /bin/sh + - -c + - pg_isready --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' || exit 1 + # ; Chksum="$$(psql --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1 + command: + - docker-entrypoint.sh + - -c + - shared_preload_libraries=vectors.so + - -c + - search_path="$$user", public, vectors + - -c + - logging_collector=on + - -c + - max_wal_size=2GB + - -c + - shared_buffers=512MB + - -c + - wal_compression=on + + volumes: + - name: config + hostPath: + path: /var/lib/immich/immich.yaml + type: File + - name: data + hostPath: + path: /var/lib/immich/app + type: DirectoryOrCreate + - name: pg-data + hostPath: + path: /var/lib/immich/db + type: DirectoryOrCreate + - name: media + hostPath: + path: /media/hayasui/media + type: Directory diff --git a/bundles/immich/files/secret.yaml b/bundles/immich/files/secret.yaml new file mode 100644 index 0000000..daf0f4f --- /dev/null +++ b/bundles/immich/files/secret.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Secret +metadata: + name: immich +data: + DB_USERNAME: ${repo.libs.util.base64("immich")} + POSTGRES_USER: ${repo.libs.util.base64("immich")} + DB_PASSWORD: ${repo.vault.password_for("immich_db_pw").b64encode().value} + POSTGRES_PASSWORD: ${repo.vault.password_for("immich_db_pw").b64encode().value} + DB_DATABASE_NAME: ${repo.libs.util.base64("immich")} + POSTGRES_DB: ${repo.libs.util.base64("immich")} + DB_HOSTNAME: ${repo.libs.util.base64(f"localhost")} diff --git a/bundles/immich/items.py b/bundles/immich/items.py new file mode 100644 index 0000000..450027c --- /dev/null +++ b/bundles/immich/items.py @@ -0,0 +1,15 @@ +files = { + "/etc/deployments/immich/kube.yaml": { + "triggers": {"svc_s6rc:immich:restart"}, + }, + "/etc/deployments/immich/secret.yaml": { + "content_type": "mako", + "mode": "0600", + "triggers": {"svc_s6rc:immich:restart"}, + }, + "/var/lib/immich/immich.yaml": { + "content_type": "mako", + "mode": "0600", + "triggers": {"svc_s6rc:immich:restart"}, + }, +} diff --git a/bundles/immich/metadata.py b/bundles/immich/metadata.py new file mode 100644 index 0000000..2a0a806 --- /dev/null +++ b/bundles/immich/metadata.py @@ -0,0 +1,17 @@ +defaults = { + # https://immich.app/docs/administration/backup-and-restore/#manual-backup-and-restore + "backup": { + "includes": { + "/var/lib/immich/app/backups", + "/var/lib/immich/app/profile", + "/var/lib/immich/app/upload", + # "/media/hayasui/media/pictures", + } + }, + "containers": { + "immich": {}, + }, + # "metrics": { + # "immich": {}, + # }, +} diff --git a/bundles/jellyfin/bcast.py b/bundles/jellyfin/bcast.py new file mode 100644 index 0000000..2318441 --- /dev/null +++ b/bundles/jellyfin/bcast.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +import socket +from time import sleep + +def main(): + msg = b'Who is JellyfinServer?' + + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # UDP + sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + sock.bind(('0.0.0.0',0)) + sock.sendto(msg, ("255.255.255.255", 7359)) + sock.settimeout(10) + recvd, (ip, port) = sock.recvfrom(4096) + sock.close() + print(f"{ip}:{port} says:") + print(recvd.decode('utf-8')) + +main() + diff --git a/bundles/jellyfin/files/SSO-Auth.xml b/bundles/jellyfin/files/SSO-Auth.xml new file mode 100644 index 0000000..50dfdc8 --- /dev/null +++ b/bundles/jellyfin/files/SSO-Auth.xml @@ -0,0 +1,71 @@ + + + + + + + authelia + + + + https://login.gzr.im + jellyfin + ${repo.vault.cmd(f"rbw get authelia/client/jellyfin")} + true + true + false + + f137a2dd21bbc1b99aa5c0f6bf02a805 + a656b907eb3a73532e40e44b968d0225 + + + admins + + + jellyfin + + true + false + false + false + + + + + jellyfin + + f137a2dd21bbc1b99aa5c0f6bf02a805 + a656b907eb3a73532e40e44b968d0225 + + + + jellyfin-anime + + abebc196cc1b8bbf6f8bb5ca7b5ad6f1 + 29391378c4118b35b77f84980d25f0a6 + + + + jellyfin-misc + + a1bc8a1aac164cd3d91bf3f4252f92f4 + + + + groups + + groups + + authelia + https + true + + false + false + false + false + + + + + diff --git a/bundles/jellyfin/files/branding.xml b/bundles/jellyfin/files/branding.xml new file mode 100644 index 0000000..487db6e --- /dev/null +++ b/bundles/jellyfin/files/branding.xml @@ -0,0 +1,17 @@ + + + <form action="https://tv.gzr.im/sso/OID/start/authelia"> + <button class="raised block emby-button button-submit"> + Sign in with SSO + </button> +</form> + a.raised.emby-button { + padding: 0.9em 1em; + color: inherit !important; +} + +.disclaimerContainer { + display: block; +} + false + diff --git a/bundles/jellyfin/files/kube.yaml b/bundles/jellyfin/files/kube.yaml new file mode 100644 index 0000000..fb6897c --- /dev/null +++ b/bundles/jellyfin/files/kube.yaml @@ -0,0 +1,72 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: jellyfin +data: + JELLYFIN_PublishedServerUrl: http://shimakaze:8096 +--- +apiVersion: v1 +kind: Pod +metadata: + name: jellyfin + labels: + traefik.enable: true + traefik.http.routers.jellyfin.tls: true + traefik.http.routers.jellyfin.tls.certresolver: le + traefik.http.routers.jellyfin.entrypoints: http,https + traefik.http.routers.jellyfin.rule: Host(`tv.gzr.im`) + traefik.http.routers.jellyfin.service: jellyfin + traefik.http.services.jellyfin.loadbalancer.server.port: 8096 + # metrics + traefik.http.routers.jellyfin-metrics.entrypoints: metrics + traefik.http.routers.jellyfin-metrics.rule: Path(`/metrics/jellyfin`) + traefik.http.routers.jellyfin-metrics.middlewares: replacepath-metrics +spec: + # NOTE: required to make discovery work + # jellyfin clients use udp broadcast messages on port 7359 + # sending "Who is JellyfinServer?". To receive those requests the + # jellyfin server can't be behind container networking + hostNetwork: true + + restartPolicy: Never + dnsPolicy: Default + containers: + - name: jellyfin + image: ghcr.io/jellyfin/jellyfin:10.10.7 + envFrom: + - configMapRef: + name: jellyfin + ports: + - containerPort: 8096 + protocol: TCP + - containerPort: 1900 + protocol: UDP + - containerPort: 7359 + protocol: UDP + volumeMounts: + - name: data + mountPath: /config + - name: cache + mountPath: /cache + - name: media + mountPath: /media + readOnly: true + resources: + requests: + podman.io/device=/dev/dri/card0: 1 + podman.io/device=/dev/dri/renderD128: 1 + limits: + #cpu: 300m + #memory: 512Mi + + volumes: + - name: data + hostPath: + path: /var/lib/jellyfin + type: DirectoryOrCreate + - name: cache + emptyDir: {} + - name: media + hostPath: + path: /media/hayasui/media + type: Directory diff --git a/bundles/jellyfin/files/logging.json b/bundles/jellyfin/files/logging.json new file mode 100644 index 0000000..b658515 --- /dev/null +++ b/bundles/jellyfin/files/logging.json @@ -0,0 +1,11 @@ +{ + "Serilog": { + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "System": "Warning" + } + } + } +} diff --git a/bundles/jellyfin/files/system.xml b/bundles/jellyfin/files/system.xml new file mode 100644 index 0000000..0696f49 --- /dev/null +++ b/bundles/jellyfin/files/system.xml @@ -0,0 +1,202 @@ + + + 3 + true + /cache + true + true + true + false + true + true + /config/metadata + en + DE + + . + + + % + + + , + & + - + { + } + ' + + + the + a + an + + 5 + 90 + 300 + 5 + 5 + 0 + 60 + 30 + Legacy + + + Book + + + + + + + + + Movie + + + + + + + + + MusicVideo + + + + The Open Movie Database + + + + The Open Movie Database + + + + + Series + + + + + + + + + MusicAlbum + + + + TheAudioDB + + + + + + + MusicArtist + + + + TheAudioDB + + + + + + + BoxSet + + + + + + + + + Season + + + + + + + + + Episode + + + + + + + + + true + jellyfin + en-US + false + + 0 + false + false + true + + + + Jellyfin Stable + https://repo.jellyfin.org/files/plugin/manifest.json + true + + + Jellyfin SSO + https://raw.githubusercontent.com/9p4/jellyfin-plugin-sso/manifest-release/manifest.json + true + + + Intro Skipper + https://manifest.intro-skipper.org/manifest.json + true + + + true + 0 + + true + 500 + + * + + 30 + 0 + 0 + false + true + 0 + MatchSource + 0 + + + F007D354 + Stable + + + 6F511C87 + Unstable + + + + false + false + false + NonBlocking + BelowNormal + 10000 + + 320 + + 10 + 10 + 4 + 90 + 1 + + diff --git a/bundles/jellyfin/items.py b/bundles/jellyfin/items.py new file mode 100644 index 0000000..5ed07ef --- /dev/null +++ b/bundles/jellyfin/items.py @@ -0,0 +1,33 @@ +files = { + "/var/lib/jellyfin/plugins/SSO-Auth.zip": { + "content_type": "download", + "source": "https://github.com/9p4/jellyfin-plugin-sso/releases/download/v3.5.2.4/sso-authentication_3.5.2.4.zip", + "content_hash": "a2f00e5acc7adf785202e27fffc969e019cbd9e8", + "triggers": {"action:extract-jellyfin-plugin-sso"}, + }, + "/var/lib/jellyfin/config/branding.xml": { + "content_type": "text", + "triggers": {"svc_s6rc:jellyfin:restart"}, + }, + "/var/lib/jellyfin/config/logging.json": { + "content_type": "mako", + "triggers": {"svc_s6rc:jellyfin:restart"}, + }, + "/var/lib/jellyfin/plugins/configurations/SSO-Auth.xml": { + "content_type": "mako", + "triggers": {"svc_s6rc:jellyfin:restart"}, + }, + + "/etc/deployments/jellyfin/kube.yaml": { + "content_type": "text", + "source": "kube.yaml", + "triggers": {"svc_s6rc:jellyfin:restart"}, + }, +} + +actions = { + "extract-jellyfin-plugin-sso": { + "command": "unzip -d /var/lib/jellyfin/plugins/SSO-Auth /var/lib/jellyfin/plugins/SSO-Auth.zip", + "triggered": True, + }, +} diff --git a/bundles/jellyfin/metadata.py b/bundles/jellyfin/metadata.py new file mode 100644 index 0000000..a614afd --- /dev/null +++ b/bundles/jellyfin/metadata.py @@ -0,0 +1,16 @@ +defaults = { + "backup": { + "includes": { + "/var/lib/jellyfin", + # "/media/hayasui/media/movies", + # "/media/hayasui/media/tvshows", + # "/media/hayasui/media/anime", + } + }, + "containers": { + "jellyfin": {}, + }, + "metrics": { + "jellyfin": {}, + }, +} diff --git a/bundles/langtool/files/kube.yaml b/bundles/langtool/files/kube.yaml new file mode 100644 index 0000000..f4f2fed --- /dev/null +++ b/bundles/langtool/files/kube.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Pod +metadata: + name: langtool + labels: + traefik.enable: true + traefik.http.routers.langtool.tls: true + traefik.http.routers.langtool.tls.certresolver: le + traefik.http.routers.langtool.entrypoints: http,https + traefik.http.routers.langtool.rule: Host(`langtool.gzr.im`) + traefik.http.routers.langtool.service: langtool + traefik.http.routers.langtool.middlewares: tls-redirect@file + traefik.http.services.langtool.loadbalancer.server.port: 8010 + prometheus.scrape.enable: false +spec: + restartPolicy: Never + dnsPolicy: Default + imagePullPolicy: Always + containers: + - name: langtool + image: docker.io/erikvl87/languagetool:latest + ports: + - containerPort: 8010 + protocol: TCP diff --git a/bundles/langtool/items.py b/bundles/langtool/items.py new file mode 100644 index 0000000..78d8a06 --- /dev/null +++ b/bundles/langtool/items.py @@ -0,0 +1,5 @@ +files = { + "/etc/deployments/langtool/kube.yaml": { + "triggers": {"svc_s6rc:langtool:restart"}, + }, +} diff --git a/bundles/langtool/metadata.py b/bundles/langtool/metadata.py new file mode 100644 index 0000000..3509857 --- /dev/null +++ b/bundles/langtool/metadata.py @@ -0,0 +1,5 @@ +defaults = { + "containers": { + "langtool": {}, + }, +} diff --git a/bundles/lhost/files/Caddyfile b/bundles/lhost/files/Caddyfile new file mode 100644 index 0000000..497ef30 --- /dev/null +++ b/bundles/lhost/files/Caddyfile @@ -0,0 +1,45 @@ +# vim: noet ts=2 sw=2 +{ + admin off + auto_https off + http_port {$CADDY_HTTP_PORT:1414} +} + +http:// { + encode zstd gzip + + handle /healthz { + respond OK 200 + } + + handle { + root . + file_server { + hide Caddyfile + hide .keep + } + } + + # catch all + respond 404 + + log { + format filter { + wrap console { + time_format wall_milli + level_format color + } + fields { + common_log delete + request>headers>Accept-Encoding delete + request>headers>Accept-Language delete + request>headers>Connection delete + request>headers>Authorization delete + request>remote_addr ip_mask { + ipv4 24 + ipv6 32 + } + } + } + } +} diff --git a/bundles/lhost/files/lhost-prune b/bundles/lhost/files/lhost-prune new file mode 100755 index 0000000..8788941 --- /dev/null +++ b/bundles/lhost/files/lhost-prune @@ -0,0 +1,20 @@ +#!/bin/sh +set -eu + +main() { + local dryrun=$(test -n "${DRYRUN:-}" && printf echo) + local root=${ROOT:-/var/www/lhost} + local maxage=${MAXAGE:-30} + + find ${root} \ + -type f \ + -mtime ${maxage} \ + -not \( \ + -name 'Caddyfile' -or \ + -name '.keep' -or \ + -name "$(cat ${root}/.keep)" \ + \) \ + -exec ${dryrun} rm -rv {} + +} + +main >/dev/null 2>&1 diff --git a/bundles/lhost/files/lhost.conf b/bundles/lhost/files/lhost.conf new file mode 100644 index 0000000..fbf2dc6 --- /dev/null +++ b/bundles/lhost/files/lhost.conf @@ -0,0 +1 @@ +www_port=${node.metadata.get("www/lhost/port")} diff --git a/bundles/lhost/files/traefik.yaml b/bundles/lhost/files/traefik.yaml new file mode 100644 index 0000000..d1722a0 --- /dev/null +++ b/bundles/lhost/files/traefik.yaml @@ -0,0 +1,18 @@ +http: + services: + lhost: + loadBalancer: + servers: + - url: http://${node.metadata.get("container_gateway", "10.89.0.1")}:${node.metadata.get("www/lhost/port")} + + routers: + lhost: + rule: Host(`l.gzr.im`) + entryPoints: + - http + - https + service: lhost + middlewares: + - tls-redirect@file + tls: + certResolver: le diff --git a/bundles/lhost/items.py b/bundles/lhost/items.py new file mode 100644 index 0000000..ac2e428 --- /dev/null +++ b/bundles/lhost/items.py @@ -0,0 +1,24 @@ +directories = { + "/var/www/lhost": { + "mode": "0775", + "group": "www-data", + "owner": "webdeploy", + }, +} + +files = { + "/var/www/lhost/Caddyfile": {}, + "/etc/traefik/lhost.yaml": { + "content_type": "mako", + "source": "traefik.yaml", + "needs": { + "bundle:traefik", + }, + }, + "/etc/s6-rc/lhost.conf": { + "content_type": "mako", + }, + "/etc/periodic/weekly/lhost-prune": { + "mode": "0755", + }, +} diff --git a/bundles/lhost/metadata.py b/bundles/lhost/metadata.py new file mode 100644 index 0000000..a0a86b6 --- /dev/null +++ b/bundles/lhost/metadata.py @@ -0,0 +1,5 @@ +defaults = { + "www": { + "lhost": {}, + }, +} diff --git a/bundles/minecraft/README b/bundles/minecraft/README new file mode 100644 index 0000000..f091b90 --- /dev/null +++ b/bundles/minecraft/README @@ -0,0 +1,5 @@ +https://www.gameslearningsociety.org/wiki/how-do-i-make-an-existing-world-into-a-server/ +https://luckperms.net/wiki/Why-LuckPerms +https://purpurmc.org/docs/purpur/configuration/ +https://github.com/YouHaveTrouble/minecraft-optimization?tab=readme-ov-file +https://flags.sh/ diff --git a/bundles/minecraft/files/kube.yaml b/bundles/minecraft/files/kube.yaml new file mode 100644 index 0000000..8e6bb93 --- /dev/null +++ b/bundles/minecraft/files/kube.yaml @@ -0,0 +1,43 @@ +--- +apiVersion: v1 +kind: Pod +metadata: + name: minecraft + labels: + traefik.enable: true + traefik.http.routers.minecraft.tls: true + traefik.http.routers.minecraft.tls.certresolver: le + traefik.http.routers.minecraft.entrypoints: http,https + traefik.http.routers.minecraft.rule: Host(`mc.gzr.im`) + traefik.http.routers.minecraft.service: jellyfin + traefik.http.services.minecraft.loadbalancer.server.port: 8096 + # # metrics + # traefik.http.routers.jellyfin-metrics.entrypoints: metrics + # traefik.http.routers.jellyfin-metrics.rule: Path(`/metrics/jellyfin`) + # traefik.http.routers.jellyfin-metrics.middlewares: replacepath-metrics +spec: + restartPolicy: Never + dnsPolicy: Default + imagePullPolicy: Always + containers: + - name: purpur + image: docker.io/josxha/minecraft-purpur:1.21.8 + # envFrom: + # - configMapRef: + # name: jellyfin + ports: + - containerPort: 25565 + protocol: TCP + volumeMounts: + - name: data + mountPath: /data + resources: + limits: + #cpu: 300m + #memory: 512Mi + + volumes: + - name: data + hostPath: + path: /var/lib/minecraft + type: DirectoryOrCreate diff --git a/bundles/minecraft/items.py b/bundles/minecraft/items.py new file mode 100644 index 0000000..d46bcf0 --- /dev/null +++ b/bundles/minecraft/items.py @@ -0,0 +1,7 @@ +files = { + "/etc/deployments/minecraft/kube.yaml": { + "content_type": "text", + "source": "kube.yaml", + "triggers": {"svc_s6rc:minecraft:restart"}, + }, +} diff --git a/bundles/minecraft/metadata.py b/bundles/minecraft/metadata.py new file mode 100644 index 0000000..e600efe --- /dev/null +++ b/bundles/minecraft/metadata.py @@ -0,0 +1,16 @@ +defaults = { + # "backup": { + # "includes": { + # "/var/lib/minecraft", + # # "/media/hayasui/media/movies", + # # "/media/hayasui/media/tvshows", + # # "/media/hayasui/media/anime", + # } + # }, + "containers": { + "minecraft": {}, + }, + # "metrics": { + # "minecraft": {}, + # }, +} diff --git a/bundles/miniflux/files/kube.yaml b/bundles/miniflux/files/kube.yaml new file mode 100644 index 0000000..83b7edf --- /dev/null +++ b/bundles/miniflux/files/kube.yaml @@ -0,0 +1,84 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: miniflux +data: + BASE_URL: https://rss.gzr.im + CREATE_ADMIN: on + RUN_MIGRATIONS: on + METRICS_COLLECTOR: 1 + METRICS_REFRESH_INTERVAL: 600 + METRICS_ALLOWED_NETWORKS: 127.0.0.1/8,10.89.0.0/24 + # use authelia + AUTH_PROXY_HEADER: 'Remote-User' + AUTH_PROXY_USER_CREATION: 0 +--- +apiVersion: v1 +kind: Pod +metadata: + name: miniflux + labels: + traefik.enable: true + traefik.http.routers.miniflux.tls: true + traefik.http.routers.miniflux.tls.certresolver: le + traefik.http.routers.miniflux.entrypoints: http,https + traefik.http.routers.miniflux.rule: Host(`rss.gzr.im`) + traefik.http.routers.miniflux.service: miniflux + traefik.http.routers.miniflux.middlewares: authelia@docker + traefik.http.services.miniflux.loadbalancer.server.port: 8080 + # metrics + traefik.http.routers.miniflux-metrics.entrypoints: metrics + traefik.http.routers.miniflux-metrics.rule: Path(`/metrics/miniflux`) + traefik.http.routers.miniflux-metrics.middlewares: replacepath-metrics +spec: + # NOTE: we need to compensate for the lack of healthcheck-based retries + restartPolicy: Always + dnsPolicy: Default + containers: + - name: miniflux + image: docker.io/miniflux/miniflux:2.2.10 + ports: + - containerPort: 8080 + protocol: TCP + envFrom: + - configMapRef: + name: miniflux + - secretRef: + name: miniflux + livenessProbe: + exec: + command: + - /usr/bin/miniflux + - -healthcheck + - auto + resources: + requests: + cpu: 10m + memory: 60Mi + limits: + cpu: 100m + memory: 128Mi + - name: postgres + image: docker.io/library/postgres:15-alpine + volumeMounts: + - name: pg-data + mountPath: /var/lib/postgresql/data + envFrom: + - secretRef: + name: miniflux + livenessProbe: + exec: + command: ["pg_isready", "-U", "miniflux"] + initialDelaySeconds: 5 + resources: + requests: + cpu: 10m + memory: 6Mi + limits: + cpu: 100m + memory: 128Mi + volumes: + - name: pg-data + hostPath: + path: /var/lib/miniflux + type: DirectoryOrCreate diff --git a/bundles/miniflux/files/secret.yaml b/bundles/miniflux/files/secret.yaml new file mode 100644 index 0000000..7a958c2 --- /dev/null +++ b/bundles/miniflux/files/secret.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: miniflux +data: + ADMIN_PASSWORD: ${repo.vault.password_for("miniflux_admin_pw").b64encode().value} + ADMIN_USERNAME: ${repo.vault.password_for("miniflux_admin_user").b64encode().value} + DATABASE_URL: ${repo.libs.util.base64(f"user=miniflux password={repo.vault.password_for("miniflux_db_pw")} dbname=miniflux sslmode=disable host=localhost")} + POSTGRES_USER: ${repo.libs.util.base64("miniflux")} + POSTGRES_PASSWORD: ${repo.vault.password_for("miniflux_db_pw").b64encode().value} diff --git a/bundles/miniflux/items.py b/bundles/miniflux/items.py new file mode 100644 index 0000000..8245778 --- /dev/null +++ b/bundles/miniflux/items.py @@ -0,0 +1,11 @@ +files = { + "/etc/deployments/miniflux/kube.yaml": { + "content_type": "text", + "triggers": {"svc_s6rc:miniflux:restart"}, + }, + "/etc/deployments/miniflux/secret.yaml": { + "content_type": "mako", + "mode": "0600", + "triggers": {"svc_s6rc:miniflux:restart"}, + }, +} diff --git a/bundles/miniflux/metadata.py b/bundles/miniflux/metadata.py new file mode 100644 index 0000000..a6e3b41 --- /dev/null +++ b/bundles/miniflux/metadata.py @@ -0,0 +1,13 @@ +defaults = { + "backup": { + "includes": { + "/var/lib/miniflux", + } + }, + "containers": { + "miniflux": {}, + }, + "metrics": { + "miniflux": {} + }, +} diff --git a/bundles/motd/items.py b/bundles/motd/items.py new file mode 100644 index 0000000..70d47fa --- /dev/null +++ b/bundles/motd/items.py @@ -0,0 +1,5 @@ +files = { + "/etc/motd": { + "source": f"{node.name}", + }, +} diff --git a/bundles/nfs/files/exports b/bundles/nfs/files/exports new file mode 100644 index 0000000..faa7da1 --- /dev/null +++ b/bundles/nfs/files/exports @@ -0,0 +1,5 @@ +/var/nfs 100.64.0.0/10(ro,sync,insecure,no_root_squash,no_subtree_check,fsid=0) +/var/nfs/bigdump *(rw,sync,insecure,no_root_squash,no_subtree_check,hide) +/var/nfs/home-robert *(rw,sync,insecure,all_squash,no_subtree_check,hide) +/var/nfs/media *(rw,sync,insecure,no_root_squash,no_subtree_check,nohide) +/var/nfs/public *(ro,sync,insecure,all_squash,no_subtree_check,hide) diff --git a/bundles/nfs/files/fstab b/bundles/nfs/files/fstab new file mode 100644 index 0000000..0da4f54 --- /dev/null +++ b/bundles/nfs/files/fstab @@ -0,0 +1,17 @@ +#UUID=02214da4-00c9-4fa9-b456-59d36525ed7c / f2fs rw,lazytime,relatime,background_gc=on,discard,no_heap,user_xattr,inline_xattr,acl,inline_data,inline_dentry,flush_merge,extent_cache,mode=adaptive,active_logs=6,alloc_mode=default,checkpoint_merge,fsync_mode=posix,discard_unit=block 0 0 +UUID=02214da4-00c9-4fa9-b456-59d36525ed7c / f2fs defaults,rw 0 0 +UUID=879E-1462 /boot/efi vfat rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=utf8,shortname=mixed,errors=remount-ro 0 2 +UUID=f602a804-51db-4712-b01e-565ae476143c swap swap defaults 0 0 +/dev/cdrom /media/cdrom iso9660 noauto,ro 0 0 +#/dev/usbdisk /media/usb vfat noauto 0 0 + +tmpfs /tmp tmpfs size=500G 0 0 + +UUID=ef037f7d-a271-4d62-b60b-0b7bb72952ca /media/hayasui btrfs defaults 0 0 +UUID=9d94e642-0ed3-4b80-915c-c154daee870c /media/bigdump btrfs defaults 0 0 + +UUID=5699-A10B /media/usb exfat defaults,uid=1000,gid=1000 0 0 + +# nfs shares +/media/hayasui/media /var/nfs/media none rbind 0 0 +/media/bigdump /var/nfs/bigdump none rbind 0 0 diff --git a/bundles/nfs/files/nfs.conf.d b/bundles/nfs/files/nfs.conf.d new file mode 100644 index 0000000..f077bfc --- /dev/null +++ b/bundles/nfs/files/nfs.conf.d @@ -0,0 +1,38 @@ +# /etc/conf.d/nfs + +# If you wish to set the port numbers for lockd, +# please see /etc/sysctl.conf + +# Optional services to include in default `/etc/init.d/nfs start` +# For NFSv4 users, you'll want to add "rpc.idmapd" here. +NFS_NEEDED_SERVICES="rpc.idmapd" + +# Options to pass to rpc.nfsd +OPTS_RPC_NFSD="--no-nfs-version 3 --nfs-version 4 8" + +# Options to pass to rpc.mountd +# ex. OPTS_RPC_MOUNTD="-p 32767" +OPTS_RPC_MOUNTD="--no-nfs-version 3 --nfs-version 4" + +# Options to pass to rpc.statd +# ex. OPTS_RPC_STATD="-p 32765 -o 32766" +OPTS_RPC_STATD="" + +# Options to pass to rpc.idmapd +OPTS_RPC_IDMAPD="" + +# Options to pass to rpc.gssd +OPTS_RPC_GSSD="" + +# Options to pass to rpc.svcgssd +OPTS_RPC_SVCGSSD="" + +# Options to pass to rpc.rquotad (requires sys-fs/quota) +OPTS_RPC_RQUOTAD="" + +# Timeout (in seconds) for exportfs +EXPORTFS_TIMEOUT=30 + +# Options to set in the nfsd filesystem (/proc/fs/nfsd/). +# Format is