This repository has been archived on 2026-10-18. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
OpenMFA/deploy/docker/Dockerfile
T
Miles WardandClaude Fable 5.1 ad3720edbf v0.1: OpenMFA — live TOTP delivery to Discord, Slack or Telegram
Platform-neutral successor to Discord Authenticator, forked from its v0.1.2
state with fresh history. Same vault, same UI, same live-message mechanism —
now behind one `Sink` that speaks three platforms.

Why each platform needs what it needs (this drove the whole settings model):
- Discord: an incoming-webhook URL. Webhooks can edit their own messages.
- Slack: a bot token (xoxb-, scope chat:write) + channel ID. Slack incoming
  webhooks can post but never edit, and editing IS the live mechanism, so
  chat.postMessage / chat.update via a bot is the only path.
- Telegram: a bot token + chat id. No webhook concept exists. The app detects
  the chat id itself from the bot's pending updates ("send /start, then
  Detect"), since users rarely know it.

- core: Provider enum; settings hold all three configs so switching back and
  forth never re-asks for tokens; per-platform shape validation that names the
  common mistakes (xoxp- user token, #name instead of channel ID); a
  destination key that clears message ids on platform/channel change but not
  on token rotation
- delivery: shared DeliveryError + pacing Governor; Discord client refactored
  onto them; new Slack client (classifies Slack's ok:false-on-HTTP-200
  errors) and Telegram client ("message is not modified" is success; chat
  detection via getUpdates); provider-agnostic message builders — code in a
  copyable span everywhere, rollover time as live as each platform allows
- http-api: setup accepts an optional provider; settings PUT/GET carry the
  full model; /api/slack/whoami, /api/telegram/whoami, /api/telegram/chats
- webui: three-way optional picker on setup and in Settings → Delivery with
  guided per-platform fields; "Check token" (Slack) and "Detect chat"
  (Telegram); inline white mark via currentColor
- brand: white mark on the dark plate; blue stays with the Discord sibling
- docs/TEAMS.md: exploration for v0.2 — Teams connectors were disabled May
  2026 and Workflows webhooks can't edit, so live tiles need a Bot Framework
  bot; options for the inbound endpoint on a homelab and a recommended scope

Verified against mock Discord, Slack and Telegram servers speaking each
platform's real protocol shape: edits target the same message rather than
re-posting, deleted tiles are recovered, dead credentials are surfaced with a
next step, renames refresh without a rollover, and switching platform reposts
on the new one and leaves the old one alone. 138 tests.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-09 00:23:45 -04:00

82 lines
3.2 KiB
Docker
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# syntax=docker/dockerfile:1.7
#
# OpenMFA — multi-stage build.
#
# build : cross-compiles to x86_64-unknown-linux-musl with cargo-zigbuild.
# runtime : Alpine + the static binary. ~15 MB total.
#
# ## Why cross-compile instead of building for the target platform
#
# The obvious `FROM --platform=linux/amd64 rust` runs the entire compiler
# under QEMU when the builder is an Apple-Silicon Mac. That is roughly 15×
# slower, and the emulated linker intermittently SIGSEGVs part-way through.
#
# Pinning this stage to $BUILDPLATFORM instead runs rustc natively at full
# speed and emits x86_64 via cargo-zigbuild, which uses `zig cc` as the
# cross-linker — zig ships a musl sysroot for every target, so there is no
# cross-gcc toolchain to assemble. Deterministic, and minutes rather than
# half an hour.
ARG RUST_VERSION=1.95
########## build ##########
FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-bookworm AS build
WORKDIR /src
# zig (via the `ziglang` pip package — cargo-zigbuild discovers it as
# `python3 -m ziglang`) supplies the musl sysroot and the linker.
RUN apt-get update \
&& apt-get install -y --no-install-recommends python3 python3-pip \
&& rm -rf /var/lib/apt/lists/* \
&& rustup target add x86_64-unknown-linux-musl \
&& pip3 install --no-cache-dir --break-system-packages ziglang \
&& cargo install --locked cargo-zigbuild
# Deliberately NOT copying rust-toolchain.toml: it pins `channel = "stable"`
# for local development, and inside the image that makes rustup download a
# second full toolchain — slow, and enough to fill a small Colima disk.
COPY Cargo.toml Cargo.lock ./
COPY crates/ crates/
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/src/target,sharing=locked \
cargo zigbuild --release --target x86_64-unknown-linux-musl \
--bin openmfa \
&& cp target/x86_64-unknown-linux-musl/release/openmfa /openmfa \
&& ls -l /openmfa
########## runtime ##########
# Alpine, not Debian: the binary is fully static musl and shells out to
# nothing, and its TLS roots are compiled in via webpki-roots — so the image
# needs no ca-certificates package and no runtime libraries at all. What it
# does need is a shell, for the bind-mount chown in the entrypoint (see there
# for why that matters on Unraid).
FROM alpine:3.21 AS runtime
RUN apk add --no-cache tini su-exec \
&& addgroup -g 10001 -S openmfa \
&& adduser -u 10001 -S -G openmfa -h /var/lib/openmfa openmfa \
&& mkdir -p /var/lib/openmfa \
&& chown -R openmfa:openmfa /var/lib/openmfa
COPY --from=build /openmfa /usr/local/bin/openmfa
COPY deploy/docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh /usr/local/bin/openmfa
# NOT `USER openmfa` — the entrypoint starts as root to fix bind-mount ownership,
# then drops to uid 10001 via su-exec. Its non-root branch handles the case
# where the platform has already assigned a uid.
WORKDIR /var/lib/openmfa
ENV OPENMFA_WORK_DIR=/var/lib/openmfa \
OPENMFA_HTTP_ADDR=0.0.0.0:8720 \
OPENMFA_LOG=info,openmfa=info
EXPOSE 8720/tcp
VOLUME ["/var/lib/openmfa"]
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget -qO- http://127.0.0.1:8720/healthz || exit 1
ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/entrypoint.sh"]