Compare commits

..

18 Commits

Author SHA1 Message Date
h7x4
0f67ad70ba WIP: bekkalokk/gitea: host gitea-web 2024-08-14 02:15:49 +02:00
h7x4
98100a40fd bekkalokk/gitea: add web-secret-provider timed script 2024-08-14 02:14:48 +02:00
Peder Bergebakken Sundt
2f3bcaf124 shell.nix: fix typo 2024-08-10 18:15:31 +02:00
Peder Bergebakken Sundt
c6684d5146 Merge pull request 'justfile: init' (!56) from justfile into main
Reviewed-on: https://git.pvv.ntnu.no/Drift/pvv-nixos-config/pulls/56
Reviewed-by: Oystein Kristoffer Tveit <oysteikt@pvv.ntnu.no>
2024-08-07 12:22:04 +02:00
Peder Bergebakken Sundt
f6cb934ffb Merge pull request 'flake.nix: simplify allMachines' (!59) from attrnames into main
Reviewed-on: https://git.pvv.ntnu.no/Drift/pvv-nixos-config/pulls/59
Reviewed-by: Daniel Lovbrotte Olsen <danio@pvv.ntnu.no>
2024-08-04 23:44:54 +02:00
Peder Bergebakken Sundt
9625258942 Merge pull request 'flake.nix: export snakeoil-certs and snappymail nixos modules' (!58) from export-modules into main
Reviewed-on: https://git.pvv.ntnu.no/Drift/pvv-nixos-config/pulls/58
Reviewed-by: Daniel Lovbrotte Olsen <danio@pvv.ntnu.no>
2024-08-04 23:44:19 +02:00
Peder Bergebakken Sundt
34637e383a justfile: add update-inputs recipe 2024-08-04 17:19:40 +02:00
Peder Bergebakken Sundt
0bfa6ac329 flake.nix: export inputs 2024-08-04 17:19:33 +02:00
Peder Bergebakken Sundt
2c3261de74 flake.nix: simplify allMachines 2024-08-04 17:11:21 +02:00
Peder Bergebakken Sundt
c2e6f294ea flake.nix: export snakeoil-certs and snappymail nixos modules 2024-08-04 16:48:21 +02:00
Peder Bergebakken Sundt
41e94695f0 Merge pull request 'editorconfig' (!55) from editorconfig into main
Reviewed-on: https://git.pvv.ntnu.no/Drift/pvv-nixos-config/pulls/55
Reviewed-by: Oystein Kristoffer Tveit <oysteikt@pvv.ntnu.no>
2024-08-04 16:20:23 +02:00
Peder Bergebakken Sundt
c6b4ea9929 add .git-blame-ignore-revs 2024-08-04 04:39:17 +02:00
Peder Bergebakken Sundt
9dbf5d56f5 fix whitespacing issues 2024-08-04 04:37:23 +02:00
Peder Bergebakken Sundt
64b5bb548b editorconfig: init 2024-08-04 04:35:25 +02:00
Peder Bergebakken Sundt
261c8e0811 Merge pull request 'Run statix' (!54) from statix into main
Reviewed-on: https://git.pvv.ntnu.no/Drift/pvv-nixos-config/pulls/54
Reviewed-by: Daniel Lovbrotte Olsen <danio@pvv.ntnu.no>
2024-08-04 04:26:23 +02:00
Peder Bergebakken Sundt
4476cdcbbc justfile: init 2024-08-04 03:28:17 +02:00
Peder Bergebakken Sundt
1714681532 statix fix 2024-08-04 01:46:00 +02:00
Peder Bergebakken Sundt
314c7960d1 statix: init 2024-08-04 01:45:20 +02:00
40 changed files with 441 additions and 198 deletions

10
.editorconfig Normal file
View File

@@ -0,0 +1,10 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.nix]
indent_style = space
indent_size = 2

1
.git-blame-ignore-revs Normal file
View File

