diff --git a/.github/workflows/_build_artifacts.yml b/.github/workflows/_build_artifacts.yml index 69fa7db4c..8e0f8d440 100644 --- a/.github/workflows/_build_artifacts.yml +++ b/.github/workflows/_build_artifacts.yml @@ -26,16 +26,16 @@ on: outputs: client_image: description: "The client image that was built" - value: ${{ jobs.data-plane.outputs.client_image }} + value: ${{ jobs.data-plane-linux.outputs.client_image }} relay_image: description: "The relay image that was built" - value: ${{ jobs.data-plane.outputs.relay_image }} + value: ${{ jobs.data-plane-linux.outputs.relay_image }} gateway_image: description: "The gateway image that was built" - value: ${{ jobs.data-plane.outputs.gateway_image }} + value: ${{ jobs.data-plane-linux.outputs.gateway_image }} http_test_server_image: description: "The http_test_server image that was built" - value: ${{ jobs.data-plane.outputs.http_test_server_image }} + value: ${{ jobs.data-plane-linux.outputs.http_test_server_image }} permissions: # write permission is required to create a github release @@ -116,7 +116,55 @@ jobs: ${{ steps.login.outputs.registry }}/firezone/${{ matrix.image_name }}:${{ inputs.sha }} ${{ steps.login.outputs.registry }}/firezone/${{ matrix.image_name }}:${{ env.CACHE_TAG }} - data-plane: + data-plane-windows: + name: client-windows-${{ matrix.target }} + runs-on: windows-2019 + defaults: + run: + working-directory: rust + strategy: + fail-fast: false + matrix: + # TODO: Add ARM64 support + artifact: [firezone-client-headless-windows] + arch: [x86_64] + target: [x86_64-pc-windows-msvc] + package: [firezone-headless-client] + # mark:next-headless-version + release_name: [headless-client-1.4.2] + # mark:next-headless-version + version: [1.4.2] + env: + ARTIFACT_PATH: ${{ matrix.artifact }}_${{ matrix.version }}_${{ matrix.arch }}.exe + RELEASE_NAME: ${{ matrix.release_name }} + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ inputs.sha }} + - uses: ./.github/actions/setup-rust + with: + targets: ${{ matrix.target }} + - name: Build binaries + shell: bash + run: | + set -xe + + if [[ "${{ inputs.profile }}" == "release" ]]; then + PROFILE="--release" + else + PROFILE="" + fi + + cargo build $PROFILE -p ${{ matrix.package }} --target ${{ matrix.target }} + mv target/${{ matrix.target }}/${{ inputs.profile }}/${{ matrix.package }}.exe $ARTIFACT_PATH + - name: Upload Release Assets + if: ${{ inputs.profile == 'release' && inputs.stage == 'release' && matrix.release_name }} + shell: bash + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: scripts/upload/github-release.sh + + data-plane-linux: name: ${{ matrix.name.image_name }}-${{ matrix.arch.shortname }} runs-on: ubuntu-22.04 defaults: @@ -328,7 +376,7 @@ jobs: merge-docker-artifacts: name: merge-${{ matrix.image.name }} - needs: data-plane + needs: data-plane-linux if: ${{ always() }} runs-on: ubuntu-22.04 strategy: diff --git a/.github/workflows/_rust.yml b/.github/workflows/_rust.yml index b62013a86..067c3e74e 100644 --- a/.github/workflows/_rust.yml +++ b/.github/workflows/_rust.yml @@ -173,16 +173,19 @@ jobs: strategy: fail-fast: false matrix: - # TODO: Add Windows as part of issue #3782 - runs-on: [ubuntu-22.04, ubuntu-24.04] - test: [linux-group, token-path] + include: + - { runs-on: windows-2019, test: token-path-windows.ps1 } + - { runs-on: windows-2022, test: token-path-windows.ps1 } + - { runs-on: ubuntu-22.04, test: linux-group.sh } + - { runs-on: ubuntu-24.04, test: linux-group.sh } + - { runs-on: ubuntu-22.04, test: token-path-linux.sh } + - { runs-on: ubuntu-24.04, test: token-path-linux.sh } runs-on: ${{ matrix.runs-on }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: ./.github/actions/setup-rust - uses: ./.github/actions/setup-tauri-v2 timeout-minutes: 5 - - run: scripts/tests/${{ matrix.test }}.sh + - run: scripts/tests/${{ matrix.test }} name: "test script" - shell: bash working-directory: ./ diff --git a/rust/headless-client/src/windows.rs b/rust/headless-client/src/windows.rs index 37365e6bc..d0a09cc7f 100644 --- a/rust/headless-client/src/windows.rs +++ b/rust/headless-client/src/windows.rs @@ -5,6 +5,8 @@ //! We must tell Windows explicitly when our service is stopping. use anyhow::Result; +use firezone_bin_shared::BUNDLE_ID; +use known_folders::{get_known_folder_path, KnownFolder}; use std::path::{Path, PathBuf}; // The return value is useful on Linux @@ -14,9 +16,11 @@ pub(crate) fn check_token_permissions(_path: &Path) -> Result<()> { Ok(()) } -pub(crate) fn default_token_path() -> std::path::PathBuf { - // TODO: For Headless Client, system-wide default token path for Windows - PathBuf::from("token.txt") +pub(crate) fn default_token_path() -> PathBuf { + get_known_folder_path(KnownFolder::ProgramData) + .expect("ProgramData folder not found. Is %PROGRAMDATA% set?") + .join(BUNDLE_ID) + .join("token.txt") } // Does nothing on Windows. On Linux this notifies systemd that we're ready. diff --git a/scripts/tests/token-path.sh b/scripts/tests/token-path-linux.sh similarity index 100% rename from scripts/tests/token-path.sh rename to scripts/tests/token-path-linux.sh diff --git a/scripts/tests/token-path-windows.ps1 b/scripts/tests/token-path-windows.ps1 new file mode 100755 index 000000000..99b1ad400 --- /dev/null +++ b/scripts/tests/token-path-windows.ps1 @@ -0,0 +1,98 @@ +# Define variables. +$PACKAGE_NAME = "firezone-headless-client" +$BINARY_NAME = "$PACKAGE_NAME.exe" +$TOKEN = "n.SFMyNTY.g2gDaANtAAAAJGM4OWJjYzhjLTkzOTItNGRhZS1hNDBkLTg4OGFlZjZkMjhlMG0AAAAkN2RhN2QxY2QtMTExYy00NGE3LWI1YWMtNDAyN2I5ZDIzMGU1bQAAACtBaUl5XzZwQmstV0xlUkFQenprQ0ZYTnFJWktXQnMyRGR3XzJ2Z0lRdkZnbgYAGUmu74wBYgABUYA.UN3vSLLcAMkHeEh5VHumPOutkuue8JA6wlxM9JxJEPE" +$TOKEN_PATH = "token" + +# Build the binary using cargo. +cargo build --manifest-path rust/Cargo.toml -p $PACKAGE_NAME +if ($LASTEXITCODE -ne 0) { + Write-Error "Cargo build failed." + exit 1 +} + +# Move the binary from rust/target/debug to the current directory. +Move-Item "rust/target/debug/$BINARY_NAME" $BINARY_NAME -Force + +# ------------------------------------------------------------------- +# Test 1: Should fail because there's no token yet. +& ".\$BINARY_NAME" --check standalone +if ($LASTEXITCODE -eq 0) { + Write-Error "Test 1: Expected failure when no token is provided." + exit 1 +} + +# ------------------------------------------------------------------- +# Test 2: Pass if we use the environment variable. +$env:FIREZONE_TOKEN = $TOKEN +& ".\$BINARY_NAME" --check standalone +if ($LASTEXITCODE -ne 0) { + Write-Error "Test 2: Expected success when token is provided via env var." + exit 1 +} +# Clear the environment variable after use. +Remove-Item Env:FIREZONE_TOKEN + +# ------------------------------------------------------------------- +# Test 3: Fails because passing tokens as CLI args is not allowed. +try { + & ".\$BINARY_NAME" --check --token $TOKEN standalone +} catch { + # Suppress the exception so the script can continue. + Write-Verbose "Caught exception: $_" +} + +if ($LASTEXITCODE -eq 0) { + Write-Error "Test 3: Expected failure when token is passed as a CLI argument." + exit 1 +} else { + Write-Host "Test 3 passed: Non-zero exit code detected." +} + +# ------------------------------------------------------------------- +# Create the token file (similar to 'touch'). +New-Item -Path $TOKEN_PATH -ItemType File -Force | Out-Null + +# Write the token to the file without adding a newline. +[System.IO.File]::WriteAllText($TOKEN_PATH, $TOKEN) + +# ------------------------------------------------------------------- +# Test 4: Fails because the token is not in the default path. +& ".\$BINARY_NAME" --check standalone +if ($LASTEXITCODE -eq 0) { + Write-Error "Test 4: Expected failure when token file is in the wrong location." + exit 1 +} + +# ------------------------------------------------------------------- +# Test 5: Pass if we tell it where to look using the --token-path argument. +& ".\$BINARY_NAME" --check --token-path $TOKEN_PATH standalone +if ($LASTEXITCODE -ne 0) { + Write-Error "Test 5: Expected success when specifying the token file location." + exit 1 +} + +# ------------------------------------------------------------------- +# Move the token file to the default path. +$defaultTokenDir = "$env:PROGRAMDATA\dev.firezone.client" +New-Item -ItemType Directory -Path $defaultTokenDir -Force | Out-Null +Move-Item -Path $TOKEN_PATH -Destination "$defaultTokenDir\token.txt" -Force + +# Show the contents of the default token directory. +Get-ChildItem -Path $defaultTokenDir + +# ------------------------------------------------------------------- +# Test 6: Now the binary should pass using the token in the default path. +& ".\$BINARY_NAME" --check standalone +if ($LASTEXITCODE -ne 0) { + Write-Error "Test 6: Expected success when the token is in the default path." + exit 1 +} + +# ------------------------------------------------------------------- +# Test 7: Fails because the user is not allowed to read the token +# TODO: Implement this test. Requires implementing the check_token_permissions +# function in rust/headless-client/src/windows.rs + +# Redundant exit with success. +exit 0