add Drone CI packaging pipelines

This commit is contained in:
曾志威
2026-08-14 16:17:45 +08:00
parent 5ef362ba22
commit b18125bf04
7 changed files with 93 additions and 357 deletions
+66
View File
@@ -0,0 +1,66 @@
---
kind: pipeline
type: docker
name: linux-package
platform:
os: linux
arch: amd64
trigger:
event:
- push
- pull_request
- tag
steps:
- name: test-and-package
image: rust:1.89-bookworm
environment:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: "1"
commands:
- apt-get update
- >-
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends
libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libxkbcommon-dev
pkg-config rpm ca-certificates
- cargo fmt --all -- --check
- cargo test --workspace --locked
- cargo clippy --workspace --all-targets --locked -- -D warnings
- rm -rf artifacts
- mkdir -p artifacts
- sh ./packaging/linux/package-deb.sh
- sh ./packaging/linux/package-rpm.sh
- sh ./packaging/edge/package-deb.sh
- >-
(cd artifacts && find . -maxdepth 1 -type f
! -name 'DRONE-SHA256SUMS.txt' -print | sort |
xargs -r sha256sum) > artifacts/DRONE-SHA256SUMS.txt
- test -s artifacts/DRONE-SHA256SUMS.txt
---
kind: pipeline
type: exec
name: windows-package
platform:
os: windows
arch: amd64
trigger:
event:
- push
- pull_request
- tag
steps:
- name: test
commands:
- powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "$ErrorActionPreference = 'Stop'; cargo fmt --all -- --check"
- powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "$ErrorActionPreference = 'Stop'; cargo test --workspace --locked"
- name: package
commands:
- powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File .\packaging\build-all.ps1 -CleanArtifacts -Culture en-us
- powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "$ErrorActionPreference = 'Stop'; if (-not (Test-Path -LiteralPath .\artifacts\BUILD-SHA256SUMS.txt)) { throw 'Windows package checksum manifest is missing' }; if (-not (Get-ChildItem -LiteralPath .\artifacts -File -Include '*.msi','*.zip')) { throw 'Windows package output is missing' }"
-156
View File
@@ -1,156 +0,0 @@
name: ci
on:
push:
pull_request:
permissions:
contents: read
jobs:
rust:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy,rustfmt
- run: cargo fmt --all -- --check
- run: cargo clippy --locked -p remotedesk-webrtc-core -p remotedesk-native-gui --all-targets -- -D warnings
- run: cargo test --workspace
native-client:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo test --locked -p remotedesk-webrtc-core
- run: cargo build --locked --release -p remotedesk-native-gui
package-windows-client:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Build native portable preview
shell: pwsh
run: ./packaging/windows/package-preview.ps1
- name: Build native Windows installer
shell: pwsh
run: ./packaging/windows/package-installer.ps1
- uses: actions/upload-artifact@v4
with:
name: remotedesk-native-windows-packages
path: |
artifacts/*.zip
artifacts/*.msi
artifacts/SHA256SUMS.txt
artifacts/INSTALLER-SHA256SUMS.txt
if-no-files-found: error
linux-agent:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy,rustfmt
- name: Install Linux media build dependencies
run: |
sudo apt-get update
sudo apt-get install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
- name: Test Linux Agent
run: |
cargo test --locked -p remotedesk-agent-runtime
cargo clippy --locked -p remotedesk-agent-runtime --all-targets -- -D warnings
cargo build --locked -p remotedesk-agent-runtime --bin remotedesk-file-session
sh ./packaging/linux/verify-file-resume.sh target/debug/remotedesk-file-session
- name: Build DEB
run: sh ./packaging/linux/package-deb.sh
- name: Build RPM
run: |
sudo apt-get update
sudo apt-get install -y rpm
sh ./packaging/linux/package-rpm.sh
- uses: actions/upload-artifact@v4
with:
name: remotedesk-linux-agent-packages
path: |
artifacts/*.deb
artifacts/*.deb.sha256
artifacts/*.rpm
artifacts/*.rpm.sha256
if-no-files-found: error
edge-service:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy,rustfmt
- name: Test Edge service
run: |
cargo test --locked -p remotedesk-edge-service
cargo clippy --locked -p remotedesk-edge-service --all-targets -- -D warnings
- name: Build Edge DEB
run: sh ./packaging/edge/package-deb.sh
- uses: actions/upload-artifact@v4
with:
name: remotedesk-edge-package
path: |
artifacts/remotedesk-edge_*.deb
artifacts/remotedesk-edge_*.deb.sha256
if-no-files-found: error
coturn-pop:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install coturn tools
run: |
sudo apt-get update
sudo apt-get install -y coturn
- name: Start isolated TLS POP and verify transports
run: |
set -eu
work=$(mktemp -d)
trap 'test -f "$work/pid" && kill "$(cat "$work/pid")" 2>/dev/null || true; rm -rf "$work"' EXIT
openssl req -x509 -newkey rsa:2048 -nodes -days 1 \
-subj '/CN=RemoteDesk Test CA' -keyout "$work/ca-key.pem" -out "$work/ca.pem"
openssl req -newkey rsa:2048 -nodes -subj '/CN=127.0.0.1' \
-addext 'subjectAltName=IP:127.0.0.1' -keyout "$work/turn-key.pem" -out "$work/turn.csr"
openssl x509 -req -days 1 -in "$work/turn.csr" -CA "$work/ca.pem" \
-CAkey "$work/ca-key.pem" -CAcreateserial -copy_extensions copy \
-out "$work/turn-cert.pem"
secret='ci-turn-rest-secret-0123456789abcdef'
turnserver --fingerprint --use-auth-secret --static-auth-secret "$secret" \
--realm ci.remotedesk.invalid --listening-ip 127.0.0.1 --relay-ip 127.0.0.1 \
--external-ip 127.0.0.1 --listening-port 3478 --tls-listening-port 5349 \
--min-port 49160 --max-port 49179 --cert "$work/turn-cert.pem" \
--pkey "$work/turn-key.pem" --no-cli --no-multicast-peers \
--pidfile "$work/pid" --log-file stdout &
for attempt in $(seq 1 50); do
if turnutils_stunclient -p 3478 127.0.0.1 >/dev/null 2>&1; then break; fi
if [ "$attempt" -eq 50 ]; then echo 'coturn did not become ready' >&2; exit 1; fi
sleep 0.1
done
REMOTEDESK_TURN_REST_SECRET="$secret" \
REMOTEDESK_TURN_VERIFY_CA="$work/ca.pem" \
sh ./packaging/edge/verify-coturn.sh
package-windows-host:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Build Windows controlled-endpoint installer
shell: pwsh
run: ./packaging/windows-host/package-host.ps1
- uses: actions/upload-artifact@v4
with:
name: remotedesk-windows-host-installer
path: |
artifacts/RemoteDesk-Host-*.msi
artifacts/HOST-INSTALLER-SHA256SUMS.txt
if-no-files-found: error
-195
View File
@@ -1,195 +0,0 @@
name: release-windows
on:
push:
tags:
- 'v*.*.*'
permissions:
contents: write
id-token: write
attestations: write
concurrency:
group: windows-production-${{ github.ref }}
cancel-in-progress: false
jobs:
release:
runs-on: windows-latest
environment: windows-production
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: dtolnay/rust-toolchain@stable
- name: Validate release tag and repository version
id: version
shell: pwsh
run: |
$metadata = cargo metadata --no-deps --format-version 1 | ConvertFrom-Json
if ($LASTEXITCODE -ne 0) { throw 'Unable to read Cargo workspace metadata' }
$package = $metadata.packages | Where-Object name -eq 'remotedesk-control-service'
if (-not $package) { throw 'remotedesk-control-service package metadata is missing' }
$version = [string]$package.version
if ($version -notmatch '^\d+\.\d+\.\d+$') { throw "Invalid release version: $version" }
if ($env:GITHUB_REF_NAME -ne "v$version") {
throw "Tag $env:GITHUB_REF_NAME does not match workspace version $version"
}
"version=$version" >> $env:GITHUB_OUTPUT
- name: Import protected signing material
id: signing
shell: pwsh
env:
WINDOWS_SIGNING_PFX_BASE64: ${{ secrets.WINDOWS_SIGNING_PFX_BASE64 }}
WINDOWS_SIGNING_PFX_PASSWORD: ${{ secrets.WINDOWS_SIGNING_PFX_PASSWORD }}
UPDATE_ED25519_PRIVATE_KEY_PEM: ${{ secrets.UPDATE_ED25519_PRIVATE_KEY_PEM }}
UPDATE_ED25519_PUBLIC_KEY_BASE64: ${{ secrets.UPDATE_ED25519_PUBLIC_KEY_BASE64 }}
run: |
foreach ($name in @(
'WINDOWS_SIGNING_PFX_BASE64',
'WINDOWS_SIGNING_PFX_PASSWORD',
'UPDATE_ED25519_PRIVATE_KEY_PEM',
'UPDATE_ED25519_PUBLIC_KEY_BASE64'
)) {
if ([string]::IsNullOrWhiteSpace((Get-Item "Env:$name").Value)) {
throw "Required production secret $name is not configured"
}
}
$pfxPath = Join-Path $env:RUNNER_TEMP "remotedesk-signing-$PID.pfx"
$keyPath = Join-Path $env:RUNNER_TEMP "remotedesk-update-$PID.pem"
try {
[IO.File]::WriteAllBytes($pfxPath, [Convert]::FromBase64String($env:WINDOWS_SIGNING_PFX_BASE64))
$password = ConvertTo-SecureString $env:WINDOWS_SIGNING_PFX_PASSWORD -AsPlainText -Force
$imported = @(Import-PfxCertificate -FilePath $pfxPath -CertStoreLocation Cert:\CurrentUser\My -Password $password)
$certificate = $imported | Where-Object HasPrivateKey | Select-Object -First 1
if (-not $certificate) { throw 'PFX did not import a certificate with a private key' }
[IO.File]::WriteAllText($keyPath, $env:UPDATE_ED25519_PRIVATE_KEY_PEM, [Text.UTF8Encoding]::new($false))
"thumbprint=$($certificate.Thumbprint)" >> $env:GITHUB_OUTPUT
"key_path=$keyPath" >> $env:GITHUB_OUTPUT
} finally {
if (Test-Path -LiteralPath $pfxPath) { Remove-Item -LiteralPath $pfxPath -Force }
}
- name: Build, sign and verify Windows installer
shell: pwsh
run: |
./packaging/windows/package-installer.ps1 `
-SigningCertificateThumbprint '${{ steps.signing.outputs.thumbprint }}' `
-TimestampUrl 'https://timestamp.digicert.com' `
-ReleaseChannel stable
./packaging/windows-host/package-host.ps1 `
-SigningCertificateThumbprint '${{ steps.signing.outputs.thumbprint }}' `
-TimestampUrl 'https://timestamp.digicert.com'
- name: Create signed update manifest
id: manifest
shell: pwsh
env:
EXPECTED_UPDATE_PUBLIC_KEY: ${{ secrets.UPDATE_ED25519_PUBLIC_KEY_BASE64 }}
run: |
$version = '${{ steps.version.outputs.version }}'
$installer = Get-ChildItem -LiteralPath artifacts -Filter "RemoteDesk-M0-$version-*-windows-x64.msi" -File
if (@($installer).Count -ne 1) { throw 'Expected exactly one versioned x64 MSI' }
$signature = Get-AuthenticodeSignature -LiteralPath $installer.FullName
if ([string]$signature.Status -ne 'Valid' -or
$signature.SignerCertificate.Thumbprint -ne '${{ steps.signing.outputs.thumbprint }}') {
throw 'Final MSI Authenticode verification failed before manifest generation'
}
$hostInstaller = Get-ChildItem -LiteralPath artifacts -Filter "RemoteDesk-Host-$version-*-windows-x64.msi" -File
if (@($hostInstaller).Count -ne 1) { throw 'Expected exactly one versioned x64 Host MSI' }
$hostSignature = Get-AuthenticodeSignature -LiteralPath $hostInstaller.FullName
if ([string]$hostSignature.Status -ne 'Valid' -or
$hostSignature.SignerCertificate.Thumbprint -ne '${{ steps.signing.outputs.thumbprint }}') {
throw 'Final Host MSI Authenticode verification failed before publication'
}
$url = "https://github.com/$env:GITHUB_REPOSITORY/releases/download/$env:GITHUB_REF_NAME/$($installer.Name)"
node ./packaging/windows/create-update-manifest.mjs `
--installer $installer.FullName `
--installer-url $url `
--private-key '${{ steps.signing.outputs.key_path }}' `
--version $version `
--channel stable `
--target windows-x64 `
--output ./artifacts/stable.json `
--public-key-output ./artifacts/update-public-key.txt
if ($LASTEXITCODE -ne 0) { throw 'Update manifest generation failed' }
$actualKey = (Get-Content -LiteralPath ./artifacts/update-public-key.txt -Raw).Trim()
if ($actualKey -cne $env:EXPECTED_UPDATE_PUBLIC_KEY.Trim()) {
throw 'Generated update public key does not match the protected expected public key'
}
"installer=$($installer.FullName)" >> $env:GITHUB_OUTPUT
"host_installer=$($hostInstaller.FullName)" >> $env:GITHUB_OUTPUT
- name: Generate SPDX SBOM
uses: anchore/sbom-action@v0
with:
path: .
format: spdx-json
output-file: artifacts/remotedesk-${{ steps.version.outputs.version }}.spdx.json
upload-artifact: false
- name: Attest final Windows installers
uses: actions/attest-build-provenance@v2
with:
subject-path: |
${{ steps.manifest.outputs.installer }}
${{ steps.manifest.outputs.host_installer }}
- name: Upload immutable workflow artifacts
uses: actions/upload-artifact@v4
with:
name: remotedesk-windows-${{ steps.version.outputs.version }}-signed
path: |
${{ steps.manifest.outputs.installer }}
${{ steps.manifest.outputs.host_installer }}
artifacts/INSTALLER-SHA256SUMS.txt
artifacts/HOST-INSTALLER-SHA256SUMS.txt
artifacts/stable.json
artifacts/update-public-key.txt
artifacts/remotedesk-${{ steps.version.outputs.version }}.spdx.json
if-no-files-found: error
retention-days: 30
- name: Publish GitHub release assets
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create $env:GITHUB_REF_NAME `
'${{ steps.manifest.outputs.installer }}' `
'${{ steps.manifest.outputs.host_installer }}' `
./artifacts/INSTALLER-SHA256SUMS.txt `
./artifacts/HOST-INSTALLER-SHA256SUMS.txt `
./artifacts/stable.json `
./artifacts/update-public-key.txt `
./artifacts/remotedesk-${{ steps.version.outputs.version }}.spdx.json `
--verify-tag `
--generate-notes `
--title "RemoteDesk ${{ steps.version.outputs.version }}"
- name: Publish stable update channel
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
run: |
$channelTag = 'stable-channel'
gh release view $channelTag *> $null
if ($LASTEXITCODE -ne 0) {
gh release create $channelTag `
--target $env:GITHUB_SHA `
--title 'RemoteDesk stable update channel' `
--notes 'Machine-readable signed update channel. Installers remain immutable on versioned releases.' `
--prerelease
if ($LASTEXITCODE -ne 0) { throw 'Unable to create stable update channel' }
}
gh release upload $channelTag `
./artifacts/stable.json `
./artifacts/update-public-key.txt `
--clobber
if ($LASTEXITCODE -ne 0) { throw 'Unable to update stable channel assets' }
- name: Remove signing material
if: always()
shell: pwsh
run: |
$thumbprint = '${{ steps.signing.outputs.thumbprint }}'
if ($thumbprint -match '^[0-9A-Fa-f]{40}$') {
Remove-Item -LiteralPath "Cert:\CurrentUser\My\$thumbprint" -Force -ErrorAction SilentlyContinue
}
$keyPath = '${{ steps.signing.outputs.key_path }}'
if ($keyPath -and (Test-Path -LiteralPath $keyPath)) {
Remove-Item -LiteralPath $keyPath -Force
}
+20
View File
@@ -0,0 +1,20 @@
# Drone CI 打包
仓库提供 `.drone.yml`,包含两个独立流水线:
- `linux-package` 使用 Linux Docker runner,执行格式检查、工作区测试和 Clippy,然后生成 Linux Agent 的 DEB/RPM 与 Edge DEB。
- `windows-package` 使用 Windows Exec runner,执行格式检查和工作区测试,然后调用 `packaging/build-all.ps1` 生成 Windows Client/Host 的 MSI、便携 ZIP 和校验清单。
两条流水线都会在 `push``pull_request``tag` 事件运行。构建脚本只生成未签名开发包;正式签名仍需要受保护的证书和独立发布流程,不能把私钥放进 Drone 配置。
## Runner 要求
Linux Docker runner 使用 `rust:1.89-bookworm`,流水线会安装 GStreamer 开发包、`rpm``pkg-config` 和 CA 证书。Runner 需要允许访问 crates.io、NuGetWindows WiX 下载)以及项目依赖的 HTTPS 服务。
Windows Exec runner 需要 Rust stable(包含 `rustfmt``cargo`)、PowerShell、Windows .NET Framework `csc.exe`,并允许脚本下载固定 SHA-256 的 WiX 4.0.6 包。Runner 应使用 x64 Windows 主机;MSI 产物不在普通 CI 中签名。
## 产物
Linux 产物位于 `artifacts/`,并生成 `DRONE-SHA256SUMS.txt`。Windows 产物位于 `artifacts/`,并生成 `BUILD-SHA256SUMS.txt`。Drone 本身不会跨 Runner 自动保存工作区文件,部署时应给 Runner 配置对象存储/制品插件或主机卷,将这两个目录上传到构建编号对应的路径;上传步骤不包含在仓库中,避免把组织级凭据和存储端点写入源码。
建议制品路径使用 `${DRONE_REPO_OWNER}/${DRONE_REPO_NAME}/${DRONE_BUILD_NUMBER}`,并只允许 tag 构建进入正式发布桶。发布前必须再次验证清单中的 SHA-256,并按 `docs/online-updates.md` 配置 Windows 签名和更新清单密钥。
+1
View File
@@ -6,6 +6,7 @@
- Node.js 22 与 npm 10。
- Windows RDP viewer 的安全 Named Pipe 和 Tauri 2 桌面壳已完成源码接线;Linux 远程 H.264 的 RFC 6184 接收、Media Foundation NV12 解码和 D3D11 VideoProcessor 窗口呈现也已接线,但尚未编译、实机验证或通过 ETW/GPUView 证明硬件解码与隐藏复制边界。Tauri 和媒体新依赖按本轮要求尚未解析或编译。
- Linux PipeWire、Portal、DRI3 与硬件编码器必须在对应 Linux 测试机执行 M0 Spike。
- Drone CI 的 Linux/Windows 打包流水线、Runner 前置条件和产物留存约定见 [Drone CI 打包](ci-drone.md)。
## 1.1 Windows Headless compatibility 验收
+1 -1
View File
@@ -44,7 +44,7 @@
| Windows 凭据适配器 | 可用 | 每个主机保存 `RemoteDesk/RDP/<profile-id>` opaque 引用;独立本地遮罩窗口写入/覆盖 Windows Credential ManagerUI 可查询状态和删除;密码不进入 React、HTTP、控制服务、argv、日志或主机配置;已在 Windows 实测缺失条目查询,真实远端 NLA 登录仍待环境验收 |
| Windows M0 便携预览包 | 部分 | ZIP 包,不是 MSI;打包源码已改为包含 Tauri 壳并以 `bin/remotedesk.exe` 作为唯一 UI 入口,不再附带或启动系统浏览器;按要求尚未重新构建包 |
| Windows M0 安装包 | 可用 | 按当前用户安装的 MSI,包含开始菜单快捷方式、升级标识和系统卸载入口;尚未签名 |
| Windows 控制端在线升级 | 可用 | 支持启动时或手动检查 Ed25519 签名的 HTTPS 清单,下载后校验大小和 SHA-256,并由独立 helper 在控制服务退出后执行 MSI 原位升级;源码新增受 `windows-production` environment 保护的版本标签工作流,强制 Authenticode 签名/验签控制端与 Host payload/MSI、公钥匹配、SPDX SBOM、GitHub build provenance、版本 Release 先发布再更新固定 `stable-channel` 清单;不内置私钥或默认信任公钥,工作流按要求尚未实际执行,当前已有 MSI 仍未签名 |
| Windows 控制端在线升级 | 可用 | 支持启动时或手动检查 Ed25519 签名的 HTTPS 清单,下载后校验大小和 SHA-256,并由独立 helper 在控制服务退出后执行 MSI 原位升级;Drone 构建入口只生成未签名开发包,正式发布需在受保护的 Drone 发布流水线或离线环境中完成 Authenticode 签名/验签控制端与 Host payload/MSI、公钥匹配、SPDX SBOM 和版本清单发布;不内置私钥或默认信任公钥,当前已有 MSI 仍未签名 |
| 协议与策略核心 | 部分 | 会话状态、显示布局、required features、零拷贝报告和 GPU 规划已有纯 Rust 模型及测试 |
| Native Video Helper | 部分 | 保留严格/兼容 GPU 管线规划和 Windows 硬件 D3D11 device/immediate-context 创建探针;H.264/DXVA allocation probe 源码会枚举适配器真实 H.264 VLD profiles、验证 NV12、选择未加密 raw-bitstream config,并实际创建 decoder、NV12 texture array 和 output view`--probe-h264-file` 使用绑定同一 device manager 的 Media Foundation Source Reader,在最多 512 次读取内严格验证真实 H.264 文件的 NV12 `IMFDXGIBuffer`、完整 subresource layout 和所属 device。`--play-h264-file` 本地窗口链路有界枚举并选中 H.264 原生类型、跟踪动态 NV12 媒体类型和可见帧尺寸、拒绝 CPU buffer/跨 device texture,以媒体时间戳调度帧;同一 device 的 `ID3D11VideoProcessor` 将 NV12 texture subresource 转换和等比缩放到 BGRA swap chain。Windows Linux 桌面 helper 进一步接入 RFC 6184 单 NAL/STAP-A/FU-A 重组、16 MiB AU 上限、跨帧 RTP 序号检测、PLI/关键帧恢复和一槽有界邮箱;Annex-B 输入送入 D3D11-aware Media Foundation H.264 MFT,只接受同 device NV12 `IMFDXGIBuffer`,再由 VideoProcessor 呈现到现有桌面窗口。协议 minor 9 在第一张原生帧呈现后执行帧边界模式确认,确认后停止完整 zlib 编码/传输,以 sequence 绑定的 H.264 呈现 ACK 驱动下一次捕获;解码、积压、轨道、sender/encoder 或 ACK 超时均显式恢复 zlib。H.264 采集、编码、解码和呈现耗时分别进入质量诊断。该链路按要求尚未编译/实机执行,且 DXGI surface/VideoProcessor 呈现不能替代 ETW/GPUView 的硬解和隐藏复制证明,所以继续报告 `hardware_decode_verified=false``zero_copy_verified=false``linux.native_video=false`IronRDP viewer 继续使用独立 D3D11 CPU framebuffer 路径 |
| Linux Agent 基础 | 部分 | 四个可运行进程已实现设备身份、限时一次性配对码、配对/重连 Ed25519 挑战签名、证书指纹固定、TLS WebSocket、`SO_PEERCRED` Unix IPC、systemd 服务和健康 CLI;控制 helper 源码新增无需用户认证的固定证书 TLS/WSS `Hello` 探测,控制 API 与 UI 可显示实际握手耗时、协议版本、终端/桌面/文件能力和 Edge 在线状态,不读取 Client 私钥或配对码;可选 Edge 客户端使用独立作用域令牌主动注册 60 秒短 TTL 路由、每 30 秒续期并在优雅退出时 connection-bound 注销,在心跳间隔内用设备私钥和一次性 nonce 主动 poll/ack 签名 Session Intent,只有 Client Ed25519 签名、本地配对公钥、permission 与 allowed user 全部通过才接受;在线/信令计数进入 CLI 和 WSS Hello/Status;已生成静态 x64 便携包与 DEBRPM/原生 DEB 由 Ubuntu CI 构建;新增探测按要求尚未编译,整体仍未在 Linux 主机运行验证 |
+5 -5
View File
@@ -43,20 +43,20 @@ node .\packaging\windows\create-update-manifest.mjs `
生成器拒绝覆盖已有清单,避免意外替换已发布元数据。将 MSI 和 `stable.json` 上传到只提供 HTTPS 的静态发布源;清单和安装包 URL 不允许凭据、query 或 fragment。
仓库的 `.github/workflows/release-windows.yml` 把该顺序固化为受保护的标签发布。标签必须精确等于 Cargo workspace 版本,例如 `v0.3.0`。工作流在 `windows-production` environment 中运行,必须配置并限制以下 secrets
Drone 构建配置只生成未签名开发包,不读取生产签名材料。正式发布应在受保护的 Drone 发布流水线或离线签名环境中执行,并把版本标签精确绑定到 Cargo workspace 版本,例如 `v0.3.0`。发布环境必须配置并限制以下 secrets
- `WINDOWS_SIGNING_PFX_BASE64`:生产 Authenticode PFX 的 Base64 字节。
- `WINDOWS_SIGNING_PFX_PASSWORD`PFX 密码。
- `UPDATE_ED25519_PRIVATE_KEY_PEM`Ed25519 PKCS#8 私钥全文。
- `UPDATE_ED25519_PUBLIC_KEY_BASE64`:预先登记的 32 字节公钥 Base64,用来阻止误用另一把私钥发布。
工作流临时导入 PFX,验证私钥、有效期和 Code Signing EKU,以 SHA-256 和 RFC 3161 时间戳签署控制端内部 EXE、PowerShell helper、控制端 MSI、Windows Host PowerShell 模块和 Host MSI,并在生成任何发布哈希前逐个验签。随后生成 Ed25519 更新清单SPDX JSON SBOM 和两个 MSI 的 GitHub build provenance attestation,先创建不可变的版本 Release,最后才替换 `stable-channel` Release 中的 `stable.json`。固定清单地址为:
发布流水线临时导入 PFX,验证私钥、有效期和 Code Signing EKU,以 SHA-256 和 RFC 3161 时间戳签署控制端内部 EXE、PowerShell helper、控制端 MSI、Windows Host PowerShell 模块和 Host MSI,并在生成任何发布哈希前逐个验签。随后生成 Ed25519 更新清单SPDX JSON SBOM,先上传不可变的版本资产,最后才替换稳定通道中的 `stable.json`。固定清单地址为:
```text
https://github.com/<owner>/<repository>/releases/download/stable-channel/stable.json
https://updates.example.com/stable/stable.json
```
GitHub 资产会重定向到 HTTPS 对象存储;客户端只跟随 HTTPS 且最多三次。`windows-production` 应配置 required reviewers,并保护版本标签的创建权限。工作流缺少任何 secret、标签与源码版本不一致、证书或时间戳无效、最终 MSI 验签失败公钥不匹配时都会在发布通道更新前失败
发布对象存储必须只提供 HTTPS;客户端只跟随 HTTPS 且最多三次。Drone 发布环境应配置审批、保护版本标签的创建权限,并在缺少任何 secret、标签与源码版本不一致、证书或时间戳无效、最终 MSI 验签失败公钥不匹配时,在更新发布通道前终止
## 4. 控制端配置
@@ -87,4 +87,4 @@ GitHub 资产会重定向到 HTTPS 对象存储;客户端只跟随 HTTPS 且
## 6. 正式发布边界
仓库已实现客户端升级机制、签名清单生成器和受保护的 GitHub 标签发布流水线,但不会内置生产私钥、PFX 或把信任公钥静默写入客户端。普通 CI 和本地打包明确生成未签名开发包;只有配置上述 environment 和 secrets 的标签工作流会生成正式签名资产。该工作流源码按当前“只开发、不编译/发布”的要求尚未实际执行,首次生产发布仍需审计 environment、证书链、时间戳服务、GitHub Release 下载和真实 MSI 原位升级。
仓库已实现客户端升级机制、签名清单生成器和 Drone 构建入口,但不会内置生产私钥、PFX 或把信任公钥静默写入客户端。普通 Drone CI 和本地打包明确生成未签名开发包;正式签名资产必须由受保护的发布环境生成。首次生产发布仍需审计 Drone 权限、证书链、时间戳服务、对象存储下载和真实 MSI 原位升级。