diff --git a/checks/mumble/default.nix b/checks/mumble/default.nix index 912943270..081b5ea75 100644 --- a/checks/mumble/default.nix +++ b/checks/mumble/default.nix @@ -1,143 +1,102 @@ -(import ../lib/test-base.nix) ( - { ... }: - let - common = - { self, pkgs, ... }: - { - imports = [ - self.clanModules.mumble - { - clan.services.mumble.user = "alice"; - } - self.nixosModules.clanCore - (self.inputs.nixpkgs + "/nixos/tests/common/x11.nix") - { - clan.core.settings.directory = ./.; - environment.systemPackages = [ pkgs.killall ]; - clan.core.facts.services.mumble.secret."mumble-key".path = "/etc/mumble-key"; - clan.core.facts.services.mumble.public."mumble-cert".path = "/etc/mumble-cert"; - } - ]; +{ + pkgs, + self, + clanLib, + ... +}: +clanLib.test.makeTestClan { + inherit pkgs self; + nixosTest = ( + { lib, ... }: + let + common = + { pkgs, modulesPath, ... }: + { + imports = [ + (modulesPath + "/../tests/common/x11.nix") + ]; + clan.services.mumble.user = "alice"; + environment.systemPackages = [ pkgs.killall ]; + }; + machines = [ + "peer1" + "peer2" + ]; + in + { + name = "mumble"; + + clan = { + directory = ./.; + inventory = { + machines = lib.genAttrs machines (_: { }); + services = { + mumble.default = { + roles.server.machines = machines; + }; + }; + }; }; - in - { - name = "mumble"; - enableOCR = true; + enableOCR = true; - nodes.peer1 = - { ... }: - { - imports = [ - common - { - environment.etc = { - "mumble-key".source = ./peer_1/peer_1_test_key; - "mumble-cert".source = ./peer_1/peer_1_test_cert; - }; - systemd.tmpfiles.settings."vmsecrets" = { - "/var/lib/murmur/sslKey" = { - C.argument = "${./peer_1/peer_1_test_key}"; - z = { - mode = "0400"; - user = "murmur"; - }; - }; - "/var/lib/murmur/sslCert" = { - C.argument = "${./peer_1/peer_1_test_cert}"; - z = { - mode = "0400"; - user = "murmur"; - }; - }; - }; - clan.core.facts.services.mumble.secret."mumble-key".path = "/etc/mumble-key"; - clan.core.facts.services.mumble.public."mumble-cert".path = "/etc/mumble-cert"; - } - ]; - }; - nodes.peer2 = - { ... }: - { - imports = [ - common - { - environment.etc = { - "mumble-key".source = ./peer_2/peer_2_test_key; - "mumble-cert".source = ./peer_2/peer_2_test_cert; - }; - systemd.tmpfiles.settings."vmsecrets" = { - "/var/lib/murmur/sslKey" = { - C.argument = "${./peer_2/peer_2_test_key}"; - z = { - mode = "0400"; - user = "murmur"; - }; - }; - "/var/lib/murmur/sslCert" = { - C.argument = "${./peer_2/peer_2_test_cert}"; - z = { - mode = "0400"; - user = "murmur"; - }; - }; - }; - } - ]; - }; - testScript = '' - start_all() + nodes.peer1 = common; + nodes.peer2 = common; - with subtest("Waiting for x"): - peer1.wait_for_x() - peer2.wait_for_x() + testScript = '' + start_all() - with subtest("Waiting for murmur"): - peer1.wait_for_unit("murmur.service") - peer2.wait_for_unit("murmur.service") + with subtest("Waiting for x"): + peer1.wait_for_x() + peer2.wait_for_x() - with subtest("Starting Mumble"): - # starting mumble is blocking - peer1.execute("mumble >&2 &") - peer2.execute("mumble >&2 &") + with subtest("Waiting for murmur"): + peer1.wait_for_unit("murmur.service") + peer2.wait_for_unit("murmur.service") - with subtest("Wait for Mumble"): - peer1.wait_for_window(r"^Mumble$") - peer2.wait_for_window(r"^Mumble$") + with subtest("Starting Mumble"): + # starting mumble is blocking + peer1.execute("mumble >&2 &") + peer2.execute("mumble >&2 &") - with subtest("Wait for certificate creation"): - peer1.wait_for_window(r"^Mumble$") - peer1.sleep(3) # mumble is slow to register handlers - peer1.send_chars("\n") - peer1.send_chars("\n") - peer2.wait_for_window(r"^Mumble$") - peer2.sleep(3) # mumble is slow to register handlers - peer2.send_chars("\n") - peer2.send_chars("\n") + with subtest("Wait for Mumble"): + peer1.wait_for_window(r"^Mumble$") + peer2.wait_for_window(r"^Mumble$") - with subtest("Wait for server connect"): - peer1.wait_for_window(r"^Mumble Server Connect$") - peer2.wait_for_window(r"^Mumble Server Connect$") + with subtest("Wait for certificate creation"): + peer1.wait_for_window(r"^Mumble$") + peer1.sleep(3) # mumble is slow to register handlers + peer1.send_chars("\n") + peer1.send_chars("\n") + peer2.wait_for_window(r"^Mumble$") + peer2.sleep(3) # mumble is slow to register handlers + peer2.send_chars("\n") + peer2.send_chars("\n") - with subtest("Check validity of server certificates"): - peer1.execute("killall .mumble-wrapped") - peer1.sleep(1) - peer1.execute("mumble mumble://peer2 >&2 &") - peer1.wait_for_window(r"^Mumble$") - peer1.sleep(3) # mumble is slow to register handlers - peer1.send_chars("\n") - peer1.send_chars("\n") - peer1.wait_for_text("Connected.") + with subtest("Wait for server connect"): + peer1.wait_for_window(r"^Mumble Server Connect$") + peer2.wait_for_window(r"^Mumble Server Connect$") - peer2.execute("killall .mumble-wrapped") - peer2.sleep(1) - peer2.execute("mumble mumble://peer1 >&2 &") - peer2.wait_for_window(r"^Mumble$") - peer2.sleep(3) # mumble is slow to register handlers - peer2.send_chars("\n") - peer2.send_chars("\n") - peer2.wait_for_text("Connected.") - ''; - } -) + with subtest("Check validity of server certificates"): + peer1.execute("killall .mumble-wrapped") + peer1.sleep(1) + peer1.execute("mumble mumble://peer2 >&2 &") + peer1.wait_for_window(r"^Mumble$") + peer1.sleep(3) # mumble is slow to register handlers + peer1.send_chars("\n") + peer1.send_chars("\n") + peer1.wait_for_text("Connected.") + + peer2.execute("killall .mumble-wrapped") + peer2.sleep(1) + peer2.execute("mumble mumble://peer1 >&2 &") + peer2.wait_for_window(r"^Mumble$") + peer2.sleep(3) # mumble is slow to register handlers + peer2.send_chars("\n") + peer2.send_chars("\n") + peer2.wait_for_text("Connected.") + ''; + } + ); +} diff --git a/checks/mumble/sops/machines/peer1/key.json b/checks/mumble/sops/machines/peer1/key.json new file mode 100755 index 000000000..1086b9b3d --- /dev/null +++ b/checks/mumble/sops/machines/peer1/key.json @@ -0,0 +1,6 @@ +[ + { + "publickey": "age1987metkajgdefk0sfhjqjjtczy9eu2lsg700rwcac6hhy2alhdsshjmpw8", + "type": "age" + } +] diff --git a/checks/mumble/sops/machines/peer2/key.json b/checks/mumble/sops/machines/peer2/key.json new file mode 100755 index 000000000..382ae5105 --- /dev/null +++ b/checks/mumble/sops/machines/peer2/key.json @@ -0,0 +1,6 @@ +[ + { + "publickey": "age1fndalxxeduekn5s8q3znl73vjfx2n8kydylyrc2j3aurc93pypvs6pcql4", + "type": "age" + } +] diff --git a/checks/mumble/sops/secrets/peer1-age.key/secret b/checks/mumble/sops/secrets/peer1-age.key/secret new file mode 100644 index 000000000..1547eb61f --- /dev/null +++ b/checks/mumble/sops/secrets/peer1-age.key/secret @@ -0,0 +1,15 @@ +{ + "data": "ENC[AES256_GCM,data:TfEsytctWPCLuo/icbicgRfy7O/txYCllTiLiUlusagGShZyXyIR46TNL9E4XWI2Lce9hIn8zczOdUWaEFPuXcvRMMMWILY3DzI=,iv:zDdq0rdYz/KIwKvIiu9MvKyX9v1pWYxZG3F/7KllBa0=,tag:mTPJGmJ+tKrgYaCZXJ37Nw==,type:str]", + "sops": { + "age": [ + { + "recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2MmFpbUJuNzRnNGRlQXcy\naEhRanpHbjZpbFZxVkZ2TXFJWk8xYm9lYmlVCmVhRFdDZyt4SjJick1CdnZseWx1\nMGdvaTBYekdBeFUyaHEvTzNJVVM4TncKLS0tIG8rZ1kyTFJTRndQNFVXOC9OTTc5\nZHZGVW1FTzlLQ0RRcjNWeEpVWmVKMDgK7UDm509nexdHqG2xU8CBDZkRStjQIAAN\nDmOz5A8uWpIiyvU2LdOBcc/FQKHaXjB7OAmfT03nJccOeqSF2N3N3g==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2025-04-16T16:40:26Z", + "mac": "ENC[AES256_GCM,data:5Qe20lbqERvSM5fDY9Orhrtv2U6zholh6uHMq0CqV1OOg+vVWSlqTqJrtz2rD/qQTUECRKzWUHB1D/kgLrJ33lRoEMqrhjmvBfxtDnNjLzoYITlLcYOm9qiv3gOqcrpdBKW10YyNlGP/+Q377Lfbo8tcZ8nmuaT8qA9PYr+AKcs=,iv:IIJEFAvoX9SY3jvkD0xVe1/L6iRPMyzmxeRmpGvZI0I=,tag:1D3BBUjj1suNeL+mVYDiKw==,type:str]", + "unencrypted_suffix": "_unencrypted", + "version": "3.10.1" + } +} diff --git a/checks/mumble/sops/secrets/peer1-age.key/users/admin b/checks/mumble/sops/secrets/peer1-age.key/users/admin new file mode 120000 index 000000000..9e21a9938 --- /dev/null +++ b/checks/mumble/sops/secrets/peer1-age.key/users/admin @@ -0,0 +1 @@ +../../../users/admin \ No newline at end of file diff --git a/checks/mumble/sops/secrets/peer2-age.key/secret b/checks/mumble/sops/secrets/peer2-age.key/secret new file mode 100644 index 000000000..b85018a69 --- /dev/null +++ b/checks/mumble/sops/secrets/peer2-age.key/secret @@ -0,0 +1,15 @@ +{ + "data": "ENC[AES256_GCM,data:NI9y5OdFkBgHf+wfn+ISDL11nh/ud+1RV5SPC64TV4Hvg0w8GKkmjJI5uiGDGI1+FfWwnHWOFexavtM2ZJr/cWfhA6dGKvzrKJc=,iv:itiZFGsGEZD/SH42akh1CLCDbuZxMSj05quMNKwvKg4=,tag:v36FGDDHIuFaABHG9we6ag==,type:str]", + "sops": { + "age": [ + { + "recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByUVVJek9Ha2ljMkt4U2pi\nSmRRd2g2R0VXZGlySG5TT1E1czFpaWFyNlFjCmRJOThCQWlCNDZnRVRFVHpSTzBW\nOWZCUU5jK2dGQTloOEZMUFFVdk04cXMKLS0tIDVzSTdXRk1UZ3psd29kdnVUcitM\nbFlqb0srUGFCVUhlNzU1dUdTTUkwN0UKAIslz1WCMZWrE+aLPJjeM+wZSXMmwnqx\nyRZT5vVzCPWv2r8sbIjhi1rFbkfF+NXHkzNZD9NS4zddwsDsz5HO1g==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2025-04-16T16:40:48Z", + "mac": "ENC[AES256_GCM,data:2iDDnVdLPWxYcjdZrDlTb8PzPVOPEZ06QXCFvnZ2gf8ioXPiSY69ZAHRHTGpqCEp5Ve7qTIELbNja2TGU0ONLIcIRWyzqgc4q+G3n2V5fYQURW114pzaK0Ct6r6yR9oZQy8H66uEYQafkyuN2R9++3w5G0LGj8UovPcYQqNEQVo=,iv:TkCAdIgjRpZpsnhhvTfMqGVD/IveFyobYa9SExFWcC4=,tag:4RLhumGqeLT15waqHT0mRg==,type:str]", + "unencrypted_suffix": "_unencrypted", + "version": "3.10.1" + } +} diff --git a/checks/mumble/sops/secrets/peer2-age.key/users/admin b/checks/mumble/sops/secrets/peer2-age.key/users/admin new file mode 120000 index 000000000..9e21a9938 --- /dev/null +++ b/checks/mumble/sops/secrets/peer2-age.key/users/admin @@ -0,0 +1 @@ +../../../users/admin \ No newline at end of file diff --git a/checks/mumble/sops/users/admin/key.json b/checks/mumble/sops/users/admin/key.json new file mode 100644 index 000000000..e408aa96b --- /dev/null +++ b/checks/mumble/sops/users/admin/key.json @@ -0,0 +1,4 @@ +{ + "publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", + "type": "age" +} diff --git a/checks/mumble/vars/per-machine/peer1/mumble/mumble-cert/value b/checks/mumble/vars/per-machine/peer1/mumble/mumble-cert/value new file mode 100644 index 000000000..769d5323e --- /dev/null +++ b/checks/mumble/vars/per-machine/peer1/mumble/mumble-cert/value @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDfzCCAmegAwIBAgIUH9AKYdV75FHHBcR4mgfTZB/7eEcwDQYJKoZIhvcNAQEL +BQAwaDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBENsYW4xDTALBgNVBAsMBENsYW4xDjAM +BgNVBAMMBXBlZXIxMB4XDTI1MDQxNjE2NDAzN1oXDTI1MDUxNjE2NDAzN1owaDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBG +cmFuY2lzY28xDTALBgNVBAoMBENsYW4xDTALBgNVBAsMBENsYW4xDjAMBgNVBAMM +BXBlZXIxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA80mo3OFSaW8F +Ni/W7WZ70bJoGGFPFK17kiRgPu6+ghDiinmzlAQOt8A/u+egl4FsvT9Oz99TjCN1 +zkK3I74ItKmumpGKGPp92bpm62vQZa4g861xKqLlcbOwJwcfofwa8r4PhhjDhdXS +k9vsgiwy0N5FEga79QbDEO/qwSvY+O8yKNG+lNXeOetymKvVbudL8A0je150vmpg +oYfYjH57Oa7DpGaIrOpbZsmaBlYHD5dhfJbuX0Gxuq42gkfcBtxv3NbY0NoPVZFV +jOvhVPyV9Xme/3JAQUSti+Fd2ZfJ+Ayl90ElA5wk25T1JBEEnMYQlQVBqPawX87C +i1EtOysfxQIDAQABoyEwHzAdBgNVHQ4EFgQUFtjyWNCF1Yxd8ymIZ4kE9fXMY5Yw +DQYJKoZIhvcNAQELBQADggEBAAHiQcWDvZjN2VTaWY2cQMYy3m8wkdoJTR20uV2z +MpjY4KwCiMzTtsFe2LhiYMYFETwqHpG+B6ElOghh/+F8l96vQRbcVI9I3XTKs0G4 ++zdUtMOyB2XZumB4HBQa3PiXXrA4kAGJV88y5QC4UkZMw6SfwjW8OrtQ5Jim4vUB +PZxY75ZIjw4JhknTqKNua7xehY4TBghRrGZAlD4eon7Yc5bIew6Gw5LHIoszOZgk +9CFEo1XLN5z8aL9L+V8dh2DNNqF4KiXCRNgwqLmLoepL2Xptd90AOZsBI9mGxMP9 +YUPsnzcGqcat1x6Fi2Guw++ESDxUp6qKjMGAxPzSXje/TiM= +-----END CERTIFICATE----- diff --git a/checks/mumble/vars/per-machine/peer1/mumble/mumble-key/machines/peer1 b/checks/mumble/vars/per-machine/peer1/mumble/mumble-key/machines/peer1 new file mode 120000 index 000000000..3e5f3fae3 --- /dev/null +++ b/checks/mumble/vars/per-machine/peer1/mumble/mumble-key/machines/peer1 @@ -0,0 +1 @@ +../../../../../../sops/machines/peer1 \ No newline at end of file diff --git a/checks/mumble/vars/per-machine/peer1/mumble/mumble-key/secret b/checks/mumble/vars/per-machine/peer1/mumble/mumble-key/secret new file mode 100644 index 000000000..bdac1c60e --- /dev/null +++ b/checks/mumble/vars/per-machine/peer1/mumble/mumble-key/secret @@ -0,0 +1,19 @@ +{ + "data": "ENC[AES256_GCM,data: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,iv:IZYhje9AgGRe0gQcodG/PQAaRBipBC/7F8qAkG35cxc=,tag:jpXpm1eghy/668gT0bmqMA==,type:str]", + "sops": { + "age": [ + { + "recipient": "age1987metkajgdefk0sfhjqjjtczy9eu2lsg700rwcac6hhy2alhdsshjmpw8", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3MDdhSTZMbXlSdDVNVVZU\ndkFyVVI0eDhOUHZRU2FFalVNR3g5dUY5T25FCnl0aXpZRVpaR1hvdm5kSHplOE0x\nckloNFF3OVhNTnAxY2ZpZjNFV3plVXMKLS0tIG4yU0w2c1VGbDVCTUhYbjVrMXhr\nb0dpUnp2YUFWSERSRTVVK3g0WTNKWE0KpUfYS71F/1J1G38/ymd/+bWhABmze1GC\nehgSMymmVdsq+ZjHdJ1XcCyecsn/9aFcaZkEbASiLU8ecLNQOEGgRQ==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWeXBUOU13M2VvZVNBNUZW\nMy9VV1dMV1FlQU9qekhZWitwb3JISTFwdENBCnB5ZHpNK29DRHBoZ2M4dEJ6UVpq\nWHFOM1lYS0ROQ2NpSTNUdkZqUkorWGsKLS0tIDhaalVJNE1oU0N3WUtodnlsQWla\nUTVmTnhPTHVCWXUyK1ZESGR1Ym5CMXcK3YqyKO/FTdxcxVy5zBGg+JCOWMBOxqd2\n9+FgUJaYaizGy+HLpP5jgtjgz7k504yqEQCo9aQ1CzbvNHom5tAu7A==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2025-04-16T16:40:41Z", + "mac": "ENC[AES256_GCM,data:R8fWg7Vwq2mnjbTTtyYuLWwrmB6TZYZVx9xPcO5NOvGAABNIxtAVSe9yTpV25OlJiXruTNhPHDxfjwDW8Nad47Sd9fV9QzH36uygT9DOaVrrOD/TH5ojvpCuognofuJ8YHgUsq+yhiQs0QKi5efUrtRVDcXXr8s/UazyuG3vYzk=,iv:eBpSr8GKvG51govZWtqTVMWsWZDctDQ2vVgMm/jq62U=,tag:Yth78awXPAPa/7J+WxTDug==,type:str]", + "unencrypted_suffix": "_unencrypted", + "version": "3.10.1" + } +} diff --git a/checks/mumble/vars/per-machine/peer1/mumble/mumble-key/users/admin b/checks/mumble/vars/per-machine/peer1/mumble/mumble-key/users/admin new file mode 120000 index 000000000..ca714e122 --- /dev/null +++ b/checks/mumble/vars/per-machine/peer1/mumble/mumble-key/users/admin @@ -0,0 +1 @@ +../../../../../../sops/users/admin \ No newline at end of file diff --git a/checks/mumble/vars/per-machine/peer2/mumble/mumble-cert/value b/checks/mumble/vars/per-machine/peer2/mumble/mumble-cert/value new file mode 100644 index 000000000..9bcd384a2 --- /dev/null +++ b/checks/mumble/vars/per-machine/peer2/mumble/mumble-cert/value @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDfzCCAmegAwIBAgIUYuUk46fwZ4CBcJ40NWnT9VDIEPUwDQYJKoZIhvcNAQEL +BQAwaDELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xDTALBgNVBAoMBENsYW4xDTALBgNVBAsMBENsYW4xDjAM +BgNVBAMMBXBlZXIyMB4XDTI1MDQxNjE2NDA1OVoXDTI1MDUxNjE2NDA1OVowaDEL +MAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBG +cmFuY2lzY28xDTALBgNVBAoMBENsYW4xDTALBgNVBAsMBENsYW4xDjAMBgNVBAMM +BXBlZXIyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA45nKnn0r3HwU +qqSRuOXbou8zpdf+5i+e1h7pmunXR7WPxPBP09t6i+99BO27GcID59zGMquabpNS +dFhj+p+KZkqN+4sokZmyBU1civQqiwX2n5KtoaG0fU3gFFK6pfx3OQawQ6mJ50GU +HhA2R3CuA0rXcssr6oPynj9z6pbaL7mKckOWE804xIWZuMEoWNdQEKmUmE5d1ioa +edlblzwhqZSS+zAAeUvmb+YUEL6T54lCYYqPPnmwmiwfYFSBGu/SGyFtIijbCuIZ +TJMDzzutx1/3Dsv2pOKC0uPb5qRcmdRePAzgBFSna4MNgfbpGHFkGPJgjiue0VIC +qyedlpF5UQIDAQABoyEwHzAdBgNVHQ4EFgQUuIeLdxGVyhFbgFRtFbPIIJWw1R0w +DQYJKoZIhvcNAQELBQADggEBAFj26XejazrXOfa67o8vGoZrR2TGXOLFWFeplO8B +29AruG9poH+sInyxYo1RWAQLQMfDud/yGg73EeYylULbG1bBznKYLLHdvy4l6eXt +SEVkEMruH0Kw93zt+NqvSO3bHCX+la1rjizyDcD4iu93xUg2uPSBmVpVpW/aeBCN +3eF4FbBocUexmIWaygmMPY5yFY2tAf+OinBf4uSWcKEpFikIqAxQWRSDMWm8xFwY +CG7rhfpwDauagpZtkjKkrrRedhdfGiXbxOVtYlBULuUMOggEI+ElpbD0UhyEYCsD +XoJn7AOC0sYCGpj2F1ESwFX/5EhyciLjMuVwohFVcyWWg+Q= +-----END CERTIFICATE----- diff --git a/checks/mumble/vars/per-machine/peer2/mumble/mumble-key/machines/peer2 b/checks/mumble/vars/per-machine/peer2/mumble/mumble-key/machines/peer2 new file mode 120000 index 000000000..6370c90d4 --- /dev/null +++ b/checks/mumble/vars/per-machine/peer2/mumble/mumble-key/machines/peer2 @@ -0,0 +1 @@ +../../../../../../sops/machines/peer2 \ No newline at end of file diff --git a/checks/mumble/vars/per-machine/peer2/mumble/mumble-key/secret b/checks/mumble/vars/per-machine/peer2/mumble/mumble-key/secret new file mode 100644 index 000000000..5b38ecb77 --- /dev/null +++ b/checks/mumble/vars/per-machine/peer2/mumble/mumble-key/secret @@ -0,0 +1,19 @@ +{ + "data": "ENC[AES256_GCM,data:LX0ZXli/xtlyI8JLDyQz2p69eKz3gMQLn/PwJh4GB1tgQ75Ei+zQyetnCKkzrm+xGDZjLgm3QXft8VyNqhxf/7a1konP9IHfZH1j1wn/1ELFyTR2IKQzIdsiilP67+nZeLFIS4wBfEMS4hGoOIVXIqohDYkryDkvoYqHOw2U5HVDf3GlqeaEfKM8zJMZgGUYAoLCJEEWmBSSYrT/jH5hVxVgd1qE+5JmhV1Uah7TWW2HX+XDpU4aeV3zQWyWZNTHDXtgnEUBb4nFsvWGrGUrB9A8FEoZFOC1jJj8NLU/aOZ8EvDkI0Us6nw8leQwwox9O9PY8MDAAlPVsTe5+vcP/svpu+P3Gor/MrJzsk1IKalIdUiSYFego3FyyQonfXdQs8oO+ufF4nkMo+BRPvXwPUGwHjyCVaL5nYtgnV4VCSBoGY612Tmc86ihJW4mUzA5ghjUTWwduDSfoWI5H0JO5TabJnMPkPcIBSms46lhCCNMSY8WvtqcGz1mUgLTjQ+fhHt4Ci/K+VCiQNjjFH5tEq6P4bhYnG1+U5rAFRIyXg+m2Z/JONKkSiVVs/0u6yzKFAji6/osqhLkFZCqpCuk2OhVtsn2Bg1ko7WjuQAZHEgh6WSmsK6nyDfGpLSdfBBRscevPA3QsE3tTwO8/i5pxaGIm7BxH6OJcbv3x/r7+8TX52orgtSO/ODiOn+ylRDUwbnTVqB8pkM6+moLDRmpmCK87a5CETfPJ/7u7bEAmcbAH5LsmBL5T8tqLpGOnCkj4ZozZ6sv8qAFxR3vmmWvpkCtJYKml9Hsqww0Laytgj76TO8xMuQSwRPgbkXRr6QaF+o1EwEW6fArb/wtsUUJSDBdv2K6UpyPwITSEQk2z0o3Cr6Y9luYlGXKmpegQWcjcBxUVWd06V21IFHTT/WM4joEBsliVAAlJBcyjYj3Sq9onxiXOHVgmNFzQpxTlSoaqVCuVLegHB8E5ipyrDtaw4gl6l3pNKdNCyILJtk226rOZpZZ/wBMs2FWUyosrlBe46oa7XynkCzPbItivxpZwqN5nPjOFQi/QXzN9i2iDCXZvoNnFKG0B/fSKb3Dho5cpqhhxUIu1AttcTj9YNx6lsw5ZGkdaLyomqIB4rRFVj9U98cY/lEP4Hgn2kl71bhloIu1R/1qxZXuk7KAC7QJ5Nk773Pb7oHVfWwaQxFmcTR2IEQm5SuXvyxWUVWpH9kuTgwhXPypJRuob3IJX2Nrm4kRKdL8U5HH/UWYhXTFcOMrbqY0b/iFAhxu/qQYvCVOSZrV43elCI70e0PGaIM9c+ifL7EZw+uHlJP6lWgtqqmbjR+K+7ZmXjVAq6KngRGetIlkjC60aMiFmqTZ1f1RADLQyEUHKbLT6YqePU698vHM+zTr+38HLOMxz80/EpC0L0qvdsMY9DXUIHow2/sffGFbWto5Jh2KcWQoSB2dMeQCOeZT45qaCwwE9ZG5sZJTWJvWgjsTegHDvlnv7LzlFOYyfWSr708v+twz/eI0Z1PmCZq8f6iN0T0fA7ncbPjp22ebg+YztDPEO1F+ThtvOrvz4ptVUr5Riywwf1aL6qGYUGZ/epOfaHosnf29l71e3xQD3Ry9DB7DEJXNGmrmF7AF1aEJXsvFSnqNa0A3pkP56cK+SfhEQbaFA7pJWqSnNqRuZR+60ItKQ/plimZRhPobKYb8bjJ0CyFtAf6fez1Q8XC/YohMNi3vZ1sdkmQ+O0CCV72sS7jRpSJOc6zG2KB4zL9JllxThNAXBvB+4rHoowG5ltCw2xm/eTQSdUivCkT67EocjS8RjucLfYvc2aH9+tLaIkwFUYsNKkd+JpNlmD6E6fp229YJ3908JrksYCRc//MQhaDMaig1Bc7JPTGqHlHZFtHhbrGIHPoRMLL6djRKXlPndQ/ZGKTca72oBNt4KXO2MQhTYtP08Zgt8E2lpjUhbWnNsyikuVju7UWi0ynT2RPNaRgffoXZtnDVsCYv1yhYnrfoqNOGZpR4GE/rwyQMw3/osNI91l7vKSGIhAQAPnTT77A5xWiVMb0VL4WzokZaWFWL9BGkSIMcbJNkZB1udwrNeqwU7N8WHwQKGuBSg1YOd9wE0A8GmNNuBo0Qjo4QCGs0Q/GAvrczppinbNkABSgW570/i+Ep7UOJ83Zcv7XhxoHdlsdmpweGIpOdjC4WMfYlNRwVJ2eg7qhvuFOzuvSZBMCmPpC/gkjYgD9FjsH1rzgxbcOvkkUzxHQImiYHxYd6h7ZqqGD7XINy1oSPJEaK,iv:zNaVGK5hNxziOoPTbwaUhUwBuFbCiGNrfVMpeMxL3JI=,tag:6v8Hf4Symd1T16MOEChtcA==,type:str]", + "sops": { + "age": [ + { + "recipient": "age1fndalxxeduekn5s8q3znl73vjfx2n8kydylyrc2j3aurc93pypvs6pcql4", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1VGVjRkdJOGx3c05YM28y\nM3dCbkU4TXBHK1VVOFFkY3FQVk0rQVp0d1g0CnZPR3FtUGlCb2lKSVc1Z3VtM0JM\nV1ZtZ3NVVndvak43cStIRWZxWldKSncKLS0tIEdJVHFFTzdaNklLVHdURndGa3Qy\nc2lEZ1hER3dGL0FKNUZrSkxMOXMvOGsKHGJ44Ey6mR3rV6NPPmn/QTsyjL08wCzu\nkUdD0jgSMLwInX5R9Gh9+Zbc9NIfEgSzLr6up6UlgW/4iWvM4oFPRg==\n-----END AGE ENCRYPTED FILE-----\n" + }, + { + "recipient": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg", + "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjcHVweTFZenhZZzVDZ2ts\nTnNxNkZLWnVQRmpoa0ZldHpxdWt0Sy9jRVFFClExS2FMM3hiSlRQR2lmb25RTEo0\nRTRGdmxCaXJoeXdNaVU3cGRIRFlibWsKLS0tIFFzVFhCR2hSOStYNk5yNmc5UkZl\nTHdWSUZTZUIyUEp2OFR0SFpzMzFFd0EKlsRWNJjapPefXxyuUtFWlPs/UIC9V1N7\nF7Ek+TAKl11SwGGA2qla1yvnDOxkZvFg7gWsurZeEBH4PuPZ1OE/Yg==\n-----END AGE ENCRYPTED FILE-----\n" + } + ], + "lastmodified": "2025-04-16T16:41:03Z", + "mac": "ENC[AES256_GCM,data:1DcuXden9WAF3frVjOMgpt0nniqiGEAA4SubPLk86GODEaOXxZSVStX1rr0GCF0t0tR4O4jl4cnRvZHF9Zjj7smA5Wf8jPpbSCrZX4oBo/HP3UU+A78yxSrj4gmoeH4m/aaJv0co77Vwcm/HglE6Q89Oc9BUqE2e4FGVmDUZTws=,iv:OAa2hvuw6aUcp3qKkRpDeLMDcq9Kkn/Bc+86DzV5h5g=,tag:wVrs9oyfaCAv3gZxsxbMPg==,type:str]", + "unencrypted_suffix": "_unencrypted", + "version": "3.10.1" + } +} diff --git a/checks/mumble/vars/per-machine/peer2/mumble/mumble-key/users/admin b/checks/mumble/vars/per-machine/peer2/mumble/mumble-key/users/admin new file mode 120000 index 000000000..ca714e122 --- /dev/null +++ b/checks/mumble/vars/per-machine/peer2/mumble/mumble-key/users/admin @@ -0,0 +1 @@ +../../../../../../sops/users/admin \ No newline at end of file diff --git a/clanModules/moonlight/README.md b/clanModules/moonlight/README.md index f9c291a7f..7e96ad597 100644 --- a/clanModules/moonlight/README.md +++ b/clanModules/moonlight/README.md @@ -1,3 +1,5 @@ --- description = "A desktop streaming client optimized for remote gaming and synchronized movie viewing." --- + +**Warning**: This module was written with our VM integration in mind likely won't work outside of this context. They will be generalized in future. diff --git a/clanModules/moonlight/default.nix b/clanModules/moonlight/default.nix index 20e3b75fe..c7aac5b8f 100644 --- a/clanModules/moonlight/default.nix +++ b/clanModules/moonlight/default.nix @@ -1,6 +1,6 @@ { pkgs, config, ... }: let - ms-accept = pkgs.callPackage ../pkgs/moonlight-sunshine-accept { }; + ms-accept = pkgs.callPackage ../../pkgs/moonlight-sunshine-accept { }; defaultPort = 48011; in { @@ -13,10 +13,10 @@ in systemd.tmpfiles.rules = [ "d '/var/lib/moonlight' 0770 'user' 'users' - -" "C '/var/lib/moonlight/moonlight.cert' 0644 'user' 'users' - ${ - config.clan.core.facts.services.moonlight.secret."moonlight.cert".path or "" + config.clan.core.vars.generators.moonlight.files."moonlight.cert".path or "" }" "C '/var/lib/moonlight/moonlight.key' 0644 'user' 'users' - ${ - config.clan.core.facts.services.moonlight.secret."moonlight.key".path or "" + config.clan.core.vars.generators.moonlight.files."moonlight.key".path or "" }" ]; @@ -45,7 +45,7 @@ in systemd.user.services.moonlight-join = { description = "Join sunshine hosts"; script = ''${ms-accept}/bin/moonlight-sunshine-accept moonlight join --port ${builtins.toString defaultPort} --cert '${ - config.clan.core.facts.services.moonlight.public."moonlight.cert".value or "" + config.clan.core.vars.generators.moonlight.files."moonlight.cert".value or "" }' --host fd2e:25da:6035:c98f:cd99:93e0:b9b8:9ca1''; serviceConfig = { Type = "oneshot"; @@ -68,19 +68,20 @@ in }; }; - clan.core.facts.services.moonlight = { - secret."moonlight.key" = { }; - secret."moonlight.cert" = { }; - public."moonlight.cert" = { }; - generator.path = [ + clan.core.vars.generators.moonlight = { + migrateFact = "moonlight"; + files."moonlight.key" = { }; + files."moonlight.cert" = { }; + files."moonlight.cert".secret = false; + runtimeInputs = [ pkgs.coreutils ms-accept ]; - generator.script = '' + script = '' moonlight-sunshine-accept moonlight init - mv credentials/cakey.pem "$secrets"/moonlight.key - cp credentials/cacert.pem "$secrets"/moonlight.cert - mv credentials/cacert.pem "$facts"/moonlight.cert + mv credentials/cakey.pem "$out"/moonlight.key + cp credentials/cacert.pem "$out"/moonlight.cert + mv credentials/cacert.pem "$out"/moonlight.cert ''; }; } diff --git a/clanModules/mumble/README.md b/clanModules/mumble/README.md index 552a9e2d1..a33b99714 100644 --- a/clanModules/mumble/README.md +++ b/clanModules/mumble/README.md @@ -1,7 +1,12 @@ --- description = "Open Source, Low Latency, High Quality Voice Chat." categories = ["Audio", "Social"] +features = [ "inventory" ] + +[constraints] +roles.server.min = 1 --- + The mumble clan module gives you: - True low latency voice communication. diff --git a/clanModules/mumble/default.nix b/clanModules/mumble/default.nix index ba3302219..ab24e1e49 100644 --- a/clanModules/mumble/default.nix +++ b/clanModules/mumble/default.nix @@ -1,122 +1,6 @@ +# Dont import this file +# It is only here for backwards compatibility. +# Dont author new modules with this file. { - lib, - config, - pkgs, - ... -}: -let - dir = config.clan.core.settings.directory; - machineDir = dir + "/machines/"; - machinesFileSet = builtins.readDir machineDir; - machines = lib.mapAttrsToList (name: _: name) machinesFileSet; - machineJson = builtins.toJSON machines; - certificateMachinePath = machines: machineDir + "/${machines}" + "/facts/mumble-cert"; - certificatesUnchecked = builtins.map ( - machine: - let - fullPath = certificateMachinePath machine; - in - if builtins.pathExists fullPath then machine else null - ) machines; - certificate = lib.filter (machine: machine != null) certificatesUnchecked; - machineCert = builtins.map ( - machine: (lib.nameValuePair machine (builtins.readFile (certificateMachinePath machine))) - ) certificate; - machineCertJson = builtins.toJSON machineCert; - -in -{ - options.clan.services.mumble = { - user = lib.mkOption { - type = lib.types.nullOr lib.types.str; - default = null; - example = "alice"; - description = "The user mumble should be set up for."; - }; - }; - - config = { - services.murmur = { - enable = true; - logDays = -1; - registerName = config.clan.core.settings.machine.name; - openFirewall = true; - bonjour = true; - sslKey = "/var/lib/murmur/sslKey"; - sslCert = "/var/lib/murmur/sslCert"; - }; - - clan.core.state.mumble.folders = [ - "/var/lib/mumble" - "/var/lib/murmur" - ]; - - systemd.tmpfiles.rules = [ - "d '/var/lib/mumble' 0770 '${config.clan.services.mumble.user}' 'users' - -" - ]; - - systemd.tmpfiles.settings."murmur" = { - "/var/lib/murmur/sslKey" = { - C.argument = config.clan.core.facts.services.mumble.secret.mumble-key.path; - Z = { - mode = "0400"; - user = "murmur"; - }; - }; - "/var/lib/murmur/sslCert" = { - C.argument = config.clan.core.facts.services.mumble.public.mumble-cert.path; - Z = { - mode = "0400"; - user = "murmur"; - }; - }; - }; - - environment.systemPackages = - let - mumbleCfgDir = "/var/lib/mumble"; - mumbleDatabasePath = "${mumbleCfgDir}/mumble.sqlite"; - mumbleCfgPath = "/var/lib/mumble/mumble_settings.json"; - populate-channels = pkgs.writers.writePython3 "mumble-populate-channels" { - libraries = [ - pkgs.python3Packages.cryptography - pkgs.python3Packages.pyopenssl - ]; - flakeIgnore = [ - # We don't live in the dark ages anymore. - # Languages like Python that are whitespace heavy will overrun - # 79 characters.. - "E501" - ]; - } (builtins.readFile ./mumble-populate-channels.py); - mumble = pkgs.writeShellScriptBin "mumble" '' - set -xeu - mkdir -p ${mumbleCfgDir} - pushd "${mumbleCfgDir}" - XDG_DATA_HOME=${mumbleCfgDir} - XDG_DATA_DIR=${mumbleCfgDir} - ${populate-channels} --ensure-config '${mumbleCfgPath}' --db-location ${mumbleDatabasePath} - echo ${machineCertJson} - ${populate-channels} --machines '${machineJson}' --username ${config.clan.core.settings.machine.name} --db-location ${mumbleDatabasePath} - ${populate-channels} --servers '${machineCertJson}' --username ${config.clan.core.settings.machine.name} --db-location ${mumbleDatabasePath} --cert True - ${pkgs.mumble}/bin/mumble --config ${mumbleCfgPath} "$@" - popd - ''; - in - [ mumble ]; - - clan.core.facts.services.mumble = { - secret.mumble-key = { }; - public.mumble-cert = { }; - generator.path = [ - pkgs.coreutils - pkgs.openssl - ]; - generator.script = '' - openssl genrsa -out $secrets/mumble-key 2048 - openssl req -new -x509 -key $secrets/mumble-key -out $facts/mumble-cert - ''; - }; - }; - + imports = [ ./roles/server.nix ]; } diff --git a/clanModules/mumble/mumble-populate-channels.py b/clanModules/mumble/roles/mumble-populate-channels.py similarity index 100% rename from clanModules/mumble/mumble-populate-channels.py rename to clanModules/mumble/roles/mumble-populate-channels.py diff --git a/clanModules/mumble/roles/server.nix b/clanModules/mumble/roles/server.nix new file mode 100644 index 000000000..4bd36a6fc --- /dev/null +++ b/clanModules/mumble/roles/server.nix @@ -0,0 +1,143 @@ +{ + lib, + config, + pkgs, + ... +}: +let + dir = config.clan.core.settings.directory; + # TODO: this should actually use the inventory to figure out which machines to use. + machineDir = dir + "/vars/per-machine"; + machinesFileSet = builtins.readDir machineDir; + machines = lib.mapAttrsToList (name: _: name) machinesFileSet; + machineJson = builtins.toJSON machines; + certificateMachinePath = machines: machineDir + "/${machines}" + "/mumble/mumble-cert/value"; + certificatesUnchecked = builtins.map ( + machine: + let + fullPath = certificateMachinePath machine; + in + if builtins.pathExists fullPath then machine else null + ) machines; + certificate = lib.filter (machine: machine != null) certificatesUnchecked; + machineCert = builtins.map ( + machine: (lib.nameValuePair machine (builtins.readFile (certificateMachinePath machine))) + ) certificate; + machineCertJson = builtins.toJSON machineCert; + +in +{ + options.clan.services.mumble = { + user = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + example = "alice"; + description = "The user mumble should be set up for."; + }; + }; + + config = { + services.murmur = { + enable = true; + logDays = -1; + registerName = config.clan.core.settings.machine.name; + openFirewall = true; + bonjour = true; + sslKey = "/var/lib/murmur/sslKey"; + sslCert = "/var/lib/murmur/sslCert"; + }; + + clan.core.state.mumble.folders = [ + "/var/lib/mumble" + "/var/lib/murmur" + ]; + + systemd.tmpfiles.rules = [ + "d '/var/lib/mumble' 0770 '${config.clan.services.mumble.user}' 'users' - -" + ]; + + systemd.tmpfiles.settings."murmur" = { + "/var/lib/murmur/sslKey" = { + C.argument = config.clan.core.vars.generators.mumble.files.mumble-key.path; + Z = { + mode = "0400"; + user = "murmur"; + }; + }; + "/var/lib/murmur/sslCert" = { + C.argument = config.clan.core.vars.generators.mumble.files.mumble-cert.path; + Z = { + mode = "0400"; + user = "murmur"; + }; + }; + }; + + environment.systemPackages = + let + mumbleCfgDir = "/var/lib/mumble"; + mumbleDatabasePath = "${mumbleCfgDir}/mumble.sqlite"; + mumbleCfgPath = "/var/lib/mumble/mumble_settings.json"; + populate-channels = pkgs.writers.writePython3 "mumble-populate-channels" { + libraries = [ + pkgs.python3Packages.cryptography + pkgs.python3Packages.pyopenssl + ]; + flakeIgnore = [ + # We don't live in the dark ages anymore. + # Languages like Python that are whitespace heavy will overrun + # 79 characters.. + "E501" + ]; + } (builtins.readFile ./mumble-populate-channels.py); + mumble = pkgs.writeShellScriptBin "mumble" '' + set -xeu + mkdir -p ${mumbleCfgDir} + pushd "${mumbleCfgDir}" + XDG_DATA_HOME=${mumbleCfgDir} + XDG_DATA_DIR=${mumbleCfgDir} + ${populate-channels} --ensure-config '${mumbleCfgPath}' --db-location ${mumbleDatabasePath} + ${populate-channels} --machines '${machineJson}' --username ${config.clan.core.settings.machine.name} --db-location ${mumbleDatabasePath} + ${populate-channels} --servers '${machineCertJson}' --username ${config.clan.core.settings.machine.name} --db-location ${mumbleDatabasePath} --cert True + ${pkgs.mumble}/bin/mumble --config ${mumbleCfgPath} "$@" + popd + ''; + in + [ mumble ]; + + clan.core.vars.generators.mumble = { + migrateFact = "mumble"; + files.mumble-key = { }; + files.mumble-cert.secret = false; + runtimeInputs = [ + pkgs.coreutils + pkgs.openssl + ]; + script = '' + openssl genrsa -out "$out/mumble-key" 2048 + + cat > mumble-cert.conf < HardwareCon if host.user != "root": config_command.insert(0, "sudo") - deps = ["nixpkgs#openssh"] + deps = ["openssh"] if opts.password: - deps += ["nixpkgs#sshpass"] + deps += ["sshpass"] - cmd = nix_shell( + cmd = run_cmd( deps, [ *(["sshpass", "-p", opts.password] if opts.password else []), diff --git a/pkgs/clan-cli/clan_cli/nix/__init__.py b/pkgs/clan-cli/clan_cli/nix/__init__.py index 4678b1f8b..2bf57c88e 100644 --- a/pkgs/clan-cli/clan_cli/nix/__init__.py +++ b/pkgs/clan-cli/clan_cli/nix/__init__.py @@ -105,6 +105,7 @@ def nix_metadata(flake_url: str | Path) -> dict[str, Any]: return data +# Deprecated: use run_cmd() instead def nix_shell(packages: list[str], cmd: list[str]) -> list[str]: # we cannot use nix-shell inside the nix sandbox # in our tests we just make sure we have all the packages @@ -124,15 +125,20 @@ def nix_shell(packages: list[str], cmd: list[str]) -> list[str]: # lazy loads list of allowed and static programs class Programs: - allowed_programs: list[str] | None = None - static_programs: list[str] | None = None + allowed_programs: set[str] | None = None + static_programs: set[str] | None = None @classmethod - def is_allowed(cls: type["Programs"], program: str) -> bool: + def ensure_allowed(cls: type["Programs"], program: str) -> None: if cls.allowed_programs is None: with (Path(__file__).parent / "allowed-programs.json").open() as f: - cls.allowed_programs = json.load(f) - return program in cls.allowed_programs + cls.allowed_programs = allowed_programs = set(json.load(f)) + else: + allowed_programs = cls.allowed_programs + + if program not in allowed_programs: + msg = f"Program not allowed: '{program}', allowed programs are:\n{'\n'.join(allowed_programs)}" + raise ClanError(msg) @classmethod def is_static(cls: type["Programs"], program: str) -> bool: @@ -140,7 +146,9 @@ class Programs: Determines if a program is statically shipped with this clan distribution """ if cls.static_programs is None: - cls.static_programs = os.environ.get("CLAN_STATIC_PROGRAMS", "").split(":") + cls.static_programs = set( + os.environ.get("CLAN_STATIC_PROGRAMS", "").split(":") + ) return program in cls.static_programs @@ -151,9 +159,7 @@ class Programs: # - build clan distributions that ship some or all packages (eg. clan-cli-full) def run_cmd(programs: list[str], cmd: list[str]) -> list[str]: for program in programs: - if not Programs.is_allowed(program): - msg = f"Program not allowed: {program}" - raise ClanError(msg) + Programs.ensure_allowed(program) if os.environ.get("IN_NIX_SANDBOX"): return cmd missing_packages = [ diff --git a/pkgs/clan-cli/clan_cli/nix/allowed-programs.json b/pkgs/clan-cli/clan_cli/nix/allowed-programs.json index 441e9e3f5..b03599487 100644 --- a/pkgs/clan-cli/clan_cli/nix/allowed-programs.json +++ b/pkgs/clan-cli/clan_cli/nix/allowed-programs.json @@ -1,22 +1,23 @@ [ "age", + "avahi", "bash", "bubblewrap", "e2fsprogs", "git", + "gnupg", "mypy", + "netcat", "nix", "openssh", + "pass", "qemu", "rsync", - "pass", + "shellcheck-minimal", "sops", "sshpass", "tor", - "virtiofsd", - "zbar", - "shellcheck-minimal", "util-linux", - "avahi", - "gnupg" + "virtiofsd", + "zbar" ] diff --git a/pkgs/clan-cli/clan_cli/ssh/host.py b/pkgs/clan-cli/clan_cli/ssh/host.py index 72eb69ed8..0cebdd101 100644 --- a/pkgs/clan-cli/clan_cli/ssh/host.py +++ b/pkgs/clan-cli/clan_cli/ssh/host.py @@ -12,7 +12,7 @@ from typing import Any from clan_cli.cmd import CmdOut, RunOpts, run from clan_cli.colors import AnsiColor from clan_cli.errors import ClanError -from clan_cli.nix import nix_shell +from clan_cli.nix import run_cmd from clan_cli.ssh.host_key import HostKeyCheck cmdlog = logging.getLogger(__name__) @@ -186,7 +186,7 @@ class Host: packages = [] password_args = [] if password: - packages.append("nixpkgs#sshpass") + packages.append("sshpass") password_args = [ "sshpass", "-p", @@ -205,7 +205,7 @@ class Host: ssh_opts.extend(["-i", self.key]) if tor_socks: - packages.append("nixpkgs#netcat") + packages.append("netcat") ssh_opts.append("-o") ssh_opts.append("ProxyCommand=nc -x 127.0.0.1:9050 -X 5 %h %p") @@ -216,7 +216,7 @@ class Host: *ssh_opts, ] - return nix_shell(packages, cmd) + return run_cmd(packages, cmd) def connect_ssh_shell( self, *, password: str | None = None, tor_socks: bool = False diff --git a/pkgs/scripts/update-vars.py b/pkgs/scripts/update-vars.py index 4a5a11a76..f7bfd298c 100755 --- a/pkgs/scripts/update-vars.py +++ b/pkgs/scripts/update-vars.py @@ -8,6 +8,7 @@ from pathlib import Path from tempfile import NamedTemporaryFile from typing import Any +from clan_cli.dirs import find_git_repo_root from clan_cli.flake import Flake from clan_cli.machines.machines import Machine from clan_cli.nix import nix_build, nix_config, nix_eval @@ -129,7 +130,7 @@ def parse_args() -> argparse.Namespace: i.e. 'nix eval #checks ...' """, required=False, - default=os.environ.get("PRJ_ROOT"), + default=os.environ.get("PRJ_ROOT", find_git_repo_root()), ) parser.add_argument( "test_dir", @@ -178,6 +179,5 @@ if __name__ == "__main__": f.write(f"# public key: {sops_pub_key}\n") f.write(sops_priv_key) f.seek(0) - print(Path(f.name).read_text()) os.environ["SOPS_AGE_KEY_FILE"] = f.name generate_vars(list(machines))