mirror of
https://git.pvv.ntnu.no/Drift/pvv-nixos-config.git
synced 2025-12-22 01:07:14 +01:00
Compare commits
6 Commits
b9c7e0f40f
...
misc-gitea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c51ecabf52 | ||
|
|
d64d8edd68 | ||
|
|
4de7bd09bd | ||
|
|
0f5c48902b | ||
|
|
36a8868f94 | ||
|
|
fe3e5d6a3d |
9
base.nix
9
base.nix
@@ -76,10 +76,19 @@
|
|||||||
# Trusted users on the nix builder machines
|
# Trusted users on the nix builder machines
|
||||||
users.groups."nix-builder-users".name = "nix-builder-users";
|
users.groups."nix-builder-users".name = "nix-builder-users";
|
||||||
|
|
||||||
|
# Let's not thermal throttle
|
||||||
|
services.thermald.enable = lib.mkIf (lib.all (x: x) [
|
||||||
|
(config.nixpkgs.system == "x86_64-linux")
|
||||||
|
(!config.boot.isContainer or false)
|
||||||
|
]) true;
|
||||||
|
|
||||||
services.openssh = {
|
services.openssh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
extraConfig = ''
|
extraConfig = ''
|
||||||
PubkeyAcceptedAlgorithms=+ssh-rsa
|
PubkeyAcceptedAlgorithms=+ssh-rsa
|
||||||
|
Match Group wheel
|
||||||
|
PasswordAuthentication no
|
||||||
|
Match All
|
||||||
'';
|
'';
|
||||||
settings.PermitRootLogin = "yes";
|
settings.PermitRootLogin = "yes";
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ let
|
|||||||
in {
|
in {
|
||||||
imports = [
|
imports = [
|
||||||
./ci.nix
|
./ci.nix
|
||||||
./import-users
|
./import-users.nix
|
||||||
./web-secret-provider
|
|
||||||
];
|
];
|
||||||
|
|
||||||
sops.secrets = {
|
sops.secrets = {
|
||||||
@@ -59,6 +58,14 @@ in {
|
|||||||
service = {
|
service = {
|
||||||
DISABLE_REGISTRATION = true;
|
DISABLE_REGISTRATION = true;
|
||||||
ENABLE_NOTIFY_MAIL = true;
|
ENABLE_NOTIFY_MAIL = true;
|
||||||
|
|
||||||
|
# Not a very commonly used feature, make opt-in
|
||||||
|
DEFAULT_ENABLE_TIMETRACKING = false;
|
||||||
|
|
||||||
|
# Everyone here are contributors
|
||||||
|
DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME = false;
|
||||||
|
|
||||||
|
DEFAULT_ORG_MEMBER_VISIBLE = true;
|
||||||
};
|
};
|
||||||
admin.DEFAULT_EMAIL_NOTIFICATIONS = "onmention";
|
admin.DEFAULT_EMAIL_NOTIFICATIONS = "onmention";
|
||||||
session.COOKIE_SECURE = true;
|
session.COOKIE_SECURE = true;
|
||||||
@@ -136,10 +143,16 @@ in {
|
|||||||
script = let
|
script = let
|
||||||
logo-svg = ../../../../assets/logo_blue_regular.svg;
|
logo-svg = ../../../../assets/logo_blue_regular.svg;
|
||||||
logo-png = ../../../../assets/logo_blue_regular.png;
|
logo-png = ../../../../assets/logo_blue_regular.png;
|
||||||
|
extraLinks = pkgs.writeText "gitea-extra-links.tmpl" ''
|
||||||
|
<a class="item" href="https://www.pvv.ntnu.no/">PVV</a>
|
||||||
|
<a class="item" href="https://wiki.pvv.ntnu.no/">Wiki</a>
|
||||||
|
<a class="item" href="https://git.pvv.ntnu.no/Drift/-/projects/4">Tokyo Drift Issues</a>
|
||||||
|
'';
|
||||||
in ''
|
in ''
|
||||||
install -Dm444 ${logo-svg} ${cfg.customDir}/public/assets/img/logo.svg
|
install -Dm444 ${logo-svg} ${cfg.customDir}/public/assets/img/logo.svg
|
||||||
install -Dm444 ${logo-png} ${cfg.customDir}/public/assets/img/logo.png
|
install -Dm444 ${logo-png} ${cfg.customDir}/public/assets/img/logo.png
|
||||||
install -Dm444 ${./loading.apng} ${cfg.customDir}/public/assets/img/loading.png
|
install -Dm444 ${./loading.apng} ${cfg.customDir}/public/assets/img/loading.png
|
||||||
|
install -Dm444 ${extraLinks} ${cfg.customDir}/templates/custom/extra_links.tmpl
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,100 +0,0 @@
|
|||||||
{ config, pkgs, lib, ... }:
|
|
||||||
let
|
|
||||||
sops.secrets = {
|
|
||||||
"gitea/web-secret-provider/Drift" = {
|
|
||||||
owner = "gitea";
|
|
||||||
group = "gitea";
|
|
||||||
restartUnits = [ "gitea-web-secret-provider@Drift" ];
|
|
||||||
};
|
|
||||||
"gitea/web-secret-provider/Projects" = {
|
|
||||||
owner = "gitea";
|
|
||||||
group = "gitea";
|
|
||||||
restartUnits = [ "gitea-web-secret-provider@Projects" ];
|
|
||||||
};
|
|
||||||
"gitea/web-secret-provider/Kurs" = {
|
|
||||||
owner = "gitea";
|
|
||||||
group = "gitea";
|
|
||||||
restartUnits = [ "gitea-web-secret-provider@Kurs" ];
|
|
||||||
};
|
|
||||||
|
|
||||||
cfg = config.services.gitea;
|
|
||||||
|
|
||||||
program = pkgs.writers.writePython3 "gitea-web-secret-provider" {
|
|
||||||
libraries = with pkgs.python3Packages; [ requests ];
|
|
||||||
makeWrapperArgs = [
|
|
||||||
"--prefix PATH : ${(lib.makeBinPath [ pkgs.openssh ])}"
|
|
||||||
];
|
|
||||||
} (builtins.readFile ./gitea-web-secret-provider.py);
|
|
||||||
in
|
|
||||||
{
|
|
||||||
|
|
||||||
# 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";
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
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";
|
|
||||||
Restart = "always";
|
|
||||||
|
|
||||||
StateDir = "%i";
|
|
||||||
WorkingDirectory = "%s/%i";
|
|
||||||
|
|
||||||
# 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;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
//
|
|
||||||
builtins.listToAttrs (map (org: lib.nameValuePair "gitea-web-secret-provider@${org}" {
|
|
||||||
serviceConfig.LoadCredential = [
|
|
||||||
"token:${config.sops.secrets."gitea/web-secret-provider/${org}".path}"
|
|
||||||
];
|
|
||||||
}));
|
|
||||||
|
|
||||||
systemd.timers = {
|
|
||||||
"gitea-web-secret-provider@" = {
|
|
||||||
description = "Run the Gitea web secret provider";
|
|
||||||
wantedBy = [ "timers.target" ];
|
|
||||||
timerConfig = {
|
|
||||||
OnCalendar = "daily";
|
|
||||||
RandomizedDelaySec = "1h";
|
|
||||||
Persistent = true;
|
|
||||||
Unit = "gitea-web-secret-provider@%i.service";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
//
|
|
||||||
builtins.listToAttrs (map (org: lib.nameValuePair "gitea-web-secret-provider@${org}" { }));
|
|
||||||
|
|
||||||
# services.nginx.virtualHosts.
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
#!/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()
|
|
||||||
Reference in New Issue
Block a user