@@ -0,0 +1 @@
e00008da1afe0d760badd34bbeddff36bb08c475

View File

@@ -37,8 +37,8 @@
"aarch64-linux"
"aarch64-darwin"
];
forAllSystems = f: nixlib.genAttrs systems (system: f system);
allMachines = nixlib.mapAttrsToList (name: _: name) self.nixosConfigurations;
forAllSystems = f: nixlib.genAttrs systems f;
allMachines = builtins.attrNames self.nixosConfigurations;
importantMachines = [
"bekkalokk"
"bicep"
@@ -47,6 +47,8 @@
"ildkule"
];
in {
inherit inputs;
nixosConfigurations = let
unstablePkgs = nixpkgs-unstable.legacyPackages.x86_64-linux;
nixosConfig = nixpkgs: name: config: nixpkgs.lib.nixosSystem (nixpkgs.lib.recursiveUpdate
@@ -124,6 +126,11 @@
buskerud = stableNixosConfig "buskerud" { };
};
nixosModules = {
snakeoil-certs = ./modules/snakeoil-certs.nix;
snappymail = ./modules/snappymail.nix;
};
devShells = forAllSystems (system: {
default = nixpkgs.legacyPackages.${system}.callPackage ./shell.nix { };
});

View File

@@ -6,7 +6,8 @@ let
in {
imports = [
./ci.nix
./import-users.nix
./import-users
./web-secret-provider
];
sops.secrets = {

View File

@@ -0,0 +1,112 @@
{ config, pkgs, lib, ... }:
let
organizations = [
"Drift"
"Projects"
"Kurs"
];
cfg = config.services.gitea;
program = pkgs.writers.writePython3 "gitea-web-secret-provider" {
libraries = with pkgs.python3Packages; [ requests ];
flakeIgnore = [
"E501" # Line over 80 chars lol
"E201" # "whitespace after {" < this looks better bruh
"E202" # "whitespace after }" < brot
"E251" # unexpected spaces around keyword / parameter equals < megabrot
"W391" # Newline at end of file < nei vil ikke
];
makeWrapperArgs = [
"--prefix PATH : ${(lib.makeBinPath [ pkgs.openssh ])}"
];
} (lib.pipe ./gitea-web-secret-provider.py [
builtins.readFile
(lib.splitString "\n")
(lib.drop 2)
lib.concatLines
]);
in
{
sops.secrets."gitea/web-secret-provider/token" = {
owner = "gitea";
group = "gitea";
restartUnits = [
"gitea-web-secret-provider@.service"
"gitea-web-secret-provider@.timer"
]
++ (map (org: "gitea-web-secret-provider@${org}.service") organizations)
++ (map (org: "gitea-web-secret-provider@${org}.timer") organizations);
};
# https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html#Specifiers
# %i - instance name (after the @)
# %d - secrets directory
# %S - /var/lib
systemd.services = {
"gitea-web-secret-provider@" = {
description = "Gitea web secret provider";
requires = [ "gitea.service" "network.target" ];
serviceConfig = {
Type = "oneshot";
ExecStart = let
args = lib.cli.toGNUCommandLineShell { } {
org = "%i";
token-path = "%d/token";
api-url = "${cfg.settings.server.ROOT_URL}api/v1";
key-dir = "%S/%i/keys";
authorized-keys-path = "%S/gitea-web/authorized_keys.d/%i";
rrsync-path = "${pkgs.rrsync}/bin/rrsync";
web-dir = "%S/gitea-web/web";
};
in "${program} ${args}";
User = "gitea";
Group = "gitea";
StateDirectory = "%i";
LoadCredential = [
"token:${config.sops.secrets."gitea/web-secret-provider/token".path}"
];
# Hardening
NoNewPrivileges = true;
PrivateTmp = true;
PrivateDevices = true;
ProtectSystem = true;
ProtectHome = true;
ProtectControlGroups = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
RestrictRealtime = true;
RestrictSUIDSGID = true;
MemoryDenyWriteExecute = true;
LockPersonality = true;
};
};
};
systemd.timers = {
"gitea-web-secret-provider@" = {
description = "Run the Gitea web secret provider";
wantedBy = [ "timers.target" ];
timerConfig = {
RandomizedDelaySec = "1h";
Persistent = true;
Unit = "gitea-web-secret-provider@%i.service";
OnCalendar = "daily";
};
};
};
systemd.targets.timers.wants = map (org: "gitea-web-secret-provider@${org}.timer") organizations;
systemd.tmpfiles.settings."10-gitea-web-secret-provider"."/var/lib/gitea-web/authorized_keys.d".d = {
user = "gitea";
group = "gitea";
mode = "700";
};
services.openssh.authorizedKeysFiles = map (org: "/var/lib/gitea-web/authorized_keys.d/${org}") organizations;
# services.nginx.virtualHosts.
}

View File

@@ -0,0 +1,105 @@
#!/usr/bin/env nix-shell
#!nix-shell -i python3 -p "python3.withPackages(ps: with ps; [ requests ])" openssh
import argparse
import hashlib
import os
import requests
import subprocess
def parse_args():
parser = argparse.ArgumentParser(description="Generate SSH keys for Gitea repositories and add them as secrets")
parser.add_argument("--org", required=True, help="The organization to generate keys for")
parser.add_argument("--token-path", metavar='PATH', required=True, help="Path to a file containing the Gitea API token")
parser.add_argument("--api-url", metavar='URL', help="The URL of the Gitea API", default="https://git.pvv.ntnu.no/api/v1")
parser.add_argument("--key-dir", metavar='PATH', help="The directory to store the generated keys in", default="/run/gitea-web-secret-provider")
parser.add_argument("--authorized-keys-path", metavar='PATH', help="The path to the resulting authorized_keys file", default="/etc/ssh/authorized_keys.d/gitea-web-secret-provider")
parser.add_argument("--rrsync-path", metavar='PATH', help="The path to the rrsync binary", default="/run/current-system/sw/bin/rrsync")
parser.add_argument("--web-dir", metavar='PATH', help="The directory to sync the repositories to", default="/var/www")
parser.add_argument("--force", action="store_true", help="Overwrite existing keys")
return parser.parse_args()
def add_secret(args, token, repo, name, secret):
result = requests.put(
f"{args.api_url}/repos/{args.org}/{repo}/actions/secrets/{name}",
json = { 'data': secret },
headers = { 'Authorization': 'token ' + token },
)
if result.status_code not in (201, 204):
raise Exception(f"Failed to add secret: {result.json()}")
def get_org_repo_list(args, token):
result = requests.get(
f"{args.api_url}/orgs/{args.org}/repos",
headers = { 'Authorization': 'token ' + token },
)
return [repo["name"] for repo in result.json()]
def generate_ssh_key(args, repository: str):
keyname = hashlib.sha256(args.org.encode() + repository.encode()).hexdigest()
if not os.path.exists(os.path.join(args.key_dir, keyname)) or args.force:
subprocess.run(
[
"ssh-keygen",
*("-t", "ed25519"),
*("-b", "4096"),
*("-f", os.path.join(args.key_dir, keyname)),
*("-N", ""),
*("-C", f"{args.org}/{repository}"),
],
check=True,
stdin=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
)
print(f"Generated SSH key for `{args.org}/{repository}`")
with open(os.path.join(args.key_dir, keyname), "r") as f:
private_key = f.read()
with open(os.path.join(args.key_dir, keyname + ".pub"), "r") as f:
public_key = f.read()
return private_key, public_key
def generate_authorized_keys(args, repo_public_keys: list[tuple[str, str]]):
result = ""
for repo, public_key in repo_public_keys:
result += f"""
command="{args.rrsync_path} -wo {args.web_dir}/{args.org}/{repo}",restrict,no-agent-forwarding,no-port-forwarding,no-pty,no-X11-forwarding {public_key}
""".strip() + "\n"
with open(args.authorized_keys_path, "w") as f:
f.write(result)
def main():
args = parse_args()
with open(args.token_path, "r") as f:
token = f.read().strip()
os.makedirs(args.key_dir, 0o700, exist_ok=True)
repos = get_org_repo_list(args, token)
print(f'Found {len(repos)} repositories in `{args.org}`')
repo_public_keys = []
for repo in repos:
print(f"Locating key for `{args.org}/{repo}`")
private_key, public_key = generate_ssh_key(args, repo)
add_secret(args, token, repo, "WEB_SYNC_SSH_KEY", private_key)
repo_public_keys.append((repo, public_key))
generate_authorized_keys(args, repo_public_keys)
print(f"Wrote authorized_keys file to `{args.authorized_keys_path}`")
if __name__ == "__main__":
main()

View File

@@ -879,7 +879,7 @@ let
inherit (pkgs) pam_krb5 pam_ccreds;
use_ldap = (config.users.ldap.enable && config.users.ldap.loginPam);
use_ldap = config.users.ldap.enable && config.users.ldap.loginPam;
pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap;
# Create a limits.conf(5) file.
@@ -1510,7 +1510,7 @@ in
it complains "Cannot create session: Already running in a
session". */
runuser-l = { rootOK = true; unixAuth = false; };
} // optionalAttrs (config.security.pam.enableFscrypt) {
} // optionalAttrs config.security.pam.enableFscrypt {
# Allow fscrypt to verify login passphrase
fscrypt = {};
};

