Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions releases/Dockerfile.deploy
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
ARG RUST_HASH="5861091755a3298619c94a6cbf0534062cfeb9afcda9f698a13df3ef64e2dd8b"
ARG TAURI_TARGET=""
ARG TAURI_RUNNER="cargo"
ARG CARGO_XWIN_VERSION="0.21.4"
ARG PNPM_VERSION="10.29.1"
ARG PNPM_TARBALL_INTEGRITY="sha512-SNriM2NaZFdoowKNGVRcrMFohjnusfNzTkLW1rlxr78iqhrJr1Khc9nDogwVhXz6QA8ZmU15ovYm/Mc/zNqbvA=="
ARG TAURI_BUNDLE_TARGETS_JSON='["appimage","deb","rpm"]'
ARG SOURCE_DATE_EPOCH="1704067200"
ARG DEBUG="0"
Expand All @@ -11,6 +14,9 @@ FROM docker.io/rust@sha256:${RUST_HASH} AS builder

ARG TAURI_TARGET
ARG TAURI_RUNNER
ARG CARGO_XWIN_VERSION
ARG PNPM_VERSION
ARG PNPM_TARBALL_INTEGRITY
ARG TAURI_BUNDLE_TARGETS_JSON
ARG SOURCE_DATE_EPOCH
ARG DEBUG
Expand Down Expand Up @@ -53,7 +59,16 @@ RUN set -eux; \
${webkit_pkgs}; \
rm -rf /var/lib/apt/lists/*

RUN npm install -g pnpm@10.29.1
RUN set -eux; \
pnpm_tgz="/tmp/pnpm-${PNPM_VERSION}.tgz"; \
curl -fsSL --retry 8 --retry-delay 2 --retry-all-errors --connect-timeout 20 \
"https://registry.npmjs.org/pnpm/-/pnpm-${PNPM_VERSION}.tgz" \
-o "${pnpm_tgz}"; \
# Verify the exact pnpm tarball bytes before installing any global tooling from npm
node -e 'const crypto=require("crypto"); const fs=require("fs"); const [file, integrity]=process.argv.slice(1); const [algo, expected]=integrity.split("-"); const actual=crypto.createHash(algo).update(fs.readFileSync(file)).digest("base64"); if (actual !== expected) { console.error(`Integrity mismatch for ${file}\\nexpected: ${integrity}\\nactual: ${algo}-${actual}`); process.exit(1); }' \
"${pnpm_tgz}" "${PNPM_TARBALL_INTEGRITY}"; \
npm install -g "${pnpm_tgz}"; \
rm -f "${pnpm_tgz}"

COPY --from=proj . .
COPY lib/docker_deploy /opt/secluso-docker-deploy
Expand All @@ -72,7 +87,7 @@ ENV RUST_LOG=tauri_bundler=info,tauri_cli_node=info,ureq=warn
RUN /opt/secluso-docker-deploy/setup_linuxdeploy_plugins.bash
RUN pnpm install --frozen-lockfile
RUN rustup target add ${TAURI_TARGET}
RUN if [ "${TAURI_RUNNER}" = "cargo-xwin" ]; then cargo install --locked cargo-xwin; fi
RUN if [ "${TAURI_RUNNER}" = "cargo-xwin" ]; then cargo install --locked cargo-xwin --version ${CARGO_XWIN_VERSION}; fi
RUN /opt/secluso-docker-deploy/run_tauri_build.bash

FROM scratch AS artifact
Expand Down
51 changes: 49 additions & 2 deletions releases/lib/compare.bash
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,26 @@ compare_runs() {

crate1="$(jq -r '.crate' <<<"$a")"
ver1="$(jq -r '.version' <<<"$a")"
p1="$run1/$(jq -r '.bin_path' <<<"$a")"
local bin_path1
bin_path1="$(jq -r '.bin_path' <<<"$a")"
p1="$(validate_manifest_bin_path "$run1" "$bin_path1" "$tgt" "$bin")" || {
echo "FAIL: invalid manifest bin_path for run1 $pkg | $tgt | $bin: $bin_path1"
status=1
continue
}
lock1="$(jq -r '.crate_lock_sha256' <<<"$a")"
dig1="$(jq -r '.rust_digest' <<<"$a")"
sha1="$(jq -r '.sha256 // empty' <<<"$a")"

crate2="$(jq -r '.crate' <<<"$b")"
ver2="$(jq -r '.version' <<<"$b")"
p2="$run2/$(jq -r '.bin_path' <<<"$b")"
local bin_path2
bin_path2="$(jq -r '.bin_path' <<<"$b")"
p2="$(validate_manifest_bin_path "$run2" "$bin_path2" "$tgt" "$bin")" || {
echo "FAIL: invalid manifest bin_path for run2 $pkg | $tgt | $bin: $bin_path2"
status=1
continue
}
lock2="$(jq -r '.crate_lock_sha256' <<<"$b")"
dig2="$(jq -r '.rust_digest' <<<"$b")"
sha2="$(jq -r '.sha256 // empty' <<<"$b")"
Expand Down Expand Up @@ -201,3 +213,38 @@ compare_runs() {

return "$status"
}

validate_manifest_bin_path() {
local run_dir="$1"
local bin_path="$2"
local expected_target="$3"
local expected_bin="$4"

# Compare should only read files from the run's artifacts tree.
# Manifest data is treated as untrusted input here.
# So reject any path that could escape the run directory or redirect hashing to an unrelated local file.
[[ -n "$bin_path" ]] || return 1
[[ "$bin_path" == "artifacts/$expected_target/"* ]] || return 1
[[ "$bin_path" != /* ]] || return 1
[[ "$(basename "$bin_path")" == "$expected_bin" ]] || return 1

local old_ifs="$IFS"
local component
local current="$run_dir"
IFS='/'
for component in $bin_path; do
if [[ -z "$component" || "$component" == "." || "$component" == ".." ]]; then
IFS="$old_ifs"
return 1
fi
current="$current/$component"
# Reject symlinks anywhere on the manifest-controlled path so a manifest cannot redirect hashing outside the selected run through the filesystem.
if [[ -L "$current" ]]; then
IFS="$old_ifs"
return 1
fi
done
IFS="$old_ifs"

printf '%s/%s' "$run_dir" "$bin_path"
}
104 changes: 89 additions & 15 deletions releases/lib/docker_deploy/run_tauri_build.bash
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,74 @@
# SPDX-License-Identifier: GPL-3.0-or-later
set -euo pipefail

verify_sha256() {
local path="$1"
local expected_sha="$2"
local label="$3"
local actual_sha

actual_sha="$(sha256sum "$path" | awk '{print $1}')"
[[ "$actual_sha" == "$expected_sha" ]] || {
echo "SHA-256 mismatch for $label" >&2
echo " expected: $expected_sha" >&2
echo " actual: $actual_sha" >&2
return 1
}
}

download_verified_file() {
local url="$1"
local path="$2"
local expected_sha="$3"
local label="$4"
local tmp_path="${path}.tmp"

rm -f "$tmp_path"
curl --fail --location \
--retry 8 \
--retry-delay 2 \
--retry-all-errors \
--connect-timeout 20 \
--output "$tmp_path" \
"$url" || {
rm -f "$tmp_path"
return 1
}
verify_sha256 "$tmp_path" "$expected_sha" "$label" || {
rm -f "$tmp_path"
return 1
}
mv "$tmp_path" "$path"
}

expected_apprun_sha256() {
case "$1" in
AppRun-x86_64) echo "f30140a43a0a59e46db21bdefdf749b9e9f2c6946e92afabbacf98b8ae73fb4f" ;;
AppRun-aarch64) echo "072f17c0895a85c490282fe5395c5007e5fc75da727e553b3b8fb680feb11578" ;;
*)
echo "Unsupported AppRun helper: $1" >&2
return 1
;;
esac
}

expected_nsis_utils_sha256() {
local version="$1"

if [[ -n "${TAURI_NSIS_UTILS_SHA256:-}" ]]; then
echo "$TAURI_NSIS_UTILS_SHA256"
return
fi

case "$version" in
0.5.3) echo "5ba143b5db4a87d32d6e7802e033330aae56cbceabe0d1e3ba41948385ad4709" ;;
*)
echo "TAURI_NSIS_UTILS_SHA256 must be set when overriding TAURI_NSIS_UTILS_VERSION (got ${version})" >&2
return 1
;;
esac
}

is_debug_enabled() {
case "${DEBUG:-0}" in
1|true|TRUE|yes|YES|on|ON) return 0 ;;
Expand Down Expand Up @@ -86,25 +154,23 @@ prepare_linux_apprun_cache() {

local cache_dir="/root/.cache/tauri"
local apprun_path="$cache_dir/$apprun_name"
local apprun_tmp="$apprun_path.tmp"
local download_url="https://github.com/tauri-apps/binary-releases/releases/download/apprun-old/${apprun_name}"
local expected_sha

expected_sha="$(expected_apprun_sha256 "$apprun_name")" || exit 1

mkdir -p "$cache_dir"
if [[ -f "$apprun_path" ]]; then
verify_sha256 "$apprun_path" "$expected_sha" "$apprun_name" || {
rm -f "$apprun_path"
}
[[ -f "$apprun_path" ]] && chmod +x "$apprun_path"
fi
if [[ ! -x "$apprun_path" ]]; then
echo "==> prefetching ${apprun_name} for tauri linux bundle"
if curl --fail --location \
--retry 8 \
--retry-delay 2 \
--retry-all-errors \
--connect-timeout 20 \
--output "$apprun_tmp" \
"$download_url"; then
mv "$apprun_tmp" "$apprun_path"
chmod +x "$apprun_path"
else
echo "==> warning: failed to prefetch ${apprun_name}; tauri will attempt its own download later" >&2
rm -f "$apprun_tmp"
fi
# Fail closed here so the build never falls back to Tauri's own unverified download path.
download_verified_file "$download_url" "$apprun_path" "$expected_sha" "$apprun_name"
chmod +x "$apprun_path"
fi

if [[ -f "$apprun_path" ]]; then
Expand All @@ -119,11 +185,19 @@ prepare_windows_nsis_cache() {
local nsis_utils_version="${TAURI_NSIS_UTILS_VERSION:-0.5.3}"
local dll_path="$cache_dir/nsis_tauri_utils.dll"
local download_url="https://github.com/tauri-apps/nsis-tauri-utils/releases/download/nsis_tauri_utils-v${nsis_utils_version}/nsis_tauri_utils.dll"
local expected_sha

expected_sha="$(expected_nsis_utils_sha256 "$nsis_utils_version")" || exit 1

mkdir -p "$cache_dir"
if [[ -f "$dll_path" ]]; then
verify_sha256 "$dll_path" "$expected_sha" "nsis_tauri_utils.dll" || {
rm -f "$dll_path"
}
fi
if [[ ! -f "$dll_path" ]]; then
echo "==> prefetching nsis_tauri_utils.dll (v${nsis_utils_version})"
curl --fail --location --retry 3 --output "$dll_path" "$download_url"
download_verified_file "$download_url" "$dll_path" "$expected_sha" "nsis_tauri_utils.dll"
fi
touch -h -d "@${SOURCE_DATE_EPOCH}" "$dll_path"
}
Expand Down
54 changes: 49 additions & 5 deletions releases/lib/docker_deploy/setup_linuxdeploy_plugins.bash
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,57 @@ set -euo pipefail

install -d /root/.cache/tauri

verify_sha256() {
local path="$1"
local expected_sha="$2"
local label="$3"
local actual_sha

actual_sha="$(sha256sum "$path" | awk '{print $1}')"
[ "$actual_sha" = "$expected_sha" ] || {
echo "SHA-256 mismatch for $label" >&2
echo " expected: $expected_sha" >&2
echo " actual: $actual_sha" >&2
return 1
}
}

download_verified_file() {
local url="$1"
local path="$2"
local expected_sha="$3"
local label="$4"
local tmp_path="${path}.tmp"

rm -f "$tmp_path"
curl -fsSL --retry 8 --retry-delay 2 --retry-all-errors --connect-timeout 20 \
"$url" -o "$tmp_path" || {
rm -f "$tmp_path"
return 1
}
verify_sha256 "$tmp_path" "$expected_sha" "$label" || {
rm -f "$tmp_path"
return 1
}
mv "$tmp_path" "$path"
}

linuxdeploy_url="https://github.com/tauri-apps/binary-releases/releases/download/linuxdeploy/linuxdeploy-x86_64.AppImage"
linuxdeploy_sha256="e762bea85c8eb0d4b3508d46e5c1f037f717d0f9303ae3b4aafc8b04991fa1ef"
linuxdeploy_bin="/root/.cache/tauri/linuxdeploy-x86_64.AppImage"
linuxdeploy_tmp="${linuxdeploy_bin}.tmp"
linuxdeploy_plugin_gtk_commit="b5eb8d05b4c0ed40107fe2158c5d8527f94568ef"
linuxdeploy_plugin_gtk_url="https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gtk/${linuxdeploy_plugin_gtk_commit}/linuxdeploy-plugin-gtk.sh"
linuxdeploy_plugin_gtk_sha256="cb379f9b0733e9ad9f8bd78f8c2fa038aef2478523bb7d4c8e64ff6a1ea3501a"

# Keep linuxdeploy at the canonical path as a real AppImage binary.
# The appimage output plugin mutates the running linuxdeploy file with the dd utility
# If this path is a shell wrapper, that mutation corrupts the wrapper shebang
if [ -f "$linuxdeploy_bin" ]; then
verify_sha256 "$linuxdeploy_bin" "$linuxdeploy_sha256" "linuxdeploy-x86_64.AppImage" || rm -f "$linuxdeploy_bin"
[ -f "$linuxdeploy_bin" ] && chmod +x "$linuxdeploy_bin"
fi
if [ ! -x "$linuxdeploy_bin" ]; then
curl -fsSL "$linuxdeploy_url" -o "$linuxdeploy_tmp"
mv "$linuxdeploy_tmp" "$linuxdeploy_bin"
download_verified_file "$linuxdeploy_url" "$linuxdeploy_bin" "$linuxdeploy_sha256" "linuxdeploy-x86_64.AppImage"
chmod +x "$linuxdeploy_bin"
fi

Expand All @@ -30,8 +71,11 @@ fi
exit 0
EOS

curl -fsSL https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh \
-o /root/.cache/tauri/linuxdeploy-plugin-gtk.real.sh
download_verified_file \
"$linuxdeploy_plugin_gtk_url" \
/root/.cache/tauri/linuxdeploy-plugin-gtk.real.sh \
"$linuxdeploy_plugin_gtk_sha256" \
"linuxdeploy-plugin-gtk.sh"
chmod +x /root/.cache/tauri/linuxdeploy-plugin-gtk.real.sh

cat >/root/.cache/tauri/linuxdeploy-plugin-gtk.sh <<'EOS' && chmod +x /root/.cache/tauri/linuxdeploy-plugin-gtk.sh
Expand Down
2 changes: 1 addition & 1 deletion releases/lib/rust_pipeline.bash
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ build_and_manifest() {
local crate_name="$pkg"

if [[ "$pkg" == "raspberry_camera_hub" ]]; then
features_args=( --build-arg "FEATURES=--features raspberry,telemetry" )
features_args=( --build-arg "FEATURES=--features raspberry" )
crate_name="camera_hub"
elif [[ "$pkg" == "ip_camera_hub" ]]; then
features_args=( --build-arg "FEATURES=--features ip" )
Expand Down