Compare commits
20 Commits
remote
...
systemd-jo
Author | SHA1 | Date | |
---|---|---|---|
112a95c050
|
|||
bd42412b94 | |||
ef3b146b58 | |||
bb4662b345 | |||
5b1c04e4b8 | |||
3fa7f67027 | |||
b0f555667c | |||
ef418bf125
|
|||
945d53cdb4
|
|||
cf3b62e01e
|
|||
c12a47cee0
|
|||
b9ef27565f
|
|||
f5c99b58c8
|
|||
c780f7954c | |||
d64d8edd68 | |||
4de7bd09bd | |||
0f5c48902b | |||
2ff69dfec6 | |||
36a8868f94 | |||
fe3e5d6a3d |
.gitignorebase.nixflake.lockjustfile
hosts
bekkalokk
configuration.nix
services
bicep
ildkule
services
modules
secrets
values.nix
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
result*
|
result*
|
||||||
/configuration.nix
|
/configuration.nix
|
||||||
/.direnv/
|
/.direnv/
|
||||||
|
*.qcow2
|
||||||
|
67
base.nix
67
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";
|
||||||
};
|
};
|
||||||
@ -124,10 +133,68 @@
|
|||||||
extraConfig = "return 444;";
|
extraConfig = "return 444;";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# source: https://github.com/logrotate/logrotate/blob/main/examples/logrotate.service
|
||||||
|
systemd.services.logrotate = {
|
||||||
|
documentation = [ "man:logrotate(8)" "man:logrotate.conf(5)" ];
|
||||||
|
unitConfig.RequiresMountsFor = "/var/log";
|
||||||
|
serviceConfig = {
|
||||||
|
Nice = 19;
|
||||||
|
IOSchedulingClass = "best-effort";
|
||||||
|
IOSchedulingPriority = 7;
|
||||||
|
|
||||||
|
ReadWritePaths = [ "/var/log" ];
|
||||||
|
|
||||||
|
AmbientCapabilities = [ "" ];
|
||||||
|
CapabilityBoundingSet = [ "" ];
|
||||||
|
DeviceAllow = [ "" ];
|
||||||
|
LockPersonality = true;
|
||||||
|
MemoryDenyWriteExecute = true;
|
||||||
|
NoNewPrivileges = true; # disable for third party rotate scripts
|
||||||
|
PrivateDevices = true;
|
||||||
|
PrivateNetwork = true; # disable for mail delivery
|
||||||
|
PrivateTmp = true;
|
||||||
|
ProtectClock = true;
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
ProtectHome = true; # disable for userdir logs
|
||||||
|
ProtectHostname = true;
|
||||||
|
ProtectKernelLogs = true;
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
ProtectProc = "invisible";
|
||||||
|
ProtectSystem = "full";
|
||||||
|
RestrictNamespaces = true;
|
||||||
|
RestrictRealtime = true;
|
||||||
|
RestrictSUIDSGID = true; # disable for creating setgid directories
|
||||||
|
SocketBindDeny = [ "any" ];
|
||||||
|
SystemCallArchitectures = "native";
|
||||||
|
SystemCallFilter = [
|
||||||
|
"@system-service"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.journald.upload = {
|
||||||
|
enable = values.services.logcollector.ipv4;
|
||||||
|
settings.Upload = {
|
||||||
|
URL = "https://logcollector.pvv.ntnu.no:19532";
|
||||||
|
ServerKeyFile = "-";
|
||||||
|
ServerCertificateFile = "-";
|
||||||
|
TrustedCertificateFile = "-";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = lib.mkIf config.services.nginx.enable [ 80 443 ];
|
networking.firewall.allowedTCPPorts = lib.mkIf config.services.nginx.enable [ 80 443 ];
|
||||||
|
|
||||||
security.acme = {
|
security.acme = {
|
||||||
acceptTerms = true;
|
acceptTerms = true;
|
||||||
defaults.email = "drift@pvv.ntnu.no";
|
defaults.email = "drift@pvv.ntnu.no";
|
||||||
};
|
};
|
||||||
|
# Let's not spam LetsEncrypt in `nixos-rebuild build-vm` mode:
|
||||||
|
virtualisation.vmVariant = {
|
||||||
|
security.acme.defaults.server = "https://127.0.0.1";
|
||||||
|
security.acme.preliminarySelfsigned = true;
|
||||||
|
|
||||||
|
users.users.root.initialPassword = "root";
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
8
flake.lock
generated
8
flake.lock
generated
@ -194,11 +194,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1693136143,
|
"lastModified": 1723850344,
|
||||||
"narHash": "sha256-amHprjftc3y/bg8yf4hITCLa+ez5HIi0yGfR7TU6UIc=",
|
"narHash": "sha256-aT37O9l9eclWEnqxASVNBL1dKwDHZUOqdbA4VO9DJvw=",
|
||||||
"ref": "refs/heads/main",
|
"ref": "refs/heads/main",
|
||||||
"rev": "a32894b305f042d561500f5799226afd1faf5abb",
|
"rev": "38b66677ab8c01aee10cd59e745af9ce3ea88092",
|
||||||
"revCount": 9,
|
"revCount": 19,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.pvv.ntnu.no/Projects/calendar-bot.git"
|
"url": "https://git.pvv.ntnu.no/Projects/calendar-bot.git"
|
||||||
},
|
},
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
./services/kerberos
|
./services/kerberos
|
||||||
./services/mediawiki
|
./services/mediawiki
|
||||||
./services/nginx.nix
|
./services/nginx.nix
|
||||||
|
./services/phpfpm.nix
|
||||||
./services/vaultwarden.nix
|
./services/vaultwarden.nix
|
||||||
./services/webmail
|
./services/webmail
|
||||||
./services/website
|
./services/website
|
||||||
|
@ -6,7 +6,8 @@ let
|
|||||||
in {
|
in {
|
||||||
imports = [
|
imports = [
|
||||||
./ci.nix
|
./ci.nix
|
||||||
./import-users.nix
|
./import-users
|
||||||
|
./web-secret-provider
|
||||||
];
|
];
|
||||||
|
|
||||||
sops.secrets = {
|
sops.secrets = {
|
||||||
@ -58,6 +59,7 @@ in {
|
|||||||
service = {
|
service = {
|
||||||
DISABLE_REGISTRATION = true;
|
DISABLE_REGISTRATION = true;
|
||||||
ENABLE_NOTIFY_MAIL = true;
|
ENABLE_NOTIFY_MAIL = true;
|
||||||
|
AUTO_WATCH_NEW_REPOS = false;
|
||||||
};
|
};
|
||||||
admin.DEFAULT_EMAIL_NOTIFICATIONS = "onmention";
|
admin.DEFAULT_EMAIL_NOTIFICATIONS = "onmention";
|
||||||
session.COOKIE_SECURE = true;
|
session.COOKIE_SECURE = true;
|
||||||
@ -135,10 +137,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,94 +0,0 @@
|
|||||||
import requests
|
|
||||||
import secrets
|
|
||||||
import os
|
|
||||||
|
|
||||||
EMAIL_DOMAIN = os.getenv('EMAIL_DOMAIN')
|
|
||||||
if EMAIL_DOMAIN is None:
|
|
||||||
EMAIL_DOMAIN = 'pvv.ntnu.no'
|
|
||||||
|
|
||||||
API_TOKEN = os.getenv('API_TOKEN')
|
|
||||||
if API_TOKEN is None:
|
|
||||||
raise Exception('API_TOKEN not set')
|
|
||||||
|
|
||||||
GITEA_API_URL = os.getenv('GITEA_API_URL')
|
|
||||||
if GITEA_API_URL is None:
|
|
||||||
GITEA_API_URL = 'https://git.pvv.ntnu.no/api/v1'
|
|
||||||
|
|
||||||
BANNED_SHELLS = [
|
|
||||||
"/usr/bin/nologin",
|
|
||||||
"/usr/sbin/nologin",
|
|
||||||
"/sbin/nologin",
|
|
||||||
"/bin/false",
|
|
||||||
"/bin/msgsh",
|
|
||||||
]
|
|
||||||
|
|
||||||
existing_users = {}
|
|
||||||
|
|
||||||
|
|
||||||
# This function should only ever be called when adding users
|
|
||||||
# from the passwd file
|
|
||||||
def add_user(username, name):
|
|
||||||
user = {
|
|
||||||
"full_name": name,
|
|
||||||
"username": username,
|
|
||||||
"login_name": username,
|
|
||||||
"source_id": 1, # 1 = SMTP
|
|
||||||
}
|
|
||||||
|
|
||||||
if username not in existing_users:
|
|
||||||
user["password"] = secrets.token_urlsafe(32)
|
|
||||||
user["must_change_password"] = False
|
|
||||||
user["visibility"] = "private"
|
|
||||||
user["email"] = username + '@' + EMAIL_DOMAIN
|
|
||||||
|
|
||||||
r = requests.post(GITEA_API_URL + '/admin/users', json=user,
|
|
||||||
headers={'Authorization': 'token ' + API_TOKEN})
|
|
||||||
if r.status_code != 201:
|
|
||||||
print('ERR: Failed to create user ' + username + ': ' + r.text)
|
|
||||||
return
|
|
||||||
|
|
||||||
print('Created user ' + username)
|
|
||||||
existing_users[username] = user
|
|
||||||
|
|
||||||
else:
|
|
||||||
user["visibility"] = existing_users[username]["visibility"]
|
|
||||||
r = requests.patch(GITEA_API_URL + f'/admin/users/{username}',
|
|
||||||
json=user,
|
|
||||||
headers={'Authorization': 'token ' + API_TOKEN})
|
|
||||||
if r.status_code != 200:
|
|
||||||
print('ERR: Failed to update user ' + username + ': ' + r.text)
|
|
||||||
return
|
|
||||||
|
|
||||||
print('Updated user ' + username)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
# Fetch existing users
|
|
||||||
r = requests.get(GITEA_API_URL + '/admin/users',
|
|
||||||
headers={'Authorization': 'token ' + API_TOKEN})
|
|
||||||
|
|
||||||
if r.status_code != 200:
|
|
||||||
raise Exception('Failed to get users: ' + r.text)
|
|
||||||
|
|
||||||
for user in r.json():
|
|
||||||
existing_users[user['login']] = user
|
|
||||||
|
|
||||||
# Read the file, add each user
|
|
||||||
with open("/tmp/passwd-import", 'r') as f:
|
|
||||||
for line in f.readlines():
|
|
||||||
uid = int(line.split(':')[2])
|
|
||||||
if uid < 1000:
|
|
||||||
continue
|
|
||||||
|
|
||||||
shell = line.split(':')[-1]
|
|
||||||
if shell in BANNED_SHELLS:
|
|
||||||
continue
|
|
||||||
|
|
||||||
username = line.split(':')[0]
|
|
||||||
name = line.split(':')[4].split(',')[0]
|
|
||||||
|
|
||||||
add_user(username, name)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -14,6 +14,9 @@ in
|
|||||||
preStart=''${pkgs.rsync}/bin/rsync -e "${pkgs.openssh}/bin/ssh -o UserKnownHostsFile=$CREDENTIALS_DIRECTORY/ssh-known-hosts -i $CREDENTIALS_DIRECTORY/sshkey" -a pvv@smtp.pvv.ntnu.no:/etc/passwd /tmp/passwd-import'';
|
preStart=''${pkgs.rsync}/bin/rsync -e "${pkgs.openssh}/bin/ssh -o UserKnownHostsFile=$CREDENTIALS_DIRECTORY/ssh-known-hosts -i $CREDENTIALS_DIRECTORY/sshkey" -a pvv@smtp.pvv.ntnu.no:/etc/passwd /tmp/passwd-import'';
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
ExecStart = pkgs.writers.writePython3 "gitea-import-users" {
|
ExecStart = pkgs.writers.writePython3 "gitea-import-users" {
|
||||||
|
flakeIgnore = [
|
||||||
|
"E501" # Line over 80 chars lol
|
||||||
|
];
|
||||||
libraries = with pkgs.python3Packages; [ requests ];
|
libraries = with pkgs.python3Packages; [ requests ];
|
||||||
} (builtins.readFile ./gitea-import-users.py);
|
} (builtins.readFile ./gitea-import-users.py);
|
||||||
LoadCredential=[
|
LoadCredential=[
|
@ -0,0 +1,198 @@
|
|||||||
|
import requests
|
||||||
|
import secrets
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
EMAIL_DOMAIN = os.getenv('EMAIL_DOMAIN')
|
||||||
|
if EMAIL_DOMAIN is None:
|
||||||
|
EMAIL_DOMAIN = 'pvv.ntnu.no'
|
||||||
|
|
||||||
|
|
||||||
|
API_TOKEN = os.getenv('API_TOKEN')
|
||||||
|
if API_TOKEN is None:
|
||||||
|
raise Exception('API_TOKEN not set')
|
||||||
|
|
||||||
|
|
||||||
|
GITEA_API_URL = os.getenv('GITEA_API_URL')
|
||||||
|
if GITEA_API_URL is None:
|
||||||
|
GITEA_API_URL = 'https://git.pvv.ntnu.no/api/v1'
|
||||||
|
|
||||||
|
|
||||||
|
def gitea_list_all_users() -> dict[str, dict[str, any]] | None:
|
||||||
|
r = requests.get(
|
||||||
|
GITEA_API_URL + '/admin/users',
|
||||||
|
headers={'Authorization': 'token ' + API_TOKEN}
|
||||||
|
)
|
||||||
|
|
||||||
|
if r.status_code != 200:
|
||||||
|
print('Failed to get users:', r.text)
|
||||||
|
return None
|
||||||
|
|
||||||
|
return {user['login']: user for user in r.json()}
|
||||||
|
|
||||||
|
|
||||||
|
def gitea_create_user(username: str, userdata: dict[str, any]) -> bool:
|
||||||
|
r = requests.post(
|
||||||
|
GITEA_API_URL + '/admin/users',
|
||||||
|
json=userdata,
|
||||||
|
headers={'Authorization': 'token ' + API_TOKEN},
|
||||||
|
)
|
||||||
|
|
||||||
|
if r.status_code != 201:
|
||||||
|
print(f'ERR: Failed to create user {username}:', r.text)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def gitea_edit_user(username: str, userdata: dict[str, any]) -> bool:
|
||||||
|
r = requests.patch(
|
||||||
|
GITEA_API_URL + f'/admin/users/{username}',
|
||||||
|
json=userdata,
|
||||||
|
headers={'Authorization': 'token ' + API_TOKEN},
|
||||||
|
)
|
||||||
|
|
||||||
|
if r.status_code != 200:
|
||||||
|
print(f'ERR: Failed to update user {username}:', r.text)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def gitea_list_teams_for_organization(org: str) -> dict[str, any] | None:
|
||||||
|
r = requests.get(
|
||||||
|
GITEA_API_URL + f'/orgs/{org}/teams',
|
||||||
|
headers={'Authorization': 'token ' + API_TOKEN},
|
||||||
|
)
|
||||||
|
|
||||||
|
if r.status_code != 200:
|
||||||
|
print(f"ERR: Failed to list teams for {org}:", r.text)
|
||||||
|
return None
|
||||||
|
|
||||||
|
return {team['name']: team for team in r.json()}
|
||||||
|
|
||||||
|
|
||||||
|
def gitea_add_user_to_organization_team(username: str, team_id: int) -> bool:
|
||||||
|
r = requests.put(
|
||||||
|
GITEA_API_URL + f'/teams/{team_id}/members/{username}',
|
||||||
|
headers={'Authorization': 'token ' + API_TOKEN},
|
||||||
|
)
|
||||||
|
|
||||||
|
if r.status_code != 204:
|
||||||
|
print(f'ERR: Failed to add user {username} to org team {team_id}:', r.text)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# If a passwd user has one of the following shells,
|
||||||
|
# it is most likely not a PVV user, but rather a system user.
|
||||||
|
# Users with these shells should thus be ignored.
|
||||||
|
BANNED_SHELLS = [
|
||||||
|
"/usr/bin/nologin",
|
||||||
|
"/usr/sbin/nologin",
|
||||||
|
"/sbin/nologin",
|
||||||
|
"/bin/false",
|
||||||
|
"/bin/msgsh",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Reads out a passwd-file line for line, and filters out
|
||||||
|
# real PVV users (as opposed to system users meant for daemons and such)
|
||||||
|
def passwd_file_parser(passwd_path):
|
||||||
|
with open(passwd_path, 'r') as f:
|
||||||
|
for line in f.readlines():
|
||||||
|
uid = int(line.split(':')[2])
|
||||||
|
if uid < 1000:
|
||||||
|
continue
|
||||||
|
|
||||||
|
shell = line.split(':')[-1]
|
||||||
|
if shell in BANNED_SHELLS:
|
||||||
|
continue
|
||||||
|
|
||||||
|
username = line.split(':')[0]
|
||||||
|
name = line.split(':')[4].split(',')[0]
|
||||||
|
yield (username, name)
|
||||||
|
|
||||||
|
|
||||||
|
# This function either creates a new user in gitea
|
||||||
|
# and fills it out with some default information if
|
||||||
|
# it does not exist, or ensures that the default information
|
||||||
|
# is correct if the user already exists. All user information
|
||||||
|
# (including non-default fields) is pulled from gitea and added
|
||||||
|
# to the `existing_users` dict
|
||||||
|
def add_or_patch_gitea_user(
|
||||||
|
username: str,
|
||||||
|
name: str,
|
||||||
|
existing_users: dict[str, dict[str, any]],
|
||||||
|
) -> None:
|
||||||
|
user = {
|
||||||
|
"full_name": name,
|
||||||
|
"username": username,
|
||||||
|
"login_name": username,
|
||||||
|
"source_id": 1, # 1 = SMTP
|
||||||
|
}
|
||||||
|
|
||||||
|
if username not in existing_users:
|
||||||
|
user["password"] = secrets.token_urlsafe(32)
|
||||||
|
user["must_change_password"] = False
|
||||||
|
user["visibility"] = "private"
|
||||||
|
user["email"] = username + '@' + EMAIL_DOMAIN
|
||||||
|
|
||||||
|
if not gitea_create_user(username, user):
|
||||||
|
return
|
||||||
|
|
||||||
|
print('Created user', username)
|
||||||
|
existing_users[username] = user
|
||||||
|
|
||||||
|
else:
|
||||||
|
user["visibility"] = existing_users[username]["visibility"]
|
||||||
|
|
||||||
|
if not gitea_edit_user(username, user):
|
||||||
|
return
|
||||||
|
|
||||||
|
print('Updated user', username)
|
||||||
|
|
||||||
|
|
||||||
|
# This function adds a user to a gitea team (part of organization)
|
||||||
|
# if the user is not already part of said team.
|
||||||
|
def ensure_gitea_user_is_part_of_team(
|
||||||
|
username: str,
|
||||||
|
org: str,
|
||||||
|
team_name: str,
|
||||||
|
) -> None:
|
||||||
|
teams = gitea_list_teams_for_organization(org)
|
||||||
|
|
||||||
|
if teams is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
if team_name not in teams:
|
||||||
|
print(f'ERR: could not find team "{team_name}" in organization "{org}"')
|
||||||
|
|
||||||
|
gitea_add_user_to_organization_team(username, teams[team_name]['id'])
|
||||||
|
|
||||||
|
print(f'User {username} is now part of {org}/{team_name}')
|
||||||
|
|
||||||
|
|
||||||
|
# List of teams that all users should be part of by default
|
||||||
|
COMMON_USER_TEAMS = [
|
||||||
|
("Projects", "Members"),
|
||||||
|
("Kurs", "Members"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
existing_users = gitea_list_all_users()
|
||||||
|
if existing_users is None:
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
for username, name in passwd_file_parser("/tmp/passwd-import"):
|
||||||
|
print(f"Processing {username}")
|
||||||
|
add_or_patch_gitea_user(username, name, existing_users)
|
||||||
|
for org, team_name in COMMON_USER_TEAMS:
|
||||||
|
ensure_gitea_user_is_part_of_team(username, org, team_name)
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
114
hosts/bekkalokk/services/gitea/web-secret-provider/default.nix
Normal file
114
hosts/bekkalokk/services/gitea/web-secret-provider/default.nix
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
let
|
||||||
|
organizations = [
|
||||||
|
"Drift"
|
||||||
|
"Projects"
|
||||||
|
"Kurs"
|
||||||
|
];
|
||||||
|
|
||||||
|
giteaCfg = config.services.gitea;
|
||||||
|
|
||||||
|
giteaWebSecretProviderScript = pkgs.writers.writePython3 "gitea-web-secret-provider" {
|
||||||
|
libraries = with pkgs.python3Packages; [ requests ];
|
||||||
|
flakeIgnore = [
|
||||||
|
"E501" # Line over 80 chars lol
|
||||||
|
"E201" # "whitespace after {"
|
||||||
|
"E202" # "whitespace after }"
|
||||||
|
"E251" # unexpected spaces around keyword / parameter equals
|
||||||
|
"W391" # Newline at end of file
|
||||||
|
];
|
||||||
|
makeWrapperArgs = [
|
||||||
|
"--prefix PATH : ${(lib.makeBinPath [ pkgs.openssh ])}"
|
||||||
|
];
|
||||||
|
} (builtins.readFile ./gitea-web-secret-provider.py);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
users.groups."gitea-web" = { };
|
||||||
|
users.users."gitea-web" = {
|
||||||
|
group = "gitea-web";
|
||||||
|
isSystemUser = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.secrets."gitea/web-secret-provider/token" = {
|
||||||
|
owner = "gitea-web";
|
||||||
|
group = "gitea-web";
|
||||||
|
restartUnits = [
|
||||||
|
"gitea-web-secret-provider@"
|
||||||
|
] ++ (map (org: "gitea-web-secret-provider@${org}") organizations);
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.slices.system-giteaweb = {
|
||||||
|
description = "Gitea web directories";
|
||||||
|
};
|
||||||
|
|
||||||
|
# https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html#Specifiers
|
||||||
|
# %i - instance name (after the @)
|
||||||
|
# %d - secrets directory
|
||||||
|
systemd.services."gitea-web-secret-provider@" = {
|
||||||
|
description = "Ensure all repos in %i has an SSH key to push web content";
|
||||||
|
requires = [ "gitea.service" "network.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Slice = "system-giteaweb.slice";
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = let
|
||||||
|
args = lib.cli.toGNUCommandLineShell { } {
|
||||||
|
org = "%i";
|
||||||
|
token-path = "%d/token";
|
||||||
|
api-url = "${giteaCfg.settings.server.ROOT_URL}api/v1";
|
||||||
|
key-dir = "/var/lib/gitea-web/keys/%i";
|
||||||
|
authorized-keys-path = "/var/lib/gitea-web/authorized_keys.d/%i";
|
||||||
|
rrsync-script = pkgs.writeShellScript "rrsync-chown" ''
|
||||||
|
${lib.getExe pkgs.rrsync} -wo "$1"
|
||||||
|
${pkgs.coreutils}/bin/chown -R gitea-web:gitea-web "$1"
|
||||||
|
'';
|
||||||
|
web-dir = "/var/lib/gitea-web/web";
|
||||||
|
};
|
||||||
|
in "${giteaWebSecretProviderScript} ${args}";
|
||||||
|
|
||||||
|
User = "gitea-web";
|
||||||
|
Group = "gitea-web";
|
||||||
|
|
||||||
|
StateDirectory = "gitea-web";
|
||||||
|
StateDirectoryMode = "0750";
|
||||||
|
LoadCredential = [
|
||||||
|
"token:${config.sops.secrets."gitea/web-secret-provider/token".path}"
|
||||||
|
];
|
||||||
|
|
||||||
|
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 = "Ensure all repos in %i has an SSH key to push web content";
|
||||||
|
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;
|
||||||
|
|
||||||
|
services.openssh.authorizedKeysFiles = map (org: "/var/lib/gitea-web/authorized_keys.d/${org}") organizations;
|
||||||
|
|
||||||
|
users.users.nginx.extraGroups = [ "gitea-web" ];
|
||||||
|
services.nginx.virtualHosts."pages.pvv.ntnu.no" = {
|
||||||
|
kTLS = true;
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
root = "/var/lib/gitea-web/web";
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(description="Generate SSH keys for Gitea repositories and add them as secrets")
|
||||||
|
parser.add_argument("--org", required=True, type=str, help="The organization to generate keys for")
|
||||||
|
parser.add_argument("--token-path", metavar='PATH', required=True, type=Path, help="Path to a file containing the Gitea API token")
|
||||||
|
parser.add_argument("--api-url", metavar='URL', type=str, help="The URL of the Gitea API", default="https://git.pvv.ntnu.no/api/v1")
|
||||||
|
parser.add_argument("--key-dir", metavar='PATH', type=Path, help="The directory to store the generated keys in", default="/run/gitea-web-secret-provider")
|
||||||
|
parser.add_argument("--authorized-keys-path", metavar='PATH', type=Path, help="The path to the resulting authorized_keys file", default="/etc/ssh/authorized_keys.d/gitea-web-secret-provider")
|
||||||
|
parser.add_argument("--rrsync-script", metavar='PATH', type=Path, help="The path to a rrsync script, taking the destination path as its single argument")
|
||||||
|
parser.add_argument("--web-dir", metavar='PATH', type=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: argparse.Namespace, token: str, repo: str, name: str, secret: str):
|
||||||
|
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: argparse.Namespace, token: str):
|
||||||
|
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: argparse.Namespace, repository: str):
|
||||||
|
keyname = hashlib.sha256(args.org.encode() + repository.encode()).hexdigest()
|
||||||
|
key_path = args.key_dir / keyname
|
||||||
|
if not key_path.is_file() or args.force:
|
||||||
|
subprocess.run(
|
||||||
|
[
|
||||||
|
"ssh-keygen",
|
||||||
|
*("-t", "ed25519"),
|
||||||
|
*("-f", key_path),
|
||||||
|
*("-N", ""),
|
||||||
|
*("-C", f"{args.org}/{repository}"),
|
||||||
|
],
|
||||||
|
check=True,
|
||||||
|
stdin=subprocess.DEVNULL,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
)
|
||||||
|
print(f"Generated SSH key for `{args.org}/{repository}`")
|
||||||
|
|
||||||
|
with open(key_path, "r") as f:
|
||||||
|
private_key = f.read()
|
||||||
|
|
||||||
|
pub_key_path = args.key_dir / (keyname + '.pub')
|
||||||
|
with open(pub_key_path, "r") as f:
|
||||||
|
public_key = f.read()
|
||||||
|
|
||||||
|
return private_key, public_key
|
||||||
|
|
||||||
|
|
||||||
|
SSH_OPTS = ",".join([
|
||||||
|
"restrict",
|
||||||
|
"no-agent-forwarding",
|
||||||
|
"no-port-forwarding",
|
||||||
|
"no-pty",
|
||||||
|
"no-X11-forwarding",
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def generate_authorized_keys(args: argparse.Namespace, repo_public_keys: list[tuple[str, str]]):
|
||||||
|
lines = []
|
||||||
|
for repo, public_key in repo_public_keys:
|
||||||
|
command = f"{args.rrsync_script} {args.web_dir}/{args.org}/{repo}"
|
||||||
|
lines.append(f'command="{command}",{SSH_OPTS} {public_key}')
|
||||||
|
|
||||||
|
with open(args.authorized_keys_path, "w") as f:
|
||||||
|
f.writelines(lines)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
os.makedirs(args.authorized_keys_path.parent, 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()
|
51
hosts/bekkalokk/services/phpfpm.nix
Normal file
51
hosts/bekkalokk/services/phpfpm.nix
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
let
|
||||||
|
pools = map (pool: "phpfpm-${pool}") [
|
||||||
|
"idp"
|
||||||
|
"mediawiki"
|
||||||
|
"pvv-nettsiden"
|
||||||
|
"roundcube"
|
||||||
|
"snappymail"
|
||||||
|
];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Source: https://www.pierreblazquez.com/2023/06/17/how-to-harden-apache-php-fpm-daemons-using-systemd/
|
||||||
|
systemd.services = lib.genAttrs pools (_: {
|
||||||
|
serviceConfig = let
|
||||||
|
caps = [
|
||||||
|
"CAP_NET_BIND_SERVICE"
|
||||||
|
"CAP_SETGID"
|
||||||
|
"CAP_SETUID"
|
||||||
|
"CAP_CHOWN"
|
||||||
|
"CAP_KILL"
|
||||||
|
"CAP_IPC_LOCK"
|
||||||
|
"CAP_DAC_OVERRIDE"
|
||||||
|
];
|
||||||
|
in {
|
||||||
|
AmbientCapabilities = caps;
|
||||||
|
CapabilityBoundingSet = caps;
|
||||||
|
DeviceAllow = [ "" ];
|
||||||
|
LockPersonality = true;
|
||||||
|
MemoryDenyWriteExecute = false;
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateMounts = true;
|
||||||
|
ProtectClock = true;
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
ProtectHome = true;
|
||||||
|
ProtectHostname = true;
|
||||||
|
ProtectKernelLogs = true;
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
RemoveIPC = true;
|
||||||
|
UMask = "0077";
|
||||||
|
RestrictNamespaces = "~mnt";
|
||||||
|
RestrictRealtime = true;
|
||||||
|
RestrictSUIDSGID = true;
|
||||||
|
SystemCallArchitectures = "native";
|
||||||
|
KeyringMode = "private";
|
||||||
|
SystemCallFilter = [
|
||||||
|
"@system-service"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
@ -65,4 +65,40 @@ in {
|
|||||||
proxyWebsockets = true;
|
proxyWebsockets = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
systemd.services.vaultwarden = lib.mkIf cfg.enable {
|
||||||
|
serviceConfig = {
|
||||||
|
AmbientCapabilities = [ "" ];
|
||||||
|
CapabilityBoundingSet = [ "" ];
|
||||||
|
DeviceAllow = [ "" ];
|
||||||
|
LockPersonality = true;
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
# MemoryDenyWriteExecute = true;
|
||||||
|
PrivateMounts = true;
|
||||||
|
PrivateUsers = true;
|
||||||
|
ProcSubset = "pid";
|
||||||
|
ProtectClock = true;
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
ProtectHostname = true;
|
||||||
|
ProtectKernelLogs = true;
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
ProtectProc = "invisible";
|
||||||
|
RestrictAddressFamilies = [
|
||||||
|
"AF_INET"
|
||||||
|
"AF_INET6"
|
||||||
|
"AF_UNIX"
|
||||||
|
];
|
||||||
|
RemoveIPC = true;
|
||||||
|
RestrictNamespaces = true;
|
||||||
|
RestrictRealtime = true;
|
||||||
|
RestrictSUIDSGID = true;
|
||||||
|
SystemCallArchitectures = "native";
|
||||||
|
SystemCallFilter = [
|
||||||
|
"@system-service"
|
||||||
|
"~@privileged"
|
||||||
|
];
|
||||||
|
UMask = "0007";
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,7 @@
|
|||||||
./services/mysql.nix
|
./services/mysql.nix
|
||||||
./services/postgres.nix
|
./services/postgres.nix
|
||||||
./services/mysql.nix
|
./services/mysql.nix
|
||||||
# TODO: fix the calendar bot
|
./services/calendar-bot.nix
|
||||||
# ./services/calendar-bot.nix
|
|
||||||
|
|
||||||
./services/matrix
|
./services/matrix
|
||||||
];
|
];
|
||||||
|
@ -2,11 +2,19 @@
|
|||||||
let
|
let
|
||||||
cfg = config.services.pvv-calendar-bot;
|
cfg = config.services.pvv-calendar-bot;
|
||||||
in {
|
in {
|
||||||
sops.secrets."calendar-bot/matrix_token" = {
|
sops.secrets = {
|
||||||
sopsFile = ../../../secrets/bicep/bicep.yaml;
|
"calendar-bot/matrix_token" = {
|
||||||
key = "calendar-bot/matrix_token";
|
sopsFile = ../../../secrets/bicep/bicep.yaml;
|
||||||
owner = cfg.user;
|
key = "calendar-bot/matrix_token";
|
||||||
group = cfg.group;
|
owner = cfg.user;
|
||||||
|
group = cfg.group;
|
||||||
|
};
|
||||||
|
"calendar-bot/mysql_password" = {
|
||||||
|
sopsFile = ../../../secrets/bicep/bicep.yaml;
|
||||||
|
key = "calendar-bot/mysql_password";
|
||||||
|
owner = cfg.user;
|
||||||
|
group = cfg.group;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services.pvv-calendar-bot = {
|
services.pvv-calendar-bot = {
|
||||||
@ -18,6 +26,11 @@ in {
|
|||||||
user = "@bot_calendar:pvv.ntnu.no";
|
user = "@bot_calendar:pvv.ntnu.no";
|
||||||
channel = "!gkNLUIhYVpEyLatcRz:pvv.ntnu.no";
|
channel = "!gkNLUIhYVpEyLatcRz:pvv.ntnu.no";
|
||||||
};
|
};
|
||||||
|
database = {
|
||||||
|
host = "mysql.pvv.ntnu.no";
|
||||||
|
user = "calendar-bot";
|
||||||
|
passwordFile = config.sops.secrets."calendar-bot/mysql_password".path;
|
||||||
|
};
|
||||||
secretsFile = config.sops.secrets."calendar-bot/matrix_token".path;
|
secretsFile = config.sops.secrets."calendar-bot/matrix_token".path;
|
||||||
onCalendar = "*-*-* 09:00:00";
|
onCalendar = "*-*-* 09:00:00";
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
{ config, pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
let
|
|
||||||
sslCert = config.security.acme.certs."postgres.pvv.ntnu.no";
|
|
||||||
in
|
|
||||||
{
|
{
|
||||||
services.postgresql = {
|
services.postgresql = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@ -79,12 +76,16 @@ in
|
|||||||
|
|
||||||
systemd.services.postgresql.serviceConfig = {
|
systemd.services.postgresql.serviceConfig = {
|
||||||
LoadCredential = [
|
LoadCredential = [
|
||||||
"cert:${sslCert.directory}/cert.pem"
|
"cert:/etc/certs/postgres.crt"
|
||||||
"key:${sslCert.directory}/key.pem"
|
"key:/etc/certs/postgres.key"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
users.groups.acme.members = [ "postgres" ];
|
environment.snakeoil-certs."/etc/certs/postgres" = {
|
||||||
|
owner = "postgres";
|
||||||
|
group = "postgres";
|
||||||
|
subject = "/C=NO/O=Programvareverkstedet/CN=postgres.pvv.ntnu.no/emailAddress=drift@pvv.ntnu.no";
|
||||||
|
};
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 5432 ];
|
networking.firewall.allowedTCPPorts = [ 5432 ];
|
||||||
networking.firewall.allowedUDPPorts = [ 5432 ];
|
networking.firewall.allowedUDPPorts = [ 5432 ];
|
||||||
|
18
hosts/ildkule/services/journald-remote.nix
Normal file
18
hosts/ildkule/services/journald-remote.nix
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
services.journald.remote = {
|
||||||
|
enable = true;
|
||||||
|
settings.Remote = {
|
||||||
|
# ServerKeyFile = "/run/credentials/systemd-journald-remote.service/key.pem";
|
||||||
|
# ServerCertificateFile = "/run/credentials/systemd-journald-remote.service/.pem";
|
||||||
|
ServerKeyFile = "/etc/journald-remote-certs/key.pem";
|
||||||
|
ServerCertificateFile = "/etc/journald-remote-certs/cert.pem";
|
||||||
|
TrustedCertificateFile = "-";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# systemd.services.systemd-journal-remote.serviceConfig.LoadCredential = [
|
||||||
|
# "key.pem:/etc/journald-remote-certs/key.pem"
|
||||||
|
# "cert.pem:/etc/journald-remote-certs/cert.pem"
|
||||||
|
# ];
|
||||||
|
}
|
4
justfile
4
justfile
@ -10,6 +10,10 @@ check:
|
|||||||
build-machine machine=`just _a_machine`:
|
build-machine machine=`just _a_machine`:
|
||||||
{{nom}} build .#nixosConfigurations.{{ machine }}.config.system.build.toplevel
|
{{nom}} build .#nixosConfigurations.{{ machine }}.config.system.build.toplevel
|
||||||
|
|
||||||
|
run-vm machine=`just _a_machine`:
|
||||||
|
nixos-rebuild build-vm --flake .#{{ machine }}
|
||||||
|
QEMU_NET_OPTS="hostfwd=tcp::8080-:80,hostfwd=tcp::8081-:443,hostfwd=tcp::2222-:22" ./result/bin/run-*-vm
|
||||||
|
|
||||||
@update-inputs:
|
@update-inputs:
|
||||||
nix eval .#inputs --apply builtins.attrNames --json \
|
nix eval .#inputs --apply builtins.attrNames --json \
|
||||||
| jq '.[]' -r \
|
| jq '.[]' -r \
|
||||||
|
@ -50,7 +50,7 @@ in
|
|||||||
serviceConfig.Type = "oneshot";
|
serviceConfig.Type = "oneshot";
|
||||||
script = let
|
script = let
|
||||||
openssl = lib.getExe pkgs.openssl;
|
openssl = lib.getExe pkgs.openssl;
|
||||||
in lib.concatMapStringsSep "\n----------------\n" ({ name, value }: ''
|
in lib.concatMapStringsSep "\n" ({ name, value }: ''
|
||||||
mkdir -p $(dirname "${value.certificate}") $(dirname "${value.certificateKey}")
|
mkdir -p $(dirname "${value.certificate}") $(dirname "${value.certificateKey}")
|
||||||
if ! ${openssl} x509 -checkend 86400 -noout -in ${value.certificate}
|
if ! ${openssl} x509 -checkend 86400 -noout -in ${value.certificate}
|
||||||
then
|
then
|
||||||
@ -69,6 +69,8 @@ in
|
|||||||
chown "${value.owner}:${value.group}" "${value.certificateKey}"
|
chown "${value.owner}:${value.group}" "${value.certificateKey}"
|
||||||
chmod "${value.mode}" "${value.certificate}"
|
chmod "${value.mode}" "${value.certificate}"
|
||||||
chmod "${value.mode}" "${value.certificateKey}"
|
chmod "${value.mode}" "${value.certificateKey}"
|
||||||
|
|
||||||
|
echo "\n-----------------\n"
|
||||||
'') (lib.attrsToList cfg);
|
'') (lib.attrsToList cfg);
|
||||||
};
|
};
|
||||||
systemd.timers."generate-snakeoil-certs" = {
|
systemd.timers."generate-snakeoil-certs" = {
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
gitea:
|
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]
|
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]
|
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]
|
email-password: ENC[AES256_GCM,data:KRwC+aL1aPvJuXt91Oq1ttATMnFTnuUy,iv:ats8TygB/2pORkaTZzPOLufZ9UmvVAKoRcWNvYF1z6w=,tag:Do0fA+4cZ3+l7JJyu8hjBg==,type:str]
|
||||||
passwd-ssh-key: ENC[AES256_GCM,data:L0lF0wvpayss1NU9m3A45cH0bCMQzODTFVrq6EPd1JHx54wIcoaRBYLmxXKXASzBlCg9zlwXMUIk3OQcS3kdzMKL0iqcSL2iicAcKjFIHyrWLqXgwV5pRSP/tRPcVw8KW8gz0bh33EgESs5ReddZ3VZ0Cy1s2YupMRQvBXr89k1+Hv70OWB6P06hvxhv/zKcMGI1N/dWLroMgrQuT9imw4+/Q1RqwzTYeEU+eUn24AM9GjcBg4qf3OI+6g0nXUat/upIYE28iF5J3lbUSmDSmirBLc8xgHLdOyyJPTObWYWYxlSL78T7IqiMm9lI3rtBlpJDDcn/YxZpVqN5bg2154GISNK+uR0TVSLdJ+drdGHIfIX3G78XSxf2L9rbJyRn8MQlgStfdBIQicLavQKVMrmj+XQfvEMez23WbPLjH4oViBQFI+GrOHOGy/f16cz8Sn4n+69OcsOeTxs3tKYdfq6r1XLYSJ/fe/zvxBpaZiyGXljsuyEdIyBL2A8D6uSXe3Nd3/DAdBtceFfIdN1olCdutixzVWgxaJnrel161z5A/4w=,iv:Uy46yY3jFYSvpxrgCHxRMUksnWfhf5DViLMvCXVMMl4=,tag:wFEJ5+icFrOKkc56gY0A5g==,type:str]
|
passwd-ssh-key: ENC[AES256_GCM,data:L0lF0wvpayss1NU9m3A45cH0bCMQzODTFVrq6EPd1JHx54wIcoaRBYLmxXKXASzBlCg9zlwXMUIk3OQcS3kdzMKL0iqcSL2iicAcKjFIHyrWLqXgwV5pRSP/tRPcVw8KW8gz0bh33EgESs5ReddZ3VZ0Cy1s2YupMRQvBXr89k1+Hv70OWB6P06hvxhv/zKcMGI1N/dWLroMgrQuT9imw4+/Q1RqwzTYeEU+eUn24AM9GjcBg4qf3OI+6g0nXUat/upIYE28iF5J3lbUSmDSmirBLc8xgHLdOyyJPTObWYWYxlSL78T7IqiMm9lI3rtBlpJDDcn/YxZpVqN5bg2154GISNK+uR0TVSLdJ+drdGHIfIX3G78XSxf2L9rbJyRn8MQlgStfdBIQicLavQKVMrmj+XQfvEMez23WbPLjH4oViBQFI+GrOHOGy/f16cz8Sn4n+69OcsOeTxs3tKYdfq6r1XLYSJ/fe/zvxBpaZiyGXljsuyEdIyBL2A8D6uSXe3Nd3/DAdBtceFfIdN1olCdutixzVWgxaJnrel161z5A/4w=,iv:Uy46yY3jFYSvpxrgCHxRMUksnWfhf5DViLMvCXVMMl4=,tag:wFEJ5+icFrOKkc56gY0A5g==,type:str]
|
||||||
ssh-known-hosts: ENC[AES256_GCM,data:zlRLoelQeumMxGqPmgMTB69X1RVWXIs2jWwc67lk0wrdNOHUs5UzV5TUA1JnQ43RslBU92+js7DkyvE5enGzw7zZE5F1ZYdGv/eCgvkTMC9BoLfzHzP6OzayPLYEt3xJ5PRocN8JUAD55cuu4LgsuebuydHPi2oWOfpbSUBKSeCh6dvk5Pp1XRDprPS5SzGLW8Xjq98QlzmfGv50meI9CDJZVF9Wq/72gkyfgtb3YVdr,iv:AF06TBitHegfWk6w07CdkHklh4ripQCmA45vswDQgss=,tag:zKh7WVXMJN2o9ZIwIkby3Q==,type:str]
|
ssh-known-hosts: ENC[AES256_GCM,data:zlRLoelQeumMxGqPmgMTB69X1RVWXIs2jWwc67lk0wrdNOHUs5UzV5TUA1JnQ43RslBU92+js7DkyvE5enGzw7zZE5F1ZYdGv/eCgvkTMC9BoLfzHzP6OzayPLYEt3xJ5PRocN8JUAD55cuu4LgsuebuydHPi2oWOfpbSUBKSeCh6dvk5Pp1XRDprPS5SzGLW8Xjq98QlzmfGv50meI9CDJZVF9Wq/72gkyfgtb3YVdr,iv:AF06TBitHegfWk6w07CdkHklh4ripQCmA45vswDQgss=,tag:zKh7WVXMJN2o9ZIwIkby3Q==,type:str]
|
||||||
import-user-env: ENC[AES256_GCM,data:vfaqjGEnUM9VtOPvBurz7nFwzGZt3L2EqijrQej4wiOcGCrRA4tN6kBV6NmhHqlFPsw=,iv:viPGkyOOacCWcgTu25da4qH7DC4wz2qdeC1W2WcMUdI=,tag:BllNqGQoaxqUo3lTz9LGnw==,type:str]
|
import-user-env: ENC[AES256_GCM,data:wArFwTd0ZoB4VXHPpichfnmykxGxN8y2EQsMgOPHv7zsm6A+m2rG9BWDGskQPr5Ns9o=,iv:gPUzYFSNoALJb1N0dsbNlgHIb7+xG7E9ANpmVNZURQ0=,tag:JghfRy2OcDFWKS9zX1XJ9A==,type:str]
|
||||||
runners:
|
runners:
|
||||||
alpha: ENC[AES256_GCM,data:gARxCufePz+EMVwEwRsL2iZUfh9HUowWqtb7Juz3fImeeAdbt+k3DvL/Nwgegg==,iv:3fEaWd7v7uLGTy2J7EFQGfN0ztI0uCOJRz5Mw8V5UOU=,tag:Aa6LwWeW2hfDz1SqEhUJpA==,type:str]
|
alpha: ENC[AES256_GCM,data:gARxCufePz+EMVwEwRsL2iZUfh9HUowWqtb7Juz3fImeeAdbt+k3DvL/Nwgegg==,iv:3fEaWd7v7uLGTy2J7EFQGfN0ztI0uCOJRz5Mw8V5UOU=,tag:Aa6LwWeW2hfDz1SqEhUJpA==,type:str]
|
||||||
beta: ENC[AES256_GCM,data:DVjS78IKWiWgf+PuijCZKx4ZaEJGhQr7vl+lc7QOg1JlA4p9Kux/tOD8+f2+jA==,iv:tk3Xk7lKWNdZ035+QVIhxXy2iJbHwunI4jRFM4It46E=,tag:9Mr6o//svYEyYhSvzkOXMg==,type:str]
|
beta: ENC[AES256_GCM,data:DVjS78IKWiWgf+PuijCZKx4ZaEJGhQr7vl+lc7QOg1JlA4p9Kux/tOD8+f2+jA==,iv:tk3Xk7lKWNdZ035+QVIhxXy2iJbHwunI4jRFM4It46E=,tag:9Mr6o//svYEyYhSvzkOXMg==,type:str]
|
||||||
@ -90,8 +92,8 @@ sops:
|
|||||||
UHpLRkdQTnhkeGlWVG9VS1hkWktyckEKAdwnA9URLYZ50lMtXrU9Q09d0L3Zfsyr
|
UHpLRkdQTnhkeGlWVG9VS1hkWktyckEKAdwnA9URLYZ50lMtXrU9Q09d0L3Zfsyr
|
||||||
4UsvjjdnFtsXwEZ9ZzOQrpiN0Oz24s3csw5KckDni6kslaloJZsLGg==
|
4UsvjjdnFtsXwEZ9ZzOQrpiN0Oz24s3csw5KckDni6kslaloJZsLGg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2024-05-26T02:07:41Z"
|
lastmodified: "2024-08-26T19:38:58Z"
|
||||||
mac: ENC[AES256_GCM,data:CRaJefV1zcJc6eyzyjTLgd0+Wv46VT8o4iz2YAGU+c2b/Cr97Tj290LoEO6UXTI3uFwVfzii2yZ2l+4FK3nVVriD4Cx1O/9qWcnLa5gfK30U0zof6AsJx8qtGu1t6oiPlGUCF7sT0BW9Wp8cPumrY6cZp9QbhmIDV0o0aJNUNN4=,iv:8OSYV1eG6kYlJD4ovZZhcD1GaYnmy7vHPa/+7egM1nE=,tag:OPI13rpDh2l1ViFj8TBFWg==,type:str]
|
mac: ENC[AES256_GCM,data:3FyfZPmJ7znQEul+IwqN1ZaM53n6os3grquJwJ9vfyDSc2h8UZBhqYG+2uW9Znp9DSIjuhCUI8iqGKRJE0M/6IDICeXms/5+ynVFOS9bA2cdzPvWaj0FFAd2x3g4Vhs47+vRlsnIe/tMiKU3IOvzOfI6KAUHc9L2ySrzH7z2+fo=,iv:1iZSR9qOIEtf+fNbtWSwJBIUEQGKadfHSVOnkFzOwq8=,tag:Sk6JEU1B6Rd1GXLYC6rQtQ==,type:str]
|
||||||
pgp:
|
pgp:
|
||||||
- created_at: "2024-08-04T00:03:28Z"
|
- created_at: "2024-08-04T00:03:28Z"
|
||||||
enc: |-
|
enc: |-
|
||||||
@ -114,4 +116,4 @@ sops:
|
|||||||
-----END PGP MESSAGE-----
|
-----END PGP MESSAGE-----
|
||||||
fp: F7D37890228A907440E1FD4846B9228E814A2AAC
|
fp: F7D37890228A907440E1FD4846B9228E814A2AAC
|
||||||
unencrypted_suffix: _unencrypted
|
unencrypted_suffix: _unencrypted
|
||||||
version: 3.8.1
|
version: 3.9.0
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
calendar-bot:
|
calendar-bot:
|
||||||
matrix_token: ENC[AES256_GCM,data:zJv9sw6pEzb9hxKT682wsD87HC9iejbps2wl2Z5QW1XZUSBHdcqyg1pxd+jFKTeKGQ==,iv:zDbvF1H98NsECjCtGXS+Y9HIhXowzz9HF9mltqnArog=,tag:/ftcOSQ13ElkVJBxYIMUGQ==,type:str]
|
matrix_token: ENC[AES256_GCM,data:zJv9sw6pEzb9hxKT682wsD87HC9iejbps2wl2Z5QW1XZUSBHdcqyg1pxd+jFKTeKGQ==,iv:zDbvF1H98NsECjCtGXS+Y9HIhXowzz9HF9mltqnArog=,tag:/ftcOSQ13ElkVJBxYIMUGQ==,type:str]
|
||||||
|
mysql_password: ENC[AES256_GCM,data:Gqag8yOgPH3ntoT5TmaqJWv1j+si2qIyz5Ryfw5E2A==,iv:kQDcxnPfwJQcFovI4f87UDt18F8ah3z5xeY86KmdCyY=,tag:A1sCSNXJziAmtUWohqwJgg==,type:str]
|
||||||
mysql:
|
mysql:
|
||||||
password: ENC[AES256_GCM,data:KqEe0TVdeMIzPKsmFg9x0X9xWijnOk306ycyXTm2Tpqo/O0F,iv:Y+hlQ8n1ZIP9ncXBzd2kCSs/DWVTWhiEluFVwZFKRCA=,tag:xlaUk0Wftk62LpYE5pKNQw==,type:str]
|
password: ENC[AES256_GCM,data:KqEe0TVdeMIzPKsmFg9x0X9xWijnOk306ycyXTm2Tpqo/O0F,iv:Y+hlQ8n1ZIP9ncXBzd2kCSs/DWVTWhiEluFVwZFKRCA=,tag:xlaUk0Wftk62LpYE5pKNQw==,type:str]
|
||||||
sops:
|
sops:
|
||||||
@ -62,8 +63,8 @@ sops:
|
|||||||
cTh5bnJ3WW90aXRCSUp6NHFYeU1tZ0kK4afdtJwGNu6wLRI0fuu+mBVeqVeB0rgX
|
cTh5bnJ3WW90aXRCSUp6NHFYeU1tZ0kK4afdtJwGNu6wLRI0fuu+mBVeqVeB0rgX
|
||||||
0q5hwyzjiRnHnyjF38CmcGgydSfDRmF6P+WIMbCwXC6LwfRhAmBGPg==
|
0q5hwyzjiRnHnyjF38CmcGgydSfDRmF6P+WIMbCwXC6LwfRhAmBGPg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2023-09-05T23:28:56Z"
|
lastmodified: "2024-08-15T21:18:33Z"
|
||||||
mac: ENC[AES256_GCM,data:pCWTkmCQgBOqhejK2sCLQ3H8bRXmXlToQxYmOG0IWDo2eGiZOLuIkZ1/1grYgfxAGiD4ysJod0nJuvo+eAsMeYAy6QJVtrOqO2d9V2NEdzLckXyYvwyJyZoFbNC5EW9471V0m4jLRSh5821ckNo/wtWFR11wfO15tI3MqtD1rtA=,iv:QDnckPl0LegaH0b7V4WAtmVXaL4LN+k3uKHQI2dkW7E=,tag:mScUQBR0ZHl1pi/YztrvFg==,type:str]
|
mac: ENC[AES256_GCM,data:uR5HgeDAYqoqB9kk1V6p0T30+v6WpQJi4+qIeCDRnoUPnQKUVR10hvBhICck+E+Uh8p+tGhM6Uf3YrAJAV0ZCUiNJjtwDJQQLUDT53vdOAXN4xADCQqNuhgVwVMaruoTheEiwOswRuhFeEwy0gBj3Ze2pu47lueHYclmEzumLeQ=,iv:t0UyXN2YaR2m7M/pV2wTLJG5wVfqTIUs7wSQMmyeTVw=,tag:O7dIffzrDAXz3kGx5uazhw==,type:str]
|
||||||
pgp:
|
pgp:
|
||||||
- created_at: "2024-08-04T00:03:40Z"
|
- created_at: "2024-08-04T00:03:40Z"
|
||||||
enc: |-
|
enc: |-
|
||||||
@ -86,4 +87,4 @@ sops:
|
|||||||
-----END PGP MESSAGE-----
|
-----END PGP MESSAGE-----
|
||||||
fp: F7D37890228A907440E1FD4846B9228E814A2AAC
|
fp: F7D37890228A907440E1FD4846B9228E814A2AAC
|
||||||
unencrypted_suffix: _unencrypted
|
unencrypted_suffix: _unencrypted
|
||||||
version: 3.7.3
|
version: 3.9.0
|
||||||
|
@ -21,6 +21,9 @@ in rec {
|
|||||||
ipv4 = pvv-ipv4 213;
|
ipv4 = pvv-ipv4 213;
|
||||||
ipv6 = pvv-ipv6 213;
|
ipv6 = pvv-ipv6 213;
|
||||||
};
|
};
|
||||||
|
log-collector = {
|
||||||
|
inherit (hosts.ildkule) ipv4 ipv6;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
hosts = {
|
hosts = {
|
||||||
|
Reference in New Issue
Block a user