View File

@@ -11,7 +11,7 @@
services.mjolnir = {
enable = true;
pantalaimon.enable = false;
homeserverUrl = http://127.0.0.1:8008;
homeserverUrl = "http://127.0.0.1:8008";
accessTokenFile = config.sops.secrets."matrix/mjolnir/access_token".path;
managementRoom = "!gsdeCoWjvYRBrzuiRq:pvv.ntnu.no";
protectedRooms = map (a: "https://matrix.to/#/${a}") [

View File

@@ -143,10 +143,10 @@ in {
services.redis.servers."".enable = true;
services.nginx.virtualHosts."matrix.pvv.ntnu.no" = lib.mkMerge [
({
{
kTLS = true;
})
({
}
{
locations."/.well-known/matrix/server" = {
return = ''
200 '{"m.server": "matrix.pvv.ntnu.no:443"}'
@@ -156,16 +156,16 @@ in {
add_header Access-Control-Allow-Origin *;
'';
};
})
({
}
{
locations = let
connectionInfo = w: matrix-lib.workerConnectionResource "metrics" w;
socketAddress = w: let c = connectionInfo w; in "${c.host}:${toString (c.port)}";
socketAddress = w: let c = connectionInfo w; in "${c.host}:${toString c.port}";
metricsPath = w: "/metrics/${w.type}/${toString w.index}";
proxyPath = w: "http://${socketAddress w}/_synapse/metrics";
in lib.mapAttrs' (n: v: lib.nameValuePair
(metricsPath v) ({
(metricsPath v) {
proxyPass = proxyPath v;
extraConfig = ''
allow ${values.hosts.ildkule.ipv4};
@@ -174,10 +174,10 @@ in {
allow ${values.hosts.ildkule.ipv6_global};
deny all;
'';
}))
cfg.workers.instances;
})
({
cfg.workers.instances;
}
{
locations."/metrics/master/1" = {
proxyPass = "http://127.0.0.1:9000/_synapse/metrics";
extraConfig = ''
@@ -202,5 +202,5 @@ in {
labels = { };
}]) + "/";
};
})];
}];
}

View File

@@ -7,7 +7,6 @@
../../misc/metrics-exporters.nix
../../modules/grzegorz.nix
../../modules/spotifyd.nix
];
boot.loader.systemd-boot.enable = true;

