diff --git a/.github/workflows/_tauri.yml b/.github/workflows/_tauri.yml index a05216cc1..29a04d7cf 100644 --- a/.github/workflows/_tauri.yml +++ b/.github/workflows/_tauri.yml @@ -115,6 +115,13 @@ jobs: name: ${{ env.ARTIFACT_DST }}-pkg path: ${{ env.ARTIFACT_SRC }}.${{ matrix.pkg-extension }} if-no-files-found: error + - name: Upload rpm package + if: ${{ runner.os == 'Linux' }} + uses: actions/upload-artifact@v4 + with: + name: ${{ env.ARTIFACT_DST }}-rpm + path: ${{ env.ARTIFACT_SRC }}.rpm + if-no-files-found: error - name: Upload Release Assets # Only upload the GUI Client build to the drafted release on main builds if: ${{ github.ref_name == 'main' }} diff --git a/rust/Dockerfile-rpm b/rust/Dockerfile-rpm new file mode 100644 index 000000000..61a8f649f --- /dev/null +++ b/rust/Dockerfile-rpm @@ -0,0 +1,8 @@ +# Dockerfile for building RPM packages for CentOS +# CentOS Stream 9 +FROM quay.io/centos/centos:stream9 + +RUN yum install -y rpm-build systemd-rpm-macros && yum clean all + +RUN mkdir -p /root/rpmbuild +WORKDIR /root/rpmbuild diff --git a/rust/gui-client/.dockerignore b/rust/gui-client/.dockerignore new file mode 100644 index 000000000..0cd0ae01b --- /dev/null +++ b/rust/gui-client/.dockerignore @@ -0,0 +1,2 @@ +node_modules/ +rpmbuild/ diff --git a/rust/gui-client/.gitignore b/rust/gui-client/.gitignore index a93be8561..97309cdad 100644 --- a/rust/gui-client/.gitignore +++ b/rust/gui-client/.gitignore @@ -7,8 +7,9 @@ yarn-error.log* pnpm-debug.log* lerna-debug.log* -node_modules dist-ssr +node_modules +rpmbuild *.local # Editor directories and files diff --git a/rust/gui-client/README.md b/rust/gui-client/README.md index 02d3e793c..7e31c04b7 100644 --- a/rust/gui-client/README.md +++ b/rust/gui-client/README.md @@ -75,6 +75,33 @@ If that doesn't work: - Click `New client secret` - Note down the secret value. This should be entered into the GitHub repository's secrets as `AZURE_CLIENT_SECRET`. +## Installing on CentOS 9 + +These instructions will move to the knowledge base once the first release supporting CentOS 9 is cut. + +### Install system tray + +GNOME Shell 40 in CentOS 9 does not have a system tray by default. Use these steps to install it. + +For other desktops like xfce4 or KDE, the system tray may already work properly. + +1. `sudo dnf install epel-release` (Needed to get GNOME extensions) +2. `sudo dnf install gnome-shell-extension-appindicator` +3. Log out and back in to restart GNOME +4. `gnome-extensions enable appindicatorsupport@rgcjonas.gmail.com` (This will tab-complete.) + +### Install Firezone + +1. Download the RPM +2. `sudo dnf install systemd-resolved` (Installing it explicitly prevents it from being auto-removed if Firezone is removed) +3. `sudo dnf install ./firezone-client-gui-*.rpm` +4. `sudo usermod -aG firezone-client $USER` +5. `sudo systemctl enable firezone-client-ipc.service` (See https://www.freedesktop.org/software/systemd/man/latest/systemd.preset.html, "It is not recommended to ship preset files within the respective software packages implementing the units". The Fedora family of distros also seem to have their own policy that installing a service should not auto-start or enable it.) +6. Reboot to finish adding yourself to the group. Logging out and back in is not enough. This also starts the new services for us. +7. `sudo cp /etc/resolv.conf /etc/resolv.conf.before-firezone` Back up your resolv.conf file. If anything goes wrong with your DNS, you can copy this back into place. +8. `sudo ln --force --symbolic /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf` This puts `systemd-resolved`, and therefore Firezone, in control of the system's DNS. `systemd-resolved` does not do this automatically, since it's under `/etc`. +9. Run `firezone-client-gui` from the app menu. + ## Running From this dir: diff --git a/rust/gui-client/build.sh b/rust/gui-client/build.sh index f97877958..01d149e38 100755 --- a/rust/gui-client/build.sh +++ b/rust/gui-client/build.sh @@ -6,6 +6,9 @@ set -euo pipefail # Dir where all the bundles are built BUNDLES_DIR=../target/release/bundle/deb +# Prep the RPM container +docker build . -f ../Dockerfile-rpm -t rpmbuild + # Copy frontend dependencies cp node_modules/flowbite/dist/flowbite.min.js src/ @@ -21,6 +24,23 @@ rm -rf "$BUNDLES_DIR" # Compile Rust and bundle pnpm tauri build +# Build the RPM file +docker run \ +--rm \ +-v $PWD/..:/root/rpmbuild \ +-v /usr/lib:/root/libs \ +-w /root/rpmbuild/gui-client \ +rpmbuild \ +rpmbuild \ +-bb src-tauri/rpm_files/firezone-gui-client.spec \ +--define "_topdir /root/rpmbuild/gui-client/rpmbuild" + +# Un-mess-up the permissions Docker gave it +sudo chown --recursive $USER:$USER rpmbuild + +# Give it a predictable name +cp rpmbuild/RPMS/*/firezone-client-gui-*rpm "firezone-client-gui.rpm" + # Delete the deb that Tauri built. We're going to modify and rebuild it. rm "$BUNDLES_DIR"/*.deb @@ -37,6 +57,6 @@ pushd "$INTERMEDIATE_DIR" tar -C "control" -czf "control.tar.gz" control md5sums postinst prerm # Rebuild the deb package, and give it a predictable name that -# `tauri-rename-ubuntu.sh` can fix +# `tauri-rename-linux.sh` can fix ar rcs "../firezone-client-gui.deb" debian-binary control.tar.gz data.tar.gz popd diff --git a/rust/gui-client/src-tauri/deb_files/firezone-client-ipc.service b/rust/gui-client/src-tauri/deb_files/firezone-client-ipc.service index 175b3a1ba..2bf0041ed 100644 --- a/rust/gui-client/src-tauri/deb_files/firezone-client-ipc.service +++ b/rust/gui-client/src-tauri/deb_files/firezone-client-ipc.service @@ -1,5 +1,7 @@ [Unit] Description=Firezone Client +After=systemd-resolved.service +Wants=systemd-resolved.service [Service] AmbientCapabilities=CAP_NET_ADMIN @@ -42,7 +44,7 @@ EnvironmentFile=-/etc/default/firezone-client-ipc ExecStart=firezone-client-ipc run Type=notify -# Unfortunately we may need root to control DNS +# Unfortunately we need root to control DNS User=root Group=firezone-client diff --git a/rust/gui-client/src-tauri/rpm_files/firezone-client-gui.desktop b/rust/gui-client/src-tauri/rpm_files/firezone-client-gui.desktop new file mode 100644 index 000000000..476ff1546 --- /dev/null +++ b/rust/gui-client/src-tauri/rpm_files/firezone-client-gui.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Categories= +Comment=Firezone +Exec=firezone-client-gui +Icon=firezone-client-gui +Name=firezone-client-gui +Terminal=false +Type=Application diff --git a/rust/gui-client/src-tauri/rpm_files/firezone-gui-client.spec b/rust/gui-client/src-tauri/rpm_files/firezone-gui-client.spec new file mode 100644 index 000000000..2a74b00a0 --- /dev/null +++ b/rust/gui-client/src-tauri/rpm_files/firezone-gui-client.spec @@ -0,0 +1,197 @@ +Name: firezone-client-gui +# mark:next-gui-version +Version: 1.3.11 +Release: 1%{?dist} +Summary: The GUI Client for Firezone + +URL: https://firezone.dev +License: Apache-2.0 +Requires: systemd-resolved +BuildRequires: systemd-rpm-macros + +# For some reason, the Ubuntu version of `rpmbuild` notices that we're providing our own WebKit and other libs, but the CentOS version doesn't. So we explicitly tell it not to worry about all these libs. +%global __requires_exclude ^(libdbus-1|libgdk-3|libgio-2.0|libglib-2.0|libgtk-3|libjavascriptcoregtk-4.1|libm|libsoup-3.0|libwebkit2gtk-4.1).so.* + +%description + +%prep + +%build + +%install +mkdir -p \ +"%{buildroot}/usr/bin" \ +"%{buildroot}/usr/lib/dev.firezone.client/unused" + +BINS="%{_topdir}/../../target/release" + +cp "$BINS/firezone-client-ipc" "%{buildroot}/usr/bin/" +cp "$BINS/firezone-client-gui" "%{buildroot}/usr/lib/dev.firezone.client/" +cp "%{_topdir}/../src-tauri/rpm_files/gui-shim.sh" "%{buildroot}/usr/bin/firezone-client-gui" + +LIBS="/root/libs/$(uname -m)-linux-gnu" + +# DNF expects libc and ld-linux to be packaged, because it checks the exes with ldd or something, but if we actually use them, the GUI process will segfault. So just dump them somewhere unused. +UNUSED_DIR="%{buildroot}/usr/lib/dev.firezone.client/unused" + +%ifarch aarch64 +cp \ +"$LIBS/ld-linux-aarch64.so.1" \ +"$LIBS/libc.so.6" \ +"$UNUSED_DIR" +%endif + +%ifarch x86_64 +cp \ +"$LIBS/ld-linux-x86-64.so.2" \ +"$LIBS/libc.so.6" \ +"$UNUSED_DIR" +%endif + +cp \ +"$LIBS/libappindicator3.so.1" \ +"$LIBS/libayatana-appindicator3.so.1" \ +"$LIBS/libayatana-ido3-0.4.so.0" \ +"$LIBS/libayatana-indicator3.so.7" \ +"$LIBS/libdbus-1.so.3" \ +"$LIBS/libdbusmenu-glib.so.4" \ +"$LIBS/libdbusmenu-gtk3.so.4" \ +"$LIBS/libfreetype.so.6" \ +"$LIBS/libgdk-3.so.0" \ +"$LIBS/libgio-2.0.so.0" \ +"$LIBS/libglib-2.0.so.0" \ +"$LIBS/libgmodule-2.0.so.0" \ +"$LIBS/libgtk-3.so.0" \ +"$LIBS/libicudata.so.70" \ +"$LIBS/libicui18n.so.70" \ +"$LIBS/libicuuc.so.70" \ +"$LIBS/libjavascriptcoregtk-4.1.so.0" \ +"$LIBS/libjpeg.so.8" \ +"$LIBS/libm.so.6" \ +"$LIBS/libmanette-0.2.so.0" \ +"$LIBS/libpcre.so.3" \ +"$LIBS/libpcre2-8.so.0" \ +"$LIBS/libsoup-3.0.so.0" \ +"$LIBS/libstdc++.so.6" \ +"$LIBS/libwayland-client.so.0" \ +"$LIBS/libwayland-server.so.0" \ +"$LIBS/libwebkit2gtk-4.1.so.0" \ +"$LIBS/libxcb.so.1" \ +"$LIBS/libxcb-shm.so.0" \ +"$LIBS/libX11.so.6" \ +"$LIBS/libX11-xcb.so.1" \ +"%{buildroot}/usr/lib/dev.firezone.client/" + +ls -lash "%{buildroot}/usr/lib/dev.firezone.client" + +WEBKIT_DIR="$(uname -m)-linux-gnu/webkit2gtk-4.1" +mkdir -p "%{buildroot}/usr/lib/$WEBKIT_DIR" + +cp \ +"/root/libs/$WEBKIT_DIR/WebKitNetworkProcess" \ +"/root/libs/$WEBKIT_DIR/WebKitWebProcess" \ +"%{buildroot}/usr/lib/$WEBKIT_DIR" + +ICONS="%{buildroot}/usr/share/icons/hicolor" + +mkdir -p \ +"%{buildroot}/usr/lib/systemd/system" \ +"%{buildroot}/usr/lib/sysusers.d" \ +"%{buildroot}/usr/share/applications" \ +"$ICONS/32x32/apps" \ +"$ICONS/128x128/apps" \ +"$ICONS/512x512/apps" + +cp \ +"%{_topdir}/../src-tauri/deb_files/firezone-client-ipc.service" \ +"%{buildroot}/usr/lib/systemd/system/" + +cp \ +"%{_topdir}/../src-tauri/deb_files/sysusers.conf" \ +"%{buildroot}/usr/lib/sysusers.d/firezone-client-ipc.conf" + +cp \ +"%{_topdir}/../src-tauri/rpm_files/firezone-client-gui.desktop" \ +"%{buildroot}/usr/share/applications/" + +cp \ +"%{_topdir}/../src-tauri/icons/32x32.png" \ +"$ICONS/32x32/apps/firezone-client-gui.png" + +cp \ +"%{_topdir}/../src-tauri/icons/128x128.png" \ +"$ICONS/128x128/apps/firezone-client-gui.png" + +cp \ +"%{_topdir}/../src-tauri/icons/icon.png" \ +"$ICONS/512x512/apps/firezone-client-gui.png" + +%files +/usr/bin/firezone-client-ipc +/usr/bin/firezone-client-gui +/usr/lib/dev.firezone.client/firezone-client-gui + +/usr/lib/dev.firezone.client/libappindicator3.so.1 +/usr/lib/dev.firezone.client/libayatana-appindicator3.so.1 +/usr/lib/dev.firezone.client/libayatana-ido3-0.4.so.0 +/usr/lib/dev.firezone.client/libayatana-indicator3.so.7 +/usr/lib/dev.firezone.client/libdbus-1.so.3 +/usr/lib/dev.firezone.client/libdbusmenu-glib.so.4 +/usr/lib/dev.firezone.client/libdbusmenu-gtk3.so.4 +/usr/lib/dev.firezone.client/libfreetype.so.6 +/usr/lib/dev.firezone.client/libgdk-3.so.0 +/usr/lib/dev.firezone.client/libgio-2.0.so.0 +/usr/lib/dev.firezone.client/libglib-2.0.so.0 +/usr/lib/dev.firezone.client/libgmodule-2.0.so.0 +/usr/lib/dev.firezone.client/libgtk-3.so.0 +/usr/lib/dev.firezone.client/libicudata.so.70 +/usr/lib/dev.firezone.client/libicui18n.so.70 +/usr/lib/dev.firezone.client/libicuuc.so.70 +/usr/lib/dev.firezone.client/libjavascriptcoregtk-4.1.so.0 +/usr/lib/dev.firezone.client/libjpeg.so.8 +/usr/lib/dev.firezone.client/libm.so.6 +/usr/lib/dev.firezone.client/libmanette-0.2.so.0 +/usr/lib/dev.firezone.client/libpcre.so.3 +/usr/lib/dev.firezone.client/libpcre2-8.so.0 +/usr/lib/dev.firezone.client/libsoup-3.0.so.0 +/usr/lib/dev.firezone.client/libstdc++.so.6 +/usr/lib/dev.firezone.client/libwayland-client.so.0 +/usr/lib/dev.firezone.client/libwayland-server.so.0 +/usr/lib/dev.firezone.client/libwebkit2gtk-4.1.so.0 +/usr/lib/dev.firezone.client/libxcb.so.1 +/usr/lib/dev.firezone.client/libxcb-shm.so.0 +/usr/lib/dev.firezone.client/libX11.so.6 +/usr/lib/dev.firezone.client/libX11-xcb.so.1 + +/usr/lib/systemd/system/firezone-client-ipc.service +/usr/lib/sysusers.d/firezone-client-ipc.conf + +/usr/share/applications/firezone-client-gui.desktop +/usr/share/icons/hicolor/32x32/apps/firezone-client-gui.png +/usr/share/icons/hicolor/128x128/apps/firezone-client-gui.png +/usr/share/icons/hicolor/512x512/apps/firezone-client-gui.png + +%ifarch aarch64 +/usr/lib/aarch64-linux-gnu/webkit2gtk-4.1/WebKitNetworkProcess +/usr/lib/aarch64-linux-gnu/webkit2gtk-4.1/WebKitWebProcess + +/usr/lib/dev.firezone.client/unused/ld-linux-aarch64.so.1 +/usr/lib/dev.firezone.client/unused/libc.so.6 +%endif + +%ifarch x86_64 +/usr/lib/x86_64-linux-gnu/webkit2gtk-4.1/WebKitNetworkProcess +/usr/lib/x86_64-linux-gnu/webkit2gtk-4.1/WebKitWebProcess + +/usr/lib/dev.firezone.client/unused/ld-linux-x86-64.so.2 +/usr/lib/dev.firezone.client/unused/libc.so.6 +%endif + +%post +%systemd_post firezone-client-ipc.service + +%preun +%systemd_preun firezone-client-ipc.service + +%postun +%systemd_postun_with_restart firezone-client-ipc.service diff --git a/rust/gui-client/src-tauri/rpm_files/gui-shim.sh b/rust/gui-client/src-tauri/rpm_files/gui-shim.sh new file mode 100755 index 000000000..49f450b98 --- /dev/null +++ b/rust/gui-client/src-tauri/rpm_files/gui-shim.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +# Installed as `/usr/bin/firezone-client-gui` on RPM systems since we bundle a bunch of libs + +set -euo pipefail + +LD_LIBRARY_PATH=/usr/lib/dev.firezone.client exec /usr/lib/dev.firezone.client/firezone-client-gui diff --git a/rust/gui-client/src-tauri/tauri.conf.json b/rust/gui-client/src-tauri/tauri.conf.json index 886e510b4..50ab9a71a 100644 --- a/rust/gui-client/src-tauri/tauri.conf.json +++ b/rust/gui-client/src-tauri/tauri.conf.json @@ -13,18 +13,11 @@ "/usr/lib/systemd/system/firezone-client-ipc.service": "./deb_files/firezone-client-ipc.service", "/usr/lib/sysusers.d/firezone-client-ipc.conf": "./deb_files/sysusers.conf" } - }, - "rpm": { - "files": { - "/usr/lib/systemd/system/firezone-client-ipc.service": "./deb_files/firezone-client-ipc.service", - "/usr/lib/sysusers.d/firezone-client-ipc.conf": "./deb_files/sysusers.conf" - } } }, "targets": [ "deb", - "msi", - "rpm" + "msi" ], "windows": { "wix": { diff --git a/scripts/build/tauri-rename-linux.sh b/scripts/build/tauri-rename-linux.sh index b4a60ef4f..d1bf00656 100755 --- a/scripts/build/tauri-rename-linux.sh +++ b/scripts/build/tauri-rename-linux.sh @@ -20,6 +20,7 @@ ls "$TARGET_DIR/release" "$TARGET_DIR/release/bundle/deb" cp "$TARGET_DIR/release/firezone-client-gui" "$BINARY_DEST_PATH" cp "$TARGET_DIR/release/firezone-gui-client.dwp" "$BINARY_DEST_PATH.dwp" cp "$TARGET_DIR/release/bundle/deb/firezone-client-gui.deb" "$BINARY_DEST_PATH.deb" +cp "$TARGET_DIR/../gui-client/firezone-client-gui.rpm" "$BINARY_DEST_PATH.rpm" # TODO: Debug symbols for Linux function make_hash() { @@ -31,6 +32,7 @@ function make_hash() { make_hash "$BINARY_DEST_PATH" make_hash "$BINARY_DEST_PATH.dwp" make_hash "$BINARY_DEST_PATH.deb" +make_hash "$BINARY_DEST_PATH.rpm" # Test the deb package, since this script is the easiest place to get a release build DEB_PATH=$(realpath "$BINARY_DEST_PATH.deb") diff --git a/scripts/build/tauri-upload-linux.sh b/scripts/build/tauri-upload-linux.sh index 96f26ff80..951f65844 100755 --- a/scripts/build/tauri-upload-linux.sh +++ b/scripts/build/tauri-upload-linux.sh @@ -15,5 +15,7 @@ fi gh release upload "$TAG_NAME" \ "$BINARY_DEST_PATH".deb \ "$BINARY_DEST_PATH".deb.sha256sum.txt \ + "$BINARY_DEST_PATH".rpm \ + "$BINARY_DEST_PATH".rpm.sha256sum.txt \ $clobber \ --repo "$REPOSITORY"