Skip to content

Commit 14ee2dd

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 14ee2dd

30 files changed

Lines changed: 1393 additions & 22 deletions

.github/workflows/pr.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,26 @@ jobs:
6666
- name: Lint Dockerfile
6767
run: hadolint "${{ matrix.dir }}/Dockerfile"
6868

69+
- id: build-args
70+
shell: bash
71+
run: |
72+
set -euo pipefail
73+
image_source="${{ github.server_url }}/${{ github.repository }}"
74+
{
75+
echo "value<<EOF"
76+
jq -r '.[]' <<< '${{ toJSON(matrix.build_args) }}'
77+
echo "ENV_IMAGE_SOURCE=${image_source}"
78+
echo "ENV_IMAGE_REVISION=${{ github.sha }}"
79+
echo "EOF"
80+
} >> "$GITHUB_OUTPUT"
81+
6982
- name: Build (no push)
7083
uses: docker/build-push-action@v6
7184
with:
7285
context: ${{ matrix.dir }}
7386
file: ${{ matrix.dir }}/Dockerfile
7487
platforms: ${{ join(matrix.platforms, ',') }}
88+
build-args: ${{ steps.build-args.outputs.value }}
7589
push: false
7690
tags: local/${{ matrix.image_name }}:pr-${{ github.sha }}
7791
cache-from: type=gha

.github/workflows/publish.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,26 @@ jobs:
9393
echo "EOF"
9494
} >> "$GITHUB_OUTPUT"
9595
96+
- id: build-args
97+
shell: bash
98+
run: |
99+
set -euo pipefail
100+
image_source="${{ github.server_url }}/${{ github.repository }}"
101+
{
102+
echo "value<<EOF"
103+
jq -r '.[]' <<< '${{ toJSON(matrix.build_args) }}'
104+
echo "ENV_IMAGE_SOURCE=${image_source}"
105+
echo "ENV_IMAGE_REVISION=${{ github.sha }}"
106+
echo "EOF"
107+
} >> "$GITHUB_OUTPUT"
108+
96109
- name: Build and push
97110
uses: docker/build-push-action@v6
98111
with:
99112
context: ${{ matrix.dir }}
100113
file: ${{ matrix.dir }}/Dockerfile
101114
platforms: ${{ join(matrix.platforms, ',') }}
115+
build-args: ${{ steps.build-args.outputs.value }}
102116
push: true
103117
tags: ${{ steps.meta.outputs.tags }}
104118
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: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ On pushes to `main`, CI publishes for each changed image:
2020
- `scripts/` contains local/CI helpers
2121

2222
See `AGENTS.md` for the full standards.
23+
See `THIRD_PARTY_LICENSES.md` for third-party license notices.
2324

2425
## Local Build
2526

2627
Build the image for your host architecture and load it into your local Docker daemon:
2728

2829
```bash
29-
./scripts/build-one.sh <image>
30+
./scripts/build-one.py <image>
3031
```
3132

3233
## Adding An Image

