From ddab9941f5698a8f01d4955a9fcbdab11c8cdc76 Mon Sep 17 00:00:00 2001 From: okxlin <61420215+okxlin@users.noreply.github.com> Date: Fri, 31 Jul 2026 03:55:21 +0800 Subject: [PATCH] feat: add tinyauth --- apps/tinyauth/5.1.3/.env.sample | 6 ++ apps/tinyauth/5.1.3/data.yml | 92 ++++++++++++++++++++++ apps/tinyauth/5.1.3/docker-compose.yml | 42 ++++++++++ apps/tinyauth/5.1.3/scripts/init.sh | 84 ++++++++++++++++++++ apps/tinyauth/5.1.3/scripts/uninstall.sh | 8 ++ apps/tinyauth/5.1.3/scripts/upgrade.sh | 3 + apps/tinyauth/README.md | 68 ++++++++++++++++ apps/tinyauth/data.yml | 31 ++++++++ apps/tinyauth/latest/.env.sample | 6 ++ apps/tinyauth/latest/data.yml | 92 ++++++++++++++++++++++ apps/tinyauth/latest/docker-compose.yml | 42 ++++++++++ apps/tinyauth/latest/scripts/init.sh | 84 ++++++++++++++++++++ apps/tinyauth/latest/scripts/uninstall.sh | 8 ++ apps/tinyauth/latest/scripts/upgrade.sh | 3 + apps/tinyauth/logo.png | Bin 0 -> 5042 bytes 15 files changed, 569 insertions(+) create mode 100644 apps/tinyauth/5.1.3/.env.sample create mode 100644 apps/tinyauth/5.1.3/data.yml create mode 100644 apps/tinyauth/5.1.3/docker-compose.yml create mode 100755 apps/tinyauth/5.1.3/scripts/init.sh create mode 100755 apps/tinyauth/5.1.3/scripts/uninstall.sh create mode 100755 apps/tinyauth/5.1.3/scripts/upgrade.sh create mode 100644 apps/tinyauth/README.md create mode 100644 apps/tinyauth/data.yml create mode 100644 apps/tinyauth/latest/.env.sample create mode 100644 apps/tinyauth/latest/data.yml create mode 100644 apps/tinyauth/latest/docker-compose.yml create mode 100755 apps/tinyauth/latest/scripts/init.sh create mode 100755 apps/tinyauth/latest/scripts/uninstall.sh create mode 100755 apps/tinyauth/latest/scripts/upgrade.sh create mode 100644 apps/tinyauth/logo.png diff --git a/apps/tinyauth/5.1.3/.env.sample b/apps/tinyauth/5.1.3/.env.sample new file mode 100644 index 000000000..48d328944 --- /dev/null +++ b/apps/tinyauth/5.1.3/.env.sample @@ -0,0 +1,6 @@ +CONTAINER_NAME=tinyauth +PANEL_APP_PORT_HTTP=3000 +TINYAUTH_APP_URL=http://tinyauth.localhost:3000 +TINYAUTH_ADMIN_USERNAME=admin +TINYAUTH_ADMIN_PASSWORD=Tinyauth_Admin_Change_Me_2026! +TINYAUTH_SECURE_COOKIE=false diff --git a/apps/tinyauth/5.1.3/data.yml b/apps/tinyauth/5.1.3/data.yml new file mode 100644 index 000000000..96949bffa --- /dev/null +++ b/apps/tinyauth/5.1.3/data.yml @@ -0,0 +1,92 @@ +additionalProperties: + formFields: + - default: 3000 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: Порт HTTP + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: http://tinyauth.localhost:3000 + edit: true + envKey: TINYAUTH_APP_URL + labelEn: External URL + labelZh: 外部访问地址 + label: + en: External URL + zh: 外部访问地址 + zh-Hant: 外部存取網址 + ja: 外部 URL + ko: 외부 URL + ru: Внешний URL + ms: URL Luaran + pt-br: URL externo + required: true + rule: paramExtUrl + type: text + - default: admin + edit: true + envKey: TINYAUTH_ADMIN_USERNAME + labelEn: Admin Username + labelZh: 管理员用户名 + label: + en: Admin Username + zh: 管理员用户名 + zh-Hant: 管理員使用者名稱 + ja: 管理者ユーザー名 + ko: 관리자 사용자 이름 + ru: Имя администратора + ms: Nama Pengguna Pentadbir + pt-br: Usuario administrador + required: true + rule: paramCommon + type: text + - default: "" + edit: true + envKey: TINYAUTH_ADMIN_PASSWORD + labelEn: Admin Password + labelZh: 管理员密码 + label: + en: Admin Password + zh: 管理员密码 + zh-Hant: 管理員密碼 + ja: 管理者パスワード + ko: 관리자 비밀번호 + ru: Пароль администратора + ms: Kata Laluan Pentadbir + pt-br: Senha do administrador + random: true + required: true + rule: paramComplexity + type: password + - default: "false" + edit: true + envKey: TINYAUTH_SECURE_COOKIE + labelEn: Secure Cookies + labelZh: 安全 Cookie + label: + en: Secure Cookies + zh: 安全 Cookie + zh-Hant: 安全 Cookie + ja: Secure Cookie + ko: 보안 Cookie + ru: Secure Cookie + ms: Cookie Selamat + pt-br: Cookie seguro + required: true + type: select + values: + - label: "false" + value: "false" + - label: "true" + value: "true" diff --git a/apps/tinyauth/5.1.3/docker-compose.yml b/apps/tinyauth/5.1.3/docker-compose.yml new file mode 100644 index 000000000..74224fc12 --- /dev/null +++ b/apps/tinyauth/5.1.3/docker-compose.yml @@ -0,0 +1,42 @@ +services: + tinyauth: + image: "ghcr.io/tinyauthapp/tinyauth:v5.1.3@sha256:a1bde189951900f84a60f133c26a1ec7a3264073ec7875b548f4fbadb7f5f735" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + user: "1000:1000" + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:3000" + environment: + - TINYAUTH_APPURL=${TINYAUTH_APP_URL} + - TINYAUTH_LABELPROVIDER=none + - TINYAUTH_AUTH_USERSFILE=/data/users + - TINYAUTH_AUTH_SECURECOOKIE=${TINYAUTH_SECURE_COOKIE} + - TINYAUTH_ANALYTICS_ENABLED=false + - TINYAUTH_SERVER_ADDRESS=0.0.0.0 + - TINYAUTH_SERVER_PORT=3000 + - TINYAUTH_DATABASE_DRIVER=sqlite + - TINYAUTH_DATABASE_PATH=/data/tinyauth.db + - TINYAUTH_RESOURCES_PATH=/data/resources + volumes: + - "./data:/data" + read_only: true + tmpfs: + - /tmp:nodev,noexec,nosuid,size=32M + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: ["CMD", "tinyauth", "healthcheck"] + interval: 30s + timeout: 5s + start_period: 10s + retries: 5 + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/tinyauth/5.1.3/scripts/init.sh b/apps/tinyauth/5.1.3/scripts/init.sh new file mode 100755 index 000000000..95c45bbff --- /dev/null +++ b/apps/tinyauth/5.1.3/scripts/init.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" +DOCKER_BIN="${DOCKER_BIN:-docker}" +IMAGE='ghcr.io/tinyauthapp/tinyauth:v5.1.3@sha256:a1bde189951900f84a60f133c26a1ec7a3264073ec7875b548f4fbadb7f5f735' + +fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +read_env_value() { + local key="$1" + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +[[ -f "$ENV_FILE" && ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must be a regular file" +command -v "$DOCKER_BIN" >/dev/null 2>&1 || fail 'Docker is required to generate the Tinyauth password hash' + +username="${TINYAUTH_ADMIN_USERNAME:-$(read_env_value TINYAUTH_ADMIN_USERNAME)}" +password="${TINYAUTH_ADMIN_PASSWORD:-$(read_env_value TINYAUTH_ADMIN_PASSWORD)}" +app_url="${TINYAUTH_APP_URL:-$(read_env_value TINYAUTH_APP_URL)}" +secure_cookie="${TINYAUTH_SECURE_COOKIE:-$(read_env_value TINYAUTH_SECURE_COOKIE)}" + +data_dir="$(realpath -m -- "$ROOT_DIR/data")" +case "$data_dir" in + "$ROOT_DIR"/*) ;; + *) fail 'Tinyauth data directory must remain inside the application version directory' ;; +esac +for path in "$data_dir" "$data_dir/resources" "$data_dir/oidc"; do + [[ ! -L "$path" ]] || fail "$path must not be a symbolic link" + [[ ! -e "$path" || -d "$path" ]] || fail "$path must be a directory" +done +users_file="$data_dir/users" +[[ ! -L "$users_file" ]] || fail 'Tinyauth users file must not be a symbolic link' +[[ ! -e "$users_file" || -f "$users_file" ]] || fail 'Tinyauth users path must be a regular file' + +[[ "$username" =~ ^[A-Za-z0-9._-]{1,64}$ ]] || fail 'TINYAUTH_ADMIN_USERNAME must contain only letters, digits, dot, underscore, or hyphen' +[[ ${#password} -ge 12 && ${#password} -le 256 ]] || fail 'TINYAUTH_ADMIN_PASSWORD must contain 12 to 256 characters' +[[ "$password" != *$'\n'* && "$password" != *$'\r'* ]] || fail 'TINYAUTH_ADMIN_PASSWORD must not contain line breaks' +[[ "$app_url" =~ ^https?://[^[:space:]]+$ ]] || fail 'TINYAUTH_APP_URL must be an absolute HTTP or HTTPS URL without whitespace' +app_host="${app_url#*://}" +app_host="${app_host%%/*}" +app_host="${app_host%%:*}" +[[ "$app_host" == *.* && "$app_host" =~ ^[A-Za-z0-9][A-Za-z0-9.-]*[A-Za-z0-9]$ ]] || fail 'TINYAUTH_APP_URL must use a domain name with at least two labels' +[[ ! "$app_host" =~ ^[0-9.]+$ ]] || fail 'TINYAUTH_APP_URL must not use an IP address' +[[ "$secure_cookie" == false || "$secure_cookie" == true ]] || fail 'TINYAUTH_SECURE_COOKIE must be true or false' + +install -d -m 0750 -o 1000 -g 1000 -- "$data_dir" "$data_dir/resources" "$data_dir/oidc" + +# Variables in this command are expanded by the shell inside the generator container. +# shellcheck disable=SC2016 +generator_command='IFS= read -r password; exec tinyauth user create --username "$1" --password "$password"' +generator_output="$( + printf '%s\n' "$password" | + "$DOCKER_BIN" run --rm -i --network none --read-only --user 1000:1000 \ + --cap-drop ALL --security-opt no-new-privileges --env NO_COLOR=1 \ + --entrypoint /bin/sh "$IMAGE" -ec \ + "$generator_command" \ + sh "$username" +)" +user_entry="$(printf '%s\n' "$generator_output" | sed -n 's/^TINYAUTH_AUTH_USERS=//p' | head -n 1)" +[[ "$user_entry" =~ ^[A-Za-z0-9._-]{1,64}:\$2[aby]\$[0-9]{2}\$[./A-Za-z0-9]{53}$ ]] || fail 'Failed to generate a valid bcrypt user entry' + +umask 077 +temporary="$(mktemp "$data_dir/.users.tmp.XXXXXX")" +trap 'rm -f -- "${temporary:-}"' EXIT +printf '%s\n' "$user_entry" >"$temporary" +chmod 0600 "$temporary" +chown 1000:1000 "$temporary" +mv -f -- "$temporary" "$users_file" +trap - EXIT + +chown 1000:1000 "$data_dir" "$data_dir/resources" "$data_dir/oidc" "$users_file" +chmod 0750 "$data_dir" "$data_dir/resources" "$data_dir/oidc" +chmod 0600 "$users_file" diff --git a/apps/tinyauth/5.1.3/scripts/uninstall.sh b/apps/tinyauth/5.1.3/scripts/uninstall.sh new file mode 100755 index 000000000..5a3f6bd6b --- /dev/null +++ b/apps/tinyauth/5.1.3/scripts/uninstall.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +if command -v docker-compose >/dev/null 2>&1; then + docker-compose down --volumes --remove-orphans +else + docker compose down --volumes --remove-orphans +fi diff --git a/apps/tinyauth/5.1.3/scripts/upgrade.sh b/apps/tinyauth/5.1.3/scripts/upgrade.sh new file mode 100755 index 000000000..9879df4c3 --- /dev/null +++ b/apps/tinyauth/5.1.3/scripts/upgrade.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -euo pipefail +exit 0 diff --git a/apps/tinyauth/README.md b/apps/tinyauth/README.md new file mode 100644 index 000000000..cc750f262 --- /dev/null +++ b/apps/tinyauth/README.md @@ -0,0 +1,68 @@ +# Tinyauth + +## 产品介绍 + +Tinyauth 是一个轻量级身份认证与授权服务,可作为 Traefik、Nginx、Caddy 等反向代理的 Forward Auth 后端,也可独立提供本地用户登录、OAuth、LDAP 和 OpenID Connect 服务。 + +本应用使用 Tinyauth 官方镜像和本地 SQLite 数据库。安装时输入的管理员密码会通过上游自带命令生成 bcrypt 哈希,运行容器只读取持久化的哈希用户文件,不接收管理员明文密码。 + +## 主要功能 + +- 本地用户登录与基于 Cookie 的认证会话 +- 适配 Traefik、Nginx 和 Caddy 的 Forward Auth 端点 +- OAuth、LDAP 和 Tailscale 身份源集成 +- 作为 OpenID Connect 身份提供方服务其他应用 +- 按用户、组、IP、域名和路径配置访问控制 + +## 访问说明 + +- Web 界面:安装表单中填写的外部访问地址 +- 初始管理员:安装表单中的管理员用户名和随机密码 +- 使用 HTTP 时保持“安全 Cookie”为 `false`;通过 HTTPS 域名访问时应将其设置为 `true` +- 外部访问地址必须使用至少两级的域名,并与浏览器实际使用的协议、域名和端口一致;Tinyauth 不接受 IP 地址或裸 `localhost` + +登录后可直接使用 Tinyauth 的独立认证界面。若要保护其他应用,需要在反向代理中把认证请求转发到 Tinyauth,并按上游文档配置对应的 Forward Auth 端点和请求头。本包不会自动发现或修改其他 1Panel 应用。 + +## 安全边界 + +本包明确设置 `TINYAUTH_LABELPROVIDER=none`,不挂载 `/var/run/docker.sock`,也不启用 Docker 或 Kubernetes label provider。容器以 UID/GID `1000:1000` 运行,根文件系统只读,删除全部 Linux capabilities,并启用 `no-new-privileges`。 + +镜像依赖扫描会报告 Docker client 库中的三个 High 漏洞,但这些代码只由已禁用的 Docker label provider 调用;默认包没有 Docker socket、Docker API 地址或 provider 自动探测路径。请勿自行把 label provider 改为 `auto` 或 `docker`,也不要向容器添加 Docker socket。 + +固定版本从 `5.1.3` 开始,因为 `5.1.2` 及更早版本受 `GHSA-r27r-rr9v-vv37` 的 Forward Auth ACL 认证绕过影响。不要降级到 `5.1.2` 或更早版本。 + +## 数据与升级 + +`./data` 挂载到容器 `/data`,保存 bcrypt 用户文件、SQLite 数据库、OIDC 密钥和资源文件。卸载只移除容器和 Compose 资源,不删除该目录。升级前应备份整个 `./data` 目录,并确认外部访问地址和反向代理配置保持一致。 + +`latest` 使用上游 `v5` 移动标签;需要可重复部署时请选择固定版本 `5.1.3`。 + +## Introduction + +Tinyauth is a lightweight authentication and authorization server for reverse proxies and standalone applications. It supports local users, OAuth, LDAP, OpenID Connect, and Forward Auth integrations for Traefik, Nginx, and Caddy. + +This package uses the official image and a local SQLite database. The install script passes the administrator password to Tinyauth's own bcrypt generator over standard input and persists only the resulting user hash for the runtime container. + +## Features + +- Local users and cookie-based authentication sessions +- Forward Auth endpoints for Traefik, Nginx, and Caddy +- OAuth, LDAP, and Tailscale identity integrations +- OpenID Connect provider support for downstream applications +- User, group, IP, domain, and path-based access controls + +The package explicitly disables label discovery with `TINYAUTH_LABELPROVIDER=none` and does not mount the Docker socket. The container runs as UID/GID `1000:1000` with a read-only root filesystem, all Linux capabilities dropped, and `no-new-privileges` enabled. Do not enable the Docker provider or add a Docker socket mount. + +Set the external URL to the exact browser-facing protocol, domain, and port. Tinyauth rejects IP addresses and bare `localhost`; use a domain with at least two labels. Use secure cookies only with HTTPS. To protect another application, configure its reverse proxy to call the appropriate Tinyauth Forward Auth endpoint; this package intentionally does not discover or modify other 1Panel applications. + +Version `5.1.3` is the oldest version packaged here because it fixes the Forward Auth ACL authentication bypass in `GHSA-r27r-rr9v-vv37`. Do not downgrade to `5.1.2` or earlier. + +Persistent users, SQLite state, OIDC keys, and resources live under `./data`. Uninstall preserves this directory. Back up the entire directory before upgrades. + +## References + +- Website: +- Documentation: +- Source: +- Security advisory: +- Official image: diff --git a/apps/tinyauth/data.yml b/apps/tinyauth/data.yml new file mode 100644 index 000000000..7f032783d --- /dev/null +++ b/apps/tinyauth/data.yml @@ -0,0 +1,31 @@ +name: Tinyauth +tags: + - Security +title: 轻量级身份认证与授权服务 +description: 轻量级身份认证与授权服务 +additionalProperties: + key: tinyauth + name: Tinyauth + tags: + - Security + shortDescZh: 轻量级身份认证与授权服务 + shortDescEn: Lightweight authentication and authorization server + description: + en: A lightweight authentication and authorization server for reverse proxies and standalone applications + zh: 为反向代理和独立应用提供身份认证、OAuth、LDAP、OIDC 与访问控制 + zh-Hant: 為反向代理和獨立應用提供身份驗證、OAuth、LDAP、OIDC 與存取控制 + ja: リバースプロキシとスタンドアロンアプリ向けの軽量な認証・認可サーバー + ko: 리버스 프록시와 독립 실행형 앱을 위한 경량 인증 및 권한 부여 서버 + ru: Легковесный сервер аутентификации и авторизации для обратных прокси и автономных приложений + ms: Pelayan pengesahan dan kebenaran ringan untuk proksi songsang dan aplikasi kendiri + pt-br: Servidor leve de autenticacao e autorizacao para proxies reversos e aplicativos independentes + type: tool + crossVersionUpdate: true + limit: 0 + recommend: 0 + website: https://tinyauth.app/ + github: https://github.com/tinyauthapp/tinyauth + document: https://tinyauth.app/docs/getting-started/ + architectures: + - amd64 + - arm64 diff --git a/apps/tinyauth/latest/.env.sample b/apps/tinyauth/latest/.env.sample new file mode 100644 index 000000000..48d328944 --- /dev/null +++ b/apps/tinyauth/latest/.env.sample @@ -0,0 +1,6 @@ +CONTAINER_NAME=tinyauth +PANEL_APP_PORT_HTTP=3000 +TINYAUTH_APP_URL=http://tinyauth.localhost:3000 +TINYAUTH_ADMIN_USERNAME=admin +TINYAUTH_ADMIN_PASSWORD=Tinyauth_Admin_Change_Me_2026! +TINYAUTH_SECURE_COOKIE=false diff --git a/apps/tinyauth/latest/data.yml b/apps/tinyauth/latest/data.yml new file mode 100644 index 000000000..96949bffa --- /dev/null +++ b/apps/tinyauth/latest/data.yml @@ -0,0 +1,92 @@ +additionalProperties: + formFields: + - default: 3000 + edit: true + envKey: PANEL_APP_PORT_HTTP + labelEn: HTTP Port + labelZh: HTTP 端口 + label: + en: HTTP Port + zh: HTTP 端口 + zh-Hant: HTTP 連接埠 + ja: HTTP ポート + ko: HTTP 포트 + ru: Порт HTTP + ms: Port HTTP + pt-br: Porta HTTP + required: true + rule: paramPort + type: number + - default: http://tinyauth.localhost:3000 + edit: true + envKey: TINYAUTH_APP_URL + labelEn: External URL + labelZh: 外部访问地址 + label: + en: External URL + zh: 外部访问地址 + zh-Hant: 外部存取網址 + ja: 外部 URL + ko: 외부 URL + ru: Внешний URL + ms: URL Luaran + pt-br: URL externo + required: true + rule: paramExtUrl + type: text + - default: admin + edit: true + envKey: TINYAUTH_ADMIN_USERNAME + labelEn: Admin Username + labelZh: 管理员用户名 + label: + en: Admin Username + zh: 管理员用户名 + zh-Hant: 管理員使用者名稱 + ja: 管理者ユーザー名 + ko: 관리자 사용자 이름 + ru: Имя администратора + ms: Nama Pengguna Pentadbir + pt-br: Usuario administrador + required: true + rule: paramCommon + type: text + - default: "" + edit: true + envKey: TINYAUTH_ADMIN_PASSWORD + labelEn: Admin Password + labelZh: 管理员密码 + label: + en: Admin Password + zh: 管理员密码 + zh-Hant: 管理員密碼 + ja: 管理者パスワード + ko: 관리자 비밀번호 + ru: Пароль администратора + ms: Kata Laluan Pentadbir + pt-br: Senha do administrador + random: true + required: true + rule: paramComplexity + type: password + - default: "false" + edit: true + envKey: TINYAUTH_SECURE_COOKIE + labelEn: Secure Cookies + labelZh: 安全 Cookie + label: + en: Secure Cookies + zh: 安全 Cookie + zh-Hant: 安全 Cookie + ja: Secure Cookie + ko: 보안 Cookie + ru: Secure Cookie + ms: Cookie Selamat + pt-br: Cookie seguro + required: true + type: select + values: + - label: "false" + value: "false" + - label: "true" + value: "true" diff --git a/apps/tinyauth/latest/docker-compose.yml b/apps/tinyauth/latest/docker-compose.yml new file mode 100644 index 000000000..6a9953ca1 --- /dev/null +++ b/apps/tinyauth/latest/docker-compose.yml @@ -0,0 +1,42 @@ +services: + tinyauth: + image: "ghcr.io/tinyauthapp/tinyauth:v5" + container_name: ${CONTAINER_NAME} + restart: unless-stopped + user: "1000:1000" + networks: + - 1panel-network + ports: + - "${PANEL_APP_PORT_HTTP}:3000" + environment: + - TINYAUTH_APPURL=${TINYAUTH_APP_URL} + - TINYAUTH_LABELPROVIDER=none + - TINYAUTH_AUTH_USERSFILE=/data/users + - TINYAUTH_AUTH_SECURECOOKIE=${TINYAUTH_SECURE_COOKIE} + - TINYAUTH_ANALYTICS_ENABLED=false + - TINYAUTH_SERVER_ADDRESS=0.0.0.0 + - TINYAUTH_SERVER_PORT=3000 + - TINYAUTH_DATABASE_DRIVER=sqlite + - TINYAUTH_DATABASE_PATH=/data/tinyauth.db + - TINYAUTH_RESOURCES_PATH=/data/resources + volumes: + - "./data:/data" + read_only: true + tmpfs: + - /tmp:nodev,noexec,nosuid,size=32M + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + healthcheck: + test: ["CMD", "tinyauth", "healthcheck"] + interval: 30s + timeout: 5s + start_period: 10s + retries: 5 + labels: + createdBy: "Apps" + +networks: + 1panel-network: + external: true diff --git a/apps/tinyauth/latest/scripts/init.sh b/apps/tinyauth/latest/scripts/init.sh new file mode 100755 index 000000000..89b2ec7e8 --- /dev/null +++ b/apps/tinyauth/latest/scripts/init.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" +ENV_FILE="${ENV_FILE:-$ROOT_DIR/.env}" +DOCKER_BIN="${DOCKER_BIN:-docker}" +IMAGE='ghcr.io/tinyauthapp/tinyauth:v5' + +fail() { + printf '%s\n' "$1" >&2 + exit 1 +} + +read_env_value() { + local key="$1" + local value + value="$(sed -n "s/^${key}=//p" "$ENV_FILE" | tail -n 1)" + case "$value" in + \"*\") value="${value#\"}"; value="${value%\"}" ;; + \'*\') value="${value#\'}"; value="${value%\'}" ;; + esac + printf '%s\n' "$value" +} + +[[ -f "$ENV_FILE" && ! -L "$ENV_FILE" ]] || fail "$ENV_FILE must be a regular file" +command -v "$DOCKER_BIN" >/dev/null 2>&1 || fail 'Docker is required to generate the Tinyauth password hash' + +username="${TINYAUTH_ADMIN_USERNAME:-$(read_env_value TINYAUTH_ADMIN_USERNAME)}" +password="${TINYAUTH_ADMIN_PASSWORD:-$(read_env_value TINYAUTH_ADMIN_PASSWORD)}" +app_url="${TINYAUTH_APP_URL:-$(read_env_value TINYAUTH_APP_URL)}" +secure_cookie="${TINYAUTH_SECURE_COOKIE:-$(read_env_value TINYAUTH_SECURE_COOKIE)}" + +data_dir="$(realpath -m -- "$ROOT_DIR/data")" +case "$data_dir" in + "$ROOT_DIR"/*) ;; + *) fail 'Tinyauth data directory must remain inside the application version directory' ;; +esac +for path in "$data_dir" "$data_dir/resources" "$data_dir/oidc"; do + [[ ! -L "$path" ]] || fail "$path must not be a symbolic link" + [[ ! -e "$path" || -d "$path" ]] || fail "$path must be a directory" +done +users_file="$data_dir/users" +[[ ! -L "$users_file" ]] || fail 'Tinyauth users file must not be a symbolic link' +[[ ! -e "$users_file" || -f "$users_file" ]] || fail 'Tinyauth users path must be a regular file' + +[[ "$username" =~ ^[A-Za-z0-9._-]{1,64}$ ]] || fail 'TINYAUTH_ADMIN_USERNAME must contain only letters, digits, dot, underscore, or hyphen' +[[ ${#password} -ge 12 && ${#password} -le 256 ]] || fail 'TINYAUTH_ADMIN_PASSWORD must contain 12 to 256 characters' +[[ "$password" != *$'\n'* && "$password" != *$'\r'* ]] || fail 'TINYAUTH_ADMIN_PASSWORD must not contain line breaks' +[[ "$app_url" =~ ^https?://[^[:space:]]+$ ]] || fail 'TINYAUTH_APP_URL must be an absolute HTTP or HTTPS URL without whitespace' +app_host="${app_url#*://}" +app_host="${app_host%%/*}" +app_host="${app_host%%:*}" +[[ "$app_host" == *.* && "$app_host" =~ ^[A-Za-z0-9][A-Za-z0-9.-]*[A-Za-z0-9]$ ]] || fail 'TINYAUTH_APP_URL must use a domain name with at least two labels' +[[ ! "$app_host" =~ ^[0-9.]+$ ]] || fail 'TINYAUTH_APP_URL must not use an IP address' +[[ "$secure_cookie" == false || "$secure_cookie" == true ]] || fail 'TINYAUTH_SECURE_COOKIE must be true or false' + +install -d -m 0750 -o 1000 -g 1000 -- "$data_dir" "$data_dir/resources" "$data_dir/oidc" + +# Variables in this command are expanded by the shell inside the generator container. +# shellcheck disable=SC2016 +generator_command='IFS= read -r password; exec tinyauth user create --username "$1" --password "$password"' +generator_output="$( + printf '%s\n' "$password" | + "$DOCKER_BIN" run --rm -i --network none --read-only --user 1000:1000 \ + --cap-drop ALL --security-opt no-new-privileges --env NO_COLOR=1 \ + --entrypoint /bin/sh "$IMAGE" -ec \ + "$generator_command" \ + sh "$username" +)" +user_entry="$(printf '%s\n' "$generator_output" | sed -n 's/^TINYAUTH_AUTH_USERS=//p' | head -n 1)" +[[ "$user_entry" =~ ^[A-Za-z0-9._-]{1,64}:\$2[aby]\$[0-9]{2}\$[./A-Za-z0-9]{53}$ ]] || fail 'Failed to generate a valid bcrypt user entry' + +umask 077 +temporary="$(mktemp "$data_dir/.users.tmp.XXXXXX")" +trap 'rm -f -- "${temporary:-}"' EXIT +printf '%s\n' "$user_entry" >"$temporary" +chmod 0600 "$temporary" +chown 1000:1000 "$temporary" +mv -f -- "$temporary" "$users_file" +trap - EXIT + +chown 1000:1000 "$data_dir" "$data_dir/resources" "$data_dir/oidc" "$users_file" +chmod 0750 "$data_dir" "$data_dir/resources" "$data_dir/oidc" +chmod 0600 "$users_file" diff --git a/apps/tinyauth/latest/scripts/uninstall.sh b/apps/tinyauth/latest/scripts/uninstall.sh new file mode 100755 index 000000000..5a3f6bd6b --- /dev/null +++ b/apps/tinyauth/latest/scripts/uninstall.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +if command -v docker-compose >/dev/null 2>&1; then + docker-compose down --volumes --remove-orphans +else + docker compose down --volumes --remove-orphans +fi diff --git a/apps/tinyauth/latest/scripts/upgrade.sh b/apps/tinyauth/latest/scripts/upgrade.sh new file mode 100755 index 000000000..9879df4c3 --- /dev/null +++ b/apps/tinyauth/latest/scripts/upgrade.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -euo pipefail +exit 0 diff --git a/apps/tinyauth/logo.png b/apps/tinyauth/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bb4c431cd781b149c11027796af49cf28e9ede2b GIT binary patch literal 5042 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