Merge pull request 'auth: add user for each administrator' (#261) from per-admin-user into main

Reviewed-on: pub-solar/infra#261
Reviewed-by: teutat3s <teutat3s@noreply.git.pub.solar>
Reviewed-by: Akshay Mankar <axeman@noreply.git.pub.solar>
This commit is contained in:
teutat3s 2024-11-28 16:16:35 +00:00
commit 3e32bfe106
Signed by: pub.solar gitea
GPG key ID: F0332B04B7054873
19 changed files with 153 additions and 139 deletions

View file

@ -28,18 +28,18 @@ People with admin access to the infrastructure are added to [`logins/admins.nix`
SSH is not reachable from the open internet. Instead, SSH Port 22 is protected by a wireguard VPN network. Thus, to get root access on the servers, at least two pieces of information have to be added to the admins config: SSH is not reachable from the open internet. Instead, SSH Port 22 is protected by a wireguard VPN network. Thus, to get root access on the servers, at least two pieces of information have to be added to the admins config:
1. **SSH Public key**: self-explanatory. Add your public key to your user attrset under `sshPubKeys`. 1. **SSH Public key**: self-explanatory. Add your public key to your user attrset under `sshPubKeys`.
2. **Wireguard device**: each wireguard device has two parts: the public key and the IP addresses it should have in the wireguard network. The pub.solar wireguard network is spaced under `10.7.6.0/24` and `fd00:fae:fae:fae:fae::/80`. To add your device, it's best to choose a free number between 200 and 255 and use that in both the ipv4 and ipv6 ranges: `10.7.6.<ip-address>/32` `fd00:fae:fae:fae:fae:<ip-address>::/96`. For more information on how to generate keypairs, see [the NixOS Wireguard docs](https://nixos.wiki/wiki/WireGuard#Generate_keypair). 2. **Wireguard device**: each wireguard device has two parts: the public key and the IP addresses it should have in the wireguard network. The pub.solar wireguard network uses the subnets `10.7.6.0/24` and `fd00:fae:fae:fae:fae::/80`. To add your device, it's best to choose a free number between 200 and 255 and use that in both the ipv4 and ipv6 ranges: `10.7.6.<ip-address>/32` `fd00:fae:fae:fae:fae:<ip-address>::/96`. For more information on how to generate keypairs, see [the NixOS Wireguard docs](https://nixos.wiki/wiki/WireGuard#Generate_keypair).
One can access our hosts using this domain scheme: One can access our hosts using this domain scheme:
``` ```
ssh barkeeper@<hostname>.wg.pub.solar ssh <unix-username>@<hostname>.wg.pub.solar
``` ```
So, for example for `nachtigall`: So, for example for `nachtigall`:
``` ```
ssh barkeeper@nachtigall.wg.pub.solar ssh teutat3s@nachtigall.wg.pub.solar
``` ```
Example NixOS snippet for WireGuard client config Example NixOS snippet for WireGuard client config

View file

@ -7,16 +7,29 @@ be manually deployed.
To deploy, make sure you have a [working development shell](./development-shell.md). To deploy, make sure you have a [working development shell](./development-shell.md).
Then, run `deploy-rs` with the hostname of the server you want to deploy: Then, run `deploy-rs` with the hostname of the server you want to deploy:
### Dry-run
Use `--dry-activate` to show a diff of updated packages and all services that
would be restarted by the update. This will also put all files in place without
switching to the new generation, enabling a quick switch to the new config at a
later moment.
For nachtigall.pub.solar: For nachtigall.pub.solar:
``` ```
deploy --targets '.#nachtigall' --magic-rollback false --auto-rollback false --keep-result --result-path ./results deploy --targets '.#nachtigall' --ssh-user <unix-username> --magic-rollback false --auto-rollback false --keep-result --result-path ./results --dry-activate
```
After reviewing the changes, apply the update with:
```
deploy --targets '.#nachtigall' --ssh-user <unix-username> --magic-rollback false --auto-rollback false --keep-result --result-path ./results
``` ```
For metronom.pub.solar (aarch64-linux): For metronom.pub.solar (aarch64-linux):
``` ```
deploy --targets '.#metronom' --magic-rollback false --auto-rollback false --keep-result --result-path ./results --remote-build deploy --targets '.#metronom' --ssh-user <unix-username> --magic-rollback false --auto-rollback false --keep-result --result-path ./results --remote-build
``` ```
Usually we skip all rollback functionality, but if you want to deploy a change Usually we skip all rollback functionality, but if you want to deploy a change
@ -25,9 +38,6 @@ that might lock you out, e.g. to SSH, it might make sense to set these to `true`
To skip flake checks, e.g. because you already ran them manually before To skip flake checks, e.g. because you already ran them manually before
deployment, add the flag `--skip-checks` at the end of the command. deployment, add the flag `--skip-checks` at the end of the command.
`--dry-activate` can be used to only put all files in place without switching,
to enable switching to the new config quickly at a later moment.
We use `--keep-result --result-path ./results` to keep the last `result` We use `--keep-result --result-path ./results` to keep the last `result`
symlink of each `deploy` from being garbage collected. That way, we keep builds symlink of each `deploy` from being garbage collected. That way, we keep builds
cached in the Nix store. This is optional and both flags can be removed if disk cached in the Nix store. This is optional and both flags can be removed if disk

View file

@ -8,7 +8,7 @@ Requirements:
- [Setup WireGuard](./administrative-access.md#ssh-access) for hosts: `trinkgenossin`, optionally: `delite`, `blue-shell` - [Setup WireGuard](./administrative-access.md#ssh-access) for hosts: `trinkgenossin`, optionally: `delite`, `blue-shell`
``` ```
ssh barkeeper@trinkgenossin.wg.pub.solar ssh <unix-username>@trinkgenossin.wg.pub.solar
``` ```
``` ```
@ -58,7 +58,7 @@ Further reading:
### Notes on manual setup steps ### Notes on manual setup steps
``` ```
ssh barkeeper@trinkgenossin.wg.pub.solar ssh <unix-username>@trinkgenossin.wg.pub.solar
# Add a few spaces to avoid leaking the secret to the shell history # Add a few spaces to avoid leaking the secret to the shell history
export GARAGE_RPC_SECRET=<secret-in-keepass> export GARAGE_RPC_SECRET=<secret-in-keepass>

View file

@ -41,3 +41,7 @@ wrapped-ruby-mastodon-gems: 4.2.1 → 4.2.3
zfs-kernel: 2.2.1-6.1.64 → 2.2.2-6.1.66 zfs-kernel: 2.2.1-6.1.64 → 2.2.2-6.1.66
zfs-user: 2.2.1 → 2.2.2 zfs-user: 2.2.1 → 2.2.2
``` ```
### Deploying updates
See [deploying.md](./deploying.md).

View file

@ -120,56 +120,43 @@
devShells.ci = pkgs.mkShell { buildInputs = with pkgs; [ nodejs ]; }; devShells.ci = pkgs.mkShell { buildInputs = with pkgs; [ nodejs ]; };
}; };
flake = flake = {
let nixosModules = builtins.listToAttrs (
username = "barkeeper"; map (x: {
in name = x;
{ value = import (./modules + "/${x}");
inherit username; }) (builtins.attrNames (builtins.readDir ./modules))
);
nixosModules = builtins.listToAttrs ( checks = builtins.mapAttrs (
map (x: { system: deployLib: deployLib.deployChecks self.deploy
name = x; ) inputs.deploy-rs.lib;
value = import (./modules + "/${x}");
}) (builtins.attrNames (builtins.readDir ./modules))
);
checks = builtins.mapAttrs ( formatter."x86_64-linux" = inputs.unstable.legacyPackages."x86_64-linux".nixfmt-rfc-style;
system: deployLib: deployLib.deployChecks self.deploy
) inputs.deploy-rs.lib;
formatter."x86_64-linux" = inputs.unstable.legacyPackages."x86_64-linux".nixfmt-rfc-style; deploy.nodes = self.lib.deploy.mkDeployNodes self.nixosConfigurations {
nachtigall = {
deploy.nodes = self.lib.deploy.mkDeployNodes self.nixosConfigurations { hostname = "nachtigall.wg.pub.solar";
nachtigall = { };
hostname = "nachtigall.wg.pub.solar"; metronom = {
sshUser = username; hostname = "metronom.wg.pub.solar";
}; };
metronom = { tankstelle = {
hostname = "metronom.wg.pub.solar"; hostname = "tankstelle.wg.pub.solar";
sshUser = username; };
}; underground = {
tankstelle = { hostname = "80.244.242.3";
hostname = "tankstelle.wg.pub.solar"; };
sshUser = username; trinkgenossin = {
}; hostname = "trinkgenossin.wg.pub.solar";
underground = { };
hostname = "80.244.242.3"; delite = {
sshUser = username; hostname = "delite.wg.pub.solar";
}; };
trinkgenossin = { blue-shell = {
hostname = "trinkgenossin.wg.pub.solar"; hostname = "blue-shell.wg.pub.solar";
sshUser = username;
};
delite = {
hostname = "delite.wg.pub.solar";
sshUser = username;
};
blue-shell = {
hostname = "blue-shell.wg.pub.solar";
sshUser = username;
};
}; };
}; };
};
}; };
} }

