Ship homelab quick wins: Recyclarr, infra pins, jd.ginnoir.com, and LAN lockdown.
Deploy to valhalla / deploy (push) Has been cancelled

Add Recyclarr to the media stack, pin database/object-store/Caddy/Vault images with Watchtower disabled, expose JDownloader at jd.ginnoir.com, and restrict minio/homarr/docs to internal_only. Mark TB-002–004 and TB-033 complete; bundle batch-3 OIDC env for FreshRSS, ownCloud, and Reactive Resume.
This commit is contained in:
ginnoir
2026-06-11 02:28:06 -05:00
parent 1ce63153ab
commit 51acb44322
34 changed files with 392 additions and 105 deletions
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
set -euo pipefail
# TB-006 batch 3: app-side auth so Authentik is not a second login screen.
# Run on valhalla after setup-batch1-oidc.py prints client_id/secret values.
#
# Requires OWNCLOUD_OIDC_* in owncloud stack.env and OAUTH_* in resume stack.env.
OWNCLOUD_ENV="${OWNCLOUD_ENV:-/tmp/owncloud-stack.env}"
RESUME_ENV="${RESUME_ENV:-/tmp/resume-stack.env}"
read_env() {
local file="$1" key="$2"
grep -E "^${key}=" "$file" | head -1 | cut -d= -f2-
}
echo "=== FreshRSS: switch to HTTP auth (Remote-User from Authentik via Caddy) ==="
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
docker cp "$SCRIPT_DIR/patch-freshrss-http-auth.php" freshrss:/tmp/patch-freshrss-http-auth.php
docker exec freshrss php /tmp/patch-freshrss-http-auth.php
docker exec freshrss grep -E "auth_type|http_auth_auto_register" /var/www/FreshRSS/data/config.php
echo "=== ownCloud: write oidc.config.php ==="
OC_ID="$(read_env "$OWNCLOUD_ENV" OWNCLOUD_OIDC_CLIENT_ID)"
OC_SECRET="$(read_env "$OWNCLOUD_ENV" OWNCLOUD_OIDC_CLIENT_SECRET)"
if [[ -z "$OC_ID" || -z "$OC_SECRET" ]]; then
echo "ERROR: set OWNCLOUD_OIDC_CLIENT_ID/SECRET in owncloud stack.env first" >&2
exit 1
fi
TMP_OC="$(mktemp)"
cat >"$TMP_OC" <<PHP
<?php
\$CONFIG = [
'http.cookie.samesite' => 'None',
'openid-connect' => [
'provider-url' => 'https://auth.ginnoir.com/application/o/owncloud/',
'client-id' => '${OC_ID}',
'client-secret' => '${OC_SECRET}',
'loginButtonName' => 'Log in with Authentik',
'mode' => 'userid',
'search-attribute' => 'preferred_username',
'autoRedirectOnLoginPage' => true,
],
];
PHP
docker cp "$TMP_OC" owncloud_server:/mnt/data/config/oidc.config.php
rm -f "$TMP_OC"
echo " wrote /mnt/data/config/oidc.config.php"
docker exec owncloud_server occ app:enable openidconnect >/dev/null 2>&1 || true
echo " owncloud openidconnect enabled"
echo "=== done — recreate resume container after stack.env OAuth vars are set ==="
if grep -q '^OAUTH_CLIENT_ID=' "$RESUME_ENV" 2>/dev/null; then
echo " resume stack.env has OAUTH_CLIENT_ID (Portainer redeploy or recreate app container)"
else
echo " WARN: add OAUTH_* vars to resume stack.env and redeploy the resume stack"
fi
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
# Restored resume DB may contain JWKS encrypted with an old AUTH_SECRET.
# Better Auth then fails session verification after OAuth (login page loop).
# Safe fix: drop JWKS + sessions; app regenerates keys on next start.
echo "=== resume: reset Better Auth JWKS (fixes session decrypt errors) ==="
docker stop resume >/dev/null
docker exec postgres_resume psql -U postgres -d postgres -c "TRUNCATE jwks, session, oauth_access_token, oauth_refresh_token CASCADE;"
docker start resume >/dev/null
sleep 15
docker logs resume --tail 15 2>&1
echo "done — retry resume.ginnoir.com → Authentik"
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
echo "=== resume: allow OAuth to link existing email/password account ==="
docker exec postgres_resume psql -U postgres -d postgres -c \
"UPDATE \"user\" SET email_verified = true WHERE username = 'ginnoir';"
docker exec postgres_resume psql -U postgres -d postgres -c \
"SELECT email, email_verified, username FROM \"user\";"
echo "done — retry Authentik login on resume.ginnoir.com"
+8
View File
@@ -0,0 +1,8 @@
<?php
$path = '/var/www/FreshRSS/data/config.php';
$text = file_get_contents($path);
$text = preg_replace("/'auth_type' => 'http'/", "'auth_type' => 'http_auth'", $text, 1);
$text = preg_replace("/'auth_type' => 'form'/", "'auth_type' => 'http_auth'", $text, 1);
$text = preg_replace("/'http_auth_auto_register' => true/", "'http_auth_auto_register' => false", $text, 1);
file_put_contents($path, $text);
echo "ok\n";
+47
View File
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
set -euo pipefail
# Restore resume postgres + minio from pre-split monolith named volumes into bind mounts.
# Safe to re-run: stops stack, copies from htpc-download-box_* volumes, restarts postgres.
OLD_PG_VOL="${OLD_PG_VOL:-htpc-download-box_postgres_data}"
OLD_MINIO_VOL="${OLD_MINIO_VOL:-htpc-download-box_minio_data}"
PG_BIND="${PG_BIND:-/config/resume/postgres}"
MINIO_BIND="${MINIO_BIND:-/storage1/labdata/resume/minio}"
echo "=== stop resume stack services ==="
docker stop resume minio_resume postgres_resume 2>/dev/null || true
echo "=== restore postgres: ${OLD_PG_VOL} -> ${PG_BIND} ==="
docker run --rm \
-v "${OLD_PG_VOL}:/from:ro" \
-v "${PG_BIND}:/to" \
alpine sh -c 'rm -rf /to/* /to/.[!.]* /to/..?* 2>/dev/null || true; cp -a /from/. /to/; chown -R 70:70 /to'
echo "=== restore minio objects: ${OLD_MINIO_VOL}/default -> ${MINIO_BIND}/default ==="
docker run --rm \
-v "${OLD_MINIO_VOL}:/from:ro" \
-v "${MINIO_BIND}:/to" \
alpine sh -c 'mkdir -p /to/default; rm -rf /to/default/* 2>/dev/null || true; cp -a /from/default/. /to/default/ 2>/dev/null || true'
echo "=== start postgres ==="
docker start postgres_resume
for i in $(seq 1 30); do
if docker exec postgres_resume pg_isready -U postgres -d postgres >/dev/null 2>&1; then
break
fi
sleep 1
done
docker exec postgres_resume pg_isready -U postgres -d postgres
echo "=== verify restored data ==="
docker exec postgres_resume psql -U postgres -d postgres -c 'SELECT email, username FROM "user"; SELECT slug, is_public FROM resume;'
echo "=== align resume user email with Authentik (3nigma.matt@gmail.com) for OAuth linking ==="
docker exec postgres_resume psql -U postgres -d postgres -c "UPDATE \"user\" SET email = '3nigma.matt@gmail.com' WHERE username = 'ginnoir';"
echo "=== start minio + resume (if compose project exists) ==="
docker start minio_resume 2>/dev/null || true
docker start resume 2>/dev/null || true
echo "done — if resume container was ad-hoc recreated, redeploy stacks/resume via Portainer after git push"
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail
docker cp /tmp/setup-batch1-oidc.py authentik_server:/tmp/setup-batch1-oidc.py
docker exec authentik_server ak shell -c "exec(open('/tmp/setup-batch1-oidc.py').read())"
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail
docker cp /tmp/apply-forward-auth-admin.py authentik_server:/tmp/apply-forward-auth-admin.py
docker exec authentik_server ak shell -c "exec(open('/tmp/apply-forward-auth-admin.py').read())"