Skip to content

Commit be8a188

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 be8a188

15 files changed

Lines changed: 263 additions & 22 deletions

File tree

.github/workflows/pr.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ jobs:
7272
context: ${{ matrix.dir }}
7373
file: ${{ matrix.dir }}/Dockerfile
7474
platforms: ${{ join(matrix.platforms, ',') }}
75+
build-args: ${{ join(matrix.build_args, '\n') }}
7576
push: false
7677
tags: local/${{ matrix.image_name }}:pr-${{ github.sha }}
7778
cache-from: type=gha

.github/workflows/publish.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ jobs:
9999
context: ${{ matrix.dir }}
100100
file: ${{ matrix.dir }}/Dockerfile
101101
platforms: ${{ join(matrix.platforms, ',') }}
102+
build-args: ${{ join(matrix.build_args, '\n') }}
102103
push: true
103104
tags: ${{ steps.meta.outputs.tags }}
104105
labels: |

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ Optional keys:
2424

2525
- `version` (string): if set, CI publishes this tag as well (example: `"1.2.3"` or `"v1.2.3"`).
2626
- `platforms` (array of strings): defaults to `["linux/amd64", "linux/arm64"]`.
27+
- `build_args` (table of string keys/values): passed to `docker build` as `--build-arg KEY=VALUE`.
2728

2829
Example:
2930

3031
```toml
3132
image = "kube-tools"
3233
version = "1.30.4"
3334
platforms = ["linux/amd64", "linux/arm64"]
35+
build_args = { TOOL_VERSION = "v1.30.4" }
3436
```
3537

3638
## Publishing Rules

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ See `AGENTS.md` for the full standards.
2626
Build the image for your host architecture and load it into your local Docker daemon:
2727

2828
```bash
29-
./scripts/build-one.sh <image>
29+
./scripts/build-one.py <image>
3030
```
3131

3232
## Adding An Image

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/_template/image.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
image = "example"
22
# version = "1.2.3"
33
platforms = ["linux/amd64", "linux/arm64"]
4-
4+
# build_args = { EXAMPLE_ARG = "value" }

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: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# syntax=docker/dockerfile:1.7
2+
3+
FROM --platform=$TARGETPLATFORM golang:alpine3.21@sha256:b4dbd292a0852331c89dfd64e84d16811f3e3aae4c73c13d026c4d200715aff6 AS build
4+
5+
ARG ENV_PROTONMAIL_BRIDGE_VERSION
6+
ARG TARGETPLATFORM
7+
8+
# Install dependencies
9+
RUN set -eux; \
10+
apk add --no-cache \
11+
bash=5.2.37-r0 \
12+
g++=14.2.0-r4 \
13+
git=2.47.3-r0 \
14+
grep=3.11-r0 \
15+
libsecret-dev=0.21.7-r0 \
16+
make=4.4.1-r2 \
17+
sed=4.9-r2
18+
19+
WORKDIR /build/
20+
RUN set -eux; \
21+
: "${ENV_PROTONMAIL_BRIDGE_VERSION:?build arg ENV_PROTONMAIL_BRIDGE_VERSION is required}"; \
22+
git clone --depth 1 --single-branch --branch "$ENV_PROTONMAIL_BRIDGE_VERSION" https://github.com/ProtonMail/proton-bridge.git; \
23+
printf '%s\n' "$ENV_PROTONMAIL_BRIDGE_VERSION" > /build/BRIDGE_VERSION
24+
WORKDIR /build/proton-bridge/
25+
RUN --mount=type=cache,target=/go/pkg/mod \
26+
--mount=type=cache,target=/root/.cache/go-build \
27+
set -eux; \
28+
make build-nogui vault-editor
29+
30+
# Working stage image
31+
FROM --platform=$TARGETPLATFORM alpine:3.21@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4709
32+
33+
# Define arguments and env variables
34+
ARG TARGETPLATFORM
35+
# Indicate (NOT define) the ports/network interface really used by Proton bridge mail.
36+
# It should be 1025/tcp and 1143/tcp but on some k3s instances it could be 1026 and 1144 (why ?)
37+
# Launch `netstat -ltnp` on a running container to be sure.
38+
ARG ENV_BRIDGE_SMTP_PORT=1025
39+
ARG ENV_BRIDGE_IMAP_PORT=1143
40+
ARG ENV_BRIDGE_HOST=127.0.0.1
41+
# Change ENV_CONTAINER_SMTP_PORT only if you have a docker port conflict on host network namespace.
42+
ARG ENV_CONTAINER_SMTP_PORT=1026
43+
ARG ENV_CONTAINER_IMAP_PORT=1144
44+
ENV PROTON_BRIDGE_SMTP_PORT=$ENV_BRIDGE_SMTP_PORT \
45+
PROTON_BRIDGE_IMAP_PORT=$ENV_BRIDGE_IMAP_PORT \
46+
PROTON_BRIDGE_HOST=$ENV_BRIDGE_HOST \
47+
CONTAINER_SMTP_PORT=$ENV_CONTAINER_SMTP_PORT \
48+
CONTAINER_IMAP_PORT=$ENV_CONTAINER_IMAP_PORT \
49+
ENV_TARGET_PLATFORM=$TARGETPLATFORM
50+
LABEL org.opencontainers.image.source="https://github.com/ProtonMail/proton-bridge" \
51+
org.opencontainers.image.version="$ENV_PROTONMAIL_BRIDGE_VERSION" \
52+
org.opencontainers.image.title="proton-bridge" \
53+
org.opencontainers.image.description="Headless Proton Mail Bridge with CLI tooling"
54+
55+
# Install dependencies
56+
RUN set -eux; \
57+
apk add --no-cache \
58+
bash=5.2.37-r0 \
59+
ca-certificates=20250911-r0 \
60+
gpg=2.4.9-r0 \
61+
gpg-agent=2.4.9-r0 \
62+
gnupg-keyboxd=2.4.9-r0 \
63+
libsecret=0.21.7-r0 \
64+
net-tools=2.10-r3 \
65+
pass=1.7.4-r3 \
66+
socat=1.8.0.3-r0
67+
68+
RUN set -eux; \
69+
addgroup -S bridge; \
70+
adduser -S -G bridge -h /home/bridge bridge; \
71+
mkdir -p /home/bridge /app; \
72+
chown -R bridge:bridge /home/bridge /app
73+
74+
# RUN rc-update add dbus
75+
# RUN touch /run/openrc/softlevel
76+
# RUN rc-service dbus start
77+
78+
# Copy executables made during previous stage
79+
WORKDIR /usr/bin/
80+
COPY --from=build /build/proton-bridge/bridge /build/proton-bridge/proton-bridge /build/proton-bridge/vault-editor /usr/bin/
81+
82+
# Install needed scripts and files
83+
WORKDIR /app/
84+
COPY --chmod=0755 entrypoint.sh /app/entrypoint.sh
85+
COPY GPGparams.txt /app/GPGparams.txt
86+
COPY --from=build /build/BRIDGE_VERSION /app/VERSION
87+
88+
# SMTP and IMAP ports are not exposed by default, so you could adjust them if necessary with ENV
89+
# variables CONTAINER_SMTP_PORT and CONTAINER_IMAP_PORT.
90+
# See README.md and/or compose.yaml file.
91+
# EXPOSE ${ENV_CONTAINER_SMTP_PORT}/tcp
92+
# EXPOSE ${ENV_CONTAINER_IMAP_PORT}/tcp
93+
94+
# Volume to save pass and bridge configurations/data
95+
VOLUME /home/bridge
96+
97+
USER bridge:bridge
98+
ENTRYPOINT ["/app/entrypoint.sh"]

