test: keycloak test goes through full registration

This commit is contained in:
b12f 2025-01-23 23:34:38 +01:00
parent c4bb36fa33
commit 4d5c077c83
Signed by: b12f
GPG key ID: 729956E1124F8F26
25 changed files with 176 additions and 99 deletions

1
.gitignore vendored
View file

@ -6,3 +6,4 @@
result
results
.nixos-test-history
/*.png

View file

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

View file

@ -32,10 +32,10 @@
modules = [
self.inputs.agenix.nixosModules.default
self.nixosModules.home-manager
self.nixosModules.core
./nachtigall
self.nixosModules.overlays
self.nixosModules.unlock-zfs-on-boot
self.nixosModules.core
self.nixosModules.docker
self.nixosModules.backups
@ -78,10 +78,10 @@
modules = [
self.inputs.agenix.nixosModules.default
self.nixosModules.home-manager
self.nixosModules.core
./metronom
self.nixosModules.overlays
self.nixosModules.unlock-zfs-on-boot
self.nixosModules.core
self.nixosModules.backups
self.nixosModules.mail
self.nixosModules.prometheus-exporters
@ -100,9 +100,9 @@
modules = [
self.inputs.agenix.nixosModules.default
self.nixosModules.home-manager
self.nixosModules.core
./tankstelle
self.nixosModules.overlays
self.nixosModules.core
self.nixosModules.backups
self.nixosModules.prometheus-exporters
self.nixosModules.promtail
@ -118,11 +118,11 @@
modules = [
self.inputs.agenix.nixosModules.default
self.nixosModules.home-manager
self.nixosModules.core
./trinkgenossin
self.nixosModules.backups
self.nixosModules.overlays
self.nixosModules.unlock-luks-on-boot
self.nixosModules.core
self.nixosModules.garage
self.nixosModules.nginx
@ -145,10 +145,10 @@
self.inputs.agenix.nixosModules.default
self.inputs.disko.nixosModules.disko
self.nixosModules.home-manager
self.nixosModules.core
./delite
self.nixosModules.overlays
self.nixosModules.unlock-luks-on-boot
self.nixosModules.core
self.nixosModules.prometheus-exporters
self.nixosModules.promtail
@ -167,10 +167,10 @@
self.inputs.agenix.nixosModules.default
self.inputs.disko.nixosModules.disko
self.nixosModules.home-manager
self.nixosModules.core
./blue-shell
self.nixosModules.overlays
self.nixosModules.unlock-luks-on-boot
self.nixosModules.core
self.nixosModules.prometheus-exporters
self.nixosModules.promtail
@ -188,10 +188,10 @@
modules = [
self.inputs.agenix.nixosModules.default
self.nixosModules.home-manager
self.nixosModules.core
./underground
self.nixosModules.overlays
self.nixosModules.unlock-luks-on-boot
self.nixosModules.core
self.nixosModules.backups
self.nixosModules.keycloak

View file

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

View file

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

View file

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

View file

@ -8,6 +8,5 @@
./networking.nix
./forgejo-actions-runner.nix
./wireguard.nix
#./backups.nix
];
}

View file

@ -1,6 +1,6 @@
{
lib,
config,
pkgs,
flake,
...
}:
@ -18,7 +18,7 @@
"fd00:fae:fae:fae:fae:4::/96"
];
privateKeyFile = config.age.secrets.wg-private-key.path;
peers = flake.self.logins.wireguardDevices ++ [
peers = (flake.self.lib.wireguardDevicesForUsers config.pub-solar-os.authentication.users) ++ [
{
# nachtigall.pub.solar
endpoint = "138.201.80.102:51820";

View file

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

View file

@ -1,5 +1,4 @@
{
self,
lib,
inputs,
...
@ -19,6 +18,7 @@
## In configs, they can be used under "lib.our"
deploy = import ./deploy.nix { inherit inputs lib; };
wireguardDevicesForUsers = callLibs ./wireguardDevicesForUsers.nix;
};
};
}

View file

@ -0,0 +1,4 @@
{ lib }: users: lib.lists.foldl (
wireguardDevices: userConfig:
wireguardDevices ++ (if userConfig ? "wireguardDevices" then userConfig.wireguardDevices else [ ])
) [ ] (lib.attrsets.attrValues users)

View file

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

View file

@ -7,10 +7,10 @@
}:
{
imports = [
./users.nix
./nix.nix
./networking.nix
./terminal-tooling.nix
./users.nix
];
options.pub-solar-os =

View file

@ -1,4 +1,4 @@
{ flake, lib, ... }:
{ flake, lib, config, ... }:
{
home-manager.users = (
lib.attrsets.foldlAttrs (
@ -28,6 +28,6 @@
};
};
}
) { } flake.self.logins.admins
) { } config.pub-solar-os.authentication.users
);
}

View file

@ -11,6 +11,40 @@
inherit (lib) mkOption types;
in
{
users = mkOption {
description = "Administrative users to add";
type = types.attrsOf (types.submodule {
options = {
sshPubKeys = mkOption {
type = types.attrsOf types.str;
default = {};
};
secretEncryptionKeys = mkOption {
type = types.attrsOf types.str;
default = {};
};
wireguardDevices = mkOption {
type = types.listOf (types.submodule {
options = {
publicKey = mkOption { type = types.str; };
allowedIPs = mkOption { type = types.listOf types.str; };
};
});
default = {};
};
};
});
default = flake.self.logins.admins;
};
robot.sshPubKeys = mkOption {
description = "SSH Keys to use for the robot user";
type = types.listOf types.str;
default = flake.self.logins.robots.sshPubKeys;
};
root.initialHashedPassword = mkOption {
description = "Hashed password of the root account";
type = types.str;
@ -22,12 +56,6 @@
type = types.str;
default = "hakkonaut";
};
robot.sshPubKeys = mkOption {
description = "SSH Keys to use for the robot user";
type = types.listOf types.str;
default = flake.self.logins.robots.sshPubKeys;
};
};
config = {
@ -47,10 +75,8 @@
openssh.authorizedKeys.keys = lib.attrsets.attrValues value.sshPubKeys;
};
}
) { } flake.self.logins.admins)
) { } config.pub-solar-os.authentication.users)
// {
# 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;
${config.pub-solar-os.authentication.robot.username} = {
@ -74,14 +100,14 @@
home.stateVersion = "23.05";
};
}
) { } flake.self.logins.admins
) { } config.pub-solar-os.authentication.users
);
users.groups =
(lib.attrsets.foldlAttrs (
acc: name: value:
acc // { "${name}" = { }; }
) { } flake.self.logins.admins)
) { } config.pub-solar-os.authentication.users)
// {
${config.pub-solar-os.authentication.robot.username} = { };
};

View file

@ -1,4 +1,4 @@
{ flake, config, ... }:
{ lib, config, ... }:
{
boot.initrd.network = {
enable = true;
@ -10,7 +10,11 @@
# Please create this manually the first time.
hostKeys = [ "/etc/secrets/initrd/ssh_host_ed25519_key" ];
authorizedKeys = flake.self.logins.sshPubKeys;
authorizedKeys = lib.lists.foldl (
sshPubKeys: userConfig:
sshPubKeys
++ (if userConfig ? "sshPubKeys" then lib.attrsets.attrValues userConfig.sshPubKeys else [ ])
) [ ] (lib.attrsets.attrValues config.pub-solar-os.authentication.users);
};
postCommands = ''
# Automatically ask for the password on SSH login

View file

@ -1,4 +1,4 @@
{ flake, config, ... }:
{ lib, config, ... }:
{
# From https://nixos.wiki/wiki/ZFS#Unlock_encrypted_zfs_via_ssh_on_boot
boot.initrd.network = {
@ -11,7 +11,11 @@
# Please create this manually the first time.
hostKeys = [ "/etc/secrets/initrd/ssh_host_ed25519_key" ];
authorizedKeys = flake.self.logins.sshPubKeys;
authorizedKeys = lib.lists.foldl (
sshPubKeys: userConfig:
sshPubKeys
++ (if userConfig ? "sshPubKeys" then lib.attrsets.attrValues userConfig.sshPubKeys else [ ])
) [ ] (lib.attrsets.attrValues config.pub-solar-os.authentication.users);
};
# this will automatically load the zfs password prompt on login
# and kill the other prompt so boot can continue

View file

@ -24,37 +24,14 @@ in
dns-server.imports = [ ./support/dns-server.nix ];
acme-server.imports = [ ./support/acme-server.nix ];
mail-server.imports = [ ./support/mail-server.nix ];
auth-server.imports = [ ./support/auth-server.nix ];
client.imports = [ ./support/client.nix ];
nachtigall = {
imports = [
self.inputs.agenix.nixosModules.default
self.nixosModules.home-manager
self.nixosModules.core
self.nixosModules.backups
self.nixosModules.nginx
self.nixosModules.keycloak
self.nixosModules.postgresql
./support/global.nix
];
systemd.tmpfiles.rules = [ "f /tmp/dbf 1777 root root 10d password" ];
virtualisation.memorySize = 4096;
pub-solar-os.auth = {
enable = true;
database-password-file = "/tmp/dbf";
};
services.keycloak.database.createLocally = true;
services.keycloak.initialAdminPassword = "password";
};
};
testScript =
{ nodes, ... }:
let
user = nodes.client.users.users.b12f;
user = nodes.client.users.users.test-user;
#uid = toString user.uid;
bus = "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u ${user.name})/bus";
gdbus = "${bus} gdbus";
@ -63,23 +40,31 @@ in
wmClass = su "${gdbus} ${gseval} global.display.focus_window.wm_class";
in
''
import time
import re
import sys
def puppeteer_run(cmd):
client.succeed(f'puppeteer-run \'{cmd}\' ')
start_all()
acme_server.wait_for_unit("system.slice")
mail_server.wait_for_unit("dovecot2.service")
mail_server.wait_for_unit("postfix.service")
nachtigall.wait_for_unit("system.slice")
nachtigall.succeed("ping 127.0.0.1 -c 2")
nachtigall.wait_for_unit("nginx.service")
mail_server.wait_for_unit("nginx.service")
mail_server.wait_until_succeeds("curl http://mail.test.pub.solar/")
nachtigall.wait_for_unit("keycloak.service")
nachtigall.wait_for_open_port(8080)
nachtigall.wait_for_open_port(443)
nachtigall.wait_until_succeeds("curl http://127.0.0.1:8080/")
nachtigall.wait_until_succeeds("curl https://auth.test.pub.solar/")
nachtigall.succeed("${pkgs.keycloak}/bin/kcadm.sh create realms -f ${realm-export} --server http://localhost:8080 --realm master --user admin --password password --no-config")
auth_server.wait_for_unit("system.slice")
auth_server.succeed("ping 127.0.0.1 -c 2")
auth_server.wait_for_unit("nginx.service")
auth_server.wait_for_unit("keycloak.service")
auth_server.wait_for_open_port(8080)
auth_server.wait_for_open_port(443)
auth_server.wait_until_succeeds("curl http://127.0.0.1:8080/")
auth_server.wait_until_succeeds("curl https://auth.test.pub.solar/")
auth_server.succeed("${pkgs.keycloak}/bin/kcadm.sh create realms -f ${realm-export} --server http://localhost:8080 --realm master --user admin --password password --no-config")
client.wait_for_unit("system.slice")
client.wait_for_file("/tmp/puppeteer.sock")
@ -98,10 +83,33 @@ in
puppeteer_run('page.locator("[name=password]").fill("Password1234")')
puppeteer_run('page.locator("[name=password-confirm]").fill("Password1234")')
client.screenshot("register-filled-in")
puppeteer_run('page.locator("button::-p-text(Register)").click()')
puppeteer_run('page.locator("input[type=submit][value=Register]").click()')
puppeteer_run('page.waitForNetworkIdle()')
client.screenshot("after-register")
client.succeed("offlineimap")
client.succeed("${su "offlineimap"}")
client.succeed("${su "[ $(messages -s ~/Maildir/test-user@test.pub.solar/INBOX) -eq 1 ]"}")
puppeteer_run('page.locator("a::-p-text(Click here)").click()')
puppeteer_run('page.waitForNetworkIdle()')
client.succeed("${su "offlineimap"}")
client.succeed("${su "[ $(messages -s ~/Maildir/test-user@test.pub.solar/INBOX) -eq 2 ]"}")
mail_text = client.execute("${su "echo p | mail -Nf ~/Maildir/test-user@test.pub.solar/INBOX"}")[1]
boundary_match = re.search('boundary="(.*)"', mail_text, flags=re.M)
if not boundary_match:
sys.exit(1)
splits = mail_text.split(f'--{boundary_match.group(1)}')
clean_plaintext = splits[1].replace("=\n", "").replace("=3D", "=")
url_match = re.search('(https://auth.test.pub.solar.*)', clean_plaintext, flags=re.M)
print(url_match)
if not url_match:
sys.exit(1)
puppeteer_run(f'page.goto("{url_match.group(1)}")')
puppeteer_run('page.waitForNetworkIdle()')
client.screenshot("email-confirmed")
sys.exit(0)
time.sleep(1)
'';
}

View file

@ -0,0 +1,35 @@
{
pkgs,
flake,
...
}:let
ca-cert = pkgs.writeTextFile {
name = "ca-cert";
text = builtins.readFile ./step/certs/root_ca.crt;
};
in {
imports = [
flake.self.inputs.agenix.nixosModules.default
flake.self.nixosModules.home-manager
flake.self.nixosModules.core
flake.self.nixosModules.backups
flake.self.nixosModules.nginx
flake.self.nixosModules.keycloak
flake.self.nixosModules.postgresql
./global.nix
];
systemd.tmpfiles.rules = [
"f /tmp/dbf 1777 root root 10d password"
];
virtualisation.memorySize = 4096;
pub-solar-os.auth = {
enable = true;
database-password-file = "/tmp/dbf";
};
services.keycloak.database.createLocally = true;
services.keycloak.initialAdminPassword = "password";
services.keycloak.settings.truststore-paths = "${ca-cert}";
}

View file

@ -21,18 +21,20 @@ in
services.xserver.displayManager.gdm.enable = true;
services.xserver.desktopManager.gnome.enable = true;
services.xserver.displayManager.autoLogin.enable = true;
services.xserver.displayManager.autoLogin.user = "b12f";
services.xserver.displayManager.autoLogin.user = "test-user";
environment.systemPackages = [
puppeteer-run
pkgs.alacritty
pkgs.mailutils
];
services.getty.autologinUser = "b12f";
services.getty.autologinUser = "test-user";
virtualisation.memorySize = 4096;
virtualisation.qemu.options = [ "-vga std" ];
home-manager.users.b12f = {
home-manager.users.test-user = {
programs.bash.profileExtra = ''
[ "$(tty)" = "/dev/tty1" ] && exec systemd-cat --identifier=sway ${pkgs.sway}/bin/sway
'';
@ -51,6 +53,8 @@ in
};
};
programs.offlineimap.enable = true;
accounts.email.accounts."test-user@${config.pub-solar-os.networking.domain}" = {
primary = true;
address = "test-user@${config.pub-solar-os.networking.domain}";
@ -69,6 +73,7 @@ in
getmail.enable = true;
getmail.mailboxes = [ "ALL" ];
msmtp.enable = true;
offlineimap.enable = true;
};
};
}

View file

@ -45,9 +45,8 @@
local-data = [
"\"mail.${config.pub-solar-os.networking.domain}. 10800 IN CNAME mail-server\""
"\"ca.${config.pub-solar-os.networking.domain}. 10800 IN CNAME acme-server\""
"\"${config.pub-solar-os.networking.domain}. 10800 IN CNAME nachtigall\""
"\"www.${config.pub-solar-os.networking.domain}. 10800 IN CNAME nachtigall\""
"\"auth.${config.pub-solar-os.networking.domain}. 10800 IN CNAME nachtigall\""
"\"www.${config.pub-solar-os.networking.domain}. 10800 IN CNAME auth-server\""
"\"auth.${config.pub-solar-os.networking.domain}. 10800 IN CNAME auth-server\""
];
tls-cert-bundle = "/etc/ssl/certs/ca-certificates.crt";

View file

@ -5,6 +5,8 @@
...
}:
{
pub-solar-os.authentication.users.test-user = {};
pub-solar-os.networking.domain = "test.pub.solar";
security.acme.defaults.server = "https://ca.${config.pub-solar-os.networking.domain}/acme/acme/directory";

View file

@ -4,11 +4,11 @@
...
}: {
imports = [
flake.inputs.simple-nixos-mailserver.nixosModule
flake.self.nixosModules.home-manager
flake.self.nixosModules.core
flake.self.nixosModules.backups
flake.self.nixosModules.mail
flake.inputs.simple-nixos-mailserver.nixosModule
./global.nix
];

View file

@ -2,5 +2,5 @@
writeShellScriptBin "puppeteer-run" ''
set -e
exec ${curl}/bin/curl --fail-with-body -X POST -d "$@" --unix-socket "/tmp/puppeteer.sock" http://puppeteer-socket
exec ${curl}/bin/curl --fail-with-body -X POST -d "$@" -s --unix-socket "/tmp/puppeteer.sock" http://puppeteer-socket
''

View file

@ -23,12 +23,12 @@ const EXECUTABLE = process.env.EXECUTABLE || 'firefox';
page.on('response', response => {
console.log(response.url());
});
const actions = [];
const server = http.createServer({}, (req, res) => {
const server = http.createServer({});
server.on('request', (req, res) => {
const chunks = [];
req.on('data', (chunk) => {
console.log(`got data ${chunk}`);
chunks.push(chunk);
});
@ -37,18 +37,17 @@ const EXECUTABLE = process.env.EXECUTABLE || 'firefox';
const content = chunks.join('');
console.log(`Executing ${content}`);
eval(`actions.push(${content})`);
const val = await eval(content);
const val = await actions[actions.length - 1];
const responseText = (() => {
try {
return JSON.stringify({ data: val });
} catch (err) {
return JSON.stringify({ data: val.toString() });
}
})();
res.writeHead(200, { 'Content-Type': 'application/json' });
let responseText;
try {
responseText = JSON.stringify({ data: val });
} catch (err) {
responseText = val.toString();
}
res.end(responseText);
} catch (err) {
console.error(err);