From bd7144b20b47e3ba2113e66570f13edceb60a17c Mon Sep 17 00:00:00 2001 From: Jonas Heinrich Date: Wed, 12 Mar 2025 12:14:17 +0100 Subject: [PATCH] change config format to env vars --- config.yaml.dist | 17 ---------- eintopf-radar-sync.py | 33 +++++------------- module.nix | 78 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 72 insertions(+), 56 deletions(-) delete mode 100644 config.yaml.dist diff --git a/config.yaml.dist b/config.yaml.dist deleted file mode 100644 index 345519e..0000000 --- a/config.yaml.dist +++ /dev/null @@ -1,17 +0,0 @@ -# Eintopf config -# Get Authorization token through login request -# http://localhost:3333/api/v1/swagger#/auth/login - -variables: - eintopf_url: "https://karlsunruh.project-insanity.org" - radar_group_id: "436012" - authorization_token: "YOUR_SECRET_TOKEN" - -eintopf: - api_endpoint: "$eintopf_url/api/v1/events/" - headers: - Authorization: "$authorization_token" - Content-Type: "application/json" - -radar: - api_endpoint: "https://radar.squat.net/api/1.2/search/events.json?fields=title,offline,date_time,body&facets[group][]=$radar_group_id" diff --git a/eintopf-radar-sync.py b/eintopf-radar-sync.py index e697e3e..3127ee0 100644 --- a/eintopf-radar-sync.py +++ b/eintopf-radar-sync.py @@ -3,29 +3,11 @@ import requests import json from bs4 import BeautifulSoup -import yaml -import string -def load_config(file_path): - with open(file_path, "r") as file: - config = yaml.safe_load(file) - - variables = config.get("variables", {}) - - # String template replacement - def resolve(value): - if isinstance(value, str): - return string.Template(value).substitute(variables) - if isinstance(value, dict): - return {k: resolve(v) for k, v in value.items()} - if isinstance(value, list): - return [resolve(v) for v in value] - return value - - return resolve(config) - -# Load configuration -config = load_config("config.yaml") +# Read environment variables (fail if missing) +EINTOPF_URL = os.environ["EINTOPF_URL"] +RADAR_GROUP_ID = os.environ["RADAR_GROUP_ID"] +EINTOPF_AUTHORIZATION_TOKEN = os.environ["EINTOPF_AUTHORIZATION_TOKEN"] def strip_html_tags(text): soup = BeautifulSoup(text, "html.parser") @@ -55,7 +37,10 @@ def eintopf_post_event(title, location, description, time_start, time_end): "tags": ["karlsruhe"], "topic": "Veranstaltung" } - response = requests.post(config["eintopf"]["api_endpoint"], json=payload, headers=config["eintopf"]["headers"]) + response = requests.post(EINTOPF_URL, json=payload, headers={ + "Authorization": EINTOPF_AUTHORIZATION_TOKEN, + "Content-Type": "application/json" + }) if response.status_code == 200: return True @@ -64,7 +49,7 @@ def eintopf_post_event(title, location, description, time_start, time_end): print("Beginning scraping Radar api ...") -response = requests.get(config["radar"]["api_endpoint"]) +response = requests.get("https://radar.squat.net/api/1.2/search/events.json?fields=title,offline,date_time,body&facets[group][]=" + RADAR_GROUP_ID) if response.status_code == 200: data = response.json() diff --git a/module.nix b/module.nix index 9f3a4c9..52b1f30 100644 --- a/module.nix +++ b/module.nix @@ -24,14 +24,14 @@ in type = types.submodule { freeformType = jsonFormat.type; options = { - eintopfUrl = mkOption { + EINTOPF_URL = mkOption { default = ""; type = types.str; description = '' Base URL of the target Eintopf host. ''; }; - radarGroupId = mkOption { + RADAR_GROUP_ID = mkOption { default = ""; type = types.str; description = '' @@ -46,18 +46,29 @@ in ''; example = literalExpression '' { - eintopfUrl = "eintopf.info"; - radarGroupId = "436012"; + EINTOPF_URL = "eintopf.info"; + RADAR_GROUP_ID = "436012"; } ''; }; - secretFile = mkOption { - type = types.nullOr types.str; - default = null; + secrets = lib.mkOption { + type = with types; listOf path; description = '' - Secret options which will be appended to the Radar sync config, for example - `{"redis":{"password":"secret"}}`. + A list of files containing the various secrets. Should be in the + format expected by systemd's `EnvironmentFile` directory. + ''; + default = [ ]; + }; + + interval = lib.mkOption { + type = lib.types.str; + default = "*:00,30:00"; + description = '' + How often we run the sync. Default is half an hour. + + The format is described in + {manpage}`systemd.time(7)`. ''; }; @@ -69,18 +80,55 @@ in systemd.services."eintopf-radar-sync" = { description = "eintopf-radar-sync script"; after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; environment.PYTHONUNBUFFERED = "1"; + environment = cfg.settings; serviceConfig = { Type = "simple"; ExecStart = lib.getExe pkgs.eintopf-radar-sync; - Restart = "on-failure"; DynamicUser = true; - RestartSec = 30; - # TODO hardening - # TODO settings + EnvironmentFile = [ cfg.secrets ]; + + # hardening + AmbientCapabilities = ""; + CapabilityBoundingSet = "" ; + DevicePolicy = "closed"; + DynamicUser = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ "@system-service" "~@privileged" ]; + UMask = "0077"; }; - restartIfChanged = true; + }; + + systemd.timers.eintopf-radar-sync = { + timerConfig = { + OnCalendar = [ + "" + cfg.interval + ]; + }; + wantedBy = [ "timers.target" ]; }; };