From 941c79b6207fa84612b4170ca3bc04984f3d79fc Mon Sep 17 00:00:00 2001 From: Vincent Haupert Date: Sat, 8 Oct 2022 01:32:55 +0200 Subject: [PATCH] nixos/github-runner: fix bugs related to `InaccessiblePaths=` This commit fixes two bugs: 1) When starting a github-runner for the very first time, the unconfigure script did not copy the `tokenFile` to the state directory. This case just was not handled so far. As a result, the runner could not configure. The unit did, however, fail even before as the state token file is configured as inaccessible for the service through `InaccessiblePaths=`. As the given path did not exist in the described case, setting up the unit's namespacing failed. 2) Similarly, the `tokenFile` is also marked as not accessible to the service user. There are, however, cases where other namespacing options make the files inaccessible even before `InaccessiblePaths=` kicks in; thus, they appear as non existing and cause the namespacing to fail yet again. Prefixing the entry with a `-` causes Systemd to ignore the entry if it cannot find it. This is the behavior we want. I also took fixing those bugs as a chance to refactor the unconfigure script to make it easier to follow. --- .../continuous-integration/github-runner.nix | 81 ++++++++++++------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/nixos/modules/services/continuous-integration/github-runner.nix b/nixos/modules/services/continuous-integration/github-runner.nix index 9abe13a89af1..2ece75722a1d 100644 --- a/nixos/modules/services/continuous-integration/github-runner.nix +++ b/nixos/modules/services/continuous-integration/github-runner.nix @@ -200,46 +200,65 @@ in ${lines} ''; - currentConfigPath = "$STATE_DIRECTORY/.nixos-current-config.json"; runnerRegistrationConfig = getAttrs [ "name" "tokenFile" "url" "runnerGroup" "extraLabels" "ephemeral" ] cfg; newConfigPath = builtins.toFile "${svcName}-config.json" (builtins.toJSON runnerRegistrationConfig); - newConfigTokenFilename = ".new-token"; + currentConfigPath = "$STATE_DIRECTORY/.nixos-current-config.json"; + newConfigTokenPath= "$STATE_DIRECTORY/.new-token"; + currentConfigTokenPath = "$STATE_DIRECTORY/${currentConfigTokenFilename}"; + runnerCredFiles = [ ".credentials" ".credentials_rsaparams" ".runner" ]; unconfigureRunner = writeScript "unconfigure" '' - differs= - - if [[ "$(ls -A "$STATE_DIRECTORY")" ]]; then - # State directory is not empty - # Set `differs = 1` if current and new runner config differ or if `currentConfigPath` does not exist - ${pkgs.diffutils}/bin/diff -q '${newConfigPath}' "${currentConfigPath}" >/dev/null 2>&1 || differs=1 - # Also trigger a registration if the token content changed - ${pkgs.diffutils}/bin/diff -q \ - "$STATE_DIRECTORY"/${currentConfigTokenFilename} \ - ${escapeShellArg cfg.tokenFile} \ - >/dev/null 2>&1 || differs=1 - # If .credentials does not exist, assume a previous run de-registered the runner on stop (ephemeral mode) - [[ ! -f "$STATE_DIRECTORY/.credentials" ]] && differs=1 - fi - - if [[ -n "$differs" ]]; then - echo "Config has changed, removing old runner state." - # In ephemeral mode, the runner deletes the `.credentials` file after de-registering it with GitHub - [[ -f "$STATE_DIRECTORY/.credentials" ]] && echo "The old runner will still appear in the GitHub Actions UI." \ - "You have to remove it manually." - find "$STATE_DIRECTORY/" -mindepth 1 -delete - + copy_tokens() { # Copy the configured token file to the state dir and allow the service user to read the file - install --mode=666 ${escapeShellArg cfg.tokenFile} "$STATE_DIRECTORY/${newConfigTokenFilename}" + install --mode=666 ${escapeShellArg cfg.tokenFile} "${newConfigTokenPath}" # Also copy current file to allow for a diff on the next start - install --mode=600 ${escapeShellArg cfg.tokenFile} "$STATE_DIRECTORY/${currentConfigTokenFilename}" + install --mode=600 ${escapeShellArg cfg.tokenFile} "${currentConfigTokenPath}" + } + + clean_state() { + find "$STATE_DIRECTORY/" -mindepth 1 -delete + copy_tokens + } + + diff_config() { + changed=0 + + # Check for module config changes + [[ -f "${currentConfigPath}" ]] \ + && ${pkgs.diffutils}/bin/diff -q '${newConfigPath}' "${currentConfigPath}" >/dev/null 2>&1 \ + || changed=1 + + # Also check the content of the token file + [[ -f "${currentConfigTokenPath}" ]] \ + && ${pkgs.diffutils}/bin/diff -q "${currentConfigTokenPath}" ${escapeShellArg cfg.tokenFile} >/dev/null 2>&1 \ + || changed=1 + + # If the config has changed, remove old state and copy tokens + if [[ "$changed" -eq 1 ]]; then + echo "Config has changed, removing old runner state." + echo "The old runner will still appear in the GitHub Actions UI." \ + "You have to remove it manually." + clean_state + fi + } + + if [[ "${optionalString cfg.ephemeral "1"}" ]]; then + # In ephemeral mode, we always want to start with a clean state + clean_state + elif [[ "$(ls -A "$STATE_DIRECTORY")" ]]; then + # There are state files from a previous run; diff them to decide if we need a new registration + diff_config + else + # The state directory is entirely empty which indicates a first start + copy_tokens fi ''; configureRunner = writeScript "configure" '' - if [[ -e "$STATE_DIRECTORY/${newConfigTokenFilename}" ]]; then + if [[ -e "${newConfigTokenPath}" ]]; then echo "Configuring GitHub Actions Runner" args=( @@ -256,7 +275,7 @@ in # If the token file contains a PAT (i.e., it starts with "ghp_"), we have to use the --pat option, # if it is not a PAT, we assume it contains a registration token and use the --token option - token=$(<"$STATE_DIRECTORY/${newConfigTokenFilename}") + token=$(<"${newConfigTokenPath}") if [[ "$token" =~ ^ghp_* ]]; then args+=(--pat "$token") else @@ -271,7 +290,7 @@ in rm -rf "$STATE_DIRECTORY/_diag/" # Cleanup token from config - rm "$STATE_DIRECTORY/${newConfigTokenFilename}" + rm "${newConfigTokenPath}" # Symlink to new config ln -s '${newConfigPath}' "${currentConfigPath}" @@ -305,8 +324,8 @@ in WorkingDirectory = runtimeDir; InaccessiblePaths = [ - # Token file path given in the configuration - cfg.tokenFile + # Token file path given in the configuration, if visible to the service + "-${cfg.tokenFile}" # Token file in the state directory "${stateDir}/${currentConfigTokenFilename}" ];