View file

@ -22,7 +22,7 @@ in
"${wireguardIPv6}/96" "${wireguardIPv6}/96"
]; ];
privateKeyFile = config.age.secrets.wg-private-key.path; privateKeyFile = config.age.secrets.wg-private-key.path;
peers = flake.self.logins.admins.wireguardDevices ++ [ peers = flake.self.logins.wireguardDevices ++ [
{ {
# trinkgenossin.pub.solar # trinkgenossin.pub.solar
publicKey = "QWgHovHxtqiQhnHLouSWiT6GIoQDmuvnThYL5c/rvU4="; publicKey = "QWgHovHxtqiQhnHLouSWiT6GIoQDmuvnThYL5c/rvU4=";

View file

@ -22,7 +22,7 @@ in
"${wireguardIPv6}/96" "${wireguardIPv6}/96"
]; ];
privateKeyFile = config.age.secrets.wg-private-key.path; privateKeyFile = config.age.secrets.wg-private-key.path;
peers = flake.self.logins.admins.wireguardDevices ++ [ peers = flake.self.logins.wireguardDevices ++ [
{ {
# trinkgenossin.pub.solar # trinkgenossin.pub.solar
publicKey = "QWgHovHxtqiQhnHLouSWiT6GIoQDmuvnThYL5c/rvU4="; publicKey = "QWgHovHxtqiQhnHLouSWiT6GIoQDmuvnThYL5c/rvU4=";

View file

@ -18,7 +18,7 @@
"fd00:fae:fae:fae:fae:3::/96" "fd00:fae:fae:fae:fae:3::/96"
]; ];
privateKeyFile = config.age.secrets.wg-private-key.path; privateKeyFile = config.age.secrets.wg-private-key.path;
peers = flake.self.logins.admins.wireguardDevices ++ [ peers = flake.self.logins.wireguardDevices ++ [
{ {
# nachtigall.pub.solar # nachtigall.pub.solar
endpoint = "138.201.80.102:51820"; endpoint = "138.201.80.102:51820";

View file

@ -18,7 +18,7 @@
"fd00:fae:fae:fae:fae:1::/96" "fd00:fae:fae:fae:fae:1::/96"
]; ];
privateKeyFile = config.age.secrets.wg-private-key.path; privateKeyFile = config.age.secrets.wg-private-key.path;
peers = flake.self.logins.admins.wireguardDevices ++ [ peers = flake.self.logins.wireguardDevices ++ [
{ {
# tankstelle.pub.solar # tankstelle.pub.solar
endpoint = "80.244.242.5:51820"; endpoint = "80.244.242.5:51820";

View file

@ -18,7 +18,7 @@
"fd00:fae:fae:fae:fae:4::/96" "fd00:fae:fae:fae:fae:4::/96"
]; ];
privateKeyFile = config.age.secrets.wg-private-key.path; privateKeyFile = config.age.secrets.wg-private-key.path;
peers = flake.self.logins.admins.wireguardDevices ++ [ peers = flake.self.logins.wireguardDevices ++ [
{ {
# nachtigall.pub.solar # nachtigall.pub.solar
endpoint = "138.201.80.102:51820"; endpoint = "138.201.80.102:51820";

View file

@ -22,7 +22,7 @@ in
"${wireguardIPv6}/96" "${wireguardIPv6}/96"
]; ];
privateKeyFile = config.age.secrets.wg-private-key.path; privateKeyFile = config.age.secrets.wg-private-key.path;
peers = flake.self.logins.admins.wireguardDevices ++ [ peers = flake.self.logins.wireguardDevices ++ [
{ {
# nachtigall.pub.solar # nachtigall.pub.solar
endpoint = "138.201.80.102:51820"; endpoint = "138.201.80.102:51820";

View file

@ -6,19 +6,16 @@ in
{ {
flake = { flake = {
logins = { logins = {
admins = admins = admins;
lib.lists.foldl wireguardDevices = lib.lists.foldl (
(logins: adminConfig: { wireguardDevices: adminConfig:
sshPubKeys = logins.sshPubKeys ++ (lib.attrsets.attrValues adminConfig.sshPubKeys); wireguardDevices ++ (if adminConfig ? "wireguardDevices" then adminConfig.wireguardDevices else [ ])
wireguardDevices = ) [ ] (lib.attrsets.attrValues admins);
logins.wireguardDevices sshPubKeys = lib.lists.foldl (
++ (if adminConfig ? "wireguardDevices" then adminConfig.wireguardDevices else [ ]); sshPubKeys: adminConfig:
}) sshPubKeys
{ ++ (if adminConfig ? "sshPubKeys" then lib.attrsets.attrValues adminConfig.sshPubKeys else [ ])
sshPubKeys = [ ]; ) [ ] (lib.attrsets.attrValues admins);
wireguardDevices = [ ];
}
(lib.attrsets.attrValues admins);
robots.sshPubKeys = lib.attrsets.attrValues robots; robots.sshPubKeys = lib.attrsets.attrValues robots;
}; };
}; };

View file

@ -54,9 +54,5 @@
}; };
time.timeZone = "Etc/UTC"; time.timeZone = "Etc/UTC";
home-manager.users.${config.pub-solar-os.authentication.username} = {
home.stateVersion = "23.05";
};
}; };
} }

View file

@ -1,19 +1,27 @@
{ flake, config, ... }: { flake, lib, ... }:
{ {
home-manager.users.${config.pub-solar-os.authentication.username} = { home-manager.users = (
programs.git.enable = true; lib.attrsets.foldlAttrs (
programs.starship.enable = true; acc: name: value:
programs.bash.enable = true; acc
programs.neovim = { // {
enable = true; ${name} = {
vimAlias = true; programs.git.enable = true;
viAlias = true; programs.starship.enable = true;
defaultEditor = true; programs.bash.enable = true;
# configure = { programs.neovim = {
# packages.myVimPackages = with pkgs.vimPlugins; { enable = true;
# start = [vim-nix vim-surrund rainbow]; vimAlias = true;
# }; viAlias = true;
# }; defaultEditor = true;
}; # configure = {
}; # packages.myVimPackages = with pkgs.vimPlugins; {
# start = [vim-nix vim-surrund rainbow];
# };
# };
};
};
}
) { } flake.self.logins.admins
);
} }

View file

@ -11,18 +11,6 @@
inherit (lib) mkOption types; inherit (lib) mkOption types;
in in
{ {
username = mkOption {
description = "Username for the adminstrative user";
type = types.str;
default = flake.self.username;
};
sshPubKeys = mkOption {
description = "SSH Keys that should have administrative root access";
type = types.listOf types.str;
default = flake.self.logins.admins.sshPubKeys;
};
root.initialHashedPassword = mkOption { root.initialHashedPassword = mkOption {
description = "Hashed password of the root account"; description = "Hashed password of the root account";
type = types.str; type = types.str;
@ -43,36 +31,60 @@
}; };
config = { config = {
users.users.${config.pub-solar-os.authentication.username} = { users.users =
name = config.pub-solar-os.authentication.username; (lib.attrsets.foldlAttrs (
group = config.pub-solar-os.authentication.username; acc: name: value:
extraGroups = [ acc
"wheel" // {
"docker" ${name} = {
]; name = name;
isNormalUser = true; group = name;
openssh.authorizedKeys.keys = config.pub-solar-os.authentication.sshPubKeys; extraGroups = [
}; "wheel"
users.groups.${config.pub-solar-os.authentication.username} = { }; "docker"
];
isNormalUser = true;
openssh.authorizedKeys.keys = lib.attrsets.attrValues value.sshPubKeys;
};
}
) { } flake.self.logins.admins)
// {
# TODO: Remove when we stop locking ourselves out.
root.openssh.authorizedKeys.keys = flake.self.logins.sshPubKeys;
root.initialHashedPassword = config.pub-solar-os.authentication.root.initialHashedPassword;
# TODO: Remove when we stop locking ourselves out. ${config.pub-solar-os.authentication.robot.username} = {
users.users.root.openssh.authorizedKeys.keys = config.pub-solar-os.authentication.sshPubKeys; description = "CI and automation user";
home = "/home/${config.pub-solar-os.authentication.robot.username}";
createHome = true;
useDefaultShell = true;
uid = 998;
group = "${config.pub-solar-os.authentication.robot.username}";
isSystemUser = true;
openssh.authorizedKeys.keys = config.pub-solar-os.authentication.robot.sshPubKeys;
};
};
users.users.${config.pub-solar-os.authentication.robot.username} = { home-manager.users = (
description = "CI and automation user"; lib.attrsets.foldlAttrs (
home = "/home/${config.pub-solar-os.authentication.robot.username}"; acc: name: value:
createHome = true; acc
useDefaultShell = true; // {
uid = 998; ${name} = {
group = "${config.pub-solar-os.authentication.robot.username}"; home.stateVersion = "23.05";
isSystemUser = true; };
openssh.authorizedKeys.keys = config.pub-solar-os.authentication.robot.sshPubKeys; }
}; ) { } flake.self.logins.admins
);
users.groups.${config.pub-solar-os.authentication.robot.username} = { }; users.groups =
(lib.attrsets.foldlAttrs (
users.users.root.initialHashedPassword = acc: name: value:
config.pub-solar-os.authentication.root.initialHashedPassword; acc // { "${name}" = { }; }
) { } flake.self.logins.admins)
// {
${config.pub-solar-os.authentication.robot.username} = { };
};
security.sudo.wheelNeedsPassword = false; security.sudo.wheelNeedsPassword = false;
}; };

View file

@ -10,7 +10,7 @@
# Please create this manually the first time. # Please create this manually the first time.
hostKeys = [ "/etc/secrets/initrd/ssh_host_ed25519_key" ]; hostKeys = [ "/etc/secrets/initrd/ssh_host_ed25519_key" ];
authorizedKeys = config.pub-solar-os.authentication.sshPubKeys; authorizedKeys = flake.self.logins.sshPubKeys;
}; };
postCommands = '' postCommands = ''
# Automatically ask for the password on SSH login # Automatically ask for the password on SSH login

View file

@ -11,7 +11,7 @@
# Please create this manually the first time. # Please create this manually the first time.
hostKeys = [ "/etc/secrets/initrd/ssh_host_ed25519_key" ]; hostKeys = [ "/etc/secrets/initrd/ssh_host_ed25519_key" ];
authorizedKeys = config.pub-solar-os.authentication.sshPubKeys; authorizedKeys = flake.self.logins.sshPubKeys;
}; };
# this will automatically load the zfs password prompt on login # this will automatically load the zfs password prompt on login
# and kill the other prompt so boot can continue # and kill the other prompt so boot can continue

View file

@ -66,7 +66,7 @@ in
testScript = testScript =
{ nodes, ... }: { nodes, ... }:
let let
user = nodes.client.users.users.${nodes.client.pub-solar-os.authentication.username}; user = nodes.client.users.users.b12f;
#uid = toString user.uid; #uid = toString user.uid;
bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u ${user.name})/bus"; bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u ${user.name})/bus";
gdbus = "${bus} gdbus"; gdbus = "${bus} gdbus";

View file

@ -11,7 +11,7 @@
services.xserver.displayManager.gdm.enable = true; services.xserver.displayManager.gdm.enable = true;
services.xserver.desktopManager.gnome.enable = true; services.xserver.desktopManager.gnome.enable = true;
services.xserver.displayManager.autoLogin.enable = true; services.xserver.displayManager.autoLogin.enable = true;
services.xserver.displayManager.autoLogin.user = config.pub-solar-os.authentication.username; services.xserver.displayManager.autoLogin.user = "b12f";
systemd.user.services = { systemd.user.services = {
"org.gnome.Shell@wayland" = { "org.gnome.Shell@wayland" = {