Release 1.1.18: fix bootstrap admin lockout, refresh screenshots
models/user_redis.js hardcoded the bootstrap anti-lockout admin username to 'proxyadmin2', while migrations/permission_bootstrap.js grants the global-admin permission to conf.auth.adminUsers[0]. An operator who customized adminUsers away from the default ended up with a bootstrapped account that had no admin permissions -- a silent lockout. user_redis.js now derives the bootstrap username the same way permission_bootstrap.js does. Also corrected a secrets.js.example comment that claimed the bootstrap password defaults to the username itself (it actually generates and logs a random password), and refreshed all README screenshots against the current UI, including a new load-balancing screenshot. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@@ -6,6 +6,15 @@ correspond to git tags (`vX.Y.Z`) and `nodejs/package.json`'s `version`.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.1.18] - 2026-07-21
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- The bootstrap anti-lockout admin account was always created as `proxyadmin2` regardless of `conf.auth.adminUsers`, while `migrations/permission_bootstrap.js` grants the global-admin permission to `conf.auth.adminUsers[0]`. If an operator customized `adminUsers` away from the default, the bootstrapped account and the permissioned account were two different (non-matching) usernames, so the anti-lockout account ended up with no admin access. `models/user_redis.js` now derives the bootstrap username from `conf.auth.adminUsers[0]` (falling back to `proxyadmin2`), matching `permission_bootstrap.js`.
|
||||||
|
- Corrected a `secrets.js.example` comment that claimed the bootstrap admin's password "defaults to the username itself" — it actually generates a random password printed to the container log on first boot.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Refreshed all README screenshots (hosts, per-host SSO auth, per-host basic auth) against the current UI, and added a new load-balancing screenshot for the multi-target feature.
|
||||||
|
|
||||||
## [1.1.17] - 2026-07-20
|
## [1.1.17] - 2026-07-20
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ management once basic auth is enabled:
|
|||||||
|
|
||||||
[](docs/images/host-auth-basic.png)
|
[](docs/images/host-auth-basic.png)
|
||||||
|
|
||||||
|
Multiple backend targets per host, load balanced round-robin:
|
||||||
|
|
||||||
|
[](docs/images/load-balancing.png)
|
||||||
|
|
||||||
## Why this over the alternatives
|
## Why this over the alternatives
|
||||||
|
|
||||||
Nginx Proxy Manager, Traefik, and Caddy are all good reverse proxies with
|
Nginx Proxy Manager, Traefik, and Caddy are all good reverse proxies with
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 368 KiB |
|
Before Width: | Height: | Size: 151 KiB After Width: | Height: | Size: 354 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 394 KiB |
|
After Width: | Height: | Size: 428 KiB |
@@ -87,7 +87,11 @@ class User extends Table{
|
|||||||
User.register();
|
User.register();
|
||||||
|
|
||||||
(async function(){
|
(async function(){
|
||||||
var defaultUser = 'proxyadmin2'
|
// Matches migrations/permission_bootstrap.js: the anti-lockout account is
|
||||||
|
// the first entry in conf.auth.adminUsers (default 'proxyadmin2'), NOT a
|
||||||
|
// hardcoded name -- otherwise an operator who customizes adminUsers ends
|
||||||
|
// up with a bootstrap account that has no admin permissions.
|
||||||
|
var defaultUser = (conf.auth && conf.auth.adminUsers && conf.auth.adminUsers[0]) || 'proxyadmin2';
|
||||||
// Optional: an orchestrator (e.g. theta-env's setup.sh) can set
|
// Optional: an orchestrator (e.g. theta-env's setup.sh) can set
|
||||||
// auth.localAdminPass in proxy-secrets.js to a generated password so this
|
// auth.localAdminPass in proxy-secrets.js to a generated password so this
|
||||||
// bootstrap account isn't left at a well-known default. Only used on first
|
// bootstrap account isn't left at a well-known default. Only used on first
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "proxy-api",
|
"name": "proxy-api",
|
||||||
"version": "1.1.17",
|
"version": "1.1.18",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "proxy-api",
|
"name": "proxy-api",
|
||||||
"version": "1.1.17",
|
"version": "1.1.18",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^7.3.0",
|
"@fortawesome/fontawesome-free": "^7.3.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "proxy-api",
|
"name": "proxy-api",
|
||||||
"version": "1.1.17",
|
"version": "1.1.18",
|
||||||
"author": [
|
"author": [
|
||||||
{
|
{
|
||||||
"name": "William Mantly",
|
"name": "William Mantly",
|
||||||
|
|||||||
@@ -67,12 +67,14 @@ module.exports = {
|
|||||||
adminUsers: ['proxyadmin'],
|
adminUsers: ['proxyadmin'],
|
||||||
groupRoleMap: {},
|
groupRoleMap: {},
|
||||||
// Optional: the local anti-lockout admin's initial password, used
|
// Optional: the local anti-lockout admin's initial password, used
|
||||||
// ONLY the first time that account is created. Leave unset and it
|
// ONLY the first time that account is created. Leave unset and a
|
||||||
// defaults to the username itself ("proxyadmin2") — fine for a quick
|
// random password is generated and printed to the container log on
|
||||||
// local test, but change it (or set this) before exposing the proxy
|
// first boot — fine for a quick local test if you copy it from the
|
||||||
// publicly. Once the account exists, this key is never read again;
|
// log right away, but set this (or change the password afterward)
|
||||||
// change the password via the app itself (or delete the Redis user
|
// before exposing the proxy publicly. Once the account exists, this
|
||||||
// to force it to be re-bootstrapped with a new value here).
|
// key is never read again; change the password via the app itself
|
||||||
|
// (or delete the Redis user to force it to be re-bootstrapped with a
|
||||||
|
// new value here).
|
||||||
// localAdminPass: 'change-me',
|
// localAdminPass: 'change-me',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||