THIRD_PARTY_LICENSES.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Third-Party Licensing Notices
2+
3+
## Proton Bridge Image (`images/proton-bridge`)
4+
5+
The `proton-bridge` image distributes binaries built from:
6+
7+
- `ProtonMail/proton-bridge` (GPL-3.0)
8+
- Adapted bootstrap/containerization ideas from `shenxn/protonmail-bridge-docker` (GPL-3.0)
9+
- Additional referenced implementation patterns from `VideoCurio/ProtonMailBridgeDocker` (GPL-3.0)
10+
11+
Relevant local files:
12+
13+
- `images/proton-bridge/LICENSE` (full GPL-3.0 license text)
14+
- `images/proton-bridge/NOTICE` (attribution and provenance)
15+
16+
Upstream sources:
17+
18+
- https://github.com/ProtonMail/proton-bridge
19+
- https://github.com/shenxn/protonmail-bridge-docker
20+
- https://github.com/VideoCurio/ProtonMailBridgeDocker

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: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# syntax=docker/dockerfile:1.7
2+
3+
FROM --platform=$TARGETPLATFORM golang:alpine3.23 AS build
4+
5+
ARG ENV_PROTONMAIL_BRIDGE_VERSION
6+
ARG ENV_PROTONMAIL_BRIDGE_COMMIT
7+
ARG TARGETPLATFORM
8+
9+
# Install dependencies
10+
# hadolint ignore=DL3018
11+
RUN set -eux; \
12+
apk add --no-cache \
13+
bash \
14+
g++ \
15+
git \
16+
grep \
17+
libcbor-dev \
18+
libfido2-dev \
19+
libsecret-dev \
20+
make \
21+
openssl-dev \
22+
pkgconf \
23+
sed
24+
25+
WORKDIR /build/
26+
RUN set -eux; \
27+
: "${ENV_PROTONMAIL_BRIDGE_VERSION:?build arg ENV_PROTONMAIL_BRIDGE_VERSION is required}"; \
28+
: "${ENV_PROTONMAIL_BRIDGE_COMMIT:?build arg ENV_PROTONMAIL_BRIDGE_COMMIT is required}"; \
29+
git clone --depth 1 --single-branch --branch "$ENV_PROTONMAIL_BRIDGE_VERSION" https://github.com/ProtonMail/proton-bridge.git; \
30+
actual_commit="$(git -C /build/proton-bridge rev-parse HEAD)"; \
31+
if [ "${actual_commit}" != "${ENV_PROTONMAIL_BRIDGE_COMMIT}" ]; then \
32+
echo "Expected commit ${ENV_PROTONMAIL_BRIDGE_COMMIT}, got ${actual_commit}" >&2; \
33+
exit 1; \
34+
fi; \
35+
printf '%s\n' "$ENV_PROTONMAIL_BRIDGE_VERSION" > /build/BRIDGE_VERSION
36+
WORKDIR /build/proton-bridge/
37+
RUN --mount=type=cache,target=/go/pkg/mod \
38+
--mount=type=cache,target=/root/.cache/go-build \
39+
set -eux; \
40+
make build-nogui vault-editor
41+
42+
# Working stage image
43+
FROM --platform=$TARGETPLATFORM alpine:3.23
44+
45+
# Define arguments and env variables
46+
ARG TARGETPLATFORM
47+
ARG ENV_PROTONMAIL_BRIDGE_VERSION
48+
ARG ENV_PROTONMAIL_BRIDGE_COMMIT
49+
ARG ENV_IMAGE_SOURCE=https://github.com/mgarratt/docker-images
50+
ARG ENV_IMAGE_REVISION=unknown
51+
# Indicate (NOT define) the ports/network interface really used by Proton bridge mail.
52+
# It should be 1025/tcp and 1143/tcp but on some k3s instances it could be 1026 and 1144 (why ?)
53+
# Launch `netstat -ltnp` on a running container to be sure.
54+
ARG ENV_BRIDGE_SMTP_PORT=1025
55+
ARG ENV_BRIDGE_IMAP_PORT=1143
56+
ARG ENV_BRIDGE_HOST=127.0.0.1
57+
# Change ENV_CONTAINER_SMTP_PORT only if you have a docker port conflict on host network namespace.
58+
ARG ENV_CONTAINER_SMTP_PORT=1026
59+
ARG ENV_CONTAINER_IMAP_PORT=1144
60+
ENV PROTON_BRIDGE_SMTP_PORT=$ENV_BRIDGE_SMTP_PORT \
61+
PROTON_BRIDGE_IMAP_PORT=$ENV_BRIDGE_IMAP_PORT \
62+
PROTON_BRIDGE_HOST=$ENV_BRIDGE_HOST \
63+
CONTAINER_SMTP_PORT=$ENV_CONTAINER_SMTP_PORT \
64+
CONTAINER_IMAP_PORT=$ENV_CONTAINER_IMAP_PORT \
65+
ENV_TARGET_PLATFORM=$TARGETPLATFORM
66+
LABEL org.opencontainers.image.source="$ENV_IMAGE_SOURCE" \
67+
org.opencontainers.image.revision="$ENV_IMAGE_REVISION" \
68+
org.opencontainers.image.version="$ENV_PROTONMAIL_BRIDGE_VERSION" \
69+
org.opencontainers.image.upstream="https://github.com/ProtonMail/proton-bridge" \
70+
org.opencontainers.image.upstream.revision="$ENV_PROTONMAIL_BRIDGE_COMMIT" \
71+
org.opencontainers.image.title="proton-bridge" \
72+
org.opencontainers.image.description="Headless Proton Mail Bridge with CLI tooling"
73+
74+
# Install dependencies
75+
# hadolint ignore=DL3018
76+
RUN set -eux; \
77+
apk add --no-cache \
78+
bash \
79+
ca-certificates \
80+
gpg \
81+
gpg-agent \
82+
gnupg-keyboxd \
83+
libcbor \
84+
libfido2 \
85+
libsecret \
86+
net-tools \
87+
pass \
88+
s6 \
89+
socat
90+
91+
RUN set -eux; \
92+
addgroup -S bridge; \
93+
adduser -S -G bridge -h /home/bridge bridge; \
94+
mkdir -p /home/bridge /app; \
95+
chown -R bridge:bridge /home/bridge /app
96+
97+
# Copy executables made during previous stage
98+
WORKDIR /usr/bin/
99+
COPY --from=build /build/proton-bridge/bridge /build/proton-bridge/proton-bridge /build/proton-bridge/vault-editor /usr/bin/
100+
101+
# Install needed scripts and files
102+
WORKDIR /app/
103+
COPY --chmod=0755 entrypoint.sh /app/entrypoint.sh
104+
COPY --chmod=0755 healthcheck.sh /app/healthcheck.sh
105+
COPY GPGparams.txt /app/GPGparams.txt
106+
COPY LICENSE /app/licenses/LICENSE
107+
COPY NOTICE /app/licenses/NOTICE
108+
COPY --chmod=0755 services/.s6-finish-policy.sh /app/services/.s6-finish-policy.sh
109+
COPY --chmod=0755 services/bridge/run /app/services/bridge/run
110+
COPY --chmod=0755 services/bridge/finish /app/services/bridge/finish
111+
COPY --chmod=0755 services/gpg-agent/run /app/services/gpg-agent/run
112+
COPY --chmod=0755 services/gpg-agent/finish /app/services/gpg-agent/finish
113+
COPY --chmod=0755 services/socat-smtp/run /app/services/socat-smtp/run
114+
COPY --chmod=0755 services/socat-smtp/finish /app/services/socat-smtp/finish
115+
COPY --chmod=0755 services/socat-imap/run /app/services/socat-imap/run
116+
COPY --chmod=0755 services/socat-imap/finish /app/services/socat-imap/finish
117+
COPY --from=build /build/BRIDGE_VERSION /app/VERSION
118+
RUN set -eu; \
119+
printf '%s\n' \
120+
"upstream_source=https://github.com/ProtonMail/proton-bridge" \
121+
"upstream_version=${ENV_PROTONMAIL_BRIDGE_VERSION}" \
122+
"upstream_revision=${ENV_PROTONMAIL_BRIDGE_COMMIT}" \
123+
"image_wrapper_source=${ENV_IMAGE_SOURCE}" \
124+
"image_wrapper_revision=${ENV_IMAGE_REVISION}" \
125+
> /app/CORRESPONDING_SOURCE
126+
RUN chown -R bridge:bridge /app
127+
128+
# SMTP and IMAP ports are not exposed by default, so you could adjust them if necessary with ENV
129+
# variables CONTAINER_SMTP_PORT and CONTAINER_IMAP_PORT.
130+
# See README.md and/or compose.yaml file.
131+
# EXPOSE ${ENV_CONTAINER_SMTP_PORT}/tcp
132+
# EXPOSE ${ENV_CONTAINER_IMAP_PORT}/tcp
133+
134+
# Volume to save pass and bridge configurations/data
135+
VOLUME /home/bridge
136+
137+
HEALTHCHECK --interval=30s --timeout=5s --start-period=45s --retries=3 \
138+
CMD ["/app/healthcheck.sh"]
139+
140+
USER bridge:bridge
141+
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

0 commit comments

Comments
 (0)