Skip to content
Draft
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
10 changes: 10 additions & 0 deletions util/RustSdk/RustSdk.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<PackageCopyToOutput>true</PackageCopyToOutput>
<Link>runtimes/linux-x64/native/libsdk.so</Link>
</Content>
<Content Include="./rust/target/release/libsdk*.so">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
<Link>runtimes/linux-arm64/native/libsdk.so</Link>
</Content>
<Content Include="./rust/target/release/libsdk*.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
Expand All @@ -50,6 +55,11 @@
<PackageCopyToOutput>true</PackageCopyToOutput>
<Link>runtimes/linux-x64/native/libsdk.so</Link>
</Content>
<Content Include="./rust/target/release/libsdk*.so">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
<Link>runtimes/linux-arm64/native/libsdk.so</Link>
</Content>
<Content Include="./rust/target/release/libsdk*.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
Expand Down
10 changes: 9 additions & 1 deletion util/SeederUtility/Configuration/ServiceCollectionExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@ public static void ConfigureServices(ServiceCollection services, bool enableMang
services.AddSingleton<IPasswordHasher<User>, PasswordHasher<User>>();
services.TryAddSingleton<ISeedReader, SeedReader>();

services.AddDataProtection().SetApplicationName("Bitwarden");
var dpBuilder = services.AddDataProtection().SetApplicationName("Bitwarden");
// Persist DataProtection keys to a shared directory when configured, so
// records this tool encrypts are decryptable by the running app. Needed
// when seeding an instance whose components share a DataProtection key ring
// (e.g. self-host). No-op when the directory isn't set.
if (!string.IsNullOrWhiteSpace(globalSettings.DataProtection.Directory))
{
dpBuilder.PersistKeysToFileSystem(new DirectoryInfo(globalSettings.DataProtection.Directory));
}

services.AddDatabaseRepositories(globalSettings);

Expand Down
115 changes: 115 additions & 0 deletions util/SeederUtility/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
###############################################
# Build stage #
###############################################
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-noble AS build

# Docker buildx supplies these values
ARG TARGETPLATFORM
ARG BUILDPLATFORM

# Install base build dependencies
RUN apt-get update && apt-get install -y \
build-essential \
curl \
pkg-config \
libssl-dev \
&& rm -rf /var/lib/apt/lists/*

# Rust toolchain: RustSdk builds a native library via cargo during publish.
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \
--default-toolchain stable \
--profile minimal \
--no-modify-path

ENV PATH="/root/.cargo/bin:${PATH}"

# Determine target architecture and install cross-compilation tools
RUN case "$TARGETPLATFORM" in \
"linux/amd64") \
RUST_TARGET=x86_64-unknown-linux-gnu && \
RID=linux-x64 && \
ARCH_PACKAGES="" \
;; \
"linux/arm64") \
RUST_TARGET=aarch64-unknown-linux-gnu && \
RID=linux-arm64 && \
ARCH_PACKAGES="gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libc6-dev-arm64-cross" \
;; \
*) \
echo "Unsupported platform: $TARGETPLATFORM" && exit 1 \
;; \
esac \
&& if [ -n "$ARCH_PACKAGES" ]; then \
apt-get update && apt-get install -y $ARCH_PACKAGES && rm -rf /var/lib/apt/lists/* ; \
fi \
&& echo "RUST_TARGET=${RUST_TARGET}" >> /etc/build-env \
&& echo "RID=${RID}" >> /etc/build-env \
&& . /etc/build-env \
&& rustup target add ${RUST_TARGET} \
&& echo "Rust target: ${RUST_TARGET}, .NET RID: ${RID}"

# Configure Rust for cross-compilation with proper linkers
RUN . /etc/build-env \
&& mkdir -p /root/.cargo \
&& case "$TARGETPLATFORM" in \
"linux/amd64") \
echo "[target.x86_64-unknown-linux-gnu]" >> /root/.cargo/config.toml \
&& echo "linker = \"gcc\"" >> /root/.cargo/config.toml \
;; \
"linux/arm64") \
echo "[target.aarch64-unknown-linux-gnu]" >> /root/.cargo/config.toml \
&& echo "linker = \"aarch64-linux-gnu-gcc\"" >> /root/.cargo/config.toml \
;; \
esac

# Copy project files
WORKDIR /source
COPY . ./

# Restore .NET dependencies
WORKDIR /source/util/SeederUtility
RUN . /etc/build-env && dotnet restore -r ${RID}

# Publish a self-contained, single-file binary
WORKDIR /source/util/SeederUtility
RUN . /etc/build-env \
&& export NoWarn="CA1305;CS1591" \
&& rustc --version \
&& cargo --version \
&& echo "Building for Rust target: ${RUST_TARGET}, .NET RID: ${RID}" \
&& dotnet publish SeederUtility.csproj \
-c Release \
--no-restore \
--self-contained \
/p:PublishSingleFile=true \
-r ${RID} \
-o /app/out

###############################################
# App stage #
###############################################
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble AS app

ARG TARGETPLATFORM
LABEL com.bitwarden.product="bitwarden"

ENV SSL_CERT_DIR=/etc/bitwarden/ca-certificates
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
ENV ASPNETCORE_ENVIRONMENT=Production
# The single-file binary extracts native libs at startup; it runs non-root, so
# point extraction at a writable dir.
ENV DOTNET_BUNDLE_EXTRACT_BASE_DIR=/tmp/.net

RUN apt-get update && apt-get install -y --no-install-recommends \
gosu \
&& rm -rf /var/lib/apt/lists/*

# Copy app from the build stage
WORKDIR /app
COPY --from=build /app/out /app
COPY ./util/SeederUtility/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

# CLI entrypoint; args pass through, e.g. `docker run <image> preset --name <preset>`.
# DB connection comes from globalSettings__* env vars.
ENTRYPOINT ["/entrypoint.sh"]
38 changes: 38 additions & 0 deletions util/SeederUtility/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/sh

GROUPNAME="bitwarden"
USERNAME="bitwarden"

LUID=${LOCAL_UID:-0}
LGID=${LOCAL_GID:-0}

# Step down from host root to well-known nobody/nogroup user
if [ $LUID -eq 0 ]
then
LUID=65534
fi
if [ $LGID -eq 0 ]
then
LGID=65534
fi

if [ "$(id -u)" = "0" ]
then
groupadd -o -g $LGID $GROUPNAME >/dev/null 2>&1 ||
groupmod -o -g $LGID $GROUPNAME >/dev/null 2>&1
useradd -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1 ||
usermod -o -u $LUID -g $GROUPNAME -s /bin/false $USERNAME >/dev/null 2>&1

chown -R $USERNAME:$GROUPNAME /app
mkdir -p /etc/bitwarden/core
mkdir -p /etc/bitwarden/logs
mkdir -p /etc/bitwarden/ca-certificates
chown -R $USERNAME:$GROUPNAME /etc/bitwarden

gosu_cmd="gosu $USERNAME:$GROUPNAME"
else
gosu_cmd=""
fi

# Pass through any CLI subcommand and arguments (e.g. `preset --name ...`).
exec $gosu_cmd /app/SeederUtility "$@"
Loading