images/proton-bridge/GPGparams.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
%no-protection
2+
%echo Generating a basic OpenPGP key
3+
Key-Type: RSA
4+
Key-Length: 2048
5+
Name-Real: ProtonMailBridge
6+
Expire-Date: 0
7+
%commit
8+
%echo done

images/proton-bridge/entrypoint.sh

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/usr/bin/env bash
2+
3+
set -ex
4+
5+
# Check if the gpg key exist, if not created it. Should be run only on first launch.
6+
if [ ! -d "${HOME}/.password-store/" ]; then
7+
gpg --generate-key --batch /app/GPGparams.txt
8+
gpg --list-keys
9+
pass init ProtonMailBridge
10+
sleep 1
11+
fi
12+
13+
# Check if some env variables exist.
14+
if ! [[ -v PROTON_BRIDGE_SMTP_PORT ]]; then
15+
echo "WARNING! Environment variable PROTON_BRIDGE_SMTP_PORT is not defined!"
16+
fi
17+
if ! [[ -v PROTON_BRIDGE_IMAP_PORT ]]; then
18+
echo "WARNING! Environment variable PROTON_BRIDGE_IMAP_PORT is not defined!"
19+
fi
20+
if ! [[ -v PROTON_BRIDGE_HOST ]]; then
21+
echo "WARNING! Environment variable PROTON_BRIDGE_HOST is not defined!"
22+
fi
23+
if ! [[ -v CONTAINER_SMTP_PORT ]]; then
24+
echo "WARNING! Environment variable CONTAINER_SMTP_PORT is not defined!"
25+
fi
26+
if ! [[ -v CONTAINER_IMAP_PORT ]]; then
27+
echo "WARNING! Environment variable CONTAINER_IMAP_PORT is not defined!"
28+
fi
29+
30+
echo "Build for ${ENV_TARGET_PLATFORM} platform."
31+
32+
# Proton mail bridge listen only on 127.0.0.1 interface, we need to forward TCP traffic on SMTP and IMAP ports:
33+
socat TCP-LISTEN:"$CONTAINER_SMTP_PORT",fork TCP:"$PROTON_BRIDGE_HOST":"$PROTON_BRIDGE_SMTP_PORT" &
34+
socat TCP-LISTEN:"$CONTAINER_IMAP_PORT",fork TCP:"$PROTON_BRIDGE_HOST":"$PROTON_BRIDGE_IMAP_PORT" &
35+
36+
# Alpine GPG / keyboxd bug
37+
#if [ -f "${HOME}/.gnupg/S.keyboxd" ]; then
38+
echo "Killing previous gpg keyboxd programs..."
39+
pkill keyboxd || true
40+
pkill gpg || true
41+
#rm "${HOME}/.gnupg/S.keyboxd"
42+
rm "${HOME}"/.gnupg/S.* || true
43+
rm "${HOME}"/.gnupg/*/*.lock || true
44+
sleep 1
45+
gpg-agent --daemon --allow-preset-passphrase
46+
#fi
47+
48+
# Start a default Proton Mail Bridge on a fake tty, so it won't stop because of EOF
49+
rm -f faketty
50+
mkfifo faketty
51+
cat faketty | /usr/bin/bridge --cli
52+
53+
echo "Done."

0 commit comments

Comments
 (0)