Runner updated and eurovote
This commit is contained in:
@@ -139,12 +139,15 @@ in
|
||||
# Monitoring — uptime-kuma has monitors/history, ntfy has user accounts
|
||||
"${dataDir}/uptime-kuma"
|
||||
"${dataDir}/ntfy"
|
||||
# Eurovision Vote — SQLite DB with votes and rankings
|
||||
"/var/lib/eurovote"
|
||||
];
|
||||
|
||||
# Exclude Nextcloud's raw DB directory in favour of the pg_dump file
|
||||
exclude = [
|
||||
"${dataDir}/nextcloud/db"
|
||||
"${dataDir}/restic-cache"
|
||||
"${dataDir}/media"
|
||||
];
|
||||
|
||||
timerConfig = {
|
||||
|
||||
+27
-6
@@ -42,19 +42,16 @@ let
|
||||
|
||||
# Reusable Authelia forward_auth snippet
|
||||
# Returns a Caddyfile snippet block that applies forward_auth.
|
||||
# Uses the v4.38+ /api/authz/forward-auth endpoint which correctly honours
|
||||
# one_factor policy without forcing TOTP enrollment on new users.
|
||||
# copy_headers makes Authelia's Remote-* headers available downstream.
|
||||
autheliaForwardAuth = ''
|
||||
forward_auth localhost:9091 {
|
||||
uri /api/verify?rd=https://auth.${domain}
|
||||
uri /api/authz/forward-auth?authelia_url=https://auth.${domain}
|
||||
copy_headers Remote-User Remote-Name Remote-Groups Remote-Email
|
||||
# Always tell Authelia the scheme is https (cloudflared terminates TLS
|
||||
# externally; Caddy's http:// vhosts are only for the tunnel loopback).
|
||||
header_up X-Forwarded-Proto https
|
||||
# On auth failure, redirect to the authelia login page
|
||||
@goauth status 401
|
||||
handle_response @goauth {
|
||||
redir https://auth.${domain}?rm={method} 302
|
||||
}
|
||||
}
|
||||
'';
|
||||
|
||||
@@ -236,6 +233,30 @@ in
|
||||
extraConfig = cfProxy 2586;
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Eurovision Vote — one_factor for all authenticated users.
|
||||
# /admin/* is restricted to group:admins by Authelia access_control.
|
||||
# Caddy passes Remote-User → X-Remote-User so Django auto-logs in
|
||||
# the SSO-authenticated user via RemoteUserMiddleware.
|
||||
# ------------------------------------------------------------------
|
||||
"eurovision-vote.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
reverse_proxy localhost:8007 {
|
||||
header_up X-Remote-User {http.request.header.Remote-User}
|
||||
}
|
||||
'';
|
||||
};
|
||||
"http://eurovision-vote.${domain}" = {
|
||||
extraConfig = ''
|
||||
${autheliaForwardAuth}
|
||||
reverse_proxy localhost:8007 {
|
||||
header_up X-Forwarded-Proto https
|
||||
header_up X-Remote-User {http.request.header.Remote-User}
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Grafana — two_factor, admins only (enforced by authelia policy).
|
||||
# After Authelia verifies the user, Caddy maps the Remote-User header
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
# between weekly GC runs.
|
||||
min-free = 2147483648; # 2 GiB
|
||||
max-free = 5368709120; # 5 GiB
|
||||
# Use the external drive for sandbox builds — the default /tmp is a
|
||||
# small RAM-backed tmpfs that fills up during large builds (e.g. wrangler).
|
||||
build-dir = "/mnt/data/nix-build";
|
||||
};
|
||||
gc = {
|
||||
automatic = true;
|
||||
@@ -37,6 +40,10 @@
|
||||
# Allow unfree packages (e.g. cloudflared binary)
|
||||
nixpkgs.config.allowUnfree = true;
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /mnt/data/nix-build 0755 root root -"
|
||||
];
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Boot — set in hardware.nix; this is just a safe default
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
@@ -128,6 +128,22 @@ let
|
||||
- domain:
|
||||
- "ntfy.${domain}"
|
||||
policy: "bypass"
|
||||
# Eurovision Vote: /admin/* for admins only; all others one_factor
|
||||
- domain:
|
||||
- "eurovision-vote.${domain}"
|
||||
resources:
|
||||
- "^/admin.*$"
|
||||
subject:
|
||||
- "group:admins"
|
||||
policy: "two_factor"
|
||||
- domain:
|
||||
- "eurovision-vote.${domain}"
|
||||
resources:
|
||||
- "^/admin.*$"
|
||||
policy: "deny"
|
||||
- domain:
|
||||
- "eurovision-vote.${domain}"
|
||||
policy: "one_factor"
|
||||
|
||||
notifier:
|
||||
filesystem:
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
{ config, lib, pkgs, homeyConfig, ... }:
|
||||
|
||||
# Eurovision Vote — Django app for ranking Eurovision performances.
|
||||
#
|
||||
# Uses the NixOS module from the eurovote flake (eurovote.nixosModules.default).
|
||||
# This wrapper wires it into the homey module system: enable flag, sops secret,
|
||||
# and uptime monitoring.
|
||||
#
|
||||
# The app uses DynamicUser + StateDirectory so systemd owns /var/lib/eurovote/;
|
||||
# no tmpfiles.rules entry needed.
|
||||
#
|
||||
# Authentication: Caddy forward_auth → Authelia; the app reads the
|
||||
# X-Remote-User header set by Caddy (from Authelia's Remote-User).
|
||||
# All authenticated users get app access; /admin/* is restricted to
|
||||
# group:admins by Authelia's access_control rules (see authelia.nix).
|
||||
#
|
||||
# Secrets consumed from sops:
|
||||
# eurovote/secret_key
|
||||
|
||||
let
|
||||
cfg = config.homey.eurovote;
|
||||
domain = homeyConfig.domain;
|
||||
in
|
||||
{
|
||||
options.homey.eurovote = {
|
||||
enable = lib.mkEnableOption "Eurovision Vote app";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# -----------------------------------------------------------------------
|
||||
# Secrets
|
||||
# -----------------------------------------------------------------------
|
||||
# mode 0444: the service runs as a DynamicUser (random UID) so it cannot
|
||||
# read a root-owned 0400 file. /run/secrets/ itself is not world-listable
|
||||
# (mode 0751), so world-readable here is acceptable on a home server.
|
||||
sops.secrets."eurovote/secret_key" = { owner = "root"; mode = "0444"; };
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Service (options provided by eurovote.nixosModules.default)
|
||||
# -----------------------------------------------------------------------
|
||||
services.eurovote = {
|
||||
enable = true;
|
||||
port = 8007;
|
||||
allowedHosts = "localhost 127.0.0.1 eurovision-vote.${domain}";
|
||||
secretKeyFile = config.sops.secrets."eurovote/secret_key".path;
|
||||
trustedOrigins = "https://eurovision-vote.${domain}";
|
||||
# After SSO logout, send the user back to Authelia's logout page
|
||||
logoutRedirectUrl = "https://auth.${domain}/logout";
|
||||
};
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Uptime Kuma monitor
|
||||
# -----------------------------------------------------------------------
|
||||
homey.monitoring.monitors = [{
|
||||
name = "Eurovision Vote";
|
||||
url = "https://eurovision-vote.${domain}";
|
||||
interval = 60;
|
||||
}];
|
||||
};
|
||||
}
|
||||
@@ -8,8 +8,8 @@
|
||||
# nested containers on a Pi 4.
|
||||
#
|
||||
# The service uses DynamicUser=true so there is no persistent system user.
|
||||
# nix/git/bash are available in jobs via the system PATH inherited from the
|
||||
# service environment.
|
||||
# Job step PATH is controlled by hostPackages (not the service PATH).
|
||||
# nix is not in the NixOS module's default hostPackages and must be added.
|
||||
#
|
||||
# Setup (one-time):
|
||||
# 1. In Gitea: Site Administration → Actions → Runners → Create Runner Token
|
||||
@@ -61,8 +61,12 @@ in
|
||||
tokenFile = config.sops.secrets."gitea/runner_token".path;
|
||||
name = cfg.name;
|
||||
labels = cfg.labels;
|
||||
# nix/git/bash are accessible via the system PATH (/run/current-system/sw/bin/)
|
||||
# without any extra configuration — the runner inherits it as a system user.
|
||||
# hostPackages controls the PATH available to job steps (host executor).
|
||||
# nix is not in the default list so must be added explicitly.
|
||||
hostPackages = with pkgs; [
|
||||
bash coreutils curl gawk gitMinimal gnused nodejs wget
|
||||
nix
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -130,11 +130,6 @@ let
|
||||
api.disconnect()
|
||||
sys.exit(1)
|
||||
|
||||
# Collect all configured notification IDs so every monitor gets them.
|
||||
notification_ids = [n["id"] for n in api.get_notifications()]
|
||||
if notification_ids:
|
||||
print(f"uptime-kuma-sync: attaching notifications: {notification_ids}")
|
||||
|
||||
# Sync monitors: add missing, update changed
|
||||
try:
|
||||
existing = {m["name"]: m for m in api.get_monitors()}
|
||||
@@ -150,7 +145,6 @@ let
|
||||
url=m["url"],
|
||||
interval=m.get("interval", 60),
|
||||
maxretries=maxretries,
|
||||
notification_id_list={str(nid): True for nid in notification_ids},
|
||||
**extra,
|
||||
)
|
||||
print(f"uptime-kuma-sync: created monitor: {m['name']}")
|
||||
@@ -163,7 +157,6 @@ let
|
||||
url=m["url"],
|
||||
interval=m.get("interval", 60),
|
||||
maxretries=maxretries,
|
||||
notification_id_list={str(nid): True for nid in notification_ids},
|
||||
**extra,
|
||||
)
|
||||
print(f"uptime-kuma-sync: updated monitor: {m['name']}")
|
||||
|
||||
Reference in New Issue
Block a user