mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-07 21:11:05 +03:00
feat(drm): opt-in DRM/KMS screen capture for Linux/Wayland
adds an opt-in `drm` feature for unattended remote access on Wayland: it captures below the compositor via libdrmtap, so there is no xdg-desktop-portal consent dialog and it works at the login screen. off by default. when the feature is off the build is byte-identical. everything is gated behind feature = "drm" or lives only in the separate rustdesk-unattended-wayland deb, whose package name is the informed consent. architecture (agreed with the maintainer): the capture runs inside the root --service, which already holds the privilege it needs, and streams frames to the user --server over a service-scoped _drm ipc channel. libdrmtap is loaded with dlopen at runtime (no link-time dependency, so the base build is unchanged and it still runs on ubuntu 18), and the .so is built in ci from the rustdesk-org/libdrmtap fork and shipped only in the drm deb. no setcap helper. - service: DrmReader reads scanout directly via the dlopen loader; an IpcDrmCapturer serves _drm consumers with a per-connection capture worker; durable availability cache + pre-warm to avoid enumerate/re-probe restarts - capture: multi-display (targets the selected crtc), hardware cursor over _drm, transient-errno retry with a bounded stall, rejects non-32bpp scanouts before the frame copy - robustness: only active, crtc-bound outputs are offered (an unbound crtc_id=0 connector is filtered and a client-selected 0 is refused, both fall back to pipewire); a per-display rapid-rebuild guard demotes a flapping display to pipewire; per-display (not global) zero-frame failure tracking - root-service hardening: bounded frame allocation and a concurrent-connection cap so a malformed scanout or a buggy consumer cannot OOM or thread-exhaust the service; a negative availability verdict expires so displays that appear after startup recover without a --server restart; exactly-one .so selection in the packaging so a stale object is never silently shipped - build: libdrmtap.so cloned at build time from rustdesk-org/libdrmtap main and bundled only for the --drm deb; ci builds a separate rustdesk-unattended-wayland deb (incl. an ubuntu 18.04 container) - DRM_CAPTURE_SECURITY.md: threat model and hardening notes
This commit is contained in:
62
.github/workflows/flutter-build.yml
vendored
62
.github/workflows/flutter-build.yml
vendored
@@ -1676,6 +1676,61 @@ jobs:
|
||||
mv "$name" /workspace/"${name%%.rpm}-suse.rpm"
|
||||
done
|
||||
|
||||
# --- opt-in unattended-wayland (DRM/KMS) variant: a separate deb ---
|
||||
# Bundles libdrmtap.so (dlopen-ed in-process by the root service) so
|
||||
# enabling consent-free capture is an explicit install choice (the package
|
||||
# name states what it does). Built last so the drm relink can't leak into
|
||||
# the stock deb/rpm above. x86_64 only (the unattended/kiosk/server use
|
||||
# case); the package Conflicts/Replaces the stock rustdesk package.
|
||||
if [[ "${{ matrix.job.arch }}" == "x86_64" ]]; then
|
||||
pushd /workspace
|
||||
echo -e "start packaging unattended-wayland (DRM) deb"
|
||||
# drm-only build deps (meson builds libdrmtap.so from the cloned source),
|
||||
# installed here — not in the stock install list — so the default
|
||||
# drm-off build stays identical to upstream. libdrmtap's meson.build
|
||||
# needs meson >= 0.57 (fs.read) + `meson compile` (>= 0.54); the distro
|
||||
# apt meson is far older on the 18.04 build container, so install it via
|
||||
# pip (pinned < 0.62 for the container's python 3.6). The EGL/GLES dev
|
||||
# packages must be the mesa-specific names (libegl1-mesa-dev /
|
||||
# libgles2-mesa-dev): the newer libegl-dev / libgles-dev metapackages
|
||||
# do not exist on the ubuntu18.04 build container.
|
||||
apt-get install -y ninja-build libdrm-dev libegl1-mesa-dev libgles2-mesa-dev python3-pip
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install 'meson>=0.57,<0.62'
|
||||
# libdrmtap is sourced by cloning the rustdesk-org fork at a pinned
|
||||
# ref (it is no longer a git submodule). DRMTAP_REPO / DRMTAP_REF are
|
||||
# exported so build.py reuses the exact same source. We clone + build
|
||||
# the .so here and hand it to build.py via DRMTAP_PREBUILT_DIR, because
|
||||
# a later build step in this container disturbs the working tree.
|
||||
# rustdesk-org/libdrmtap main tracks the current release (0.4.8+).
|
||||
export DRMTAP_REPO="https://github.com/rustdesk-org/libdrmtap"
|
||||
export DRMTAP_REF="main"
|
||||
git config --global --add safe.directory '*' || true
|
||||
rm -rf third_party/libdrmtap
|
||||
git clone --depth 1 --branch "$DRMTAP_REF" "$DRMTAP_REPO" third_party/libdrmtap
|
||||
test -f third_party/libdrmtap/meson.build || { echo "FATAL: libdrmtap source missing"; exit 1; }
|
||||
# Build libdrmtap.so now, while the cloned source is definitely
|
||||
# present, and stash the real object OUTSIDE the source tree. A later
|
||||
# build step in this container disturbs that working tree (it ends up
|
||||
# empty by the time build.py runs), so build.py picks up this prebuilt
|
||||
# .so via DRMTAP_PREBUILT_DIR instead of rebuilding from source.
|
||||
meson setup third_party/libdrmtap/build-pkg third_party/libdrmtap --buildtype=release
|
||||
meson compile -C third_party/libdrmtap/build-pkg drmtap
|
||||
mkdir -p "$PWD/prebuilt-libdrmtap"
|
||||
find third_party/libdrmtap/build-pkg -maxdepth 1 -name 'libdrmtap.so.0.*' -type f \
|
||||
-exec cp -a {} "$PWD/prebuilt-libdrmtap/" \;
|
||||
export DRMTAP_PREBUILT_DIR="$PWD/prebuilt-libdrmtap"
|
||||
[ -n "$(find "$DRMTAP_PREBUILT_DIR" -name 'libdrmtap.so.0.*' -type f)" ] \
|
||||
|| { echo "FATAL: prebuilt libdrmtap.so missing"; exit 1; }
|
||||
ls -l "$DRMTAP_PREBUILT_DIR"
|
||||
cargo build --locked --lib $JOBS --features hwcodec,flutter,unix-file-copy-paste,drm --release
|
||||
python3 ./build.py --flutter --drm --skip-cargo
|
||||
for name in rustdesk-unattended-wayland*??.deb; do
|
||||
mv "$name" "${name%%.deb}-${{ matrix.job.arch }}.deb"
|
||||
done
|
||||
popd
|
||||
fi
|
||||
|
||||
- name: Publish debian/rpm package
|
||||
if: env.UPLOAD_ARTIFACT == 'true'
|
||||
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
|
||||
@@ -1693,6 +1748,13 @@ jobs:
|
||||
name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb
|
||||
path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb
|
||||
|
||||
- name: Upload unattended-wayland deb
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
if: matrix.job.arch == 'x86_64' && env.UPLOAD_ARTIFACT == 'true'
|
||||
with:
|
||||
name: rustdesk-unattended-wayland-${{ env.VERSION }}-${{ matrix.job.arch }}.deb
|
||||
path: rustdesk-unattended-wayland-${{ env.VERSION }}-${{ matrix.job.arch }}.deb
|
||||
|
||||
# only x86_64 for arch since we can not find newest arm64 docker image to build
|
||||
# old arch image does not make sense for arch since it is "arch" which always update to date
|
||||
# and failed to makepkg arm64 on x86_64
|
||||
|
||||
Reference in New Issue
Block a user