View File

@@ -34,13 +34,13 @@ in {
{
name = "Ildkule Prometheus";
type = "prometheus";
url = ("http://${config.services.prometheus.listenAddress}:${toString config.services.prometheus.port}");
url = "http://${config.services.prometheus.listenAddress}:${toString config.services.prometheus.port}";
isDefault = true;
}
{
name = "Ildkule loki";
type = "loki";
url = ("http://${config.services.loki.configuration.server.http_listen_address}:${toString config.services.loki.configuration.server.http_listen_port}");
url = "http://${config.services.loki.configuration.server.http_listen_address}:${toString config.services.loki.configuration.server.http_listen_port}";
}
];
dashboards.settings.providers = [

21
justfile Normal file
View File

@@ -0,0 +1,21 @@
export GUM_FILTER_HEIGHT := "15"
nom := `if command -v nom >/dev/null; then echo nom; else echo nix; fi`
@_default:
just "$(gum choose --ordered --header "Pick a recipie..." $(just --summary --unsorted))"
check:
nix flake check --keep-going
build-machine machine=`just _a_machine`:
{{nom}} build .#nixosConfigurations.{{ machine }}.config.system.build.toplevel
@update-inputs:
nix eval .#inputs --apply builtins.attrNames --json \
| jq '.[]' -r \
| gum choose --no-limit --height=15 \
| xargs nix flake update --commit-lock-file
_a_machine:
nix eval .#nixosConfigurations --apply builtins.attrNames --json | jq .[] -r | gum filter

View File

@@ -1,44 +0,0 @@
{ lib, config, ... }:
{
services.spotifyd.enable = true;
# https://docs.spotifyd.rs/config/File.html
services.spotifyd.settings = {
device_name = "${config.networking.hostName}-spotifyd";
device_type = "t_v"; # in ["unknown" "computer" "tablet" "smartphone" "speaker" "t_v"],
bitrate = 160; # in [96 160 320]
volume_normalisation = true;
zeroconf_port = 44677; # instead of user/password
# this is the place you add blinkenlights
#on_song_change_hook = "rm -rf / --no-preserve-root";
};
systemd.services.spotifyd.serviceConfig = {
SupplementaryGroups = [
"audio"
"pipewire"
];
};
services.avahi.enable = true;
services.avahi.publish.enable = true;
services.avahi.publish.addresses = true;
services.avahi.publish.domain = true;
services.avahi.extraServiceFiles.spotifyd = ''
<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
<name replace-wildcards="yes">%h</name>
<service>
<type>_spotify-connect._tcp</type>
<port>${builtins.toString config.services.spotifyd.settings.zeroconf_port}</port>
</service>
</service-group>
'';
networking.firewall.allowedTCPPorts = [ config.services.spotifyd.settings.zeroconf_port ];
networking.firewall.allowedUDPPorts = [ 5353 ]; # mDNS
}

View File

@@ -1,4 +1,6 @@
gitea:
web-secret-provider:
token: ENC[AES256_GCM,data:pHmBKxrNcLifl4sjR44AGEElfdachja35Tl/InsqvBWturaeTv4R0w==,iv:emBWfXQs2VNqtpDp5iA5swNC+24AWDYYXo6nvN+Fwx4=,tag:lkhSVSs6IqhHpfDPOX0wQA==,type:str]
password: ENC[AES256_GCM,data:hlNzdU1ope0t50/3aztyLeXjMHd2vFPpwURX+Iu8f49DOqgSnEMtV+KtLA==,iv:qljRnSnchL5cFmaUAfCH9GQYQxcy5cyWejgk1x6bFgI=,tag:tIhboFU5kZsj5oAQR3hLbw==,type:str]
database: ENC[AES256_GCM,data:UlS33IdCEyeSvT6ngpmnkBWHuSEqsB//DT+3b7C+UwbD8UXWJlsLf1X8/w==,iv:mPRW5ldyZaHP+y/0vC2JGSLZmlkhgmkvXPk4LazkSDs=,tag:gGk6Z/nbPvzE1zG+tJC8Sw==,type:str]
email-password: ENC[AES256_GCM,data:KRwC+aL1aPvJuXt91Oq1ttATMnFTnuUy,iv:ats8TygB/2pORkaTZzPOLufZ9UmvVAKoRcWNvYF1z6w=,tag:Do0fA+4cZ3+l7JJyu8hjBg==,type:str]
@@ -90,8 +92,8 @@ sops:
UHpLRkdQTnhkeGlWVG9VS1hkWktyckEKAdwnA9URLYZ50lMtXrU9Q09d0L3Zfsyr
4UsvjjdnFtsXwEZ9ZzOQrpiN0Oz24s3csw5KckDni6kslaloJZsLGg==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-05-26T02:07:41Z"
mac: ENC[AES256_GCM,data:CRaJefV1zcJc6eyzyjTLgd0+Wv46VT8o4iz2YAGU+c2b/Cr97Tj290LoEO6UXTI3uFwVfzii2yZ2l+4FK3nVVriD4Cx1O/9qWcnLa5gfK30U0zof6AsJx8qtGu1t6oiPlGUCF7sT0BW9Wp8cPumrY6cZp9QbhmIDV0o0aJNUNN4=,iv:8OSYV1eG6kYlJD4ovZZhcD1GaYnmy7vHPa/+7egM1nE=,tag:OPI13rpDh2l1ViFj8TBFWg==,type:str]
lastmodified: "2024-08-13T19:49:24Z"
mac: ENC[AES256_GCM,data:AeJ53D+8A8mHYRmVHdqhcS1ZTbqVe5gQqJsJjMk4T/ZlNX8/V4M9mqAW2FB9m/JSdj234gDu+PBHcW70ZrCqeVsoUW/ETVgUX3W2gBmBgYJiRETp8I7/eks/5YEV6vIIxQsZNP/9dZTNX4T2wD74ELl23NSTXA/6k2tyzBlTMYo=,iv:DABafHvw+5w0PHCKqLgpwmQnv0uHOTyj+s8gdnHFTZ4=,tag:SNZ7W+6zdyuuv2AB9ir8eg==,type:str]
pgp:
- created_at: "2024-08-04T00:03:28Z"
enc: |-
@@ -114,4 +116,4 @@ sops:
-----END PGP MESSAGE-----
fp: F7D37890228A907440E1FD4846B9228E814A2AAC
unencrypted_suffix: _unencrypted
version: 3.8.1
version: 3.9.0

View File

@@ -1,9 +1,14 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShellNoCC {
packages = with pkgs; [
just
jq
gum
sops
gnupg
statix
openstackclient
editorconfig-checker
];
shellHook = ''

24
statix.toml Normal file
View File

@@ -0,0 +1,24 @@
ignore = [".direnv"]
nix_version = '2.18' # '2.4'
disabled = [
# "bool_comparison", # W01
# "empty_let_in", # W02
"manual_inherit", # W03
"manual_inherit_from", # W04
# "legacy_let_syntax", # W05
"collapsible_let_in", # W06
# "eta_reduction", # W07
# "useless_parens", # W08
"empty_pattern", # W10
# "redundant_pattern_bind", # W11
# "unquoted_uri", # W12
# "deprecated_is_null", # W13
# "empty_inherit", # W14
# "faster_groupby", # W15
# "faster_zipattrswith", # W16
# "deprecated_to_path", # W17
# "bool_simplification", # W18
# "useless_has_attr", # W19
"repeated_keys", # W20
"empty_list_concat", # W23
]