ALPHA: wire is a tool to deploy nixos systems wire.althaea.zone/
at stable 123 lines 4.6 kB view raw
1# SPDX-License-Identifier: AGPL-3.0-or-later 2# Copyright 2024-2025 wire Contributors 3 4from typing import TYPE_CHECKING 5 6if TYPE_CHECKING: 7 from test_driver.machine import Machine 8 from tools import collect_store_objects, assert_store_not_poisoned 9 10 deployer: Machine = None # type: ignore[invalid-assignment] 11 receiver: Machine = None # type: ignore[invalid-assignment] 12 TEST_DIR = "" 13 14# typing-end 15 16deployer_so = collect_store_objects(deployer) 17receiver_so = collect_store_objects(receiver) 18 19# build receiver with no keys 20deployer.succeed( 21 f"wire apply --no-progress --on receiver --path {TEST_DIR}/hive.nix --no-keys --ssh-accept-host -vvv >&2" 22) 23 24receiver.wait_for_unit("sshd.service") 25 26# --no-keys should never push a key 27receiver.fail("test -f /run/keys/source_string_name") 28deployer.fail("test -f /run/keys/source_string_name") 29 30# key services are created 31receiver.succeed("systemctl cat source_string_name-key.service") 32 33_, is_failed = receiver.execute("systemctl is-failed source_string_name-key.service") 34assert is_failed == "inactive\n", ( 35 f"source_string_name-key.service must be inactive before key exists ({is_failed})" 36) 37 38 39def test_keys(target, target_object, non_interactive): 40 if non_interactive: 41 deployer.succeed( 42 f"wire apply keys --on {target} --no-progress --path {TEST_DIR}/hive.nix --non-interactive --ssh-accept-host -vvv >&2" 43 ) 44 else: 45 deployer.succeed( 46 f"wire apply keys --on {target} --no-progress --path {TEST_DIR}/hive.nix --ssh-accept-host -vvv >&2" 47 ) 48 49 keys = [ 50 ( 51 "/run/keys/source_string_name", 52 "hello_world_source", 53 "root root 600", 54 "source_string_name", 55 ), 56 ("/etc/keys/file", "hello_world_file", "root root 644", "file"), 57 ( 58 "/home/owner/some/deep/path/command", 59 "hello_world_command", 60 "owner owner 644", 61 "command", 62 ), 63 ( 64 "/run/keys/environment", 65 "string_from_environment", 66 "root root 600", 67 "environment", 68 ), 69 ] 70 71 for path, value, permissions, name in keys: 72 # test existence & value 73 source_string = target_object.succeed(f"cat {path}") 74 assert value in source_string, f"{path} has correct contents ({target})" 75 76 stat = target_object.succeed(f"stat -c '%U %G %a' {path}").rstrip() 77 assert permissions == stat, f"{path} has correct permissions ({target})" 78 79 80def perform_routine(target, target_object, non_interactive): 81 test_keys(target, target_object, non_interactive) 82 83 # only check systemd units on receiver since deployer apply's are one time only 84 if target == "receiver": 85 target_object.succeed("systemctl start source_string_name-key.path") 86 target_object.succeed("systemctl start command-key.path") 87 target_object.wait_for_unit("source_string_name-key.service") 88 target_object.wait_for_unit("command-key.service") 89 90 # Mess with the keys to make sure that every push refreshes the permissions 91 target_object.succeed("echo 'incorrect_value' > /run/keys/source_string") 92 target_object.succeed("chown 600 /etc/keys/file") 93 # Test having a key that doesn't exist mixed with keys that do 94 target_object.succeed("rm /home/owner/some/deep/path/command") 95 96 if target == "receiver": 97 _, is_failed = target_object.execute("systemctl is-active command-key.service") 98 assert is_failed == "failed\n", ( 99 f"command-key.service is failed after deletion ({is_failed})" 100 ) 101 102 # Test keys twice to ensure the operation is idempotent, 103 # especially around directory creation. 104 test_keys(target, target_object, non_interactive) 105 106 107perform_routine("receiver", receiver, True) 108perform_routine("deployer", deployer, True) 109perform_routine("receiver", receiver, False) 110perform_routine("deployer", deployer, False) 111 112new_deployer_store_objects = collect_store_objects(deployer).difference(deployer_so) 113new_receiver_store_objects = collect_store_objects(receiver).difference(receiver_so) 114 115# no one should have any keys introduced by the operation 116for node, objects in [ 117 (deployer, new_deployer_store_objects), 118 (receiver, new_receiver_store_objects), 119]: 120 assert_store_not_poisoned(node, "hello_world_source", objects) 121 assert_store_not_poisoned(node, "hello_world_file", objects) 122 assert_store_not_poisoned(node, "hello_world_command", objects) 123 assert_store_not_poisoned(node, "string_from_environment", objects)