Fix letsencrypt auto-renew.

This commit is contained in:
Greyson Parrelli committed 2026-10-13 15:03:24 -04:00
1 parent 3406c622c5
commit 54b640aef0
7 files changed
+102 -18

No files matched your search

+14 -7
View File
@@ -284,7 +284,8 @@ dokku google-auth:allow my-app alice@signal.org # …or just on one app
dokku google-auth:deny --global former@signal.org # cut someone off dokku google-auth:deny --global former@signal.org # cut someone off
dokku google-auth:disable my-app # turn SSO off for an app dokku google-auth:disable my-app # turn SSO off for an app
dokku google-auth:logs -t # follow auth service logs (sign-ins, denials) dokku google-auth:logs -t # follow auth service logs (sign-ins, denials)
dokku google-auth:restart # restart the auth service dokku google-auth:restart # restart the auth service (rebuilds if the source moved)
dokku google-auth:rebuild # force a rebuild when the source looks unchanged
``` ```
Users can check who they're signed in as at `https://<any-app>/_google-auth/` Users can check who they're signed in as at `https://<any-app>/_google-auth/`
@@ -315,12 +316,18 @@ and sign out at `https://<any-app>/_google-auth/logout`.
- **Plugin updates:** `sudo dokku plugin:update google-auth` rebuilds the - **Plugin updates:** `sudo dokku plugin:update google-auth` rebuilds the
service image and recreates the container with freshly written settings. service image and recreates the container with freshly written settings.
Editing the plugin directory in place works too: the image is tagged with a Editing the plugin directory in place works too: the image is tagged with a
fingerprint of its source, so `google-auth:restart` (and anything else that fingerprint of `Dockerfile`, `go.mod`, `cmd/`, and `internal/`, so
starts the service) rebuilds when the Go code has moved rather than reusing `google-auth:restart` (and anything else that starts the service) rebuilds
the binary that happens to be tagged. This matters because the shell half of when the Go code has moved rather than reusing the binary that happens to be
the plugin takes effect the moment the files change while the binary does tagged. This matters because the shell half of the plugin takes effect the
not, and a container running an older build accepts the mount, accepts moment the files change while the binary does not, and a container running an
`GOOGLE_AUTH_APP_CONFIG_DIR`, and ignores both. older build accepts the mount, accepts `GOOGLE_AUTH_APP_CONFIG_DIR`, and
ignores both.
That fingerprint covers this plugin's own source and nothing else, so when
the inputs it cannot see change — a patched `golang:1.24-alpine`, a build that
died halfway — force the rebuild: `dokku google-auth:rebuild`, or
`dokku google-auth:rebuild --no-cache` to also discard the layer cache and
re-pull the base images.
`dokku google-auth:report` names the running service's state — it asks the `dokku google-auth:report` names the running service's state — it asks the
service itself rather than trusting Docker's metadata — and if it ever says service itself rather than trusting Docker's metadata — and if it ever says
it is ignoring per-app lists, `dokku google-auth:restart` recreates it. it is ignoring per-app lists, `dokku google-auth:restart` recreates it.
+2 -1
View File
@@ -18,7 +18,8 @@ case "$1" in
google-auth:report [app], Show global and per-app google-auth status google-auth:report [app], Show global and per-app google-auth status
google-auth:start, Start the shared auth service container google-auth:start, Start the shared auth service container
google-auth:stop, Stop the shared auth service container google-auth:stop, Stop the shared auth service container
google-auth:restart, Restart the shared auth service container (picks up config changes) google-auth:restart, Restart the shared auth service container (rebuilds the image if the plugin source changed)
google-auth:rebuild [--no-cache], Force a rebuild of the service image even if the source looks unchanged
google-auth:logs [--tail|-t], Show logs from the shared auth service container google-auth:logs [--tail|-t], Show logs from the shared auth service container
help_content help_content
} }
+12 -4
View File
@@ -637,11 +637,19 @@ fn-ga-image-current() {
} }
fn-ga-build-image() { fn-ga-build-image() {
declare NO_CACHE="${1:-false}"
command -v docker >/dev/null 2>&1 || dokku_log_fail "docker is required to build the google-auth service image" command -v docker >/dev/null 2>&1 || dokku_log_fail "docker is required to build the google-auth service image"
local args=(image build)
if [[ "$NO_CACHE" == "true" ]]; then
# --pull too: the source hash cannot see the base images, so a rebuild that
# keeps a cached golang:1.24-alpine is not actually a fresh build.
args+=(--no-cache --pull)
dokku_log_info1 "Building $GOOGLE_AUTH_IMAGE from scratch (ignoring the layer cache, re-pulling base images)"
else
dokku_log_info1 "Building $GOOGLE_AUTH_IMAGE (first build downloads the Go toolchain image; this can take a few minutes)" dokku_log_info1 "Building $GOOGLE_AUTH_IMAGE (first build downloads the Go toolchain image; this can take a few minutes)"
docker image build \ fi
--label "google-auth.source-hash=$(fn-ga-source-hash)" \ args+=(--label "google-auth.source-hash=$(fn-ga-source-hash)" -t "$GOOGLE_AUTH_IMAGE" "$GOOGLE_AUTH_PLUGIN_DIR")
-t "$GOOGLE_AUTH_IMAGE" "$GOOGLE_AUTH_PLUGIN_DIR" docker "${args[@]}"
} }
fn-ga-service-running() { fn-ga-service-running() {
@@ -739,7 +747,7 @@ fn-ga-service-start() {
# Starting the service is how an operator expects to pick up an upgraded # Starting the service is how an operator expects to pick up an upgraded
# plugin, and reusing the old binary there is what makes a new feature look # plugin, and reusing the old binary there is what makes a new feature look
# broken rather than absent. # broken rather than absent.
fn-ga-image-current || fn-ga-build-image fn-ga-image-current || fn-ga-build-image false
fn-ga-write-env-file fn-ga-write-env-file
local port local port
port="$(fn-ga-global-get port "$GOOGLE_AUTH_DEFAULT_PORT")" port="$(fn-ga-global-get port "$GOOGLE_AUTH_DEFAULT_PORT")"
+8 -4
View File
@@ -9,9 +9,15 @@ DATA_ROOT="$DOKKU_LIB_ROOT/data/google-auth"
mkdir -p "$DATA_ROOT/global" "$DATA_ROOT/apps" mkdir -p "$DATA_ROOT/global" "$DATA_ROOT/apps"
# shellcheck disable=SC1091
source "$PLUGIN_DIR/functions"
if command -v docker >/dev/null 2>&1; then if command -v docker >/dev/null 2>&1; then
echo "-----> Building dokku-google-auth service image (first build can take a few minutes)" # Build through fn-ga-build-image rather than calling docker directly, so the
docker image build -t dokku-google-auth:latest "$PLUGIN_DIR" # image carries the source-hash label. An unlabelled image reads as stale
# forever: every service start would rebuild it, and the report would claim
# the binary is older than the plugin right after an upgrade installed it.
fn-ga-build-image false
# Pick up the new image if the service is already running. This goes through # Pick up the new image if the service is already running. This goes through
# fn-ga-service-start rather than its own `docker run` so an upgrade always # fn-ga-service-start rather than its own `docker run` so an upgrade always
# rewrites service.env first: recreating the container from the file an older # rewrites service.env first: recreating the container from the file an older
@@ -20,8 +26,6 @@ if command -v docker >/dev/null 2>&1; then
# access list and fall back to the global one. # access list and fall back to the global one.
if [[ "$(docker container inspect -f '{{.State.Running}}' dokku-google-auth 2>/dev/null)" == "true" ]]; then if [[ "$(docker container inspect -f '{{.State.Running}}' dokku-google-auth 2>/dev/null)" == "true" ]]; then
echo "-----> Restarting google-auth service with the new image" echo "-----> Restarting google-auth service with the new image"
# shellcheck disable=SC1091
source "$PLUGIN_DIR/functions"
if fn-ga-configured; then if fn-ga-configured; then
fn-ga-service-start fn-ga-service-start
else else
+4 -2
View File
@@ -39,8 +39,10 @@ description = "Check the help output shapes and that every subcommand is documen
run = "test/help-test.sh" run = "test/help-test.sh"
[tasks.docker-build] [tasks.docker-build]
description = "Build the auth service docker image" description = "Build the auth service docker image, labelled the way the plugin does"
run = "docker build -t dokku-google-auth:latest ." # Via fn-ga-build-image so the image carries its source-hash label. A build
# without it reads as permanently stale to the plugin.
run = "bash -c 'source ./functions && fn-ga-build-image'"
[tasks.check] [tasks.check]
description = "Run everything CI would run" description = "Run everything CI would run"
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
set -eo pipefail
[[ $DOKKU_TRACE ]] && set -x
source "$(dirname "$(dirname "${BASH_SOURCE[0]}")")/functions"
cmd-google-auth-rebuild() {
declare desc="rebuild the auth service image from the plugin source and restart"
local cmd="google-auth:rebuild"
[[ "$1" == "$cmd" ]] && shift 1
# Starting the service already rebuilds when the Go source has moved, so this
# exists for the case that check cannot see: the source is unchanged but the
# build inputs are not — a patched base image, a half-written image, a build
# that failed partway.
local no_cache=false
while [[ $# -gt 0 ]]; do
case "$1" in
--no-cache) no_cache=true ;;
*) dokku_log_fail "usage: dokku $cmd [--no-cache]" ;;
esac
shift
done
fn-ga-build-image "$no_cache"
if fn-ga-service-running; then
# The image is current now, so this recreates the container without
# building a second time.
fn-ga-service-start
else
dokku_log_verbose "auth service is not running; the new image is used when it starts"
fi
}
cmd-google-auth-rebuild "$@"
+28
View File
@@ -238,6 +238,7 @@ cat >"$WORK/bin-docker/docker" <<'EOF'
case "$1 $2" in case "$1 $2" in
"container inspect") cat "$DOCKER_INSPECT_FIXTURE" ;; "container inspect") cat "$DOCKER_INSPECT_FIXTURE" ;;
"image inspect") printf '%s\n' "$DOCKER_IMAGE_FIXTURE" ;; "image inspect") printf '%s\n' "$DOCKER_IMAGE_FIXTURE" ;;
"image build") printf '%s\n' "$*" >"$DOCKER_ARGS_FIXTURE" ;;
esac esac
exit 0 exit 0
EOF EOF
@@ -308,6 +309,33 @@ image_current "$SOURCE_HASH" || fail "an image labelled with the current source
image_current "0000000000000000" && fail "an image built from other source is not current" image_current "0000000000000000" && fail "an image built from other source is not current"
image_current "<no value>" && fail "an unlabelled image (built before this check) is not current" image_current "<no value>" && fail "an unlabelled image (built before this check) is not current"
image_current "" && fail "an image with no label value is not current" image_current "" && fail "an image with no label value is not current"
# Every build must apply the label, or the image it produces is stale on
# arrival — the plugin would rebuild on every start and the report would call a
# freshly installed binary out of date.
# shellcheck disable=SC2030,SC2031 # the subshell is what keeps the stubs local
build_args() (
export DOCKER_ARGS_FIXTURE="$WORK/build-args"
PATH="$WORK/bin-docker:$PATH"
fn-ga-build-image "$1" >/dev/null
cat "$DOCKER_ARGS_FIXTURE"
)
DEFAULT_BUILD="$(build_args false)"
grep -q -- "--label google-auth.source-hash=$SOURCE_HASH" <<<"$DEFAULT_BUILD" ||
fail "a build must label the image with the source hash, got: $DEFAULT_BUILD"
grep -q -- "-t dokku-google-auth:latest" <<<"$DEFAULT_BUILD" ||
fail "a build must tag the service image, got: $DEFAULT_BUILD"
grep -q -- "--no-cache" <<<"$DEFAULT_BUILD" &&
fail "an ordinary build should use the layer cache"
FORCED_BUILD="$(build_args true)"
grep -q -- "--no-cache" <<<"$FORCED_BUILD" || fail "a forced build should ignore the layer cache"
# The source hash covers this plugin's files, not the images the Dockerfile
# pulls, so a forced build that reuses a cached base image is not a fresh one.
grep -q -- "--pull" <<<"$FORCED_BUILD" || fail "a forced build should re-pull the base images"
grep -q -- "--label google-auth.source-hash=$SOURCE_HASH" <<<"$FORCED_BUILD" ||
fail "a forced build must still label the image, got: $FORCED_BUILD"
echo "ok: service image tracks the plugin source" echo "ok: service image tracks the plugin source"
# --- lifecycle triggers carry per-app lists --- # --- lifecycle triggers carry per-app lists ---