Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .daemonless/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ build:
- tag: latest
containerfile: Containerfile
default: true
tag_desc: "**daemonless Appliance**. Managed Caddy reverse proxy with a web UI, built from source."
tag_desc: "**daemonless Appliance**. Managed Caddy reverse proxy with a web UI. Caddy built from source; the admin from daemonless/reverse-proxy-src releases."

cit:
mode: health
Expand Down
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1 @@
sbom-results
admin/reverse-proxy
admin/ui/node_modules/
admin/ui/dist/*
!admin/ui/dist/.gitkeep
67 changes: 29 additions & 38 deletions Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

# reverse-proxy: daemonless appliance, one container on base-rcd. Two units:
# custom Caddy (DNS modules compiled in) runs as root to bind 80/443; the
# admin (Go, embedded UI) runs as bsd and drives Caddy over unix sockets in
# /var/run/reverse-proxy.
# admin (Go, embedded UI, from daemonless/reverse-proxy-src releases) runs as
# bsd and drives Caddy over unix sockets in /var/run/reverse-proxy.
ARG BASE_VERSION=15.1
ARG FREEBSD_ARCH=amd64

Expand Down Expand Up @@ -35,38 +35,25 @@ RUN v="${CADDY_VERSION:-$(fetch -v -o /dev/null https://github.com/caddyserver/c
--output /out/caddy && \
/out/caddy version

FROM ghcr.io/daemonless/base:${BASE_VERSION} AS ui-amd64
RUN pkg update && pkg install -y node24 npm-node24 && pkg clean -ay && rm -rf /var/cache/pkg/* /var/db/pkg/repos/*
COPY admin/ui/package.json admin/ui/package-lock.json /src/ui/
RUN cd /src/ui && npm ci --no-fund --no-audit
COPY admin/ui/ /src/ui/
RUN cd /src/ui && npm run build && /src/ui/source-hash > /src/ui/source.sha256

# The UI's build tools (rolldown, tailwind, lightningcss) have no FreeBSD
# arm64 binaries, and the UI is the same on every arch: take it from the
# amd64 image, which CI publishes before the arm64 build starts. The source
# hash stops an older amd64 image's UI from shipping here.
FROM ghcr.io/daemonless/base:${BASE_VERSION} AS ui-aarch64
COPY admin/ui/ /src/ui/
COPY --from=ghcr.io/daemonless/reverse-proxy:latest-amd64 /usr/local/share/reverse-proxy/ui/ /src/ui/dist/
COPY --from=ghcr.io/daemonless/reverse-proxy:latest-amd64 /usr/local/share/reverse-proxy/ui-source.sha256 /src/ui/amd64.sha256
RUN h="$(/src/ui/source-hash)" && \
{ [ "$h" = "$(cat /src/ui/amd64.sha256)" ] || { echo "latest-amd64 has a different UI than this commit: build amd64 first" >&2; exit 1; }; } && \
cp /src/ui/amd64.sha256 /src/ui/source.sha256

FROM ui-${FREEBSD_ARCH} AS ui-build

FROM ghcr.io/daemonless/base:${BASE_VERSION} AS admin-build
ARG APP_VER=0.2.0
RUN pkg update && pkg install -y go && pkg clean -ay && rm -rf /var/cache/pkg/* /var/db/pkg/repos/*
COPY admin/ /src/
COPY --from=ui-build /src/ui/dist/ /src/ui/dist/
RUN cd /src && CGO_ENABLED=0 go build -trimpath \
-ldflags "-s -w -X main.version=${APP_VER}" \
-o /out/reverse-proxy ./cmd/reverse-proxy
# The admin (Go, UI embedded) is released from daemonless/reverse-proxy-src.
# Empty: the newest stable release, read from GitHub's releases/latest
# redirect (no pre-releases; the API rate-limits shared CI runners). Set it
# to pin one (0.3.0).
FROM ghcr.io/daemonless/base:${BASE_VERSION} AS admin-fetch
ARG RP_VERSION=
RUN v="${RP_VERSION:-$(fetch -v -o /dev/null https://github.com/daemonless/reverse-proxy-src/releases/latest 2>&1 | grep -o 'releases/tag/v[^ ]*' | head -1 | cut -d/ -f3 | sed 's/^v//')}" && \
test -n "$v" && \
case "$(uname -m)" in amd64) a=amd64 ;; arm64|aarch64) a=arm64 ;; *) echo "no admin build for $(uname -m)" >&2; exit 1 ;; esac && \
f="reverse-proxy_${v}_freebsd_${a}.tar.gz" && \
u="https://github.com/daemonless/reverse-proxy-src/releases/download/v${v}" && \
mkdir -p /out && cd /out && \
fetch -q "$u/$f" "$u/SHA256SUMS" && \
want="$(grep " $f\$" SHA256SUMS | cut -d' ' -f1)" && \
test -n "$want" && test "$want" = "$(sha256 -q "$f")" && \
tar -xzf "$f" --strip-components 1 && rm -f "$f" SHA256SUMS && \
echo "$v" > /out/version && ./reverse-proxy dns-providers >/dev/null

FROM ghcr.io/daemonless/base-rcd:${BASE_VERSION}
ARG APP_VER=0.2.0
ARG FREEBSD_ARCH=amd64
ARG PACKAGES="ca_root_nss"

Expand All @@ -82,19 +69,23 @@ LABEL org.opencontainers.image.title="Daemonless Reverse Proxy" \
io.daemonless.volumes="/config,/data" \
io.daemonless.healthcheck-url="https://localhost:8443/healthz" \
io.daemonless.arch="${FREEBSD_ARCH}" \
io.daemonless.packages="${PACKAGES}"
io.daemonless.packages="${PACKAGES}" \
io.daemonless.upstream-url="https://api.github.com/repos/daemonless/reverse-proxy-src/releases/latest" \
io.daemonless.upstream-jq=".tag_name | ltrimstr(\"v\")"

RUN pkg update && \
pkg install -y ${PACKAGES} && \
pkg clean -ay && \
rm -rf /var/cache/pkg/* /var/db/pkg/repos/*

COPY --from=caddy-build /out/caddy /usr/local/bin/caddy
COPY --from=admin-build /out/reverse-proxy /usr/local/bin/reverse-proxy
# Embedded in the binary; kept as files for the arm64 build (above).
COPY --from=ui-build /src/ui/dist/ /usr/local/share/reverse-proxy/ui/
COPY --from=ui-build /src/ui/source.sha256 /usr/local/share/reverse-proxy/ui-source.sha256
RUN mkdir -p /app /config /data && echo "${APP_VER}" > /app/version
COPY --from=admin-fetch /out/reverse-proxy /usr/local/bin/reverse-proxy
COPY --from=admin-fetch /out/version /app/version
# Every DNS provider the UI offers is compiled into Caddy, and the reverse.
RUN reverse-proxy dns-providers | sort > /tmp/offered && \
caddy list-modules | sed -n 's/^dns\.providers\.//p' | sort > /tmp/built && \
diff -u /tmp/offered /tmp/built && rm -f /tmp/offered /tmp/built && \
mkdir -p /config /data

COPY root/ /

Expand Down
67 changes: 29 additions & 38 deletions Containerfile.j2
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# reverse-proxy: daemonless appliance, one container on base-rcd. Two units:
# custom Caddy (DNS modules compiled in) runs as root to bind 80/443; the
# admin (Go, embedded UI) runs as bsd and drives Caddy over unix sockets in
# /var/run/reverse-proxy.
# admin (Go, embedded UI, from daemonless/reverse-proxy-src releases) runs as
# bsd and drives Caddy over unix sockets in /var/run/reverse-proxy.
ARG BASE_VERSION=15.1
ARG FREEBSD_ARCH=amd64

Expand Down Expand Up @@ -29,38 +29,25 @@ RUN v="${CADDY_VERSION:-$(fetch -v -o /dev/null https://github.com/caddyserver/c
--output /out/caddy && \
/out/caddy version

FROM ghcr.io/daemonless/base:${BASE_VERSION} AS ui-amd64
RUN pkg update && pkg install -y node24 npm-node24 && pkg clean -ay && rm -rf /var/cache/pkg/* /var/db/pkg/repos/*
COPY admin/ui/package.json admin/ui/package-lock.json /src/ui/
RUN cd /src/ui && npm ci --no-fund --no-audit
COPY admin/ui/ /src/ui/
RUN cd /src/ui && npm run build && /src/ui/source-hash > /src/ui/source.sha256

# The UI's build tools (rolldown, tailwind, lightningcss) have no FreeBSD
# arm64 binaries, and the UI is the same on every arch: take it from the
# amd64 image, which CI publishes before the arm64 build starts. The source
# hash stops an older amd64 image's UI from shipping here.
FROM ghcr.io/daemonless/base:${BASE_VERSION} AS ui-aarch64
COPY admin/ui/ /src/ui/
COPY --from=ghcr.io/daemonless/reverse-proxy:latest-amd64 /usr/local/share/reverse-proxy/ui/ /src/ui/dist/
COPY --from=ghcr.io/daemonless/reverse-proxy:latest-amd64 /usr/local/share/reverse-proxy/ui-source.sha256 /src/ui/amd64.sha256
RUN h="$(/src/ui/source-hash)" && \
{ [ "$h" = "$(cat /src/ui/amd64.sha256)" ] || { echo "latest-amd64 has a different UI than this commit: build amd64 first" >&2; exit 1; }; } && \
cp /src/ui/amd64.sha256 /src/ui/source.sha256

FROM ui-${FREEBSD_ARCH} AS ui-build

FROM ghcr.io/daemonless/base:${BASE_VERSION} AS admin-build
ARG APP_VER=0.2.0
RUN pkg update && pkg install -y go && pkg clean -ay && rm -rf /var/cache/pkg/* /var/db/pkg/repos/*
COPY admin/ /src/
COPY --from=ui-build /src/ui/dist/ /src/ui/dist/
RUN cd /src && CGO_ENABLED=0 go build -trimpath \
-ldflags "-s -w -X main.version=${APP_VER}" \
-o /out/reverse-proxy ./cmd/reverse-proxy
# The admin (Go, UI embedded) is released from daemonless/reverse-proxy-src.
# Empty: the newest stable release, read from GitHub's releases/latest
# redirect (no pre-releases; the API rate-limits shared CI runners). Set it
# to pin one (0.3.0).
FROM ghcr.io/daemonless/base:${BASE_VERSION} AS admin-fetch
ARG RP_VERSION=
RUN v="${RP_VERSION:-$(fetch -v -o /dev/null https://github.com/daemonless/reverse-proxy-src/releases/latest 2>&1 | grep -o 'releases/tag/v[^ ]*' | head -1 | cut -d/ -f3 | sed 's/^v//')}" && \
test -n "$v" && \
case "$(uname -m)" in amd64) a=amd64 ;; arm64|aarch64) a=arm64 ;; *) echo "no admin build for $(uname -m)" >&2; exit 1 ;; esac && \
f="reverse-proxy_${v}_freebsd_${a}.tar.gz" && \
u="https://github.com/daemonless/reverse-proxy-src/releases/download/v${v}" && \
mkdir -p /out && cd /out && \
fetch -q "$u/$f" "$u/SHA256SUMS" && \
want="$(grep " $f\$" SHA256SUMS | cut -d' ' -f1)" && \
test -n "$want" && test "$want" = "$(sha256 -q "$f")" && \
tar -xzf "$f" --strip-components 1 && rm -f "$f" SHA256SUMS && \
echo "$v" > /out/version && ./reverse-proxy dns-providers >/dev/null

FROM ghcr.io/daemonless/base-rcd:${BASE_VERSION}
ARG APP_VER=0.2.0
ARG FREEBSD_ARCH=amd64
ARG PACKAGES="ca_root_nss"

Expand All @@ -76,19 +63,23 @@ LABEL org.opencontainers.image.title="Daemonless Reverse Proxy" \
io.daemonless.volumes="/config,/data" \
io.daemonless.healthcheck-url="https://localhost:8443/healthz" \
io.daemonless.arch="${FREEBSD_ARCH}" \
io.daemonless.packages="${PACKAGES}"
io.daemonless.packages="${PACKAGES}" \
io.daemonless.upstream-url="https://api.github.com/repos/daemonless/reverse-proxy-src/releases/latest" \
io.daemonless.upstream-jq=".tag_name | ltrimstr(\"v\")"

RUN pkg update && \
pkg install -y ${PACKAGES} && \
pkg clean -ay && \
rm -rf /var/cache/pkg/* /var/db/pkg/repos/*

COPY --from=caddy-build /out/caddy /usr/local/bin/caddy
COPY --from=admin-build /out/reverse-proxy /usr/local/bin/reverse-proxy
# Embedded in the binary; kept as files for the arm64 build (above).
COPY --from=ui-build /src/ui/dist/ /usr/local/share/reverse-proxy/ui/
COPY --from=ui-build /src/ui/source.sha256 /usr/local/share/reverse-proxy/ui-source.sha256
RUN mkdir -p /app /config /data && echo "${APP_VER}" > /app/version
COPY --from=admin-fetch /out/reverse-proxy /usr/local/bin/reverse-proxy
COPY --from=admin-fetch /out/version /app/version
# Every DNS provider the UI offers is compiled into Caddy, and the reverse.
RUN reverse-proxy dns-providers | sort > /tmp/offered && \
caddy list-modules | sed -n 's/^dns\.providers\.//p' | sort > /tmp/built && \
diff -u /tmp/offered /tmp/built && rm -f /tmp/offered /tmp/built && \
mkdir -p /config /data

COPY root/ /

Expand Down
47 changes: 25 additions & 22 deletions Containerfile.linux
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# reverse-proxy on Linux, built only by dbuild's shared GitHub workflow
# (build-linux job): the same Caddy with the same DNS modules, the same
# admin and UI, on Alpine with s6. Caddy runs as root to bind 80/443; the
# admin runs as bsd (PUID/PGID), as on FreeBSD. Keep APP_VER with
# Containerfile.j2's: the manifest adds this image to a tag only when the
# versions agree.
ARG APP_VER=0.2.0
# admin release, on Alpine with s6. Caddy runs as root to bind 80/443; the
# admin runs as bsd (PUID/PGID), as on FreeBSD. The admin is released from
# daemonless/reverse-proxy-src; keep RP_VERSION with Containerfile.j2's: the
# manifest adds this image to a tag only when the versions agree.
ARG RP_VERSION=
ARG CADDY_VERSION=

FROM golang:1.26-alpine AS caddy-build
Expand All @@ -27,22 +27,21 @@ RUN v="${CADDY_VERSION:-$(curl -fsSI https://github.com/caddyserver/caddy/releas
--output /out/caddy && \
/out/caddy version

FROM node:24-alpine AS ui-build
COPY admin/ui/package.json admin/ui/package-lock.json /src/ui/
RUN cd /src/ui && npm ci --no-fund --no-audit
COPY admin/ui/ /src/ui/
RUN cd /src/ui && npm run build

FROM golang:1.26-alpine AS admin-build
ARG APP_VER
COPY admin/ /src/
COPY --from=ui-build /src/ui/dist/ /src/ui/dist/
RUN cd /src && CGO_ENABLED=0 go build -trimpath \
-ldflags "-s -w -X main.version=${APP_VER}" \
-o /out/reverse-proxy ./cmd/reverse-proxy
FROM alpine:3.24 AS admin-fetch
ARG RP_VERSION
RUN apk add --no-cache curl && \
v="${RP_VERSION:-$(curl -fsSI https://github.com/daemonless/reverse-proxy-src/releases/latest | grep -io 'releases/tag/v[^[:space:]]*' | head -1 | cut -d/ -f3 | sed 's/^v//')}" && \
test -n "$v" && \
case "$(uname -m)" in x86_64) a=amd64 ;; aarch64) a=arm64 ;; *) echo "no admin build for $(uname -m)" >&2; exit 1 ;; esac && \
f="reverse-proxy_${v}_linux_${a}.tar.gz" && \
u="https://github.com/daemonless/reverse-proxy-src/releases/download/v${v}" && \
mkdir -p /out && cd /out && \
curl -fsSLO "$u/$f" -fsSLO "$u/SHA256SUMS" && \
grep " $f\$" SHA256SUMS | sha256sum -c - && \
tar -xzf "$f" --strip-components 1 && rm -f "$f" SHA256SUMS && \
echo "$v" > /out/version && ./reverse-proxy dns-providers >/dev/null

FROM alpine:3.24
ARG APP_VER
LABEL org.opencontainers.image.title="Daemonless Reverse Proxy" \
org.opencontainers.image.description="Friendly HTTPS names for the services on your network: a managed Caddy reverse proxy with a web UI, secure by default." \
org.opencontainers.image.source="https://github.com/daemonless/reverse-proxy" \
Expand All @@ -55,10 +54,14 @@ LABEL org.opencontainers.image.title="Daemonless Reverse Proxy" \
io.daemonless.volumes="/config,/data"
RUN apk add --no-cache s6 ca-certificates tzdata curl && \
addgroup -g 1000 bsd && adduser -D -H -u 1000 -G bsd -s /sbin/nologin bsd && \
mkdir -p /app /config /data && echo "${APP_VER}" > /app/version
mkdir -p /app /config /data
COPY --from=caddy-build /out/caddy /usr/local/bin/caddy
COPY --from=admin-build /out/reverse-proxy /usr/local/bin/reverse-proxy
COPY --from=ui-build /src/ui/dist/ /usr/local/share/reverse-proxy/ui/
COPY --from=admin-fetch /out/reverse-proxy /usr/local/bin/reverse-proxy
COPY --from=admin-fetch /out/version /app/version
# Every DNS provider the UI offers is compiled into Caddy, and the reverse.
RUN reverse-proxy dns-providers | sort > /tmp/offered && \
caddy list-modules | sed -n 's/^dns\.providers\.//p' | sort > /tmp/built && \
diff -u /tmp/offered /tmp/built && rm -f /tmp/offered /tmp/built
COPY root/etc/reverse-proxy/bootstrap.json /etc/reverse-proxy/bootstrap.json
COPY root-linux/ /
RUN chmod +x /init /healthz /usr/local/bin/s6-ready-when /etc/cont-init.d/* /etc/services.d/*/run
Expand Down
Loading
Loading