Compare commits

..

3 Commits

Author SHA1 Message Date
Eirik Witterso 60e1bbfd08 WIP: Add bluemap
Co-authored-by: Daniel Olsen <daniel.olsen99@gmail.com>
2024-06-09 07:33:43 +02:00
Oystein Kristoffer Tveit ceaa67fc19
WIP: fix loki 2024-06-08 20:58:25 +02:00
Oystein Kristoffer Tveit 375e0f1486
treewide: fix issues for 24.05 upgrade 2024-06-08 20:58:10 +02:00
116 changed files with 1198 additions and 6429 deletions

View File

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

1
.envrc
View File

@ -1 +0,0 @@
use flake

View File

@ -1 +0,0 @@
e00008da1afe0d760badd34bbeddff36bb08c475

2
.gitignore vendored
View File

@ -1,4 +1,2 @@
result*
/configuration.nix
/.direnv/
*.qcow2

View File

@ -4,15 +4,13 @@ keys:
- &user_felixalb age1mrnldl334l2nszuta6ywvewng0fswv2dz9l5g4qcwe3nj4yxf92qjskdx6
- &user_oysteikt F7D37890228A907440E1FD4846B9228E814A2AAC
- &user_eirikwit age1ju7rd26llahz3g8tz7cy5ld52swj8gsmg0flrmrxngc0nj0avq3ssh0sn5
- &user_pederbs_sopp age1hmpdk4h69wxpwqk9tkud39f66hprhehxtzhgw97r6dvr7v0mx5jscsuhkn
- &user_pederbs_nord age1wrssr4z4g6vl3fd3qme5cewchmmhm0j2xe6wf2meu4r6ycn37anse98mfs
- &user_pederbs_bjarte age1zhxul786an743u0fascv4wtc5xduu7qfy803lfs539yzhgmlq5ds2lznt5
# Hosts
- &host_jokum age1gp8ye4g2mmw3may5xg0zsy7mm04glfz3788mmdx9cvcsdxs9hg0s0cc9kt
- &host_ildkule age1x28hmzvuv6f2n66c0jtqcca3h9rput8d7j5uek6jcpx8n9egd52sqpejq0
- &host_bekkalokk age12nj59tguy9wg882updc2vjdusx5srnxmjyfaqve4zx6jnnsaw3qsyjq6zd
- &host_bicep age1sl43gc9cw939z5tgha2lpwf0xxxgcnlw7w4xem4sqgmt2pt264vq0dmwx2
- &host_buskerud age1tmn5qahlyf0e579e4camckdyxrexjzffv54hdzdnrw7lzqs7kyqq0f2fr3
creation_rules:
# Global secrets
@ -23,23 +21,17 @@ creation_rules:
- *user_danio
- *user_felixalb
- *user_eirikwit
- *user_pederbs_sopp
- *user_pederbs_nord
- *user_pederbs_bjarte
pgp:
- *user_oysteikt
# Host specific secrets
- path_regex: secrets/bekkalokk/[^/]+\.yaml$
key_groups:
- age:
- *host_bekkalokk
- *user_danio
- *user_felixalb
- *user_pederbs_sopp
- *user_pederbs_nord
- *user_pederbs_bjarte
pgp:
- *user_oysteikt
@ -49,9 +41,6 @@ creation_rules:
- *host_jokum
- *user_danio
- *user_felixalb
- *user_pederbs_sopp
- *user_pederbs_nord
- *user_pederbs_bjarte
pgp:
- *user_oysteikt
@ -61,20 +50,21 @@ creation_rules:
- *host_ildkule
- *user_danio
- *user_felixalb
- *user_pederbs_sopp
- *user_pederbs_nord
- *user_pederbs_bjarte
pgp:
- *user_oysteikt
- path_regex: secrets/bicep/[^/]+\.yaml$
key_groups:
- age:
- *host_bicep
- *user_danio
- *user_felixalb
- *user_pederbs_sopp
- *user_pederbs_nord
- *user_pederbs_bjarte
pgp:
- *user_oysteikt
- path_regex: secrets/buskerud/[^/]+\.yaml$
key_groups:
- age:
- *host_buskerud
- *user_danio
- *user_eirikwit

View File

@ -26,14 +26,10 @@ Det er sikkert lurt å lage en PR først om du ikke er vandt til nix enda.
Innen 24h skal alle systemene hente ned den nye konfigurasjonen og deploye den.
Du kan tvinge en maskin til å oppdatere seg før dette ved å kjøre:
`nixos-rebuild switch --update-input nixpkgs --update-input nixpkgs-unstable --no-write-lock-file --refresh --upgrade --flake git+https://git.pvv.ntnu.no/Drift/pvv-nixos-config.git`
`nixos-rebuild switch --update-input nixpkgs --update-input nixpkgs-unstable --no-write-lock-file --refresh --flake git+https://git.pvv.ntnu.no/Drift/pvv-nixos-config.git --upgrade`
som root på maskinen.
Hvis du ikke har lyst til å oppdatere alle pakkene (og kanskje måtte vente en stund!) kan du kjøre
`nixos-rebuild switch --override-input nixpkgs nixpkgs --override-input nixpkgs-unstable nixpkgs-unstable --flake git+https://git.pvv.ntnu.no/Drift/pvv-nixos-config.git`
## Seksjonen for hemmeligheter
For at hemmeligheter ikke skal deles med hele verden i git - eller å være world

133
base.nix Normal file
View File

@ -0,0 +1,133 @@
{ config, lib, pkgs, inputs, values, ... }:
{
imports = [
./users
./modules/snakeoil-certs.nix
];
networking.domain = "pvv.ntnu.no";
networking.useDHCP = false;
# networking.search = [ "pvv.ntnu.no" "pvv.org" ];
# networking.nameservers = lib.mkDefault [ "129.241.0.200" "129.241.0.201" ];
# networking.tempAddresses = lib.mkDefault "disabled";
# networking.defaultGateway = values.hosts.gateway;
systemd.network.enable = true;
services.resolved = {
enable = lib.mkDefault true;
dnssec = "false"; # Supposdly this keeps breaking and the default is to allow downgrades anyways...
};
time.timeZone = "Europe/Oslo";
i18n.defaultLocale = "en_US.UTF-8";
console = {
font = "Lat2-Terminus16";
keyMap = "no";
};
system.autoUpgrade = {
enable = true;
flake = "git+https://git.pvv.ntnu.no/Drift/pvv-nixos-config.git";
flags = [
"--update-input" "nixpkgs"
"--update-input" "nixpkgs-unstable"
"--no-write-lock-file"
];
};
nix.gc.automatic = true;
nix.gc.options = "--delete-older-than 2d";
nix.settings.experimental-features = [ "nix-command" "flakes" ];
/* This makes commandline tools like
** nix run nixpkgs#hello
** and nix-shell -p hello
** use the same channel the system
** was built with
*/
nix.registry = {
nixpkgs.flake = inputs.nixpkgs;
};
nix.nixPath = [ "nixpkgs=${inputs.nixpkgs}" ];
environment.systemPackages = with pkgs; [
file
git
gnupg
htop
nano
ripgrep
rsync
screen
tmux
vim
wget
kitty.terminfo
];
programs.zsh.enable = true;
users.groups."drift".name = "drift";
# Trusted users on the nix builder machines
users.groups."nix-builder-users".name = "nix-builder-users";
services.openssh = {
enable = true;
extraConfig = ''
PubkeyAcceptedAlgorithms=+ssh-rsa
'';
settings.PermitRootLogin = "yes";
};
# nginx return 444 for all nonexistent virtualhosts
systemd.services.nginx.after = [ "generate-snakeoil-certs.service" ];
environment.snakeoil-certs = lib.mkIf config.services.nginx.enable {
"/etc/certs/nginx" = {
owner = "nginx";
group = "nginx";
};
};
services.nginx = {
recommendedTlsSettings = true;
recommendedProxySettings = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
appendConfig = ''
pcre_jit on;
worker_processes auto;
worker_rlimit_nofile 100000;
'';
eventsConfig = ''
worker_connections 2048;
use epoll;
multi_accept on;
'';
};
systemd.services.nginx.serviceConfig = lib.mkIf config.services.nginx.enable {
LimitNOFILE = 65536;
};
services.nginx.virtualHosts."_" = lib.mkIf config.services.nginx.enable {
sslCertificate = "/etc/certs/nginx.crt";
sslCertificateKey = "/etc/certs/nginx.key";
addSSL = true;
extraConfig = "return 444;";
};
networking.firewall.allowedTCPPorts = lib.mkIf config.services.nginx.enable [ 80 443 ];
security.acme = {
acceptTerms = true;
defaults.email = "drift@pvv.ntnu.no";
};
}

View File

@ -1,60 +0,0 @@
{ pkgs, lib, fp, ... }:
{
imports = [
(fp /users)
(fp /modules/snakeoil-certs.nix)
./networking.nix
./nix.nix
./services/acme.nix
./services/auto-upgrade.nix
./services/irqbalance.nix
./services/logrotate.nix
./services/nginx.nix
./services/openssh.nix
./services/postfix.nix
./services/smartd.nix
./services/thermald.nix
];
boot.tmp.cleanOnBoot = lib.mkDefault true;
time.timeZone = "Europe/Oslo";
i18n.defaultLocale = "en_US.UTF-8";
console = {
font = "Lat2-Terminus16";
keyMap = "no";
};
environment.systemPackages = with pkgs; [
file
git
gnupg
htop
nano
ripgrep
rsync
screen
tmux
vim
wget
kitty.terminfo
];
programs.zsh.enable = true;
security.sudo.execWheelOnly = true;
security.sudo.extraConfig = ''
Defaults lecture = never
'';
users.groups."drift".name = "drift";
# Trusted users on the nix builder machines
users.groups."nix-builder-users".name = "nix-builder-users";
}

View File

@ -1,13 +0,0 @@
{ lib, values, ... }:
{
systemd.network.enable = true;
networking.domain = "pvv.ntnu.no";
networking.useDHCP = false;
# The rest of the networking configuration is usually sourced from /values.nix
services.resolved = {
enable = lib.mkDefault true;
dnssec = "false"; # Supposdly this keeps breaking and the default is to allow downgrades anyways...
};
}

View File

@ -1,34 +0,0 @@
{ inputs, ... }:
{
nix = {
gc = {
automatic = true;
options = "--delete-older-than 2d";
};
settings = {
allow-dirty = true;
auto-optimise-store = true;
builders-use-substitutes = true;
experimental-features = [ "nix-command" "flakes" ];
log-lines = 50;
use-xdg-base-directories = true;
};
/* This makes commandline tools like
** nix run nixpkgs#hello
** and nix-shell -p hello
** use the same channel the system
** was built with
*/
registry = {
"nixpkgs".flake = inputs.nixpkgs;
"nixpkgs-unstable".flake = inputs.nixpkgs-unstable;
"pvv-nix".flake = inputs.self;
};
nixPath = [
"nixpkgs=${inputs.nixpkgs}"
"unstable=${inputs.nixpkgs-unstable}"
];
};
}

View File

@ -1,15 +0,0 @@
{ ... }:
{
security.acme = {
acceptTerms = true;
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";
};
}

View File

@ -1,26 +0,0 @@
{ inputs, pkgs, lib, ... }:
{
system.autoUpgrade = {
enable = true;
flake = "git+https://git.pvv.ntnu.no/Drift/pvv-nixos-config.git";
flags = [
# --update-input is deprecated since nix 2.22, and removed in lix 2.90
# https://git.lix.systems/lix-project/lix/issues/400
"--refresh"
"--override-input" "nixpkgs" "github:nixos/nixpkgs/nixos-24.05-small"
"--override-input" "nixpkgs-unstable" "github:nixos/nixpkgs/nixos-unstable-small"
"--no-write-lock-file"
];
};
# workaround for https://github.com/NixOS/nix/issues/6895
# via https://git.lix.systems/lix-project/lix/issues/400
environment.etc."current-system-flake-inputs.json".source
= pkgs.writers.writeJSON "flake-inputs.json" (
lib.flip lib.mapAttrs inputs (name: input:
# inputs.*.sourceInfo sans outPath, since writeJSON will otherwise serialize sourceInfo like a derivation
lib.removeAttrs (input.sourceInfo or {}) [ "outPath" ]
// { store-path = input.outPath; } # comment this line if you don't want to retain a store reference to the flake inputs
)
);
}

View File

@ -1,4 +0,0 @@
{ ... }:
{
services.irqbalance.enable = true;
}

View File

@ -1,42 +0,0 @@
{ ... }:
{
# 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"
];
};
};
}

View File

@ -1,44 +0,0 @@
{ config, lib, ... }:
{
# nginx return 444 for all nonexistent virtualhosts
systemd.services.nginx.after = [ "generate-snakeoil-certs.service" ];
environment.snakeoil-certs = lib.mkIf config.services.nginx.enable {
"/etc/certs/nginx" = {
owner = "nginx";
group = "nginx";
};
};
networking.firewall.allowedTCPPorts = lib.mkIf config.services.nginx.enable [ 80 443 ];
services.nginx = {
recommendedTlsSettings = true;
recommendedProxySettings = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
appendConfig = ''
pcre_jit on;
worker_processes auto;
worker_rlimit_nofile 100000;
'';
eventsConfig = ''
worker_connections 2048;
use epoll;
multi_accept on;
'';
};
systemd.services.nginx.serviceConfig = lib.mkIf config.services.nginx.enable {
LimitNOFILE = 65536;
};
services.nginx.virtualHosts."_" = lib.mkIf config.services.nginx.enable {
sslCertificate = "/etc/certs/nginx.crt";
sslCertificateKey = "/etc/certs/nginx.key";
addSSL = true;
extraConfig = "return 444;";
};
}

View File

@ -1,21 +0,0 @@
{ ... }:
{
services.openssh = {
enable = true;
startWhenNeeded = true;
extraConfig = ''
PubkeyAcceptedAlgorithms=+ssh-rsa
Match Group wheel
PasswordAuthentication no
Match All
'';
settings.PermitRootLogin = "yes";
};
users.users."root".openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCqVt4LCe0YIttr9swFxjkjn37ZDY9JxwVC+2gvfSINDJorOCtqPjDOTD2fTS1Gz08QCwpnLWq2kyvRchu6WgriAbSACpbZZBgxRaF/FVh3oiMVFGnNKGnv6/fdo/vZtu8mUVuqtmTrgLYpZdbR4oD3XiBlDKs7Cv5hPqt95lnP6MNFvE8mICCfd1PwhsABd2IQ5laz3u77/RXhNFJL0Kf2/+6gk9awcLuwHrPdvq7c3BxRHbc9UMRQENyjyQPa7aLe+uJBFLKP51I8VBuDpDacuibQx7nMt6N2UJ2KWI0JxRMHuJNq4S5jidR82aOw9gzGbTv30SKNLMqsZ0xj4LtdqCXDiZF6Lr09PsJYsvnBUFWa14HGcThKDtgwQwBryNViYmfv//0h9+RLZiU0ab+NEwSs7Zh5iAD+vhx64QqNX3tR7Le4SWXh8W0eShU9N78qYdSkiC3Ui7htxeqOocXM/P4AwbnHsLELIvkHdvgchCPvl8ygZa4WJTEWv16+ICskJcAKWGuqjvXAFuwjJJmPp9xLW9O0DFfQhMELiGamQR9wK07yYQVr34iah6qZO7cwhSKyEPFrVPIaNtfDhsjED639F7vmktf26SWNJHWfW0wOHILjI6TgqUvy0JDd8W8w0CHlAfz6Fs2l99NNgNF8dB3vBASbxS0hu/y0PVu/xQ== openstack-sleipner"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICCbgJ0Uwh9VSVhfId7l9i5/jk4CvAK5rbkiab8R+moF root@sleipner"
];
}

View File

@ -1,23 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.postfix;
in
{
services.postfix = {
enable = true;
hostname = "${config.networking.hostName}.pvv.ntnu.no";
domain = "pvv.ntnu.no";
relayHost = "smtp.pvv.ntnu.no";
relayPort = 465;
config = {
smtp_tls_wrappermode = "yes";
smtp_tls_security_level = "encrypt";
};
# Nothing should be delivered to this machine
destination = [ ];
};
}

View File

@ -1,18 +0,0 @@
{ config, pkgs, lib, ... }:
{
services.smartd = {
enable = lib.mkDefault true;
notifications = {
mail = {
enable = true;
sender = "root@pvv.ntnu.no";
recipient = "root@pvv.ntnu.no";
};
wall.enable = false;
};
};
environment.systemPackages = lib.optionals config.services.smartd.enable (with pkgs; [
smartmontools
]);
}

View File

@ -1,8 +0,0 @@
{ config, lib, ... }:
{
# 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;
}

View File

