mirror of
https://github.com/jmcorgan/fips.git
synced 2026-10-30 07:37:36 +00:00
Each left a host misconfigured or without its daemon after an upgrade or install. Keep fips-dns working on a renamed TUN and respect a disabled fips-dns. fips-dns-setup waited for an interface named fips0 before trying any resolver backend, so on a host whose config sets tun.name to anything else it timed out after 30 seconds and exited 1. Debian's postinst starts fips-dns on upgrade and fails the configure when it does not come up, so upgrading such a host left the package half-configured with apt exiting 100. postinst also re-enabled fips-dns.service on every configure, undoing an operator's disable on each upgrade or reinstall. fips-dns-setup now reads the interface name from the daemon config (tun.name under the top-level tun: block, falling back to fips0) and waits for it only in the resolvectl backend, the one backend that uses the interface; that backend records the interface in its state file and fips-dns-teardown reverts it from there. postinst enables fips-dns only on first install, or when the package is installed again after a remove that disabled it while it was enabled: prerm's remove records under /var/lib/fips whether fips-dns was enabled, postinst consumes the record, and purge removes it. An older package's remove left no record, and its postinst enabled fips-dns on every configure, so preinst treats an install from config-files state with no record as enabled at remove; otherwise removing 0.5.3 and installing this package would leave .fips names unresolved. Tests: a dns-resolver scenario with the TUN renamed to mesh0, a container-free test of the name lookup run with the other static checks in both CI runners, and two Debian upgrade scenarios covering the renamed-TUN upgrade, a disable surviving a reinstall, and enablement across remove and reinstall, including a remove that left no record. Leave the running daemon alone on a FreeBSD package upgrade. On an upgrade the old package's pre-deinstall stopped fips and fips_dns, and the new package's post-install started them again when enabled. pkg 2.8.4 ends a process that a package script starts once pkg returns, even under daemon(8), so the started daemon was gone by the time pkg exited: every upgrade left the node with no daemon and without its .fips resolver drop-in until the operator ran service fips restart. pre-deinstall now stops the services and tears down the drop-in only on removal, and post-install no longer starts anything. The running daemon keeps its replaced binary until the operator restarts it, and the package prints an upgrade message saying to run service fips restart (which also starts a daemon that is not running) and service fips_dns restart if it is used. Upgrades from a package before this one still run that package's pre-deinstall, which stops the daemon, so they still need the restart; the README says so. packaging/freebsd/test-upgrade.sh upgrades over a running, enabled daemon to a package built from the same binaries at version 99.0.0 and checks that a daemon is still running up to 10 seconds after pkg returns, that the restart instruction was printed, and that the restart moves the daemon onto the new binary with one process. The FreeBSD Package workflow runs it in its VM after the log rotation check. Repoint an existing Windows service registration at the installed fips.exe. When the fips service already existed, --install-service opened it and set only its description, then reported success. A service registered from another folder, by hand or by an older install, therefore kept starting that folder's fips.exe after install-service.ps1 had copied the new release into Program Files. On ERROR_SERVICE_EXISTS, --install-service now reads the existing configuration and rewrites it with this executable and --service, keeping the service's start type and account, prints the old and new binary paths when they differ, and says "updated" rather than "installed". The service commands it, install-service.ps1 and the ZIP README print are now sc.exe: in PowerShell a bare sc is the Set-Content alias. The Windows Package workflow gains a step that registers the service from C:\fips-old, runs install-service.ps1 under Windows PowerShell, and requires the registration to name Program Files' fips.exe with --service, and the ZIP check now fails on a README line that names a bare sc.
272 lines
9.6 KiB
YAML
272 lines
9.6 KiB
YAML
name: Windows Package
|
|
on:
|
|
push:
|
|
branches:
|
|
- master
|
|
- maint
|
|
- next
|
|
tags:
|
|
- "v*"
|
|
pull_request:
|
|
workflow_dispatch:
|
|
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
|
|
jobs:
|
|
determine-versioning:
|
|
runs-on: windows-latest
|
|
outputs:
|
|
package_version: ${{ steps.version.outputs.package_version }}
|
|
steps:
|
|
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Derive package version
|
|
id: version
|
|
shell: pwsh
|
|
run: |
|
|
$cargoToml = Get-Content Cargo.toml -Raw
|
|
if ($cargoToml -match 'version\s*=\s*"([^"]+)"') {
|
|
$baseVersion = $Matches[1]
|
|
} else {
|
|
throw "Could not determine version from Cargo.toml"
|
|
}
|
|
|
|
if ($env:GITHUB_REF -like "refs/tags/*") {
|
|
$version = $env:GITHUB_REF_NAME -replace '^v', ''
|
|
} else {
|
|
$branch = $env:GITHUB_REF_NAME -replace '[^A-Za-z0-9]', '.' -replace '\.\.+', '.' -replace '^\.|\.$$', ''
|
|
$height = git rev-list --count HEAD
|
|
$hash = git rev-parse --short HEAD
|
|
if (-not $branch) { $branch = "ref" }
|
|
$version = "$baseVersion+$branch.$height.$hash"
|
|
}
|
|
|
|
echo "package_version=$version" >> $env:GITHUB_OUTPUT
|
|
|
|
build:
|
|
name: Build Windows package
|
|
runs-on: windows-latest
|
|
needs: determine-versioning
|
|
|
|
steps:
|
|
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Set SOURCE_DATE_EPOCH from git
|
|
shell: pwsh
|
|
run: |
|
|
$epoch = git log -1 --format=%ct
|
|
echo "SOURCE_DATE_EPOCH=$epoch" >> $env:GITHUB_ENV
|
|
|
|
- name: Install Rust toolchain
|
|
uses: actions-rust-lang/setup-rust-toolchain@166cdcfd11aee3cb47222f9ddb555ce30ddb9659 # v1
|
|
with:
|
|
cache: false
|
|
rustflags: ''
|
|
|
|
- name: Cache Cargo registry + build
|
|
uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
target
|
|
key: windows-release-x86_64-${{ hashFiles('**/Cargo.lock') }}
|
|
restore-keys: |
|
|
windows-release-x86_64-
|
|
|
|
- name: Build release binaries
|
|
run: cargo build --release
|
|
|
|
- name: Build Windows package
|
|
shell: pwsh
|
|
run: |
|
|
powershell -File packaging/windows/build-zip.ps1 `
|
|
-Version "${{ needs.determine-versioning.outputs.package_version }}" `
|
|
-NoBuild
|
|
|
|
- name: Verify ZIP structural correctness
|
|
shell: pwsh
|
|
run: |
|
|
$zip = Get-ChildItem deploy\fips-*-windows-*.zip | Select-Object -First 1
|
|
if (-not $zip) { Write-Error "No ZIP artifact found in deploy\"; exit 1 }
|
|
Write-Host "Verifying: $($zip.Name)"
|
|
|
|
$extractDir = "verify-extract"
|
|
if (Test-Path $extractDir) { Remove-Item -Recurse -Force $extractDir }
|
|
Expand-Archive -Path $zip.FullName -DestinationPath $extractDir
|
|
|
|
# Expected top-level files (flat ZIP, no wrapper directory).
|
|
# Source of truth: packaging/windows/build-zip.ps1
|
|
$expected = @(
|
|
"fips.exe",
|
|
"fipsctl.exe",
|
|
"fipstop.exe",
|
|
"fips.yaml",
|
|
"hosts",
|
|
"install-service.ps1",
|
|
"uninstall-service.ps1",
|
|
"README.txt"
|
|
)
|
|
|
|
$missing = @()
|
|
foreach ($f in $expected) {
|
|
$path = Join-Path $extractDir $f
|
|
if (Test-Path -LiteralPath $path -PathType Leaf) {
|
|
Write-Host "PASS: $f"
|
|
} else {
|
|
Write-Host "FAIL: $f"
|
|
$missing += $f
|
|
}
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host "ZIP contents:"
|
|
Get-ChildItem -Path $extractDir -Recurse | ForEach-Object {
|
|
Write-Host " $($_.FullName.Substring((Resolve-Path $extractDir).Path.Length + 1))"
|
|
}
|
|
|
|
if ($missing.Count -gt 0) {
|
|
Write-Error "Missing expected files: $($missing -join ', ')"
|
|
exit 1
|
|
}
|
|
Write-Host ""
|
|
Write-Host "All expected files present."
|
|
|
|
# The service control command is sc.exe. In PowerShell, where the
|
|
# README tells the operator to run these, a bare `sc` is the
|
|
# Set-Content alias, so `sc stop fips` writes a file instead.
|
|
$bareSc = Select-String -Path (Join-Path $extractDir "README.txt") -Pattern '^\s*sc (start|stop|query)\b'
|
|
if ($bareSc) {
|
|
$bareSc | ForEach-Object { Write-Host "FAIL: README.txt line $($_.LineNumber): $($_.Line)" }
|
|
Write-Error "README.txt names the service command as a bare sc, not sc.exe"
|
|
exit 1
|
|
}
|
|
Write-Host "PASS: README.txt names sc.exe, not a bare sc"
|
|
|
|
# A service registered from another folder must be repointed at the
|
|
# installed fips.exe when install-service.ps1 runs, or the node keeps
|
|
# starting the old binary. Runner jobs are elevated and the VM is thrown
|
|
# away, so this registers and removes a real service. The installer runs
|
|
# under Windows PowerShell, the edition it targets. It runs whenever the
|
|
# ZIP was extracted, so a failed README check above does not hide it.
|
|
- name: Installer repoints an existing service registration at the installed fips.exe
|
|
if: ${{ !cancelled() && hashFiles('verify-extract/fips.exe') != '' }}
|
|
shell: pwsh
|
|
run: |
|
|
function Get-BinaryPath {
|
|
$qc = & sc.exe qc fips
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Error "sc.exe qc fips exited $LASTEXITCODE`n$($qc -join "`n")"
|
|
exit 1
|
|
}
|
|
$line = $qc | Where-Object { $_ -match '^\s*BINARY_PATH_NAME\s*:' } | Select-Object -First 1
|
|
if (-not $line) {
|
|
Write-Error "sc.exe qc fips printed no BINARY_PATH_NAME`n$($qc -join "`n")"
|
|
exit 1
|
|
}
|
|
return ($line -replace '^\s*BINARY_PATH_NAME\s*:\s*', '')
|
|
}
|
|
|
|
$old = "C:\fips-old"
|
|
New-Item -ItemType Directory -Force -Path $old | Out-Null
|
|
Copy-Item verify-extract\fips.exe "$old\fips.exe" -Force
|
|
& "$old\fips.exe" --install-service
|
|
if ($LASTEXITCODE -ne 0) { Write-Error "registering the service from $old exited $LASTEXITCODE"; exit 1 }
|
|
$before = Get-BinaryPath
|
|
Write-Host "Registered from ${old}: $before"
|
|
# Precondition: the registration this check repoints exists and
|
|
# names the old folder.
|
|
if ($before -notlike "*$old\fips.exe*") {
|
|
Write-Error "precondition failed: the service does not name $old\fips.exe"
|
|
exit 1
|
|
}
|
|
|
|
powershell -File verify-extract\install-service.ps1
|
|
$installRc = $LASTEXITCODE
|
|
$after = Get-BinaryPath
|
|
Write-Host "After install-service.ps1: $after"
|
|
$want = "$env:ProgramFiles\fips\fips.exe"
|
|
& "$want" --uninstall-service
|
|
if ($installRc -ne 0) { Write-Error "install-service.ps1 exited $installRc"; exit 1 }
|
|
if (($after -notlike "*$want*") -or ($after -notlike "*--service*")) {
|
|
Write-Error "install-service.ps1 left the service at $after; want $want --service"
|
|
exit 1
|
|
}
|
|
Write-Host "PASS: install-service.ps1 repointed the service at $after"
|
|
|
|
- name: SHA-256 hash
|
|
shell: pwsh
|
|
run: |
|
|
Write-Host "==> Windows release asset:"
|
|
Get-ChildItem deploy\fips-*-windows-*.zip | ForEach-Object {
|
|
Get-FileHash $_.FullName -Algorithm SHA256 | Format-Table -AutoSize
|
|
}
|
|
|
|
- name: Upload artifact
|
|
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
|
|
with:
|
|
name: fips_${{ needs.determine-versioning.outputs.package_version }}_x86_64_windows
|
|
path: deploy/fips-*-windows-*.zip
|
|
retention-days: 30
|
|
|
|
- name: Build summary
|
|
shell: pwsh
|
|
run: |
|
|
$pkg = Get-ChildItem deploy\fips-*-windows-*.zip | Select-Object -First 1
|
|
Write-Host "Build Summary for Windows/x86_64:"
|
|
Write-Host " Package: $($pkg.Name)"
|
|
Write-Host " Size: $([math]::Round($pkg.Length / 1MB, 2)) MB"
|
|
|
|
release:
|
|
name: Publish Windows assets to GitHub Release
|
|
runs-on: ubuntu-latest
|
|
needs: build
|
|
if: startsWith(github.ref, 'refs/tags/')
|
|
permissions:
|
|
contents: write
|
|
|
|
steps:
|
|
- name: Download Windows artifacts
|
|
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8
|
|
with:
|
|
path: dist
|
|
merge-multiple: true
|
|
|
|
- name: Generate Windows release checksums
|
|
run: |
|
|
cd dist
|
|
find . -maxdepth 1 -type f -name '*.zip' -printf '%P\n' \
|
|
| LC_ALL=C sort \
|
|
| xargs sha256sum \
|
|
> checksums-windows.txt
|
|
|
|
- name: Wait for tag release
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
for attempt in $(seq 1 20); do
|
|
if gh release view "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then
|
|
exit 0
|
|
fi
|
|
echo "Release ${GITHUB_REF_NAME} not available yet; waiting..."
|
|
sleep 15
|
|
done
|
|
|
|
echo "Timed out waiting for release ${GITHUB_REF_NAME}" >&2
|
|
exit 1
|
|
|
|
- name: Upload Windows assets
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
gh release upload "${GITHUB_REF_NAME}" \
|
|
dist/*.zip \
|
|
dist/checksums-windows.txt \
|
|
--clobber \
|
|
--repo "${GITHUB_REPOSITORY}"
|