test(ntfy-sh): hack! direct import waiting for nix-pkgs
Some checks failed
Check meta / check_meta (pull_request) Successful in 19s
Check meta / check_dns (pull_request) Successful in 19s
Check workflows / check_workflows (pull_request) Successful in 19s
Run pre-commit on all files / pre-commit (push) Failing after 28s
Build all the nodes / netcore00 (pull_request) Successful in 27s
Build all the nodes / netaccess01 (pull_request) Successful in 28s
Build all the nodes / netcore01 (pull_request) Successful in 28s
Run pre-commit on all files / pre-commit (pull_request) Failing after 34s
Build all the nodes / ap01 (pull_request) Successful in 42s
Build all the nodes / netcore02 (pull_request) Successful in 30s
Build all the nodes / hypervisor01 (pull_request) Successful in 57s
Build all the nodes / hypervisor03 (pull_request) Successful in 1m3s
Build all the nodes / bridge01 (pull_request) Successful in 1m6s
Build all the nodes / geo02 (pull_request) Successful in 1m6s
Build all the nodes / geo01 (pull_request) Successful in 1m6s
Build all the nodes / cof02 (pull_request) Successful in 1m7s
Build all the nodes / lab-router01 (pull_request) Successful in 1m7s
Build all the nodes / hypervisor02 (pull_request) Successful in 1m8s
Build all the nodes / build01 (pull_request) Successful in 1m15s
Build the shell / build-shell (pull_request) Failing after 15s
Build all the nodes / iso (pull_request) Successful in 1m22s
Build all the nodes / compute01 (pull_request) Successful in 1m25s
Build all the nodes / tower01 (pull_request) Successful in 57s
Build all the nodes / rescue01 (pull_request) Successful in 1m7s
Build all the nodes / vault01 (pull_request) Successful in 1m4s
Build all the nodes / web02 (pull_request) Successful in 54s
Build all the nodes / krz01 (pull_request) Successful in 1m40s
Build all the nodes / storage01 (pull_request) Successful in 1m16s
Build all the nodes / web03 (pull_request) Successful in 50s
Build all the nodes / web01 (pull_request) Failing after 1m40s
Some checks failed
Check meta / check_meta (pull_request) Successful in 19s
Check meta / check_dns (pull_request) Successful in 19s
Check workflows / check_workflows (pull_request) Successful in 19s
Run pre-commit on all files / pre-commit (push) Failing after 28s
Build all the nodes / netcore00 (pull_request) Successful in 27s
Build all the nodes / netaccess01 (pull_request) Successful in 28s
Build all the nodes / netcore01 (pull_request) Successful in 28s
Run pre-commit on all files / pre-commit (pull_request) Failing after 34s
Build all the nodes / ap01 (pull_request) Successful in 42s
Build all the nodes / netcore02 (pull_request) Successful in 30s
Build all the nodes / hypervisor01 (pull_request) Successful in 57s
Build all the nodes / hypervisor03 (pull_request) Successful in 1m3s
Build all the nodes / bridge01 (pull_request) Successful in 1m6s
Build all the nodes / geo02 (pull_request) Successful in 1m6s
Build all the nodes / geo01 (pull_request) Successful in 1m6s
Build all the nodes / cof02 (pull_request) Successful in 1m7s
Build all the nodes / lab-router01 (pull_request) Successful in 1m7s
Build all the nodes / hypervisor02 (pull_request) Successful in 1m8s
Build all the nodes / build01 (pull_request) Successful in 1m15s
Build the shell / build-shell (pull_request) Failing after 15s
Build all the nodes / iso (pull_request) Successful in 1m22s
Build all the nodes / compute01 (pull_request) Successful in 1m25s
Build all the nodes / tower01 (pull_request) Successful in 57s
Build all the nodes / rescue01 (pull_request) Successful in 1m7s
Build all the nodes / vault01 (pull_request) Successful in 1m4s
Build all the nodes / web02 (pull_request) Successful in 54s
Build all the nodes / krz01 (pull_request) Successful in 1m40s
Build all the nodes / storage01 (pull_request) Successful in 1m16s
Build all the nodes / web03 (pull_request) Successful in 50s
Build all the nodes / web01 (pull_request) Failing after 1m40s
This commit is contained in:
parent
f51b01ee8c
commit
3b3b61d795
3 changed files with 235 additions and 1 deletions
|
@ -39,6 +39,7 @@
|
||||||
"extranix"
|
"extranix"
|
||||||
"openbao"
|
"openbao"
|
||||||
"forgejo-multiuser-nix-runners"
|
"forgejo-multiuser-nix-runners"
|
||||||
|
"ntfy-sh"
|
||||||
])
|
])
|
||||||
++ [
|
++ [
|
||||||
"${sources.agenix}/modules/age.nix"
|
"${sources.agenix}/modules/age.nix"
|
||||||
|
@ -55,7 +56,6 @@
|
||||||
"services/systemd-notify"
|
"services/systemd-notify"
|
||||||
"services/victorialogs"
|
"services/victorialogs"
|
||||||
"services/victoriametrics"
|
"services/victoriametrics"
|
||||||
"services/ntfy-sh"
|
|
||||||
]
|
]
|
||||||
++ nodeMeta.nix-modules
|
++ nodeMeta.nix-modules
|
||||||
));
|
));
|
||||||
|
|
148
modules/nixos/ntfy-sh/default.nix
Normal file
148
modules/nixos/ntfy-sh/default.nix
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (lib)
|
||||||
|
mapAttrsToList
|
||||||
|
mkEnableOption
|
||||||
|
mkIf
|
||||||
|
mkOption
|
||||||
|
xor
|
||||||
|
;
|
||||||
|
inherit (lib.types)
|
||||||
|
attrsOf
|
||||||
|
enum
|
||||||
|
listOf
|
||||||
|
nullOr
|
||||||
|
path
|
||||||
|
str
|
||||||
|
submodule
|
||||||
|
;
|
||||||
|
|
||||||
|
cfg = config.services.ntfy-sh.accessControl;
|
||||||
|
|
||||||
|
inherit (config.services.ntfy-sh) settings;
|
||||||
|
|
||||||
|
acl_file = (pkgs.formats.json { }).generate "acl.json" { inherit (cfg) access users; };
|
||||||
|
|
||||||
|
ntfy-acl = pkgs.substituteAll {
|
||||||
|
inherit (pkgs) python3;
|
||||||
|
inherit acl_file;
|
||||||
|
|
||||||
|
user_db = settings.auth-file;
|
||||||
|
|
||||||
|
name = "ntfy-acl";
|
||||||
|
src = ./ntfy-acl.py;
|
||||||
|
dir = "bin";
|
||||||
|
isExecutable = true;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
|
||||||
|
{
|
||||||
|
options.services.ntfy-sh.accessControl = {
|
||||||
|
enable = mkEnableOption "declarative management of users and acl for ntfy.sh";
|
||||||
|
|
||||||
|
users = mkOption {
|
||||||
|
type = attrsOf (submodule {
|
||||||
|
options = {
|
||||||
|
role = mkOption {
|
||||||
|
type = enum [
|
||||||
|
"admin"
|
||||||
|
"user"
|
||||||
|
];
|
||||||
|
description = "Role of the user.";
|
||||||
|
default = "user";
|
||||||
|
};
|
||||||
|
passwordFile = mkOption {
|
||||||
|
type = nullOr path;
|
||||||
|
description = ''
|
||||||
|
Path to a file containing the password of the user.
|
||||||
|
|
||||||
|
Conflicts with `hashedPassword`.
|
||||||
|
'';
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
hashedPassword = mkOption {
|
||||||
|
type = nullOr str;
|
||||||
|
description = ''
|
||||||
|
Hashed password of the user.
|
||||||
|
|
||||||
|
Conflicts with `passwordFile`.
|
||||||
|
'';
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
description = ''
|
||||||
|
Attribute set defining users of the ntfy.sh instance.
|
||||||
|
'';
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
access = mkOption {
|
||||||
|
type = listOf (submodule {
|
||||||
|
options = {
|
||||||
|
username = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = ''
|
||||||
|
A USERNAME is an existing user, as created with ntfy user add (see users and roles),
|
||||||
|
or the anonymous user everyone or *, which represents clients that access the API
|
||||||
|
without username/password.
|
||||||
|
'';
|
||||||
|
default = "*";
|
||||||
|
};
|
||||||
|
topic = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = ''
|
||||||
|
A TOPIC is either a specific topic name (e.g. mytopic, or phil_alerts),
|
||||||
|
or a wildcard pattern that matches any number of topics (e.g. alerts_* or ben-*).
|
||||||
|
Only the wildcard character * is supported.
|
||||||
|
It stands for zero to any number of characters.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
permission = mkOption {
|
||||||
|
type = enum [
|
||||||
|
"rw"
|
||||||
|
"ro"
|
||||||
|
"wo"
|
||||||
|
"none"
|
||||||
|
];
|
||||||
|
description = ''
|
||||||
|
Permission for this access.
|
||||||
|
- rw: Allows publishing messages to the given topic, as well as subscribing and reading messages
|
||||||
|
- ro: Allows only subscribing and reading messages, but not publishing to the topic
|
||||||
|
- wo: Allows only publishing to the topic, but not subscribing to it
|
||||||
|
- none: Allows neither publishing nor subscribing to a topic
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
description = "List of access control rules.";
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
assertions = mapAttrsToList (name: user: {
|
||||||
|
assertion = xor (user.hashedPassword != null) (user.passwordFile != null);
|
||||||
|
message = ''
|
||||||
|
Exactly one of `services.ntfy-sh.accessControl.users.<name>.hashedPassword`
|
||||||
|
and `services.ntfy-sh.accessControl.users.<name>.passwordFile`
|
||||||
|
is required for `name` = `${name}`.
|
||||||
|
'';
|
||||||
|
}) cfg.users;
|
||||||
|
|
||||||
|
services.ntfy-sh.settings = {
|
||||||
|
enable-signup = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"f /var/lib/ntfy-sh/.acl-path 0600 ${config.services.ntfy-sh.user} ${config.services.ntfy-sh.group} - -"
|
||||||
|
];
|
||||||
|
systemd.services.ntfy-sh.preStart = "${ntfy-acl}/bin/ntfy-acl";
|
||||||
|
};
|
||||||
|
}
|
86
modules/nixos/ntfy-sh/ntfy-acl.py
Normal file
86
modules/nixos/ntfy-sh/ntfy-acl.py
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
#!@python3@/bin/python
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sqlite3
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
def ntfy(*args: str, env=None):
|
||||||
|
subprocess.run(["ntfy"] + list(args), env=env).check_returncode()
|
||||||
|
|
||||||
|
|
||||||
|
def create_user(u: str, role: str, passwordFile: str, hashedPassword: str):
|
||||||
|
# Create the user with the required role and password
|
||||||
|
if passwordFile != None:
|
||||||
|
with open(passwordFile) as pwd_fp:
|
||||||
|
env = {"NTFY_PASSWORD": pwd_fp.read().strip()}
|
||||||
|
|
||||||
|
ntfy("user", "add", f"--role={role}", u, env=env)
|
||||||
|
else:
|
||||||
|
env = {"NTFY_PASSWORD": hashedPassword}
|
||||||
|
|
||||||
|
ntfy("user", "add", f"--role={role}", u, env=env)
|
||||||
|
# HACK: add does not supports hashedPassword entry
|
||||||
|
ntfy("user", "change-pass-hash", u, env=env)
|
||||||
|
|
||||||
|
def update_user(u: str, role: str, passwordFile: str, hashedPassword: str):
|
||||||
|
# Update the user with the required role and password
|
||||||
|
if passwordFile != None:
|
||||||
|
with open(passwordFile) as pwd_fp:
|
||||||
|
env = {"NTFY_PASSWORD": pwd_fp.read().strip()}
|
||||||
|
|
||||||
|
ntfy("user", "change-pass", u, env=env)
|
||||||
|
else:
|
||||||
|
env = {"NTFY_PASSWORD": hashedPassword}
|
||||||
|
|
||||||
|
ntfy("user", "change-pass-hash", u, env=env)
|
||||||
|
|
||||||
|
ntfy("user", "change-role", u, role)
|
||||||
|
|
||||||
|
|
||||||
|
# Compare the ACL file path to the one used to get the actual data
|
||||||
|
try:
|
||||||
|
with open("/var/lib/ntfy-sh/.acl-path") as acl_path_fp:
|
||||||
|
acl_path: str = acl_path_fp.read().strip()
|
||||||
|
except OSError:
|
||||||
|
print("[!] Cannot open .acl-path")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
if acl_path == "@acl_file@":
|
||||||
|
print("[-] Unchanged ACL file, exiting")
|
||||||
|
exit(0)
|
||||||
|
else:
|
||||||
|
print("[+] ACL file has changed, updating data")
|
||||||
|
|
||||||
|
# Get the wanted state
|
||||||
|
with open("@acl_file@") as acl_fp:
|
||||||
|
acl_data = json.load(acl_fp)
|
||||||
|
|
||||||
|
# Connect to the db to recover the list of current users
|
||||||
|
with sqlite3.connect("@user_db@") as con:
|
||||||
|
c = con.cursor()
|
||||||
|
existing_users: set[str] = set(c.execute("SELECT user FROM user")) - {"*"}
|
||||||
|
|
||||||
|
wanted_users: set[str] = set(acl_data["users"].keys())
|
||||||
|
|
||||||
|
# Delete extraneous users
|
||||||
|
for user in existing_users - wanted_users:
|
||||||
|
ntfy("user", "del", user)
|
||||||
|
|
||||||
|
# Create new users
|
||||||
|
for user in wanted_users - existing_users:
|
||||||
|
create_user(user, **acl_data["users"][user])
|
||||||
|
|
||||||
|
# Update existing users
|
||||||
|
for user in existing_users & wanted_users:
|
||||||
|
update_user(user, **acl_data["users"][user])
|
||||||
|
|
||||||
|
# Reset ACL rules
|
||||||
|
ntfy("access", "--reset")
|
||||||
|
|
||||||
|
for rule in acl_data["access"]:
|
||||||
|
ntfy("access", rule["user"], rule["topic"], rule["permission"])
|
||||||
|
|
||||||
|
# Write the new ACL file path
|
||||||
|
with open("/var/lib/ntfy-sh/.acl-path", "w") as f:
|
||||||
|
f.write("@acl_file@")
|
Loading…
Add table
Add a link
Reference in a new issue