Skip to content

Commit 7ed5fd8

Browse files
committed
Add proton-bridge image (multi-arch, k8s-friendly)
Build Proton Bridge from source for amd64/arm64 and ship a simple entrypoint that starts DBus+gnome-keyring, forwards IMAP/SMTP via socat, and execs bridge so container lifecycle matches the main process.
1 parent a91dda8 commit 7ed5fd8

6 files changed

Lines changed: 218 additions & 0 deletions

File tree

images/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@ Each subdirectory in `images/` is one Docker image.
55
- Create a new image by copying `images/_template/` to `images/<image>/` and editing.
66
- CI only builds directories that contain both `Dockerfile` and `image.toml`.
77

8+
## Available
9+
10+
- `proton-bridge`: Proton Bridge built from source (multi-arch) with a k8s-friendly entrypoint.

images/proton-bridge/.dockerignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.git
2+
.github
3+
node_modules
4+
npm-debug.log
5+
dist
6+
build
7+
.DS_Store

images/proton-bridge/Dockerfile

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# syntax=docker/dockerfile:1
2+
3+
ARG BRIDGE_VERSION=v3.21.2
4+
5+
FROM golang:1.24.2-bookworm AS build
6+
ARG BRIDGE_VERSION
7+
8+
# hadolint ignore=DL3008
9+
RUN apt-get update \
10+
&& apt-get install -y --no-install-recommends \
11+
ca-certificates \
12+
gcc \
13+
git \
14+
libsecret-1-dev \
15+
libc6-dev \
16+
pkg-config \
17+
&& rm -rf /var/lib/apt/lists/*
18+
19+
WORKDIR /src
20+
21+
# Upstream generates `internal/bridge/credits.go` during their release build, but
22+
# the tagged source doesn't ship it. The CLI references it, so create a minimal
23+
# stub if missing.
24+
RUN git clone --depth 1 --branch "${BRIDGE_VERSION}" https://github.com/ProtonMail/proton-bridge.git . \
25+
&& if [ ! -f internal/bridge/credits.go ]; then \
26+
printf '%s\n' \
27+
'// Code generated during container build. DO NOT EDIT.' \
28+
'' \
29+
'package bridge' \
30+
'' \
31+
'const Credits = ""' \
32+
> internal/bridge/credits.go; \
33+
fi
34+
35+
# Build the headless bridge binary (no GUI).
36+
# Upstream uses `./cmd/Desktop-Bridge/` for v3.x; keep fallbacks for older layouts.
37+
RUN if [ -d ./cmd/Desktop-Bridge ]; then pkg=./cmd/Desktop-Bridge; \
38+
elif [ -d ./cmd/bridge ]; then pkg=./cmd/bridge; \
39+
elif [ -d ./cmd/protonmail-bridge ]; then pkg=./cmd/protonmail-bridge; \
40+
else echo "unknown bridge cmd package; expected ./cmd/Desktop-Bridge or ./cmd/bridge or ./cmd/protonmail-bridge" >&2; exit 1; \
41+
fi \
42+
&& go build -trimpath -ldflags "-s -w" -o /out/bridge "$pkg"
43+
44+
45+
FROM debian:bookworm-slim
46+
47+
# hadolint ignore=DL3008
48+
RUN apt-get update \
49+
&& apt-get install -y --no-install-recommends \
50+
ca-certificates \
51+
dbus \
52+
gnome-keyring \
53+
libsecret-1-0 \
54+
socat \
55+
tini \
56+
&& rm -rf /var/lib/apt/lists/*
57+
58+
RUN useradd -m -d /home/bridge -s /usr/sbin/nologin -u 1000 bridge \
59+
&& mkdir -p /data \
60+
&& chown -R bridge:bridge /data
61+
62+
COPY --from=build /out/bridge /usr/local/bin/bridge
63+
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
64+
RUN chmod 0755 /usr/local/bin/bridge /usr/local/bin/entrypoint.sh
65+
66+
USER bridge:bridge
67+
68+
ENV XDG_CONFIG_HOME=/data/config \
69+
XDG_DATA_HOME=/data/data \
70+
XDG_CACHE_HOME=/data/cache
71+
72+
# Defaults are unprivileged ports.
73+
EXPOSE 1025 1143
74+
75+
ENTRYPOINT ["tini", "-g", "--", "/usr/local/bin/entrypoint.sh"]
76+
CMD ["run"]

images/proton-bridge/README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# proton-bridge
2+
3+
Builds Proton Bridge from source for multi-arch (`linux/amd64`, `linux/arm64`) and runs it headlessly for IMAP/SMTP.
4+
5+
## Runtime Notes
6+
7+
- State lives under `/data` (mount a persistent volume there).
8+
- By default this image avoids privileged ports. It listens on:
9+
- SMTP: `1025` (forwarded to bridge on `127.0.0.1:1026`)
10+
- IMAP: `1143` (forwarded to bridge on `127.0.0.1:1144`)
11+
- If you want to expose standard ports (`25`/`143`), set `CONTAINER_SMTP_PORT=25` and `CONTAINER_IMAP_PORT=143`
12+
and grant `NET_BIND_SERVICE` (or run as root, not recommended).
13+
14+
## One-Time Login / Init
15+
16+
You need to initialize credentials once with a writable `/data` volume mounted:
17+
18+
```bash
19+
docker run --rm -it \
20+
-v proton-bridge-data:/data \
21+
ghcr.io/<owner>/<repo>/proton-bridge:latest init
22+
```
23+
24+
In Kubernetes, prefer a one-off init pod/job (because `kubectl exec` won’t inherit the
25+
entrypoint-provisioned DBus/keyring environment):
26+
27+
- Run a temporary pod with `args: ["init"]` and the same PVC mounted at `/data`.
28+
- Complete login (supports 2FA interactively), then delete the pod.
29+
30+
After that, start normally (default command is `run`).
31+
32+
## Configuration
33+
34+
Environment variables:
35+
36+
- `CONTAINER_SMTP_PORT` (default `1025`)
37+
- `CONTAINER_IMAP_PORT` (default `1143`)
38+
- `PROTON_BRIDGE_SMTP_PORT` (default `1026`)
39+
- `PROTON_BRIDGE_IMAP_PORT` (default `1144`)
40+
- `SOCAT_OPTS` (extra args appended to each `socat` invocation, optional)
41+
- `KEYRING_PASSWORD` (optional; used to best-effort unlock gnome-keyring on startup)

images/proton-bridge/entrypoint.sh

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/bin/sh
2+
set -eu
3+
4+
cmd="${1:-run}"
5+
6+
export HOME="${HOME:-/home/bridge}"
7+
8+
# Persist everything under /data by default (works well with k8s PVCs).
9+
export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-/data/config}"
10+
export XDG_DATA_HOME="${XDG_DATA_HOME:-/data/data}"
11+
export XDG_CACHE_HOME="${XDG_CACHE_HOME:-/data/cache}"
12+
13+
mkdir -p "$XDG_CONFIG_HOME" "$XDG_DATA_HOME" "$XDG_CACHE_HOME"
14+
15+
uid="$(id -u)"
16+
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/tmp/runtime-$uid}"
17+
mkdir -p "$XDG_RUNTIME_DIR"
18+
chmod 700 "$XDG_RUNTIME_DIR"
19+
20+
DBUS_SOCKET="${DBUS_SOCKET:-$XDG_RUNTIME_DIR/dbus-session.sock}"
21+
export DBUS_SESSION_BUS_ADDRESS="unix:path=$DBUS_SOCKET"
22+
23+
start_dbus_and_keyring() {
24+
# Start a private session bus and gnome-keyring (Secret Service) so the bridge
25+
# can store/retrieve credentials.
26+
rm -f "$DBUS_SOCKET"
27+
dbus-daemon --session --address="unix:path=$DBUS_SOCKET" --nofork --nopidfile &
28+
29+
# gnome-keyring-daemon prints shell exports. Keep output quiet.
30+
# shellcheck disable=SC2046
31+
eval "$(gnome-keyring-daemon --start --components=secrets 2>/dev/null)"
32+
33+
# Best-effort unlock. If the keyring is configured with an empty password this
34+
# will succeed; otherwise Bridge may prompt/require manual unlock.
35+
printf '%s' "${KEYRING_PASSWORD:-}" | gnome-keyring-daemon --unlock >/dev/null 2>&1 || true
36+
}
37+
38+
start_socat_forwarders() {
39+
container_smtp="${CONTAINER_SMTP_PORT:-1025}"
40+
container_imap="${CONTAINER_IMAP_PORT:-1143}"
41+
bridge_smtp="${PROTON_BRIDGE_SMTP_PORT:-1026}"
42+
bridge_imap="${PROTON_BRIDGE_IMAP_PORT:-1144}"
43+
socat_opts="${SOCAT_OPTS:-}"
44+
45+
socat "TCP-LISTEN:${container_smtp},fork,reuseaddr${socat_opts:+,${socat_opts}}" \
46+
"TCP:127.0.0.1:${bridge_smtp}" &
47+
socat "TCP-LISTEN:${container_imap},fork,reuseaddr${socat_opts:+,${socat_opts}}" \
48+
"TCP:127.0.0.1:${bridge_imap}" &
49+
}
50+
51+
bridge_cli_args() {
52+
# Proton Bridge has used both "--cli" and "-c" for interactive CLI mode across versions.
53+
if bridge --help 2>&1 | grep -q -- "--cli"; then
54+
printf '%s\n' "--cli"
55+
return 0
56+
fi
57+
printf '%s\n' "-c"
58+
}
59+
60+
start_dbus_and_keyring
61+
62+
case "$cmd" in
63+
init)
64+
shift || true
65+
exec bridge "$(bridge_cli_args)" "$@"
66+
;;
67+
run)
68+
shift || true
69+
start_socat_forwarders
70+
add_noninteractive=1
71+
for a in "$@"; do
72+
case "$a" in
73+
--noninteractive|-n|--cli|-c)
74+
add_noninteractive=0
75+
;;
76+
esac
77+
done
78+
if [ "$add_noninteractive" -eq 1 ]; then
79+
set -- --noninteractive "$@"
80+
fi
81+
exec bridge "$@"
82+
;;
83+
*)
84+
# Allow running arbitrary commands (debug, etc).
85+
exec "$@"
86+
;;
87+
esac

images/proton-bridge/image.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
image = "proton-bridge"
2+
version = "3.21.2"
3+
platforms = ["linux/amd64", "linux/arm64"]
4+

0 commit comments

Comments
 (0)