@ -7,11 +7,11 @@
]
},
"locked": {
"lastModified": 1731746438,
"narHash": "sha256-f3SSp1axoOk0NAI7oFdRzbxG2XPBSIXC+/DaAXnvS1A=",
"lastModified": 1716431128,
"narHash": "sha256-t3T8HlX3udO6f4ilLcN+j5eC3m2gqsouzSGiriKK6vk=",
"owner": "nix-community",
"repo": "disko",
"rev": "cb64993826fa7a477490be6ccb38ba1fa1e18fa8",
"rev": "7ffc4354dfeb37c8c725ae1465f04a9b45ec8606",
"type": "github"
},
"original": {
@ -20,25 +20,64 @@
"type": "github"
}
},
"greg-ng": {
"fix-python": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"grzegorz",
"nixpkgs"
],
"rust-overlay": "rust-overlay"
]
},
"locked": {
"lastModified": 1730249639,
"narHash": "sha256-G3URSlqCcb+GIvGyki+HHrDM5ZanX/dP9BtppD/SdfI=",
"ref": "refs/heads/main",
"rev": "80e0447bcb79adad4f459ada5610f3eae987b4e3",
"revCount": 34,
"type": "git",
"url": "https://git.pvv.ntnu.no/Projects/greg-ng.git"
"lastModified": 1713887124,
"narHash": "sha256-hGTSm0p9xXUYDgsAAr/ORZICo6T6u33vLfX3tILikaQ=",
"owner": "GuillaumeDesforges",
"repo": "fix-python",
"rev": "f7f4b33e22414071fc1f9cbf68072c413c3a7fdf",
"type": "github"
},
"original": {
"type": "git",
"url": "https://git.pvv.ntnu.no/Projects/greg-ng.git"
"owner": "GuillaumeDesforges",
"repo": "fix-python",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1689068808,
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
"type": "github"
},
"original": {
"id": "flake-utils",
"type": "indirect"
}
},
"grzegorz": {
"inputs": {
"fix-python": "fix-python",
"nixpkgs": [
"nixpkgs-unstable"
]
},
"locked": {
"lastModified": 1716065905,
"narHash": "sha256-08uhxBzfakfhl/ooc+gMzDupWKYvTeyQZwuvB1SBS7A=",
"owner": "Programvareverkstedet",
"repo": "grzegorz",
"rev": "0481aef6553ae9aee86e4edb4ca0ed4f2eba2058",
"type": "github"
},
"original": {
"owner": "Programvareverkstedet",
"repo": "grzegorz",
"type": "github"
}
},
"grzegorz-clients": {
@ -48,17 +87,17 @@
]
},
"locked": {
"lastModified": 1726861934,
"narHash": "sha256-lOzPDwktd+pwszUTbpUdQg6iCzInS11fHLfkjmnvJrM=",
"ref": "refs/heads/master",
"rev": "546d921ec46735dbf876e36f4af8df1064d09432",
"revCount": 78,
"type": "git",
"url": "https://git.pvv.ntnu.no/Projects/grzegorz-clients.git"
"lastModified": 1716115695,
"narHash": "sha256-aI65l4x+U5v3i/nfn6N3eW5IZodmf4pyAByE7vTJh8I=",
"owner": "Programvareverkstedet",
"repo": "grzegorz-clients",
"rev": "b9444658fbb39cd1bf1c61ee5a1d5f0641c49abe",
"type": "github"
},
"original": {
"type": "git",
"url": "https://git.pvv.ntnu.no/Projects/grzegorz-clients.git"
"owner": "Programvareverkstedet",
"repo": "grzegorz-clients",
"type": "github"
}
},
"matrix-next": {
@ -68,35 +107,20 @@
]
},
"locked": {
"lastModified": 1727410897,
"narHash": "sha256-tWsyxvf421ieWUJYgjV7m1eTdr2ZkO3vId7vmtvfFpQ=",
"lastModified": 1717234745,
"narHash": "sha256-MFyKRdw4WQD6V3vRGbP6MYbtJhZp712zwzjW6YiOBYM=",
"owner": "dali99",
"repo": "nixos-matrix-modules",
"rev": "ff787d410cba17882cd7b6e2e22cc88d4064193c",
"rev": "d7dc42c9bbb155c5e4aa2f0985d0df75ce978456",
"type": "github"
},
"original": {
"owner": "dali99",
"ref": "v0.6.1",
"ref": "v0.6.0",
"repo": "nixos-matrix-modules",
"type": "github"
}
},
"minecraft-data": {
"locked": {
"lastModified": 1725277886,
"narHash": "sha256-Fw4VbbE3EfypQWSgPDFfvVH47BHeg3ptsO715NlUM8Q=",
"ref": "refs/heads/master",
"rev": "1b4087bd3322a2e2ba84271c8fcc013e6b641a58",
"revCount": 2,
"type": "git",
"url": "https://git.pvv.ntnu.no/Drift/minecraft-data.git"
},
"original": {
"type": "git",
"url": "https://git.pvv.ntnu.no/Drift/minecraft-data.git"
}
},
"nix-gitea-themes": {
"inputs": {
"nixpkgs": [
@ -119,50 +143,48 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1731663789,
"narHash": "sha256-x07g4NcqGP6mQn6AISXJaks9sQYDjZmTMBlKIvajvyc=",
"lastModified": 1716586607,
"narHash": "sha256-PzpeC/xi0+YTGJS5rdbcOqVgIryuWHkimMVXoCIidgA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "035d434d48f4375ac5d3a620954cf5fda7dd7c36",
"rev": "03309929e115bba1339308814f8b6e63f250fedf",
"type": "github"
},
"original": {
"owner": "NixOS",
"id": "nixpkgs",
"ref": "nixos-24.05-small",
"repo": "nixpkgs",
"type": "github"
"type": "indirect"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1730602179,
"narHash": "sha256-efgLzQAWSzJuCLiCaQUCDu4NudNlHdg2NzGLX5GYaEY=",
"lastModified": 1716061101,
"narHash": "sha256-H0eCta7ahEgloGIwE/ihkyGstOGu+kQwAiHvwVoXaA0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "3c2f1c4ca372622cb2f9de8016c9a0b1cbd0f37c",
"rev": "e7cc61784ddf51c81487637b3031a6dd2d6673a2",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-24.05",
"ref": "release-23.11",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1731745710,
"narHash": "sha256-SVeiClbgqL071JpAspOu0gCkPSAL51kSIRwo4C/pghA=",
"lastModified": 1716660083,
"narHash": "sha256-QO7cdjtDhx72KEw6m0NOtuE5FS4asaRExZ65uFR/q8g=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "dfaa4cb76c2d450d8f396bb6b9f43cede3ade129",
"rev": "6de51d98ec2ae46730f11845e221aab9d2470a8a",
"type": "github"
},
"original": {
"owner": "NixOS",
"id": "nixpkgs",
"ref": "nixos-unstable-small",
"repo": "nixpkgs",
"type": "github"
"type": "indirect"
}
},
"pvv-calendar-bot": {
@ -172,11 +194,11 @@
]
},
"locked": {
"lastModified": 1723850344,
"narHash": "sha256-aT37O9l9eclWEnqxASVNBL1dKwDHZUOqdbA4VO9DJvw=",
"lastModified": 1693136143,
"narHash": "sha256-amHprjftc3y/bg8yf4hITCLa+ez5HIi0yGfR7TU6UIc=",
"ref": "refs/heads/main",
"rev": "38b66677ab8c01aee10cd59e745af9ce3ea88092",
"revCount": 19,
"rev": "a32894b305f042d561500f5799226afd1faf5abb",
"revCount": 9,
"type": "git",
"url": "https://git.pvv.ntnu.no/Projects/calendar-bot.git"
},
@ -192,11 +214,11 @@
]
},
"locked": {
"lastModified": 1725212759,
"narHash": "sha256-yZBsefIarFUEhFRj+rCGMp9Zvag3MCafqV/JfGVRVwc=",
"lastModified": 1716150352,
"narHash": "sha256-c13lzYbLmbrcbEdPTYZYtlX2Qsz1W+2sLsIMGShPgwo=",
"ref": "refs/heads/master",
"rev": "e7b66b4bc6a89bab74bac45b87e9434f5165355f",
"revCount": 473,
"rev": "2cab4df4b119e08a1f90ea1c944652cd78b4d478",
"revCount": 459,
"type": "git",
"url": "https://git.pvv.ntnu.no/Projects/nettsiden.git"
},
@ -208,10 +230,9 @@
"root": {
"inputs": {
"disko": "disko",
"greg-ng": "greg-ng",
"grzegorz": "grzegorz",
"grzegorz-clients": "grzegorz-clients",
"matrix-next": "matrix-next",
"minecraft-data": "minecraft-data",
"nix-gitea-themes": "nix-gitea-themes",
"nixpkgs": "nixpkgs",
"nixpkgs-unstable": "nixpkgs-unstable",
@ -220,27 +241,6 @@
"sops-nix": "sops-nix"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": [
"greg-ng",
"nixpkgs"
]
},
"locked": {
"lastModified": 1729391507,
"narHash": "sha256-as0I9xieJUHf7kiK2a9znDsVZQTFWhM1pLivII43Gi0=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "784981a9feeba406de38c1c9a3decf966d853cca",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"sops-nix": {
"inputs": {
"nixpkgs": [
@ -249,11 +249,11 @@
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1731748189,
"narHash": "sha256-Zd/Uukvpcu26M6YGhpbsgqm6LUSLz+Q8mDZ5LOEGdiE=",
"lastModified": 1716400300,
"narHash": "sha256-0lMkIk9h3AzOHs1dCL9RXvvN4PM8VBKb+cyGsqOKa4c=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "d2bd7f433b28db6bc7ae03d5eca43564da0af054",
"rev": "b549832718b8946e875c016a4785d204fcfc2e53",
"type": "github"
},
"original": {
@ -261,6 +261,21 @@
"repo": "sops-nix",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",

View File

@ -2,8 +2,8 @@
description = "PVV System flake";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05-small"; # remember to also update the url in base/services/auto-upgrade.nix
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable-small";
nixpkgs.url = "nixpkgs/nixos-24.05-small";
nixpkgs-unstable.url = "nixpkgs/nixos-unstable-small";
sops-nix.url = "github:Mic92/sops-nix";
sops-nix.inputs.nixpkgs.follows = "nixpkgs";
@ -17,21 +17,19 @@
pvv-calendar-bot.url = "git+https://git.pvv.ntnu.no/Projects/calendar-bot.git";
pvv-calendar-bot.inputs.nixpkgs.follows = "nixpkgs";
matrix-next.url = "github:dali99/nixos-matrix-modules/v0.6.1";
matrix-next.url = "github:dali99/nixos-matrix-modules/v0.6.0";
matrix-next.inputs.nixpkgs.follows = "nixpkgs";
nix-gitea-themes.url = "git+https://git.pvv.ntnu.no/oysteikt/nix-gitea-themes.git";
nix-gitea-themes.inputs.nixpkgs.follows = "nixpkgs";
greg-ng.url = "git+https://git.pvv.ntnu.no/Projects/greg-ng.git";
greg-ng.inputs.nixpkgs.follows = "nixpkgs";
grzegorz-clients.url = "git+https://git.pvv.ntnu.no/Projects/grzegorz-clients.git";
grzegorz.url = "github:Programvareverkstedet/grzegorz";
grzegorz.inputs.nixpkgs.follows = "nixpkgs-unstable";
grzegorz-clients.url = "github:Programvareverkstedet/grzegorz-clients";
grzegorz-clients.inputs.nixpkgs.follows = "nixpkgs";
minecraft-data.url = "git+https://git.pvv.ntnu.no/Drift/minecraft-data.git";
};
outputs = { self, nixpkgs, nixpkgs-unstable, sops-nix, disko, ... }@inputs:
outputs = { self, nixpkgs, nixpkgs-unstable, pvv-nettsiden, sops-nix, disko, ... }@inputs:
let
nixlib = nixpkgs.lib;
systems = [
@ -39,8 +37,8 @@
"aarch64-linux"
"aarch64-darwin"
];
forAllSystems = f: nixlib.genAttrs systems f;
allMachines = builtins.attrNames self.nixosConfigurations;
forAllSystems = f: nixlib.genAttrs systems (system: f system);
allMachines = nixlib.mapAttrsToList (name: _: name) self.nixosConfigurations;
importantMachines = [
"bekkalokk"
"bicep"
@ -49,17 +47,14 @@
"ildkule"
];
in {
inherit inputs;
nixosConfigurations = let
unstablePkgs = nixpkgs-unstable.legacyPackages.x86_64-linux;
nixosConfig = nixpkgs: name: config: nixpkgs.lib.nixosSystem (nixpkgs.lib.recursiveUpdate
rec {
system = "x86_64-linux";
specialArgs = {
inherit unstablePkgs inputs;
inherit nixpkgs-unstable inputs;
values = import ./values.nix;
fp = path: ./${path};
};
modules = [
@ -95,7 +90,6 @@
heimdal = unstablePkgs.heimdal;
mediawiki-extensions = final.callPackage ./packages/mediawiki-extensions { };
simplesamlphp = final.callPackage ./packages/simplesamlphp { };
bluemap = final.callPackage ./packages/bluemap.nix { };
})
inputs.nix-gitea-themes.overlays.default
inputs.pvv-nettsiden.overlays.default
@ -117,27 +111,17 @@
brzeczyszczykiewicz = stableNixosConfig "brzeczyszczykiewicz" {
modules = [
inputs.grzegorz.nixosModules.grzegorz-kiosk
inputs.grzegorz-clients.nixosModules.grzegorz-webui
inputs.greg-ng.nixosModules.default
];
overlays = [
inputs.greg-ng.overlays.default
];
};
georg = stableNixosConfig "georg" {
modules = [
inputs.grzegorz.nixosModules.grzegorz-kiosk
inputs.grzegorz-clients.nixosModules.grzegorz-webui
inputs.greg-ng.nixosModules.default
];
overlays = [
inputs.greg-ng.overlays.default
];
};
};
nixosModules = {
snakeoil-certs = ./modules/snakeoil-certs.nix;
snappymail = ./modules/snappymail.nix;
buskerud = stableNixosConfig "buskerud" { };
};
devShells = forAllSystems (system: {

View File

@ -1,25 +1,21 @@
{ fp, pkgs, values, ... }:
{ pkgs, values, ... }:
{
imports = [
./hardware-configuration.nix
(fp /base)
(fp /misc/metrics-exporters.nix)
../../base.nix
../../misc/metrics-exporters.nix
./services/bluemap/default.nix
./services/gitea/default.nix
./services/idp-simplesamlphp
./services/kerberos
./services/mediawiki
./services/nginx.nix
./services/phpfpm.nix
./services/vaultwarden.nix
./services/webmail
./services/website
./services/well-known
./services/nginx.nix
./services/gitea/default.nix
./services/kerberos
./services/webmail
./services/mediawiki
./services/idp-simplesamlphp
];
sops.defaultSopsFile = fp /secrets/bekkalokk/bekkalokk.yaml;
sops.defaultSopsFile = ../../secrets/bekkalokk/bekkalokk.yaml;
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
sops.age.generateKey = true;
@ -34,8 +30,6 @@
address = with values.hosts.bekkalokk; [ (ipv4 + "/25") (ipv6 + "/64") ];
};
services.btrfs.autoScrub.enable = true;
# Do not change, even during upgrades.
# See https://search.nixos.org/options?show=system.stateVersion
system.stateVersion = "22.11";

View File

@ -1,83 +0,0 @@
{ config, lib, pkgs, inputs, ... }:
let
vanillaSurvival = "/var/lib/bluemap/vanilla_survival_world";
in {
imports = [
./module.nix # From danio, pending upstreaming
];
disabledModules = [ "services/web-servers/bluemap.nix" ];
sops.secrets."bluemap/ssh-key" = { };
sops.secrets."bluemap/ssh-known-hosts" = { };
services.bluemap = {
enable = true;
eula = true;
onCalendar = "*-*-* 05:45:00"; # a little over an hour after auto-upgrade
host = "minecraft.pvv.ntnu.no";
maps = {
"verden" = {
settings = {
world = vanillaSurvival;
sorting = 0;
ambient-light = 0.1;
cave-detection-ocean-floor = -5;
marker-sets = inputs.minecraft-data.map-markers.vanillaSurvival.verden;
};
};
"underverden" = {
settings = {
world = "${vanillaSurvival}/DIM-1";
sorting = 100;
sky-color = "#290000";
void-color = "#150000";
ambient-light = 0.6;
world-sky-light = 0;
remove-caves-below-y = -10000;
cave-detection-ocean-floor = -5;
cave-detection-uses-block-light = true;
max-y = 90;
marker-sets = inputs.minecraft-data.map-markers.vanillaSurvival.underverden;
};
};
"enden" = {
settings = {
world = "${vanillaSurvival}/DIM1";
sorting = 200;
sky-color = "#080010";
void-color = "#080010";
ambient-light = 0.6;
world-sky-light = 0;
remove-caves-below-y = -10000;
cave-detection-ocean-floor = -5;
};
};
};
};
services.nginx.virtualHosts."minecraft.pvv.ntnu.no" = {
enableACME = true;
forceSSL = true;
};
# TODO: render somewhere else lmao
systemd.services."render-bluemap-maps" = {
preStart = ''
mkdir -p /var/lib/bluemap/world
${pkgs.rsync}/bin/rsync \
-e "${pkgs.openssh}/bin/ssh -o UserKnownHostsFile=$CREDENTIALS_DIRECTORY/ssh-known-hosts -i $CREDENTIALS_DIRECTORY/sshkey" \
-avz --no-owner --no-group \
root@innovation.pvv.ntnu.no:/ \
${vanillaSurvival}
'';
serviceConfig = {
LoadCredential = [
"sshkey:${config.sops.secrets."bluemap/ssh-key".path}"
"ssh-known-hosts:${config.sops.secrets."bluemap/ssh-known-hosts".path}"
];
};
};
}

View File

@ -1,343 +0,0 @@
{ config, lib, pkgs, ... }:
let
cfg = config.services.bluemap;
format = pkgs.formats.hocon { };
coreConfig = format.generate "core.conf" cfg.coreSettings;
webappConfig = format.generate "webapp.conf" cfg.webappSettings;
webserverConfig = format.generate "webserver.conf" cfg.webserverSettings;
storageFolder = pkgs.linkFarm "storage"
(lib.attrsets.mapAttrs' (name: value:
lib.nameValuePair "${name}.conf"
(format.generate "${name}.conf" value))
cfg.storage);
mapsFolder = pkgs.linkFarm "maps"
(lib.attrsets.mapAttrs' (name: value:
lib.nameValuePair "${name}.conf"
(format.generate "${name}.conf" value.settings))
cfg.maps);
webappConfigFolder = pkgs.linkFarm "bluemap-config" {
"maps" = mapsFolder;
"storages" = storageFolder;
"core.conf" = coreConfig;
"webapp.conf" = webappConfig;
"webserver.conf" = webserverConfig;
"packs" = cfg.resourcepacks;
"addons" = cfg.resourcepacks; # TODO
};
renderConfigFolder = name: value: pkgs.linkFarm "bluemap-${name}-config" {
"maps" = pkgs.linkFarm "maps" {
"${name}.conf" = (format.generate "${name}.conf" value.settings);
};
"storages" = storageFolder;
"core.conf" = coreConfig;
"webapp.conf" = format.generate "webapp.conf" (cfg.webappSettings // { "update-settings-file" = false; });
"webserver.conf" = webserverConfig;
"packs" = value.resourcepacks;
"addons" = cfg.resourcepacks; # TODO
};
inherit (lib) mkOption;
in {
options.services.bluemap = {
enable = lib.mkEnableOption "bluemap";
eula = mkOption {
type = lib.types.bool;
description = ''
By changing this option to true you confirm that you own a copy of minecraft Java Edition,
and that you agree to minecrafts EULA.
'';
default = false;
};
defaultWorld = mkOption {
type = lib.types.path;
description = ''
The world used by the default map ruleset.
If you configure your own maps you do not need to set this.
'';
example = lib.literalExpression "\${config.services.minecraft.dataDir}/world";
};
enableRender = mkOption {
type = lib.types.bool;
description = "Enable rendering";
default = true;
};
webRoot = mkOption {
type = lib.types.path;
default = "/var/lib/bluemap/web";
description = "The directory for saving and serving the webapp and the maps";
};
enableNginx = mkOption {
type = lib.types.bool;
default = true;
description = "Enable configuring a virtualHost for serving the bluemap webapp";
};
host = mkOption {
type = lib.types.str;
default = "bluemap.${config.networking.domain}";
defaultText = lib.literalExpression "bluemap.\${config.networking.domain}";
description = "Domain to configure nginx for";
};
onCalendar = mkOption {
type = lib.types.str;
description = ''
How often to trigger rendering the map,
in the format of a systemd timer onCalendar configuration.
See {manpage}`systemd.timer(5)`.
'';
default = "*-*-* 03:10:00";
};
coreSettings = mkOption {
type = lib.types.submodule {
freeformType = format.type;
options = {
data = mkOption {
type = lib.types.path;
description = "Folder for where bluemap stores its data";
default = "/var/lib/bluemap";
};
metrics = lib.mkEnableOption "Sending usage metrics containing the version of bluemap in use";
};
};
description = "Settings for the core.conf file, [see upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/blob/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/core.conf).";
};
webappSettings = mkOption {
type = lib.types.submodule {
freeformType = format.type;
};
default = {
enabled = true;
webroot = cfg.webRoot;
};
defaultText = lib.literalExpression ''
{
enabled = true;
webroot = config.services.bluemap.webRoot;
}
'';
description = "Settings for the webapp.conf file, see [upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/blob/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/webapp.conf).";
};
webserverSettings = mkOption {
type = lib.types.submodule {
freeformType = format.type;
options = {
enabled = mkOption {
type = lib.types.bool;
description = ''
Enable bluemap's built-in webserver.
Disabled by default in nixos for use of nginx directly.
'';
default = false;
};
};
};
default = { };
description = ''
Settings for the webserver.conf file, usually not required.
[See upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/blob/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/webserver.conf).
'';
};
maps = mkOption {
type = lib.types.attrsOf (lib.types.submodule {
options = {
resourcepacks = mkOption {
type = lib.types.path;
default = cfg.resourcepacks;
defaultText = lib.literalExpression "config.services.bluemap.resourcepacks";
description = "A set of resourcepacks/mods to extract models from loaded in alphabetical order";
};
settings = mkOption {
type = (lib.types.submodule {
freeformType = format.type;
options = {
world = mkOption {
type = lib.types.path;
description = "Path to world folder containing the dimension to render";
};
};
});
description = ''
Settings for files in `maps/`.
See the default for an example with good options for the different world types.
For valid values [consult upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/blob/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/maps/map.conf).
'';
};
};
});
default = {
"overworld".settings = {
world = "${cfg.defaultWorld}";
ambient-light = 0.1;
cave-detection-ocean-floor = -5;
};
"nether".settings = {
world = "${cfg.defaultWorld}/DIM-1";
sorting = 100;
sky-color = "#290000";
void-color = "#150000";
ambient-light = 0.6;
world-sky-light = 0;
remove-caves-below-y = -10000;
cave-detection-ocean-floor = -5;
cave-detection-uses-block-light = true;
max-y = 90;
};
"end".settings = {
world = "${cfg.defaultWorld}/DIM1";
sorting = 200;
sky-color = "#080010";
void-color = "#080010";
ambient-light = 0.6;
world-sky-light = 0;
remove-caves-below-y = -10000;
cave-detection-ocean-floor = -5;
};
};
defaultText = lib.literalExpression ''
{
"overworld".settings = {
world = "''${cfg.defaultWorld}";
ambient-light = 0.1;
cave-detection-ocean-floor = -5;
};
"nether".settings = {
world = "''${cfg.defaultWorld}/DIM-1";
sorting = 100;
sky-color = "#290000";
void-color = "#150000";
ambient-light = 0.6;
world-sky-light = 0;
remove-caves-below-y = -10000;
cave-detection-ocean-floor = -5;
cave-detection-uses-block-light = true;
max-y = 90;
};
"end".settings = {
world = "''${cfg.defaultWorld}/DIM1";
sorting = 200;
sky-color = "#080010";
void-color = "#080010";
ambient-light = 0.6;
world-sky-light = 0;
remove-caves-below-y = -10000;
cave-detection-ocean-floor = -5;
};
};
'';
description = ''
map-specific configuration.
These correspond to views in the webapp and are usually
different dimension of a world or different render settings of the same dimension.
If you set anything in this option you must configure all dimensions yourself!
'';
};
storage = mkOption {
type = lib.types.attrsOf (lib.types.submodule {
freeformType = format.type;
options = {
storage-type = mkOption {
type = lib.types.enum [ "FILE" "SQL" ];
description = "Type of storage config";
default = "FILE";
};
};
});
description = ''
Where the rendered map will be stored.
Unless you are doing something advanced you should probably leave this alone and configure webRoot instead.
[See upstream docs](https://github.com/BlueMap-Minecraft/BlueMap/tree/master/BlueMapCommon/src/main/resources/de/bluecolored/bluemap/config/storages)
'';
default = {
"file" = {
root = "${cfg.webRoot}/maps";
};
};
defaultText = lib.literalExpression ''
{
"file" = {
root = "''${config.services.bluemap.webRoot}/maps";
};
}
'';
};
resourcepacks = mkOption {
type = lib.types.path;
default = pkgs.linkFarm "resourcepacks" { };
description = ''
A set of resourcepacks/mods to extract models from loaded in alphabetical order.
Can be overriden on a per-map basis with `services.bluemap.maps.<name>.resourcepacks`.
'';
};
};
config = lib.mkIf cfg.enable {
assertions =
[ { assertion = config.services.bluemap.eula;
message = ''
You have enabled bluemap but have not accepted minecraft's EULA.
You can achieve this through setting `services.bluemap.eula = true`
'';
}
];
services.bluemap.coreSettings.accept-download = cfg.eula;
systemd.services."render-bluemap-maps" = lib.mkIf cfg.enableRender {
serviceConfig = {
Type = "oneshot";
Group = "nginx";
UMask = "026";
};
script = lib.strings.concatStringsSep "\n" ((lib.attrsets.mapAttrsToList
(name: value: "${lib.getExe pkgs.bluemap} -c ${renderConfigFolder name value} -r")
cfg.maps) ++ [ "${lib.getExe pkgs.bluemap} -c ${webappConfigFolder} -gs" ]);
};
systemd.timers."render-bluemap-maps" = lib.mkIf cfg.enableRender {
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = cfg.onCalendar;
Persistent = true;
Unit = "render-bluemap-maps.service";
};
};
services.nginx.virtualHosts = lib.mkIf cfg.enableNginx {
"${cfg.host}" = {
root = config.services.bluemap.webRoot;
locations = {
"~* ^/maps/[^/]*/tiles/".extraConfig = ''
error_page 404 = @empty;
'';
"@empty".return = "204";
};
};
};
};
meta = {
maintainers = with lib.maintainers; [ dandellion h7x4 ];
};
}

View File

@ -15,9 +15,9 @@ let
enable = true;
name = "git-runner-${name}"; url = "https://git.pvv.ntnu.no";
labels = [
"debian-latest:docker://node:18-bullseye"
"ubuntu-latest:docker://node:18-bullseye"
];
"debian-latest:docker://node:18-bullseye"
"ubuntu-latest:docker://node:18-bullseye"
];
tokenFile = config.sops.secrets."gitea/runners/${name}".path;
};
};

View File

@ -1,4 +1,4 @@
{ config, values, fp, pkgs, lib, ... }:
{ config, values, pkgs, ... }:
let
cfg = config.services.gitea;
domain = "git.pvv.ntnu.no";
@ -6,9 +6,7 @@ let
in {
imports = [
./ci.nix
./gpg.nix
./import-users
./web-secret-provider
./import-users.nix
];
sops.secrets = {
@ -24,6 +22,7 @@ in {
services.gitea = {
enable = true;
stateDir = "/data/gitea";
appName = "PVV Git";
database = {
@ -36,7 +35,6 @@ in {
mailerPasswordFile = config.sops.secrets."gitea/email-password".path;
# https://docs.gitea.com/administration/config-cheat-sheet
settings = {
server = {
DOMAIN = domain;
@ -44,8 +42,6 @@ in {
PROTOCOL = "http+unix";
SSH_PORT = sshPort;
START_SSH_SERVER = true;
START_LFS_SERVER = true;
LANDING_PAGE = "explore";
};
mailer = {
ENABLED = true;
@ -54,81 +50,16 @@ in {
SMTP_ADDR = "smtp.pvv.ntnu.no";
SMTP_PORT = 587;
USER = "gitea@pvv.ntnu.no";
SUBJECT_PREFIX = "[pvv-git]";
};
metrics = {
ENABLED = true;
ENABLED_ISSUE_BY_LABEL = true;
ENABLED_ISSUE_BY_REPOSITORY = true;
};
indexer.REPO_INDEXER_ENABLED = true;
service = {
DISABLE_REGISTRATION = true;
ENABLE_NOTIFY_MAIL = true;
AUTO_WATCH_NEW_REPOS = false;
};
admin.DEFAULT_EMAIL_NOTIFICATIONS = "onmention";
service.DISABLE_REGISTRATION = true;
session.COOKIE_SECURE = true;
database.LOG_SQL = false;
repository = {
PREFERRED_LICENSES = lib.concatStringsSep "," [
"AGPL-3.0-only"
"AGPL-3.0-or-later"
"Apache-2.0"
"BSD-3-Clause"
"CC-BY-4.0"
"CC-BY-NC-4.0"
"CC-BY-NC-ND-4.0"
"CC-BY-NC-SA-4.0"
"CC-BY-ND-4.0"
"CC-BY-SA-4.0"
"CC0-1.0"
"GPL-2.0-only"
"GPL-3.0-only"
"GPL-3.0-or-later"
"LGPL-3.0-linking-exception"
"LGPL-3.0-only"
"LGPL-3.0-or-later"
"MIT"
"MPL-2.0"
"Unlicense"
];
DEFAULT_REPO_UNITS = lib.concatStringsSep "," [
"repo.code"
"repo.issues"
"repo.pulls"
"repo.releases"
];
};
picture = {
DISABLE_GRAVATAR = true;
ENABLE_FEDERATED_AVATAR = false;
};
actions.ENABLED = true;
ui = {
REACTIONS = lib.concatStringsSep "," [
"+1"
"-1"
"laugh"
"confused"
"heart"
"hooray"
"rocket"
"eyes"
"100"
"anger"
"astonished"
"no_good"
"ok_hand"
"pensive"
"pizza"
"point_up"
"sob"
"skull"
"upside_down_face"
"shrug"
];
};
"ui.meta".DESCRIPTION = "Bokstavelig talt programvareverkstedet";
};
};
@ -139,20 +70,11 @@ in {
forceSSL = true;
enableACME = true;
kTLS = true;
locations = {
"/" = {
proxyPass = "http://unix:${cfg.settings.server.HTTP_ADDR}";
extraConfig = ''
client_max_body_size 512M;
'';
};
"/metrics" = {
proxyPass = "http://unix:${cfg.settings.server.HTTP_ADDR}";
extraConfig = ''
allow ${values.hosts.ildkule.ipv4}/32;
deny all;
'';
};
locations."/" = {
proxyPass = "http://unix:${cfg.settings.server.HTTP_ADDR}";
extraConfig = ''
client_max_body_size 512M;
'';
};
};
@ -174,18 +96,12 @@ in {
};
script = let
logo-svg = fp /assets/logo_blue_regular.svg;
logo-png = fp /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>
'';
logo-svg = ../../../../assets/logo_blue_regular.svg;
logo-png = ../../../../assets/logo_blue_regular.png;
in ''
install -Dm444 ${logo-svg} ${cfg.customDir}/public/assets/img/logo.svg
install -Dm444 ${logo-png} ${cfg.customDir}/public/assets/img/logo.png
install -Dm444 ${./loading.apng} ${cfg.customDir}/public/assets/img/loading.png
install -Dm444 ${extraLinks} ${cfg.customDir}/templates/custom/extra_links.tmpl
install -Dm444 ${logo-svg} ${cfg.customDir}/public/img/logo.svg
install -Dm444 ${logo-png} ${cfg.customDir}/public/img/logo.png
install -Dm444 ${./loading.apng} ${cfg.customDir}/public/img/loading.png
'';
};
}

View File

@ -0,0 +1,94 @@
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()

View File

@ -1,38 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.gitea;
GNUPGHOME = "${config.users.users.gitea.home}/gnupg";
in
{
sops.secrets."gitea/gpg-signing-key" = {
owner = cfg.user;
inherit (cfg) group;
};
systemd.services.gitea.environment = { inherit GNUPGHOME; };
systemd.tmpfiles.settings."20-gitea-gnugpg".${GNUPGHOME}.d = {
inherit (cfg) user group;
mode = "700";
};
systemd.services.gitea-ensure-gnupg-homedir = {
description = "Import gpg key for gitea";
environment = { inherit GNUPGHOME; };
serviceConfig = {
Type = "oneshot";
User = cfg.user;
PrivateNetwork = true;
};
script = ''
${lib.getExe pkgs.gnupg} --import ${config.sops.secrets."gitea/gpg-signing-key".path}
'';
};
services.gitea.settings."repository.signing" = {
SIGNING_KEY = "0549C43374D2253C";
SIGNING_NAME = "PVV Git";
SIGNING_EMAIL = "gitea@git.pvv.ntnu.no";
INITIAL_COMMIT = "always";
};
}

View File

@ -14,9 +14,6 @@ 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'';
serviceConfig = {
ExecStart = pkgs.writers.writePython3 "gitea-import-users" {
flakeIgnore = [
"E501" # Line over 80 chars lol
];
libraries = with pkgs.python3Packages; [ requests ];
} (builtins.readFile ./gitea-import-users.py);
LoadCredential=[

View File

@ -1,198 +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'
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()

View File

@ -1,114 +0,0 @@
{ 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";
};
}

View File

@ -1,112 +0,0 @@
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()

View File

@ -112,7 +112,7 @@ class PwAuth extends \SimpleSAML\Module\core\Auth\UserPassBase
array_shift($groups);
array_shift($groups);
array_pop($groups);
$info = posix_getpwnam($uid);
$group = $info['gid'];
if (!in_array($group, $groups)) {

View File

@ -58,7 +58,7 @@ $config = [
/*
* The following settings are *filesystem paths* which define where
* SimpleSAMLphp can find or write the following things:
* - 'cachedir': Where SimpleSAMLphp can write its cache.
* - 'cachedir': Where SimpleSAMLphp can write its cache.
* - 'loggingdir': Where to write logs. MUST be set to NULL when using a logging
* handler other than `file`.
* - 'datadir': Storage of general data.

View File

@ -22,78 +22,78 @@ let
# openssl req -newkey rsa:4096 -new -x509 -days 365 -nodes -out idp.crt -keyout idp.pem
"metadata/saml20-idp-hosted.php" = pkgs.writeText "saml20-idp-remote.php" ''
<?php
$metadata['https://idp.pvv.ntnu.no/'] = array(
'host' => '__DEFAULT__',
'privatekey' => '${config.sops.secrets."idp/privatekey".path}',
'certificate' => '${./idp.crt}',
'auth' => 'pwauth',
);
?>
$metadata['https://idp.pvv.ntnu.no/'] = array(
'host' => '__DEFAULT__',
'privatekey' => '${config.sops.secrets."idp/privatekey".path}',
'certificate' => '${./idp.crt}',
'auth' => 'pwauth',
);
?>
'';
"metadata/saml20-sp-remote.php" = pkgs.writeText "saml20-sp-remote.php" ''
<?php
${ lib.pipe config.services.idp.sp-remote-metadata [
(map (url: ''
$metadata['${url}'] = [
'SingleLogoutService' => [
[
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
'Location' => '${url}module.php/saml/sp/saml2-logout.php/default-sp',
],
[
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP',
'Location' => '${url}module.php/saml/sp/saml2-logout.php/default-sp',
],
],
'AssertionConsumerService' => [
[
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
'Location' => '${url}module.php/saml/sp/saml2-acs.php/default-sp',
'index' => 0,
],
[
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
'Location' => '${url}module.php/saml/sp/saml2-acs.php/default-sp',
'index' => 1,
],
],
];
''))
(lib.concatStringsSep "\n")
]}
?>
${ lib.pipe config.services.idp.sp-remote-metadata [
(map (url: ''
$metadata['${url}'] = [
'SingleLogoutService' => [
[
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
'Location' => '${url}module.php/saml/sp/saml2-logout.php/default-sp',
],
[
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP',
'Location' => '${url}module.php/saml/sp/saml2-logout.php/default-sp',
],
],
'AssertionConsumerService' => [
[
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
'Location' => '${url}module.php/saml/sp/saml2-acs.php/default-sp',
'index' => 0,
],
[
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
'Location' => '${url}module.php/saml/sp/saml2-acs.php/default-sp',
'index' => 1,
],
],
];
''))
(lib.concatStringsSep "\n")
]}
?>
'';
"config/authsources.php" = pkgs.writeText "idp-authsources.php" ''
<?php
$config = array(
'admin' => array(
'core:AdminPassword'
),
'admin' => array(
'core:AdminPassword'
),
'pwauth' => array(
'authpwauth:PwAuth',
'pwauth_bin_path' => '${lib.getExe pwAuthScript}',
'mail_domain' => '@pvv.ntnu.no',
'authpwauth:PwAuth',
'pwauth_bin_path' => '${lib.getExe pwAuthScript}',
'mail_domain' => '@pvv.ntnu.no',
),
);
?>
?>
'';
"config/config.php" = pkgs.runCommandLocal "simplesamlphp-config.php" { } ''
cp ${./config.php} "$out"
substituteInPlace "$out" \
--replace-warn '$SAML_COOKIE_SECURE' 'true' \
--replace-warn '$SAML_COOKIE_SALT' 'file_get_contents("${config.sops.secrets."idp/cookie_salt".path}")' \
--replace-warn '$SAML_ADMIN_NAME' '"Drift"' \
--replace-warn '$SAML_ADMIN_EMAIL' '"drift@pvv.ntnu.no"' \
--replace-warn '$SAML_ADMIN_PASSWORD' 'file_get_contents("${config.sops.secrets."idp/admin_password".path}")' \
--replace-warn '$SAML_TRUSTED_DOMAINS' 'array( "idp.pvv.ntnu.no" )' \
--replace-warn '$SAML_DATABASE_DSN' '"pgsql:host=postgres.pvv.ntnu.no;port=5432;dbname=idp"' \
--replace-warn '$SAML_DATABASE_USERNAME' '"idp"' \
--replace-warn '$SAML_DATABASE_PASSWORD' 'file_get_contents("${config.sops.secrets."idp/postgres_password".path}")' \
--replace-warn '$CACHE_DIRECTORY' '/var/cache/idp'
--replace '$SAML_COOKIE_SECURE' 'true' \
--replace '$SAML_COOKIE_SALT' 'file_get_contents("${config.sops.secrets."idp/cookie_salt".path}")' \
--replace '$SAML_ADMIN_NAME' '"Drift"' \
--replace '$SAML_ADMIN_EMAIL' '"drift@pvv.ntnu.no"' \
--replace '$SAML_ADMIN_PASSWORD' 'file_get_contents("${config.sops.secrets."idp/admin_password".path}")' \
--replace '$SAML_TRUSTED_DOMAINS' 'array( "idp.pvv.ntnu.no" )' \
--replace '$SAML_DATABASE_DSN' '"pgsql:host=postgres.pvv.ntnu.no;port=5432;dbname=idp"' \
--replace '$SAML_DATABASE_USERNAME' '"idp"' \
--replace '$SAML_DATABASE_PASSWORD' 'file_get_contents("${config.sops.secrets."idp/postgres_password".path}")' \
--replace '$CACHE_DIRECTORY' '/var/cache/idp'
'';
"modules/authpwauth/src/Auth/Source/PwAuth.php" = ./authpwauth.php;
@ -108,7 +108,7 @@ in
List of urls point to (simplesamlphp) service profiders, which the idp should trust.
:::{.note}
Make sure the url ends with a `/`
Make sure the url ends with a `/`
:::
'';
};
@ -132,7 +132,7 @@ in
owner = "idp";
group = "idp";
};
};
};
users.groups."idp" = { };
users.users."idp" = {
@ -199,15 +199,9 @@ in
'';
};
"^~ /simplesaml/".extraConfig = ''
rewrite ^/simplesaml/(.*)$ /$1 redirect;
return 404;
'';
"/robots.txt" = {
root = pkgs.writeTextDir "robots.txt" ''
User-agent: *
Disallow: /
'';
};
rewrite ^/simplesaml/(.*)$ /$1 redirect;
return 404;
'';
};
};
};

View File

@ -1,5 +1,18 @@
{ config, pkgs, lib, ... }:
{
#######################
# TODO: remove these once nixos 24.05 gets released
#######################
# imports = [
# ./krb5.nix
# ./pam.nix
# ];
# disabledModules = [
# "config/krb5/default.nix"
# "security/pam.nix"
# ];
#######################
security.krb5 = {
enable = true;
settings = {

View File

@ -1,88 +0,0 @@
{ pkgs, lib, ... }:
# Based on
# - https://web.mit.edu/kerberos/krb5-1.12/doc/admin/conf_files/krb5_conf.html
# - https://manpages.debian.org/unstable/heimdal-docs/krb5.conf.5heimdal.en.html
let
inherit (lib) boolToString concatMapStringsSep concatStringsSep filter
isAttrs isBool isList mapAttrsToList mdDoc mkOption singleton splitString;
inherit (lib.types) attrsOf bool coercedTo either int listOf oneOf path
str submodule;
in
{ }: {
type = let
section = attrsOf relation;
relation = either (attrsOf value) value;
value = either (listOf atom) atom;
atom = oneOf [int str bool];
in submodule {
freeformType = attrsOf section;
options = {
include = mkOption {
default = [ ];
description = mdDoc ''
Files to include in the Kerberos configuration.
'';
type = coercedTo path singleton (listOf path);
};
includedir = mkOption {
default = [ ];
description = mdDoc ''
Directories containing files to include in the Kerberos configuration.
'';
type = coercedTo path singleton (listOf path);
};
module = mkOption {
default = [ ];
description = mdDoc ''
Modules to obtain Kerberos configuration from.
'';
type = coercedTo path singleton (listOf path);
};
};
};
generate = let
indent = str: concatMapStringsSep "\n" (line: " " + line) (splitString "\n" str);
formatToplevel = args @ {
include ? [ ],
includedir ? [ ],
module ? [ ],
...
}: let
sections = removeAttrs args [ "include" "includedir" "module" ];
in concatStringsSep "\n" (filter (x: x != "") [
(concatStringsSep "\n" (mapAttrsToList formatSection sections))
(concatMapStringsSep "\n" (m: "module ${m}") module)
(concatMapStringsSep "\n" (i: "include ${i}") include)
(concatMapStringsSep "\n" (i: "includedir ${i}") includedir)
]);
formatSection = name: section: ''
[${name}]
${indent (concatStringsSep "\n" (mapAttrsToList formatRelation section))}
'';
formatRelation = name: relation:
if isAttrs relation
then ''
${name} = {
${indent (concatStringsSep "\n" (mapAttrsToList formatValue relation))}
}''
else formatValue name relation;
formatValue = name: value:
if isList value
then concatMapStringsSep "\n" (formatAtom name) value
else formatAtom name value;
formatAtom = name: atom: let
v = if isBool atom then boolToString atom else toString atom;
in "${name} = ${v}";
in
name: value: pkgs.writeText name ''
${formatToplevel value}
'';
}

View File

@ -1,90 +0,0 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) mdDoc mkIf mkOption mkPackageOption mkRemovedOptionModule;
inherit (lib.types) bool;
mkRemovedOptionModule' = name: reason: mkRemovedOptionModule ["krb5" name] reason;
mkRemovedOptionModuleCfg = name: mkRemovedOptionModule' name ''
The option `krb5.${name}' has been removed. Use
`security.krb5.settings.${name}' for structured configuration.
'';
cfg = config.security.krb5;
format = import ./krb5-conf-format.nix { inherit pkgs lib; } { };
in {
imports = [
(mkRemovedOptionModuleCfg "libdefaults")
(mkRemovedOptionModuleCfg "realms")
(mkRemovedOptionModuleCfg "domain_realm")
(mkRemovedOptionModuleCfg "capaths")
(mkRemovedOptionModuleCfg "appdefaults")
(mkRemovedOptionModuleCfg "plugins")
(mkRemovedOptionModuleCfg "config")
(mkRemovedOptionModuleCfg "extraConfig")
(mkRemovedOptionModule' "kerberos" ''
The option `krb5.kerberos' has been moved to `security.krb5.package'.
'')
];
options = {
security.krb5 = {
enable = mkOption {
default = false;
description = mdDoc "Enable and configure Kerberos utilities";
type = bool;
};
package = mkPackageOption pkgs "krb5" {
example = "heimdal";
};
settings = mkOption {
default = { };
type = format.type;
description = mdDoc ''
Structured contents of the {file}`krb5.conf` file. See
{manpage}`krb5.conf(5)` for details about configuration.
'';
example = {
include = [ "/run/secrets/secret-krb5.conf" ];
includedir = [ "/run/secrets/secret-krb5.conf.d" ];
libdefaults = {
default_realm = "ATHENA.MIT.EDU";
};
realms = {
"ATHENA.MIT.EDU" = {
admin_server = "athena.mit.edu";
kdc = [
"athena01.mit.edu"
"athena02.mit.edu"
];
};
};
domain_realm = {
"mit.edu" = "ATHENA.MIT.EDU";
};
logging = {
kdc = "SYSLOG:NOTICE";
admin_server = "SYSLOG:NOTICE";
default = "SYSLOG:NOTICE";
};
};
};
};
};
config = mkIf cfg.enable {
environment = {
systemPackages = [ cfg.package ];
etc."krb5.conf".source = format.generate "krb5.conf" cfg.settings;
};
};
meta.maintainers = builtins.attrValues {
inherit (lib.maintainers) dblsaiko h7x4;
};
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
{ pkgs, lib, fp, config, values, pkgs-unstable, ... }: let
{ pkgs, lib, config, values, pkgs-unstable, ... }: let
cfg = config.services.mediawiki;
# "mediawiki"
@ -17,16 +17,16 @@
cp ${./simplesaml-config.php} "$out"
substituteInPlace "$out" \
--replace-warn '$SAML_COOKIE_SECURE' 'true' \
--replace-warn '$SAML_COOKIE_SALT' 'file_get_contents("${config.sops.secrets."mediawiki/simplesamlphp/cookie_salt".path}")' \
--replace-warn '$SAML_ADMIN_NAME' '"Drift"' \
--replace-warn '$SAML_ADMIN_EMAIL' '"drift@pvv.ntnu.no"' \
--replace-warn '$SAML_ADMIN_PASSWORD' 'file_get_contents("${config.sops.secrets."mediawiki/simplesamlphp/admin_password".path}")' \
--replace-warn '$SAML_TRUSTED_DOMAINS' 'array( "wiki.pvv.ntnu.no" )' \
--replace-warn '$SAML_DATABASE_DSN' '"pgsql:host=postgres.pvv.ntnu.no;port=5432;dbname=mediawiki_simplesamlphp"' \
--replace-warn '$SAML_DATABASE_USERNAME' '"mediawiki_simplesamlphp"' \
--replace-warn '$SAML_DATABASE_PASSWORD' 'file_get_contents("${config.sops.secrets."mediawiki/simplesamlphp/postgres_password".path}")' \
--replace-warn '$CACHE_DIRECTORY' '/var/cache/mediawiki/idp'
--replace '$SAML_COOKIE_SECURE' 'true' \
--replace '$SAML_COOKIE_SALT' 'file_get_contents("${config.sops.secrets."mediawiki/simplesamlphp/cookie_salt".path}")' \
--replace '$SAML_ADMIN_NAME' '"Drift"' \
--replace '$SAML_ADMIN_EMAIL' '"drift@pvv.ntnu.no"' \
--replace '$SAML_ADMIN_PASSWORD' 'file_get_contents("${config.sops.secrets."mediawiki/simplesamlphp/admin_password".path}")' \
--replace '$SAML_TRUSTED_DOMAINS' 'array( "wiki.pvv.ntnu.no" )' \
--replace '$SAML_DATABASE_DSN' '"pgsql:host=postgres.pvv.ntnu.no;port=5432;dbname=mediawiki_simplesamlphp"' \
--replace '$SAML_DATABASE_USERNAME' '"mediawiki_simplesamlphp"' \
--replace '$SAML_DATABASE_PASSWORD' 'file_get_contents("${config.sops.secrets."mediawiki/simplesamlphp/postgres_password".path}")' \
--replace '$CACHE_DIRECTORY' '/var/cache/mediawiki/idp'
'';
};
};
@ -86,20 +86,7 @@ in {
};
extensions = {
inherit (pkgs.mediawiki-extensions)
CodeEditor
CodeMirror
DeleteBatch
PluggableAuth
Popups
Scribunto
SimpleSAMLphp
TemplateData
TemplateStyles
UserMerge
VisualEditor
WikiEditor
;
inherit (pkgs.mediawiki-extensions) DeleteBatch UserMerge PluggableAuth SimpleSAMLphp VisualEditor;
};
extraConfig = ''
@ -133,27 +120,13 @@ in {
# Misc
$wgEmergencyContact = "${cfg.passwordSender}";
$wgShowIPinHeader = false;
$wgUseTeX = false;
$wgLocalInterwiki = $wgSitename;
# Fix https://github.com/NixOS/nixpkgs/issues/183097
$wgDBserver = "${toString cfg.database.host}";
$wgAllowCopyUploads = true;
# Misc program paths
$wgFFmpegLocation = '${pkgs.ffmpeg}/bin/ffmpeg';
$wgExiftool = '${pkgs.exiftool}/bin/exiftool';
$wgExiv2Command = '${pkgs.exiv2}/bin/exiv2';
# See https://gist.github.com/sergejmueller/088dce028b6dd120a16e
$wgJpegTran = '${pkgs.mozjpeg}/bin/jpegtran';
$wgGitBin = '${pkgs.git}/bin/git';
# Debugging
$wgShowExceptionDetails = false;
$wgShowIPinHeader = false;
# EXT:{SimpleSAML,PluggableAuth}
# SimpleSAML
$wgSimpleSAMLphp_InstallDir = "${simplesamlphp}/share/php/simplesamlphp/";
$wgPluggableAuth_Config['Log in using SAML'] = [
$wgPluggableAuth_Config['Log in using my SAML'] = [
'plugin' => 'SimpleSAMLphp',
'data' => [
'authSourceId' => 'default-sp',
@ -163,12 +136,8 @@ in {
]
];
# EXT:Scribunto
$wgScribuntoDefaultEngine = 'luastandalone';
$wgScribuntoEngineConf['luastandalone']['luaPath'] = '${pkgs.lua}/bin';
# EXT:WikiEditor
$wgWikiEditorRealtimePreview = true;
# Fix https://github.com/NixOS/nixpkgs/issues/183097
$wgDBserver = "${toString cfg.database.host}";
'';
};
@ -199,7 +168,7 @@ in {
extraConfig = ''
location ~ ^/simplesaml/(?<phpfile>.+?\.php)(?<pathinfo>/.*)?$ {
include ${pkgs.nginx}/conf/fastcgi_params;
fastcgi_pass unix:${config.services.phpfpm.pools.mediawiki.socket};
fastcgi_pass unix:${config.services.phpfpm.pools.mediawiki.socket};
fastcgi_param SCRIPT_FILENAME ${simplesamlphp}/share/php/simplesamlphp/public/$phpfile;
# Must be prepended with the baseurlpath
@ -210,8 +179,8 @@ in {
'';
};
"= /PNG/PVV-logo.svg".alias = fp /assets/logo_blue_regular.svg;
"= /PNG/PVV-logo.png".alias = fp /assets/logo_blue_regular.png;
"= /PNG/PVV-logo.svg".alias = ../../../../assets/logo_blue_regular.svg;
"= /PNG/PVV-logo.png".alias = ../../../../assets/logo_blue_regular.png;
"= /favicon.ico".alias = pkgs.runCommandLocal "mediawiki-favicon.ico" {
buildInputs = with pkgs; [ imagemagick ];
} ''
@ -219,7 +188,7 @@ in {
-resize x64 \
-gravity center \
-crop 64x64+0+0 \
${fp /assets/logo_blue_regular.png} \
${../../../../assets/logo_blue_regular.png} \
-flatten \
-colors 256 \
-background transparent \

View File

@ -58,7 +58,7 @@ $config = [
/*
* The following settings are *filesystem paths* which define where
* SimpleSAMLphp can find or write the following things:
* - 'cachedir': Where SimpleSAMLphp can write its cache.
* - 'cachedir': Where SimpleSAMLphp can write its cache.
* - 'loggingdir': Where to write logs. MUST be set to NULL when using a logging
* handler other than `file`.
* - 'datadir': Storage of general data.

View File

@ -1,51 +0,0 @@
{ 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"
];
};
});
}

View File

@ -1,104 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.vaultwarden;
domain = "pw.pvv.ntnu.no";
address = "127.0.1.2";
port = 3011;
wsPort = 3012;
in {
sops.secrets."vaultwarden/environ" = {
owner = "vaultwarden";
group = "vaultwarden";
};
services.vaultwarden = {
enable = true;
dbBackend = "postgresql";
environmentFile = config.sops.secrets."vaultwarden/environ".path;
config = {
domain = "https://${domain}";
rocketAddress = address;
rocketPort = port;
websocketEnabled = true;
websocketAddress = address;
websocketPort = wsPort;
signupsAllowed = true;
signupsVerify = true;
signupsDomainsWhitelist = "pvv.ntnu.no";
smtpFrom = "vaultwarden@pvv.ntnu.no";
smtpFromName = "VaultWarden PVV";
smtpHost = "smtp.pvv.ntnu.no";
smtpUsername = "vaultwarden";
smtpSecurity = "force_tls";
smtpAuthMechanism = "Login";
# Configured in environ:
# databaseUrl = "postgresql://vaultwarden@/vaultwarden";
# smtpPassword = hemli
};
};
services.nginx.virtualHosts."${domain}" = {
forceSSL = true;
enableACME = true;
kTLS = true;
extraConfig = ''
client_max_body_size 128M;
'';
locations."/" = {
proxyPass = "http://${address}:${toString port}";
proxyWebsockets = true;
};
locations."/notifications/hub" = {
proxyPass = "http://${address}:${toString wsPort}";
proxyWebsockets = true;
};
locations."/notifications/hub/negotiate" = {
proxyPass = "http://${address}:${toString port}";
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";
};
};
}

View File

@ -4,13 +4,8 @@ with lib;
let
cfg = config.services.roundcube;
domain = "webmail.pvv.ntnu.no";
in
in
{
sops.secrets."roundcube/postgres_password" = {
owner = "nginx";
group = "nginx";
};
services.roundcube = {
enable = true;
@ -25,11 +20,6 @@ in
maxAttachmentSize = 20;
hostName = "roundcubeplaceholder.example.com";
database = {
host = "postgres.pvv.ntnu.no";
passwordFile = config.sops.secrets."roundcube/postgres_password".path;
};
extraConfig = ''
$config['enable_installer'] = false;
$config['default_host'] = "ssl://imap.pvv.ntnu.no";

View File

@ -1,8 +1,8 @@
{ config, lib, fp, pkgs, ... }:
{ config, lib, pkgs, ... }:
let
cfg = config.services.snappymail;
in {
imports = [ (fp /modules/snappymail.nix) ];
imports = [ ../../../../modules/snappymail.nix ];
services.snappymail = {
enable = true;

View File

@ -21,8 +21,8 @@ in {
services.idp.sp-remote-metadata = [
"https://www.pvv.ntnu.no/simplesaml/"
"https://pvv.ntnu.no/simplesaml/"
"https://www.pvv.org/simplesaml/"
"https://pvv.org/simplesaml/"
"https://www.pvv.org/simplesaml/"
"https://pvv.org/simplesaml/"
];
services.pvv-nettsiden = {
@ -43,7 +43,7 @@ in {
'idp' => 'https://idp.pvv.ntnu.no/',
),
);
'';
'';
};
};
@ -116,6 +116,16 @@ in {
"/drift".return = "301 https://wiki.pvv.ntnu.no/wiki/Drift";
"/diverse/abuse.php".return = "301 https://wiki.pvv.ntnu.no/wiki/CERT/Abuse";
"/nerds/".return = "301 https://wiki.pvv.ntnu.no/wiki/Nerdepizza";
# Proxy the matrix well-known files
# Host has be set before proxy_pass
# The header must be set so nginx on the other side routes it to the right place
"^~ /.well-known/matrix/" = {
extraConfig = ''
proxy_set_header Host matrix.pvv.ntnu.no;
proxy_pass https://matrix.pvv.ntnu.no/.well-known/matrix/;
'';
};
};
};
}

View File

@ -46,7 +46,7 @@ in {
while IFS= read fname; do
# Skip this file if an up-to-date thumbnail already exists
if [ -f ".thumbnails/$fname.png" ] && \
[ "$(date -R -r "$fname")" == "$(date -R -r ".thumbnails/$fname.png")" ]
[ "$(date -R -r "$fname")" == "$(date -R -r ".thumbnails/$fname.png")" ]
then
continue
fi
@ -54,7 +54,7 @@ in {
echo "Creating thumbnail for $fname"
mkdir -p $(dirname ".thumbnails/$fname")
convert -define jpeg:size=200x200 "$fname" -thumbnail 300 -auto-orient ".thumbnails/$fname.png" ||:
touch -m -d "$(date -R -r "$fname")" ".thumbnails/$fname.png"
touch -m -d "$(date -R -r "$fname")" ".thumbnails/$fname.png"
done <<< "$images"
'';
@ -62,33 +62,6 @@ in {
WorkingDirectory = galleryDir;
User = config.services.pvv-nettsiden.user;
Group = config.services.pvv-nettsiden.group;
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"
];
};
};
}

View File

@ -1,18 +0,0 @@
{ ... }:
{
services.nginx.virtualHosts."www.pvv.ntnu.no".locations = {
"^~ /.well-known/" = {
alias = (toString ./root) + "/";
};
# Proxy the matrix well-known files
# Host has be set before proxy_pass
# The header must be set so nginx on the other side routes it to the right place
"^~ /.well-known/matrix/" = {
extraConfig = ''
proxy_set_header Host matrix.pvv.ntnu.no;
proxy_pass https://matrix.pvv.ntnu.no/.well-known/matrix/;
'';
};
};
}

View File

@ -1,31 +0,0 @@
<?xml version="1.0"?>
<clientConfig version="1.1">
<emailProvider id="pvv.ntnu.no">
<domain>pvv.ntnu.no</domain>
<domain>pvv.org</domain>
<displayName>Programvareverkstedet</displayName>
<incomingServer type="imap">
<hostname>imap.pvv.ntnu.no</hostname>
<port>993</port>
<socketType>SSL</socketType>
<username>%EMAILLOCALPART%</username>
<authentication>password-cleartext</authentication>
</incomingServer>
<outgoingServer type="smtp">
<hostname>smtp.pvv.ntnu.no</hostname>
<port>587</port>
<socketType>STARTTLS</socketType>
<username>%EMAILLOCALPART%</username>
<authentication>password-cleartext</authentication>
<useGlobalPreferredServer>true</useGlobalPreferredServer>
</outgoingServer>
<documentation url="https://www.pvv.ntnu.no/pvv/Drift/Mail/IMAP_POP3">
<descr lang="en">Setup programvareverkstedet email user with IMAP or POP3</descr>
<descr lang="nb">Sett opp programvareverkstedet email bruker med IMAP eller POP3</descr>
</documentation>
</emailProvider>
</clientConfig>

View File

@ -1,12 +0,0 @@
Contact: mailto:drift@pvv.ntnu.no
Contact: mailto:cert@pvv.ntnu.no
# drift@pvv.ntnu.no is read by more people and have a quicker reaction time,
# but cert@pvv.ntnu.no can be used for more severe issues.
Preferred-Languages: no, en
Expires: 2032-12-31T23:59:59.000Z
# This file was last updated 2024-09-14.
# You can find a wikipage for our security policies at:
# https://wiki.pvv.ntnu.no/wiki/CERT

24
hosts/bicep/acmeCert.nix Normal file
View File

@ -0,0 +1,24 @@
{ values, ... }:
{
users.groups.acme.members = [ "nginx" ];
security.acme.certs."postgres.pvv.ntnu.no" = {
group = "acme";
extraDomainNames = [
# "postgres.pvv.org"
"bicep.pvv.ntnu.no"
# "bicep.pvv.org"
# values.hosts.bicep.ipv4
# values.hosts.bicep.ipv6
];
};
services.nginx = {
enable = true;
virtualHosts."postgres.pvv.ntnu.no" = {
forceSSL = true;
enableACME = true;
# useACMEHost = "postgres.pvv.ntnu.no";
};
};
}

View File

@ -1,21 +1,24 @@
{ fp, pkgs, values, ... }:
{ pkgs, values, ... }:
{
imports = [
./hardware-configuration.nix
(fp /base)
(fp /misc/metrics-exporters.nix)
../../base.nix
../../misc/metrics-exporters.nix
./services/nginx
./acmeCert.nix
./services/mysql.nix
./services/postgres.nix
./services/mysql.nix
./services/calendar-bot.nix
# TODO: fix the calendar bot
# ./services/calendar-bot.nix
./services/matrix
];
sops.defaultSopsFile = fp /secrets/bicep/bicep.yaml;
sops.defaultSopsFile = ../../secrets/bicep/bicep.yaml;
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
sops.age.generateKey = true;
@ -34,9 +37,6 @@
anyInterface = true;
};
# There are no smart devices
services.smartd.enable = false;
# Do not change, even during upgrades.
# See https://search.nixos.org/options?show=system.stateVersion
system.stateVersion = "22.11";

View File

@ -1,20 +1,12 @@
{ config, fp, lib, pkgs, ... }:
{ config, lib, pkgs, ... }:
let
cfg = config.services.pvv-calendar-bot;
in {
sops.secrets = {
"calendar-bot/matrix_token" = {
sopsFile = fp /secrets/bicep/bicep.yaml;
key = "calendar-bot/matrix_token";
owner = cfg.user;
group = cfg.group;
};
"calendar-bot/mysql_password" = {
sopsFile = fp /secrets/bicep/bicep.yaml;
key = "calendar-bot/mysql_password";
owner = cfg.user;
group = cfg.group;
};
sops.secrets."calendar-bot/matrix_token" = {
sopsFile = ../../../secrets/bicep/bicep.yaml;
key = "calendar-bot/matrix_token";
owner = cfg.user;
group = cfg.group;
};
services.pvv-calendar-bot = {
@ -26,11 +18,6 @@ in {
user = "@bot_calendar: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;
onCalendar = "*-*-* 09:00:00";
};

View File

@ -1,14 +1,14 @@
{ config, lib, fp, pkgs, secrets, values, ... }:
{ config, lib, pkgs, secrets, ... }:
{
sops.secrets."matrix/synapse/turnconfig" = {
sopsFile = fp /secrets/bicep/matrix.yaml;
sopsFile = ../../../../secrets/bicep/matrix.yaml;
key = "synapse/turnconfig";
owner = config.users.users.matrix-synapse.name;
group = config.users.users.matrix-synapse.group;
};
sops.secrets."matrix/coturn/static-auth-secret" = {
sopsFile = fp /secrets/bicep/matrix.yaml;
sopsFile = ../../../../secrets/bicep/matrix.yaml;
key = "coturn/static-auth-secret";
owner = config.users.users.turnserver.name;
group = config.users.users.turnserver.group;
@ -26,7 +26,7 @@
"turns:turn.pvv.ntnu.no:5349?transport=tcp"
"turns:turn.pvv.ntnu.no:5349?transport=udp"
"turns:turn.pvv.ntnu.no:3478?transport=udp"
"turns:turn.pvv.ntnu.no:3478?transport=tcp"
"turn:turn.pvv.ntnu.no:3478?transport=udp"
@ -60,18 +60,16 @@
pkey = "${config.security.acme.certs.${realm}.directory}/key.pem";
use-auth-secret = true;
# World readable but I dont think it's that bad
static-auth-secret-file = config.sops.secrets."matrix/coturn/static-auth-secret".path;
secure-stun = true;
listening-ips = [
values.services.turn.ipv4
# values.services.turn.ipv6
];
listening-ips = [ "129.241.210.213" "2001:700:300:1900::213" ];
tls-listening-port = 443;
alt-tls-listening-port = 5349;
listening-port = 3478;
min-port = 49000;
@ -118,7 +116,7 @@
#total-quota=1200
'';
};
networking.firewall = {
interfaces.enp6s0f0 = let
range = with config.services.coturn; [ {

View File

@ -10,9 +10,8 @@
./mjolnir.nix
./discord.nix
./hookshot
];
}

View File

@ -1,4 +1,4 @@
{ config, lib, fp, ... }:
{ config, lib, ... }:
let
cfg = config.services.mx-puppet-discord;
@ -6,42 +6,15 @@ in
{
users.groups.keys-matrix-registrations = { };
sops.secrets."matrix/discord/as_token" = {
sopsFile = fp /secrets/bicep/matrix.yaml;
key = "discord/as_token";
};
sops.secrets."matrix/discord/hs_token" = {
sopsFile = fp /secrets/bicep/matrix.yaml;
key = "discord/hs_token";
};
sops.templates."discord-registration.yaml" = {
sops.secrets."matrix/registrations/mx-puppet-discord" = {
sopsFile = ../../../../secrets/bicep/matrix.yaml;
key = "registrations/mx-puppet-discord";
owner = config.users.users.matrix-synapse.name;
group = config.users.groups.keys-matrix-registrations.name;
content = ''
as_token: "${config.sops.placeholder."matrix/discord/as_token"}"
hs_token: "${config.sops.placeholder."matrix/discord/hs_token"}"
id: discord-puppet
namespaces:
users:
- exclusive: true
regex: '@_discordpuppet_.*'
rooms: []
aliases:
- exclusive: true
regex: '#_discordpuppet_.*'
protocols: []
rate_limited: false
sender_localpart: _discordpuppet_bot
url: 'http://localhost:8434'
de.sorunome.msc2409.push_ephemeral: true
'';
};
systemd.services.mx-puppet-discord = {
serviceConfig.SupplementaryGroups = [
config.users.groups.keys-matrix-registrations.name
];
serviceConfig.SupplementaryGroups = [ config.users.groups.keys-matrix-registrations.name ];
};
@ -56,16 +29,11 @@ in
relay.whitelist = [ ".*" ];
selfService.whitelist = [ "@danio:pvv\\.ntnu\\.no" "@dandellion:dodsorf\\.as" ];
};
services.mx-puppet-discord.serviceDependencies = [
"matrix-synapse.target"
"nginx.service"
];
services.mx-puppet-discord.serviceDependencies = [ "matrix-synapse.target" "nginx.service" ];
services.matrix-synapse-next.settings = {
app_service_config_files = [
config.sops.templates."discord-registration.yaml".path
];
app_service_config_files = [ config.sops.secrets."matrix/registrations/mx-puppet-discord".path ];
use_appservice_legacy_authorization = true;
};

View File

@ -1,139 +0,0 @@
{ config, lib, fp, unstablePkgs, inputs, ... }:
let
cfg = config.services.matrix-hookshot;
webhookListenAddress = "127.0.0.1";
webhookListenPort = 8435;
in
{
imports = [
./module.nix
];
sops.secrets."matrix/hookshot/as_token" = {
sopsFile = fp /secrets/bicep/matrix.yaml;
key = "hookshot/as_token";
};
sops.secrets."matrix/hookshot/hs_token" = {
sopsFile = fp /secrets/bicep/matrix.yaml;
key = "hookshot/hs_token";
};
sops.templates."hookshot-registration.yaml" = {
owner = config.users.users.matrix-synapse.name;
group = config.users.groups.keys-matrix-registrations.name;
content = ''
id: matrix-hookshot
as_token: "${config.sops.placeholder."matrix/hookshot/as_token"}"
hs_token: "${config.sops.placeholder."matrix/hookshot/hs_token"}"
namespaces:
rooms: []
users:
- regex: "@_webhooks_.*:pvv.ntnu.no"
exclusive: true
- regex: "@bot_feeds:pvv.ntnu.no"
exclusive: true
aliases: []
sender_localpart: hookshot
url: "http://${cfg.settings.bridge.bindAddress}:${toString cfg.settings.bridge.port}"
rate_limited: false
# If enabling encryption
de.sorunome.msc2409.push_ephemeral: true
push_ephemeral: true
org.matrix.msc3202: true
'';
};
systemd.services.matrix-hookshot = {
serviceConfig.SupplementaryGroups = [
config.users.groups.keys-matrix-registrations.name
];
};
services.matrix-hookshot = {
enable = true;
package = unstablePkgs.matrix-hookshot;
registrationFile = config.sops.templates."hookshot-registration.yaml".path;
settings = {
bridge = {
bindAddress = "127.0.0.1";
domain = "pvv.ntnu.no";
url = "https://matrix.pvv.ntnu.no";
mediaUrl = "https://matrix.pvv.ntnu.no";
port = 9993;
};
listeners = [
{
bindAddress = webhookListenAddress;
port = webhookListenPort;
resources = [
"webhooks"
# "metrics"
# "provisioning"
"widgets"
];
}
];
generic = {
enabled = true;
outbound = true;
urlPrefix = "https://hookshot.pvv.ntnu.no/webhook/";
userIdPrefix = "_webhooks_";
allowJsTransformationFunctions = false;
waitForComplete = false;
};
feeds = {
enabled = true;
pollIntervalSeconds = 600;
};
serviceBots = [
{ localpart = "bot_feeds";
displayname = "Aya";
avatar = ./feeds.png;
prefix = "!aya";
service = "feeds";
}
];
permissions = [
# Users of the PVV Server
{ actor = "pvv.ntnu.no";
services = [ { service = "*"; level = "commands"; } ];
}
# Members of Medlem space (for people with their own hs)
{ actor = "!pZOTJQinWyyTWaeOgK:pvv.ntnu.no";
services = [ { service = "*"; level = "commands"; } ];
}
# Members of Drift
{ actor = "!eYgeufLrninXxQpYml:pvv.ntnu.no";
services = [ { service = "*"; level = "admin"; } ];
}
# Dan bootstrap
{ actor = "@dandellion:dodsorf.as";
services = [ { service = "*"; level = "admin"; } ];
}
];
};
};
services.matrix-hookshot.serviceDependencies = [
"matrix-synapse.target"
"nginx.service"
];
services.matrix-synapse-next.settings = {
app_service_config_files = [
config.sops.templates."hookshot-registration.yaml".path
];
};
services.nginx.virtualHosts."hookshot.pvv.ntnu.no" = {
enableACME = true;
locations."/" = {
proxyPass = "http://${webhookListenAddress}:${toString webhookListenPort}";
};
};
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -1,127 +0,0 @@
{
config,
pkgs,
lib,
...
}:
let
cfg = config.services.matrix-hookshot;
settingsFormat = pkgs.formats.yaml { };
configFile = settingsFormat.generate "matrix-hookshot-config.yml" cfg.settings;
in
{
options = {
services.matrix-hookshot = {
enable = lib.mkEnableOption "matrix-hookshot, a bridge between Matrix and project management services";
package = lib.mkPackageOption pkgs "matrix-hookshot" { };
registrationFile = lib.mkOption {
type = lib.types.path;
description = ''
Appservice registration file.
As it contains secret tokens, you may not want to add this to the publicly readable Nix store.
'';
example = lib.literalExpression ''
pkgs.writeText "matrix-hookshot-registration" \'\'
id: matrix-hookshot
as_token: aaaaaaaaaa
hs_token: aaaaaaaaaa
namespaces:
rooms: []
users:
- regex: "@_webhooks_.*:foobar"
exclusive: true
sender_localpart: hookshot
url: "http://localhost:9993"
rate_limited: false
\'\'
'';
};
settings = lib.mkOption {
description = ''
{file}`config.yml` configuration as a Nix attribute set.
For details please see the [documentation](https://matrix-org.github.io/matrix-hookshot/latest/setup/sample-configuration.html).
'';
example = {
bridge = {
domain = "example.com";
url = "http://localhost:8008";
mediaUrl = "https://example.com";
port = 9993;
bindAddress = "127.0.0.1";
};
listeners = [
{
port = 9000;
bindAddress = "0.0.0.0";
resources = [ "webhooks" ];
}
{
port = 9001;
bindAddress = "localhost";
resources = [
"metrics"
"provisioning"
];
}
];
};
default = { };
type = lib.types.submodule {
freeformType = settingsFormat.type;
options = {
passFile = lib.mkOption {
type = lib.types.path;
default = "/var/lib/matrix-hookshot/passkey.pem";
description = ''
A passkey used to encrypt tokens stored inside the bridge.
File will be generated if not found.
'';
};
};
};
};
serviceDependencies = lib.mkOption {
type = with lib.types; listOf str;
default = lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit;
defaultText = lib.literalExpression ''
lib.optional config.services.matrix-synapse.enable config.services.matrix-synapse.serviceUnit
'';
description = ''
List of Systemd services to require and wait for when starting the application service,
such as the Matrix homeserver if it's running on the same host.
'';
};
};
};
config = lib.mkIf cfg.enable {
systemd.services.matrix-hookshot = {
description = "a bridge between Matrix and multiple project management services";
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ] ++ cfg.serviceDependencies;
after = [ "network-online.target" ] ++ cfg.serviceDependencies;
preStart = ''
if [ ! -f '${cfg.settings.passFile}' ]; then
mkdir -p $(dirname '${cfg.settings.passFile}')
${pkgs.openssl}/bin/openssl genpkey -out '${cfg.settings.passFile}' -outform PEM -algorithm RSA -pkeyopt rsa_keygen_bits:4096
fi
'';
serviceConfig = {
Type = "simple";
Restart = "always";
ExecStart = "${cfg.package}/bin/matrix-hookshot ${configFile} ${cfg.registrationFile}";
};
};
};
meta.maintainers = with lib.maintainers; [ flandweber ];
}

View File

@ -1,8 +1,8 @@
{ config, lib, fp, ... }:
{ config, lib, ... }:
{
sops.secrets."matrix/mjolnir/access_token" = {
sopsFile = fp /secrets/bicep/matrix.yaml;
sopsFile = ../../../../secrets/bicep/matrix.yaml;
key = "mjolnir/access_token";
owner = config.users.users.mjolnir.name;
group = config.users.users.mjolnir.group;
@ -11,7 +11,7 @@
services.mjolnir = {
enable = true;
pantalaimon.enable = false;
homeserverUrl = "https://matrix.pvv.ntnu.no";
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

@ -1,4 +1,4 @@
{ config, lib, fp, pkgs, values, inputs, ... }:
{ config, lib, pkgs, values, inputs, ... }:
let
cfg = config.services.matrix-synapse-next;
@ -10,18 +10,23 @@ let
in {
sops.secrets."matrix/synapse/signing_key" = {
key = "synapse/signing_key";
sopsFile = fp /secrets/bicep/matrix.yaml;
sopsFile = ../../../../secrets/bicep/matrix.yaml;
owner = config.users.users.matrix-synapse.name;
group = config.users.users.matrix-synapse.group;
};
sops.secrets."matrix/synapse/user_registration" = {
sopsFile = fp /secrets/bicep/matrix.yaml;
sopsFile = ../../../../secrets/bicep/matrix.yaml;
key = "synapse/signing_key";
owner = config.users.users.matrix-synapse.name;
group = config.users.users.matrix-synapse.group;
};
sops.secrets."matrix/sliding-sync/env" = {
sopsFile = ../../../../secrets/bicep/matrix.yaml;
key = "sliding-sync/env";
};
services.matrix-synapse-next = {
enable = true;
@ -38,6 +43,8 @@ in {
workers.eventPersisters = 2;
workers.useUserDirectoryWorker = true;
enableSlidingSync = true;
enableNginx = true;
settings = {
@ -130,13 +137,16 @@ in {
};
};
services.redis.servers."".enable = true;
services.matrix-synapse.sliding-sync.environmentFile = config.sops.secrets."matrix/sliding-sync/env".path;
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"}'
@ -146,43 +156,35 @@ in {
add_header Access-Control-Allow-Origin *;
'';
};
}
{
locations."/_synapse/admin" = {
proxyPass = "http://$synapse_backend";
extraConfig = ''
allow 127.0.0.1;
allow ::1;
allow ${values.hosts.bicep.ipv4};
allow ${values.hosts.bicep.ipv6};
deny all;
'';
};
}
{
})
({
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};
allow ${values.hosts.ildkule.ipv6};
allow ${values.hosts.ildkule.ipv4_global};
allow ${values.hosts.ildkule.ipv6_global};
deny all;
'';
})
}))
cfg.workers.instances;
}
{
})
({
locations."/metrics/master/1" = {
proxyPass = "http://127.0.0.1:9000/_synapse/metrics";
extraConfig = ''
allow ${values.hosts.ildkule.ipv4};
allow ${values.hosts.ildkule.ipv6};
allow ${values.hosts.ildkule.ipv4_global};
allow ${values.hosts.ildkule.ipv6_global};
deny all;
'';
};
@ -200,5 +202,5 @@ in {
labels = { };
}]) + "/";
};
}];
})];
}

View File

@ -15,12 +15,12 @@
mysqld = {
# PVV allows a lot of connections at the same time
max_connect_errors = 10000;
bind-address = values.services.mysql.ipv4;
skip-networking = 0;
bind-address = values.services.mysql.ipv4;
skip-networking = 0;
# This was needed in order to be able to use all of the old users
# during migration from knakelibrak to bicep in Sep. 2023
secure_auth = 0;
# This was needed in order to be able to use all of the old users
# during migration from knakelibrak to bicep in Sep. 2023
secure_auth = 0;
};
};

View File

@ -1,4 +1,7 @@
{ config, pkgs, ... }:
let
sslCert = config.security.acme.certs."postgres.pvv.ntnu.no";
in
{
services.postgresql = {
enable = true;
@ -76,16 +79,12 @@
systemd.services.postgresql.serviceConfig = {
LoadCredential = [
"cert:/etc/certs/postgres.crt"
"key:/etc/certs/postgres.key"
"cert:${sslCert.directory}/cert.pem"
"key:${sslCert.directory}/key.pem"
];
};
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";
};
users.groups.acme.members = [ "postgres" ];
networking.firewall.allowedTCPPorts = [ 5432 ];
networking.firewall.allowedUDPPorts = [ 5432 ];

View File

@ -35,10 +35,10 @@
# Workaround for bug https://github.com/NixOS/nixpkgs/issues/162686
useHostResolvConf = mkForce false;
};
system.stateVersion = "23.11";
services.resolved.enable = true;
};
};
};
};

View File

@ -1,16 +1,16 @@
{ config, fp, pkgs, values, ... }:
{ config, pkgs, values, ... }:
{
imports = [
# Include the results of the hardware scan.
./hardware-configuration.nix
(fp /base)
(fp /misc/metrics-exporters.nix)
../../base.nix
../../misc/metrics-exporters.nix
./disks.nix
(fp /misc/builder.nix)
../../misc/builder.nix
];
sops.defaultSopsFile = fp /secrets/bob/bob.yaml;
sops.defaultSopsFile = ../../secrets/bob/bob.yaml;
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
sops.age.generateKey = true;

View File

@ -1,10 +1,10 @@
{ config, fp, pkgs, values, ... }:
{ config, pkgs, values, ... }:
{
imports = [
# Include the results of the hardware scan.
./hardware-configuration.nix
(fp /base)
(fp /misc/metrics-exporters.nix)
../../base.nix
../../misc/metrics-exporters.nix
./services/grzegorz.nix
];

View File

@ -1,6 +1,6 @@
{ config, fp, ... }:
{ config, ... }:
{
imports = [ (fp /modules/grzegorz.nix) ];
imports = [ ../../../modules/grzegorz.nix ];
services.nginx.virtualHosts."${config.networking.fqdn}" = {
serverAliases = [

View File

@ -0,0 +1,42 @@
{ config, pkgs, values, ... }:
{
imports = [
./hardware-configuration.nix
../../base.nix
../../misc/metrics-exporters.nix
];
sops.defaultSopsFile = ../../secrets/buskerud/buskerud.yaml;
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
sops.age.generateKey = true;
# buskerud does not support efi?
# boot.loader.systemd-boot.enable = true;
# boot.loader.efi.canTouchEfiVariables = true;
boot.loader.grub.enable = true;
boot.loader.grub.device = "/dev/sdb";
networking.hostName = "buskerud";
networking.search = [ "pvv.ntnu.no" "pvv.org" ];
networking.nameservers = [ "129.241.0.200" "129.241.0.201" ];
networking.tempAddresses = "disabled";
systemd.network.networks."enp3s0f0" = values.defaultNetworkConfig // {
matchConfig.Name = "enp3s0f0";
address = with values.hosts.buskerud; [ (ipv4 + "/25") (ipv6 + "/64") ];
};
# List packages installed in system profile
environment.systemPackages = with pkgs; [
];
# This value determines the NixOS release from which the default
# settings for stateful data, like file locations and database versions
# on your system were taken. Its perfectly fine and recommended to leave
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "23.05"; # Did you read the comment?
}

View File

@ -0,0 +1,37 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[ (modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "uhci_hcd" "ehci_pci" "ata_piix" "hpsa" "usb_storage" "usbhid" "sd_mod" "sr_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-uuid/ed9654fe-575a-4fb3-b6ff-1b059479acff";
fsType = "ext4";
};
swapDevices = [ ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.enp14s0f0.useDHCP = lib.mkDefault true;
# networking.interfaces.enp14s0f1.useDHCP = lib.mkDefault true;
# networking.interfaces.enp3s0f0.useDHCP = lib.mkDefault true;
# networking.interfaces.enp3s0f1.useDHCP = lib.mkDefault true;
# networking.interfaces.enp4s0f0.useDHCP = lib.mkDefault true;
# networking.interfaces.enp4s0f1.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View File

@ -0,0 +1,21 @@
{config, ...}:
{
sops.secrets."bluemap_ssh_key" = {
owner = "root";
mode = "0400";
};
services.bluemap = {
enable = true;
eula = true;
defaultWorld = "/var/lib/bluemap/vanilla";
host = "minecraft.pvv.ntnu.no";
};
systemd.services."render-bluemap-maps".preStart = ''
rsync -e 'ssh -i ${config.sops.secrets."bluemap_ssh_key".path} -o "StrictHostKeyChecking accept-new"' \
root@innovation.pvv.ntnu.no:/var/backups/minecraft/current/ \
/var/lib/bluemap/vanilla"
'';
}

View File

@ -1,12 +1,12 @@
{ config, fp, pkgs, values, ... }:
{ config, pkgs, values, ... }:
{
imports = [
# Include the results of the hardware scan.
./hardware-configuration.nix
(fp /base)
(fp /misc/metrics-exporters.nix)
../../base.nix
../../misc/metrics-exporters.nix
(fp /modules/grzegorz.nix)
../../modules/grzegorz.nix
];
boot.loader.systemd-boot.enable = true;

View File

@ -1,16 +1,16 @@
{ config, fp, pkgs, lib, values, ... }:
{ config, pkgs, values, ... }:
{
imports = [
# Include the results of the hardware scan.
./hardware-configuration.nix
(fp /base)
(fp /misc/metrics-exporters.nix)
../../base.nix
../../misc/metrics-exporters.nix
./services/monitoring
./services/nginx
];
sops.defaultSopsFile = fp /secrets/ildkule/ildkule.yaml;
sops.defaultSopsFile = ../../secrets/ildkule/ildkule.yaml;
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
sops.age.generateKey = true;
@ -19,37 +19,17 @@
boot.tmp.cleanOnBoot = true;
zramSwap.enable = true;
# Openstack Neutron and systemd-networkd are not best friends, use something else:
systemd.network.enable = lib.mkForce false;
networking = let
hostConf = values.hosts.ildkule;
in {
hostName = "ildkule";
tempAddresses = "disabled";
useDHCP = lib.mkForce true;
search = values.defaultNetworkConfig.domains;
nameservers = values.defaultNetworkConfig.dns;
defaultGateway.address = hostConf.ipv4_internal_gw;
interfaces."ens4" = {
ipv4.addresses = [
{ address = hostConf.ipv4; prefixLength = 32; }
{ address = hostConf.ipv4_internal; prefixLength = 24; }
];
ipv6.addresses = [
{ address = hostConf.ipv6; prefixLength = 64; }
];
};
networking.hostName = "ildkule"; # Define your hostname.
systemd.network.networks."30-all" = values.defaultNetworkConfig // {
matchConfig.Name = "en*";
DHCP = "yes";
gateway = [ ];
};
# List packages installed in system profile
environment.systemPackages = with pkgs; [
];
# No devices with SMART
services.smartd.enable = false;
system.stateVersion = "23.11"; # Did you read the comment?
}

View File

@ -3,14 +3,7 @@
imports = [ (modulesPath + "/profiles/qemu-guest.nix") ];
boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "xen_blkfront" "vmw_pvscsi" ];
boot.initrd.kernelModules = [ "nvme" ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/e35eb4ce-aac3-4f91-8383-6e7cd8bbf942";
fsType = "ext4";
};
fileSystems."/data" = {
device = "/dev/disk/by-uuid/0a4c1234-02d3-4b53-aeca-d95c4c8d534b";
fsType = "ext4";
};
fileSystems."/" = { device = "/dev/vda1"; fsType = "ext4"; };
networking.useDHCP = lib.mkDefault true;
}

View File

@ -23187,4 +23187,4 @@
"uid": "rYdddlPWk",
"version": 9,
"weekStart": ""
}
}

View File

@ -3164,4 +3164,4 @@
"title": "PostgreSQL Database",
"uid": "000000039",
"version": 1
}
}

View File

@ -34,13 +34,13 @@ in {
{
name = "Ildkule Prometheus";
type = "prometheus";
url = "http://${config.services.prometheus.listenAddress}:${toString config.services.prometheus.port}";
isDefault = true;
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 = [
@ -56,13 +56,13 @@ in {
url = "https://raw.githubusercontent.com/matrix-org/synapse/develop/contrib/grafana/synapse.json";
options.path = dashboards/synapse.json;
}
# TODO: enable once https://github.com/NixOS/nixpkgs/pull/242365 gets merged
# {
# name = "MySQL";
# type = "file";
# url = "https://raw.githubusercontent.com/prometheus/mysqld_exporter/main/mysqld-mixin/dashboards/mysql-overview.json";
# options.path = dashboards/mysql.json;
# }
# TODO: enable once https://github.com/NixOS/nixpkgs/pull/242365 gets merged
# {
# name = "MySQL";
# type = "file";
# url = "https://raw.githubusercontent.com/prometheus/mysqld_exporter/main/mysqld-mixin/dashboards/mysql-overview.json";
# options.path = dashboards/mysql.json;
# }
{
name = "Postgresql";
type = "file";
@ -75,12 +75,6 @@ in {
url = "https://grafana.com/api/dashboards/240/revisions/3/download";
options.path = dashboards/go-processes.json;
}
{
name = "Gitea Dashbaord";
type = "file";
url = "https://grafana.com/api/dashboards/17802/revisions/3/download";
options.path = dashboards/gitea-dashbaord.json;
}
];
};

View File

@ -2,7 +2,6 @@
let
cfg = config.services.loki;
stateDir = "/data/monitoring/loki";
in {
services.loki = {
enable = true;
@ -17,7 +16,7 @@ in {
ingester = {
wal = {
enabled = true;
dir = "${stateDir}/wal";
dir = "/var/lib/loki/wal";
};
lifecycler = {
address = "127.0.0.1";
@ -49,30 +48,33 @@ in {
storage_config = {
boltdb_shipper = {
active_index_directory = "${stateDir}/boltdb-shipper-index";
cache_location = "${stateDir}/boltdb-shipper-cache";
active_index_directory = "/var/lib/loki/boltdb-shipper-index";
cache_location = "/var/lib/loki/boltdb-shipper-cache";
# shared_store = "filesystem";
cache_ttl = "24h";
};
filesystem = {
directory = "${stateDir}/chunks";
directory = "/var/lib/loki/chunks";
};
};
limits_config = {
allow_structured_metadata = false;
# enforce_metric_name = false;
reject_old_samples = true;
reject_old_samples_max_age = "72h";
};
compactor = {
working_directory = "${stateDir}/compactor";
working_directory = "/var/lib/loki/compactor";
# shared_store = "filesystem";
};
# ruler = {
# storage = {
# type = "local";
# local = {
# directory = "${stateDir}/rules";
# directory = "/var/lib/loki/rules";
# };
# };
# rule_path = "/etc/loki/rules";

View File

@ -1,26 +1,18 @@
{ config, ... }: let
stateDir = "/data/monitoring/prometheus";
in {
{ config, ... }: {
imports = [
./gitea.nix
./gogs.nix
./matrix-synapse.nix
# TODO: enable once https://github.com/NixOS/nixpkgs/pull/242365 gets merged
# ./mysqld.nix
./node.nix
./postgres.nix
./machines.nix
];
services.prometheus = {
enable = true;
listenAddress = "127.0.0.1";
port = 9001;
ruleFiles = [ rules/synapse-v2.rules ];
};
fileSystems."/var/lib/prometheus2" = {
device = stateDir;
options = [ "bind" ];
};
}

View File

@ -1,16 +0,0 @@
{ ... }:
{
services.prometheus.scrapeConfigs = [{
job_name = "gitea";
scrape_interval = "60s";
scheme = "https";
static_configs = [
{
targets = [
"git.pvv.ntnu.no:443"
];
}
];
}];
}

View File

@ -0,0 +1,16 @@
{ config, ... }: let
cfg = config.services.prometheus;
in {
services.prometheus.scrapeConfigs = [{
job_name = "git-gogs";
scheme = "https";
metrics_path = "/-/metrics";
static_configs = [
{
targets = [
"essendrop.pvv.ntnu.no:443"
];
}
];
}];
}

View File

@ -1,54 +0,0 @@
{ config, ... }: let
cfg = config.services.prometheus;
in {
services.prometheus.scrapeConfigs = [{
job_name = "base_info";
static_configs = [
{ labels.hostname = "ildkule";
targets = [
"ildkule.pvv.ntnu.no:${toString cfg.exporters.node.port}"
"ildkule.pvv.ntnu.no:${toString cfg.exporters.systemd.port}"
];
}
{ labels.hostname = "bekkalokk";
targets = [
"bekkalokk.pvv.ntnu.no:9100"
"bekkalokk.pvv.ntnu.no:9101"
];
}
{ labels.hostname = "bicep";
targets = [
"bicep.pvv.ntnu.no:9100"
"bicep.pvv.ntnu.no:9101"
];
}
{ labels.hostname = "brzeczyszczykiewicz";
targets = [
"brzeczyszczykiewicz.pvv.ntnu.no:9100"
"brzeczyszczykiewicz.pvv.ntnu.no:9101"
];
}
{ labels.hostname = "georg";
targets = [
"georg.pvv.ntnu.no:9100"
"georg.pvv.ntnu.no:9101"
];
}
{ labels.hostname = "hildring";
targets = [
"hildring.pvv.ntnu.no:9100"
];
}
{ labels.hostname = "isvegg";
targets = [
"isvegg.pvv.ntnu.no:9100"
];
}
{ labels.hostname = "microbel";
targets = [
"microbel.pvv.ntnu.no:9100"
];
}
];
}];
}

View File

@ -0,0 +1,22 @@
{ config, ... }: let
cfg = config.services.prometheus;
in {
services.prometheus.scrapeConfigs = [{
job_name = "node";
static_configs = [
{
targets = [
"ildkule.pvv.ntnu.no:${toString cfg.exporters.node.port}"
"microbel.pvv.ntnu.no:9100"
"isvegg.pvv.ntnu.no:9100"
"knakelibrak.pvv.ntnu.no:9100"
"hildring.pvv.ntnu.no:9100"
"bicep.pvv.ntnu.no:9100"
"essendrop.pvv.ntnu.no:9100"
"andresbu.pvv.ntnu.no:9100"
"bekkalokk.pvv.ntnu.no:9100"
];
}
];
}];
}

View File

@ -38,7 +38,7 @@ in {
};
systemd.services.prometheus-postgres-exporter-knakelibrak.serviceConfig = let
localCfg = config.services.prometheus.exporters.postgres;
localCfg = config.services.prometheus.exporters.postgres;
in lib.recursiveUpdate config.systemd.services.prometheus-postgres-exporter.serviceConfig {
EnvironmentFile = config.sops.secrets."keys/postgres/postgres_exporter_knakelibrak_env".path;
ExecStart = ''

View File

@ -2,7 +2,6 @@
let
cfg = config.services.uptime-kuma;
domain = "status.pvv.ntnu.no";
stateDir = "/data/monitoring/uptime-kuma";
in {
services.uptime-kuma = {
enable = true;
@ -18,9 +17,4 @@ in {
kTLS = true;
locations."/".proxyPass = "http://${cfg.settings.HOST}:${cfg.settings.PORT}";
};
fileSystems."/var/lib/uptime-kuma" = {
device = stateDir;
options = [ "bind" ];
};
}

View File

@ -1,13 +1,13 @@
{ config, fp, pkgs, values, ... }:
{ config, pkgs, values, ... }:
{
imports = [
# Include the results of the hardware scan.
./hardware-configuration.nix
(fp /base)
(fp /misc/metrics-exporters.nix)
../../base.nix
../../misc/metrics-exporters.nix
];
sops.defaultSopsFile = fp /secrets/shark/shark.yaml;
sops.defaultSopsFile = ../../secrets/shark/shark.yaml;
sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
sops.age.generateKey = true;

View File

@ -1,25 +0,0 @@
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
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:
nix eval .#inputs --apply builtins.attrNames --json \
| jq '.[]' -r \
| gum choose --no-limit --height=15 \
| xargs -L 1 nix flake lock --update-input
_a_machine:
nix eval .#nixosConfigurations --apply builtins.attrNames --json | jq .[] -r | gum filter

View File

@ -14,31 +14,13 @@
"::1"
values.hosts.ildkule.ipv4
values.hosts.ildkule.ipv6
values.hosts.ildkule.ipv4_global
values.hosts.ildkule.ipv6_global
];
};
services.prometheus.exporters.systemd = {
enable = true;
port = 9101;
extraFlags = [
"--systemd.collector.enable-restart-count"
"--systemd.collector.enable-ip-accounting"
];
};
systemd.services.prometheus-systemd-exporter.serviceConfig = {
IPAddressDeny = "any";
IPAddressAllow = [
"127.0.0.1"
"::1"
values.hosts.ildkule.ipv4
values.hosts.ildkule.ipv6
];
};
networking.firewall.allowedTCPPorts = [ 9100 9101 ];
networking.firewall.allowedTCPPorts = [ 9100 ];
services.promtail = {
enable = true;

View File

@ -32,7 +32,7 @@
color = "red";
command = "hostname | ${pkgs.toilet}/bin/toilet -f mono9";
};
service_status = {
Accounts = "accounts-daemon";
Cron = "cron";
@ -40,16 +40,16 @@
Matrix = "matrix-synapse";
sshd = "sshd";
};
uptime = {
prefix = "Uptime: ";
};
# Not relevant for server
# user_service_status = {
# Gpg-agent = "gpg-agent";
# };
filesystems = let
inherit (lib.attrsets) attrNames listToAttrs nameValuePair;
inherit (lib.lists) imap1;
@ -61,7 +61,7 @@
getName = i: v: if (v.label != null) then v.label else "<? ${toString i}>";
in
imap1Attrs' (i: n: v: nameValuePair (getName i v) n) fileSystems;
memory = {
swap_pos = "beside"; # or "below" or "none"
};
@ -70,14 +70,14 @@
inherit (lib.lists) imap1;
inherit (lib.attrsets) filterAttrs nameValuePair attrValues listToAttrs;
inherit (config.users) users;
normalUsers = filterAttrs (n: v: v.isNormalUser || n == "root") users;
userNPVs = imap1 (index: user: nameValuePair user.name index) (attrValues normalUsers);
in listToAttrs userNPVs;
last_run = {};
};
toml = pkgs.formats.toml {};
in toml.generate "rust-motd.toml" cfg;

View File

@ -1,26 +1,31 @@
{config, lib, pkgs, ...}:
let
grg = config.services.greg-ng;
grg = config.services.grzegorz;
grgw = config.services.grzegorz-webui;
in {
services.greg-ng = {
enable = true;
settings.host = "localhost";
settings.port = 31337;
enableSway = true;
enablePipewire = true;
services.pipewire.enable = true;
services.pipewire.alsa.enable = true;
services.pipewire.alsa.support32Bit = true;
services.pipewire.pulse.enable = true;
users.users.pvv = {
isNormalUser = true;
description = "pvv";
};
services.grzegorz-webui = {
enable = true;
listenAddr = "localhost";
listenPort = 42069;
listenWebsocketPort = 42042;
hostName = "${config.networking.fqdn}";
apiBase = "http://${grg.settings.host}:${toString grg.settings.port}/api";
};
services.grzegorz.enable = true;
services.grzegorz.listenAddr = "localhost";
services.grzegorz.listenPort = 31337;
services.grzegorz-webui.enable = true;
services.grzegorz-webui.listenAddr = "localhost";
services.grzegorz-webui.listenPort = 42069;
services.grzegorz-webui.listenWebsocketPort = 42042;
services.grzegorz-webui.hostName = "${config.networking.fqdn}";
services.grzegorz-webui.apiBase = "http://${toString grg.listenAddr}:${toString grg.listenPort}/api";
services.nginx.enable = true;
services.nginx.virtualHosts."${config.networking.fqdn}" = {
forceSSL = true;
enableACME = true;
@ -35,19 +40,20 @@ in {
'';
locations."/" = {
proxyPass = "http://${grgw.listenAddr}:${toString grgw.listenPort}";
proxyPass = "http://localhost:${builtins.toString config.services.grzegorz-webui.listenPort}";
};
# https://github.com/rawpython/remi/issues/216
locations."/websocket" = {
proxyPass = "http://${grgw.listenAddr}:${toString grgw.listenWebsocketPort}";
proxyPass = "http://localhost:${builtins.toString config.services.grzegorz-webui.listenWebsocketPort}";
proxyWebsockets = true;
};
locations."/api" = {
proxyPass = "http://${grg.settings.host}:${toString grg.settings.port}";
proxyPass = "http://localhost:${builtins.toString config.services.grzegorz.listenPort}";
};
locations."/docs" = {
proxyPass = "http://${grg.settings.host}:${toString grg.settings.port}";
proxyPass = "http://localhost:${builtins.toString config.services.grzegorz.listenPort}";
};
};
}

View File

@ -36,10 +36,10 @@ in
type = lib.types.str;
default = "${name}.key";
};
subject = lib.mkOption {
type = lib.types.str;
default = "/C=NO/O=Programvareverkstedet/CN=*.pvv.ntnu.no/emailAddress=drift@pvv.ntnu.no";
};
subject = lib.mkOption {
type = lib.types.str;
default = "/C=NO/O=Programvareverkstedet/CN=*.pvv.ntnu.no/emailAddress=drift@pvv.ntnu.no";
};
};
}));
};
@ -50,27 +50,25 @@ in
serviceConfig.Type = "oneshot";
script = let
openssl = lib.getExe pkgs.openssl;
in lib.concatMapStringsSep "\n" ({ name, value }: ''
in lib.concatMapStringsSep "\n----------------\n" ({ name, value }: ''
mkdir -p $(dirname "${value.certificate}") $(dirname "${value.certificateKey}")
if ! ${openssl} x509 -checkend 86400 -noout -in ${value.certificate}
then
echo "Regenerating '${value.certificate}'"
${openssl} req \
-newkey rsa:4096 \
-new -x509 \
-days "${toString value.daysValid}" \
-nodes \
-subj "${value.subject}" \
-out "${value.certificate}" \
-keyout "${value.certificateKey}" \
${lib.escapeShellArgs value.extraOpenSSLArgs}
echo "Regenerating '${value.certificate}'"
${openssl} req \
-newkey rsa:4096 \
-new -x509 \
-days "${toString value.daysValid}" \
-nodes \
-subj "${value.subject}" \
-out "${value.certificate}" \
-keyout "${value.certificateKey}" \
${lib.escapeShellArgs value.extraOpenSSLArgs}
fi
chown "${value.owner}:${value.group}" "${value.certificate}"
chown "${value.owner}:${value.group}" "${value.certificateKey}"
chmod "${value.mode}" "${value.certificate}"
chmod "${value.mode}" "${value.certificateKey}"
echo "\n-----------------\n"
'') (lib.attrsToList cfg);
};
systemd.timers."generate-snakeoil-certs" = {

View File

@ -1,30 +0,0 @@
{ lib, stdenvNoCC, fetchurl, makeWrapper, jre }:
stdenvNoCC.mkDerivation rec {
pname = "bluemap";
version = "5.2";
src = fetchurl {
url = "https://github.com/BlueMap-Minecraft/BlueMap/releases/download/v${version}/BlueMap-${version}-cli.jar";
hash = "sha256-4vld+NBwzBxdwbMtsKuqvO6immkbh4HB//6wdjXaxoU=";
};
dontUnpack = true;
nativeBuildInputs = [ makeWrapper ];
installPhase = ''
runHook preInstall
makeWrapper ${jre}/bin/java $out/bin/bluemap --add-flags "-jar $src"
runHook postInstall
'';
meta = {
description = "3D minecraft map renderer";
homepage = "https://bluemap.bluecolored.de/";
sourceProvenance = with lib.sourceTypes; [ binaryBytecode ];
license = lib.licenses.mit;
maintainers = with lib.maintainers; [ dandellion ];
mainProgram = "bluemap";
};
}

View File

@ -1,95 +1,8 @@
{ pkgs, lib }:
let
kebab-case-name = project-name: lib.pipe project-name [
(builtins.replaceStrings
lib.upperChars
(map (x: "-${x}") lib.lowerChars)
)
(lib.removePrefix "-")
];
mw-ext = {
name
, commit
, hash
, tracking-branch ? "REL1_41"
, kebab-name ? kebab-case-name name
, fetchgit ? pkgs.fetchgit
}:
{
${name} = (fetchgit {
name = "mediawiki-${kebab-name}-source";
url = "https://gerrit.wikimedia.org/r/mediawiki/extensions/${name}";
rev = commit;
inherit hash;
}).overrideAttrs (_: {
passthru = { inherit name kebab-name tracking-branch; };
});
};
in
# NOTE: to add another extension, you can add an mw-ext expression
# with an empty (or even wrong) commit and empty hash, and
# run the update script
lib.mergeAttrsList [
(mw-ext {
name = "CodeEditor";
commit = "7d8447035e381d76387e38b92e4d1e2b8d373a01";
hash = "sha256-v2AlbP0vZma3qZyEAWGjZ/rLcvOpIMroyc1EixKjlAU=";
})
(mw-ext {
name = "CodeMirror";
commit = "a7b4541089f9b88a0b722d9d790e4cf0f13aa328";
hash = "sha256-clyzN3v3+J4GjdyhrCsytBrH7VR1tq5yd0rB+32eWCg=";
})
(mw-ext {
name = "DeleteBatch";
commit = "cad869fbd95637902673f744581b29e0f3e3f61a";
hash = "sha256-M1ek1WdO1/uTjeYlrk3Tz+nlb/fFZH+O0Ok7b10iKak=";
})
(mw-ext {
name = "PluggableAuth";
commit = "4111a57c34e25bde579cce5d14ea094021e450c8";
hash = "sha256-aPtN8A9gDxLlq2+EloRZBO0DfHtE0E5kbV/adk82jvM=";
})
(mw-ext {
name = "Popups";
commit = "f1bcadbd8b868f32ed189feff232c47966c2c49e";
hash = "sha256-PQAjq/X4ZYwnnZ6ADCp3uGWMIucJy0ZXxsTTbAyxlSE=";
})
(mw-ext {
name = "Scribunto";
commit = "7b99c95f588b06635ee3c487080d6cb04617d4b5";
hash = "sha256-pviueRHQAsSlv4AtnUpo2Cjci7CbJ5aM75taEXY+WrI=";
})
(mw-ext {
name = "SimpleSAMLphp";
kebab-name = "simple-saml-php";
commit = "ecb47191fecd1e0dc4c9d8b90a9118e393d82c23";
hash = "sha256-gKu+O49XrAVt6hXdt36Ru7snjsKX6g2CYJ0kk/d+CI8=";
})
(mw-ext {
name = "TemplateData";
commit = "1ec66ce80f8a4322138efa56864502d0ee069bad";
hash = "sha256-Lv3Lq9dYAtdgWcwelveTuOhkP38MTu0m5kmW8+ltRis=";
})
(mw-ext {
name = "TemplateStyles";
commit = "581180e898d6a942e2a65c8f13435a5d50fffa67";
hash = "sha256-zW8O0mzG4jYfQoKi2KzsP+8iwRCLnWgH7qfmDE2R+HU=";
})
(mw-ext {
name = "UserMerge";
commit = "c17c919bdb9b67bb69f80df43e9ee9d33b1ecf1b";
hash = "sha256-+mkzTCo8RVlGoFyfCrSb5YMh4J6Pbi1PZLFu5ps8bWY=";
})
(mw-ext {
name = "VisualEditor";
commit = "90bb3d455892e25317029ffd4bda93159e8faac8";
hash = "sha256-SZAVELQUKZtwSM6NVlxvIHdFPodko8fhZ/uwB0LCFDA=";
})
(mw-ext {
name = "WikiEditor";
commit = "8dba5b13246d7ae09193f87e6273432b3264de5f";
hash = "sha256-vF9PBuM+VfOIs/a2X1JcPn6WH4GqP/vUJDFkfXzWyFU=";
})
]
{
DeleteBatch = pkgs.callPackage ./delete-batch { };
PluggableAuth = pkgs.callPackage ./pluggable-auth { };
SimpleSAMLphp = pkgs.callPackage ./simple-saml-php { };
UserMerge = pkgs.callPackage ./user-merge { };
VisualEditor = pkgs.callPackage ./visual-editor { };
}

View File

@ -0,0 +1,13 @@
{ fetchzip }:
let
commit = "a53af3b8269ed19ede3cf1fa811e7ec8cb00af92";
project-name = "UserMerge";
tracking-branch = "REL1_41";
in
fetchzip {
name = "mediawiki-delete-batch";
url = "https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/extensions/${project-name}/+archive/${commit}.tar.gz";
hash = "sha256-0ofCZhhv4aVTGq469Fdu7k0oVQu3kG3HFa8zaBbUr/M=";
stripRoot = false;
passthru = { inherit project-name tracking-branch; };
}

View File

@ -0,0 +1,13 @@
{ fetchzip }:
let
commit = "d5b3ad8f03b65d3746e025cdd7fe3254ad6e4026";
project-name = "PluggableAuth";
tracking-branch = "REL1_41";
in
fetchzip {
name = "mediawiki-pluggable-auth-source";
url = "https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/extensions/${project-name}/+archive/${commit}.tar.gz";
hash = "sha256-mLepavgeaNUGYxrrCKVpybGO2ecjc3B5IU8q+gZTx2U=";
stripRoot = false;
passthru = { inherit project-name tracking-branch; };
}

View File

@ -0,0 +1,13 @@
{ fetchzip }:
let
commit = "9ae0678d77a9175285a1cfadd5adf28379dbdb3d";
project-name = "SimpleSAMLphp";
tracking-branch = "REL1_41";
in
fetchzip {
name = "mediawiki-simple-saml-php-source";
url = "https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/extensions/${project-name}/+archive/${commit}.tar.gz";
hash = "sha256-s6Uw1fNzGBF0HEMl0LIRLhJkOHugrCE0aTnqawYi/pE=";
stripRoot = false;
passthru = { inherit project-name tracking-branch; };
}

View File

@ -1,5 +1,5 @@
#!/usr/bin/env nix-shell
#!nix-shell -i python3 -p "python3.withPackages(ps: with ps; [ beautifulsoup4 requests ])" nix-prefetch-git
#!nix-shell -i python3 -p "python3.withPackages(ps: with ps; [ beautifulsoup4 requests ])"
import os
from pathlib import Path
@ -8,148 +8,99 @@ import subprocess
from collections import defaultdict
from pprint import pprint
from dataclasses import dataclass
from functools import cache
import json
import bs4
import requests
BASE_WEB_URL = "https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/extensions"
BASE_GIT_URL = "https://gerrit.wikimedia.org/r/mediawiki/extensions/"
BASE_URL = "https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/extensions"
@dataclass
class PluginMetadata:
project_name: str
tracking_branch: str | None
tracking_branch: str
commit: str
hash_: str
@cache
def get_package_listing_path():
return Path(__file__).parent / "default.nix"
@cache
def get_global_tracking_branch() -> str:
with open(get_package_listing_path()) as file:
file_content = file.read()
return re.search(r'\btracking-branch\b \? "([^"]+?)"', file_content).group(1)
def get_metadata(package_expression: str) -> PluginMetadata | None:
project_name_search = re.search(r'\bname\b = "([^"]+?)";', package_expression)
tracking_branch_search = re.search(r'\btracking-branch\b = "([^"]+?)";', package_expression)
commit_search = re.search(r'\bcommit\b = "([^"]*?)";', package_expression)
hash_search = re.search(r'\bhash\b = "([^"]*?)";', package_expression)
if project_name_search is None:
print("Could not find project name in package:")
print(package_expression)
return None
tracking_branch = None;
if tracking_branch_search is not None:
tracking_branch = tracking_branch_search.group(1)
def get_metadata(file_content: str) -> dict[str,str] | None:
commit_search = re.search(f'commit = "([^"]*?)";', file_content)
tracking_branch_search = re.search(f'tracking-branch = "([^"]+?)";', file_content)
project_name_search = re.search(f'project-name = "([^"]+?)";', file_content)
if commit_search is None:
print("Could not find commit in package:")
print(package_expression)
print("Could not find commit in file:")
print(file_content)
return None
if hash_search is None:
print("Could not find hash in package:")
print(package_expression)
if tracking_branch_search is None:
print("Could not find tracking branch in file:")
print(file_content)
return None
if project_name_search is None:
print("Could not find project name in file:")
print(file_content)
return None
return PluginMetadata(
commit = commit_search.group(1),
tracking_branch = tracking_branch,
tracking_branch = tracking_branch_search.group(1),
project_name = project_name_search.group(1),
hash_ = hash_search.group(1),
)
def update_metadata(package_expression: str, metadata: PluginMetadata) -> str:
result = package_expression
result = re.sub(r'\bcommit\b = "[^"]*";', f'commit = "{metadata.commit}";', result)
result = re.sub(r'\bhash\b = "[^"]*";', f'hash = "{metadata.hash_}";', result)
return result
def get_newest_commit(project_name: str, tracking_branch: str) -> str:
content = requests.get(f"{BASE_WEB_URL}/{project_name}/+log/refs/heads/{tracking_branch}/").text
content = requests.get(f"{BASE_URL}/{project_name}/+log/refs/heads/{tracking_branch}/").text
soup = bs4.BeautifulSoup(content, features="html.parser")
try:
a = soup.find('li').findChild('a')
commit_sha = a['href'].split('/')[-1]
except AttributeError:
print(f"ERROR: Could not parse page for {project_name}:")
print(soup.prettify())
exit(1)
a = soup.find('li').findChild('a')
commit_sha = a['href'].split('/')[-1]
return commit_sha
def get_nix_hash(url: str, commit: str) -> str:
def get_nix_hash(tar_gz_url: str) -> str:
out, err = subprocess.Popen(
["nix-prefetch-git", "--url", url, "--rev", commit, "--fetch-submodules", "--quiet"],
["nix-prefetch-url", "--unpack", "--type", "sha256", tar_gz_url],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
).communicate()
out, err = subprocess.Popen(
["nix", "hash", "to-sri", "--type", "sha256", out.decode().strip()],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
).communicate()
return json.loads(out.decode().strip())['hash']
return out.decode().strip()
def update_expression(package_expression: str) -> str:
old_metadata = get_metadata(package_expression)
if old_metadata is None:
print("ERROR: could not find metadata for expression:")
print(package_expression)
return
def set_commit_and_hash(file_content: str, commit: str, sha256: str) -> str:
result = file_content
result = re.sub('commit = "[^"]*";', f'commit = "{commit}";', result)
result = re.sub('hash = "[^"]*";', f'hash = "{sha256}";', result)
return result
if old_metadata.commit == "":
old_metadata.commit = "<none>"
if old_metadata.hash_ == "":
old_metadata.hash_ = "<none>"
tracking_branch = old_metadata.tracking_branch
if tracking_branch is None:
tracking_branch = get_global_tracking_branch()
new_commit = get_newest_commit(old_metadata.project_name, tracking_branch)
new_hash = get_nix_hash(f"{BASE_GIT_URL}/{old_metadata.project_name}", new_commit)
if new_hash is None or new_hash == "":
print(f"ERROR: could not fetch hash for {old_metadata.project_name}")
exit(1)
print(f"Updating {old_metadata.project_name}[{tracking_branch}]: {old_metadata.commit} -> {new_commit}")
new_metadata = PluginMetadata(
project_name = old_metadata.project_name,
tracking_branch = old_metadata.tracking_branch,
commit = new_commit,
hash_ = new_hash,
)
return update_metadata(package_expression, new_metadata)
def update_all_expressions_in_default_nix() -> None:
with open(get_package_listing_path()) as file:
def update(package_file: Path) -> None:
with open(package_file) as file:
file_content = file.read()
new_file_content = re.sub(
r"\(mw-ext\s*\{(?:.|\n)+?\}\)",
lambda m: update_expression(m.group(0)),
file_content,
flags = re.MULTILINE,
)
metadata = get_metadata(file_content)
if metadata is None:
return
if metadata.commit == "":
metadata.commit = "<none>"
with open(get_package_listing_path(), 'w') as file:
new_commit = get_newest_commit(metadata.project_name, metadata.tracking_branch)
if new_commit == metadata.commit:
return
new_url = f"{BASE_URL}/{metadata.project_name}/+archive/{new_commit}.tar.gz"
new_hash = get_nix_hash(new_url)
print(f"Updating {metadata.project_name}: {metadata.commit} -> {new_commit}")
new_file_content = set_commit_and_hash(file_content, new_commit, new_hash)
with open(package_file, 'w') as file:
file.write(new_file_content)
if __name__ == "__main__":
update_all_expressions_in_default_nix()
for direntry in os.scandir(Path(__file__).parent):
if direntry.is_dir():
package_file = Path(direntry) / "default.nix"
assert package_file.is_file()
update(package_file)

View File

@ -0,0 +1,13 @@
{ fetchzip }:
let
commit = "a53af3b8269ed19ede3cf1fa811e7ec8cb00af92";
project-name = "UserMerge";
tracking-branch = "REL1_41";
in
fetchzip {
name = "mediawiki-user-merge-source";
url = "https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/extensions/${project-name}/+archive/${commit}.tar.gz";
hash = "sha256-0ofCZhhv4aVTGq469Fdu7k0oVQu3kG3HFa8zaBbUr/M=";
stripRoot = false;
passthru = { inherit project-name tracking-branch; };
}

View File

@ -0,0 +1,13 @@
{ fetchzip }:
let
commit = "bb92d4b0bb81cebd73a3dbabfb497213dac349f2";
project-name = "VisualEditor";
tracking-branch = "REL1_40";
in
fetchzip {
name = "mediawiki-visual-editor-source";
url = "https://gerrit.wikimedia.org/r/plugins/gitiles/mediawiki/extensions/${project-name}/+archive/${commit}.tar.gz";
hash = "sha256-lShpSoR+NLfdd5i7soM6J40pq+MzCMG0M1tSYsS+jAg=";
stripRoot = false;
passthru = { inherit project-name tracking-branch; };
}

View File

@ -29,7 +29,7 @@ php.buildComposerProject rec {
mkdir -p $(dirname "${target_path}")
cp -r "${source_path}" "${target_path}"
''))
lib.concatLines
(lib.concatStringsSep "\n")
];
postInstall = ''

Some files were not shown because too many files have changed in this diff Show More