Files
BitBetter/build.sh
Lorenzo Moscati 9962717481 Fast patching via IL rewriting of Bitwarden images (#278)
* Fast patching via IL rewriting of Bitwarden images

Brings back the pre-047c4dd approach of patching pre-built Bitwarden
images instead of cloning and building from source. The fast patch mode
(now default) pulls ghcr.io/bitwarden/{api,identity} and rewrites
Core.dll in-place using Mono.Cecil, bypassing the full source build.

Updated to work with current Bitwarden:
- Uses SingleFileExtractor.Core to extract Core.dll from the
  PublishSingleFile bundle before patching; replaces the native
  launcher with a shell script wrapper (exec dotnet /app/Api.dll)
  so entrypoint.sh continues to work unchanged
- LicensingService search is now namespace-agnostic (handles the
  Bit.Core.Services → Bit.Core.Billing.Services rename)
- Thumbprint matching uses Contains() instead of Equals() to handle
  the hidden Unicode LRM character prepended to the production
  thumbprint string literal in the compiled IL

The original source-build path is preserved and accessible via
BITBETTER_BUILD_FROM_SOURCE=1.

Signed-off-by: Lorenzo Moscati <lorenzo@moscati.page>

* Address review: fix correctness and robustness

- dotnet publish -c Release with explicit -o to match Dockerfile expectation
- Add --platform "$TARGETPLATFORM" to fast-patch docker builds for parity with source-build mode
- mkdir -p for idempotent .keys directory creation
- Align namespace to BitwardenSelfLicensor (repo convention)
- Branch bundle extraction on .dll extension instead of bare catch; exit 1 with clear message on failure
- Replace First() with FirstOrDefault() + targeted error on missing licensing resource
- FixRuntimeConfig derives framework name/version from includedFrameworks; switch to LatestPatch rollForward

Signed-off-by: Lorenzo Moscati <lorenzo@moscati.page>

* Add BITBETTER_BUILD_FROM_SOURCE notes to README.md

Signed-off-by: Lorenzo Moscati <lorenzo@moscati.page>

---------

Signed-off-by: Lorenzo Moscati <lorenzo@moscati.page>
Co-authored-by: h44z <christoph.h@sprinternet.at>
2026-04-12 11:03:25 +01:00

100 lines
3.7 KiB
Bash
Executable File

#!/bin/sh
set -e
DIR=`dirname "$0"`
DIR=`exec 2>/dev/null;(cd -- "$DIR") && cd -- "$DIR"|| cd "$DIR"; unset PWD; /usr/bin/pwd || /bin/pwd || pwd`
BW_VERSION=$(curl -sL https://go.btwrdn.co/bw-sh-versions | grep '^ *"'coreVersion'":' | awk -F\: '{ print $2 }' | sed -e 's/,$//' -e 's/^"//' -e 's/"$//')
echo "Building BitBetter for BitWarden version $BW_VERSION"
# Enable BuildKit for better build experience and to ensure platform args are populated
export DOCKER_BUILDKIT=1
export COMPOSE_DOCKER_CLI_BUILD=1
# Determine host architecture to use as default BUILDPLATFORM / TARGETPLATFORM if not supplied.
# Allow override via environment variables when invoking the script.
HOST_UNAME_ARCH=$(uname -m 2>/dev/null || echo unknown)
case "$HOST_UNAME_ARCH" in
x86_64|amd64) DEFAULT_ARCH=amd64 ;;
aarch64|arm64) DEFAULT_ARCH=arm64 ;;
armv7l|armv7) DEFAULT_ARCH=arm/v7 ;;
*) DEFAULT_ARCH=amd64 ;;
esac
: "${BUILDPLATFORM:=linux/${DEFAULT_ARCH}}"
: "${TARGETPLATFORM:=linux/${DEFAULT_ARCH}}"
echo "Using BUILDPLATFORM=$BUILDPLATFORM TARGETPLATFORM=$TARGETPLATFORM"
# If there aren't any keys, generate them first.
[ -e "$DIR/.keys/cert.cert" ] || "$DIR/.keys/generate-keys.sh"
if [ "${BITBETTER_BUILD_FROM_SOURCE:-0}" = "1" ]; then
echo "--- Source build mode ---"
# Prepare Bitwarden server repository
rm -rf $DIR/server
git clone --branch "v${BW_VERSION}" --depth 1 https://github.com/bitwarden/server.git $DIR/server
# Replace certificate file and thumbprint
old_thumbprint=$(openssl x509 -inform DER -fingerprint -noout -in $DIR/server/src/Core/licensing.cer | cut -d= -f2 | tr -d ':')
new_thumbprint=$(openssl x509 -inform DER -fingerprint -noout -in $DIR/.keys/cert.cert | cut -d= -f2 | tr -d ':')
sed -i -e "s/$old_thumbprint/$new_thumbprint/g" $DIR/server/src/Core/Billing/Services/Implementations/LicensingService.cs
cp $DIR/.keys/cert.cert $DIR/server/src/Core/licensing.cer
docker build \
--no-cache \
--platform "$TARGETPLATFORM" \
--build-arg BUILDPLATFORM="$BUILDPLATFORM" \
--build-arg TARGETPLATFORM="$TARGETPLATFORM" \
--label com.bitwarden.product="bitbetter" \
-f $DIR/server/src/Api/Dockerfile \
-t bitbetter/api \
$DIR/server
docker build \
--no-cache \
--platform "$TARGETPLATFORM" \
--build-arg BUILDPLATFORM="$BUILDPLATFORM" \
--build-arg TARGETPLATFORM="$TARGETPLATFORM" \
--label com.bitwarden.product="bitbetter" \
-f $DIR/server/src/Identity/Dockerfile \
-t bitbetter/identity \
$DIR/server
else
echo "--- Fast patch mode ---"
mkdir -p "$DIR/src/bitBetter/.keys"
cp "$DIR/.keys/cert.cert" "$DIR/src/bitBetter/.keys/cert.cert"
# Build the patcher tool inside the SDK container
docker run --rm \
-v "$DIR/src/bitBetter:/bitBetter" \
-w /bitBetter \
mcr.microsoft.com/dotnet/sdk:8.0 sh build.sh
docker build \
--no-cache \
--platform "$TARGETPLATFORM" \
--label com.bitwarden.product="bitbetter" \
--build-arg BITWARDEN_TAG="ghcr.io/bitwarden/api:$BW_VERSION" \
-t bitbetter/api \
"$DIR/src/bitBetter"
docker build \
--no-cache \
--platform "$TARGETPLATFORM" \
--label com.bitwarden.product="bitbetter" \
--build-arg BITWARDEN_TAG="ghcr.io/bitwarden/identity:$BW_VERSION" \
-t bitbetter/identity \
"$DIR/src/bitBetter"
fi
docker tag bitbetter/api bitbetter/api:latest
docker tag bitbetter/identity bitbetter/identity:latest
docker tag bitbetter/api bitbetter/api:$BW_VERSION
docker tag bitbetter/identity bitbetter/identity:$BW_VERSION
# Remove old instances of the image after a successful build.
ids=$( docker image ls --format="{{ .ID }} {{ .Tag }}" 'bitbetter/*' | grep -E -v -- "CREATED|latest|${BW_VERSION}" | awk '{ ids = (ids ? ids FS $1 : $1) } END { print ids }' )
[ -n "$ids" ] && docker rmi $ids || true