Commit Graph

43 Commits

Author SHA1 Message Date
wmantly 2acc3644c4 Permissions: rename Grants, add wildcards, local groups, profile
- Rename Grant -> Permission end-to-end (model, routes, view, frontend,
  bootstrap) and add an idempotent redis migration for existing records.
- utils/roles.js: glob domain matching (* = one label, ** = any depth) against
  the full host; authz passes the full hostname.
- Local groups: LocalGroup model + admin routes/UI; membership merged into
  Permission.effectiveFor so app groups behave like SSO groups.
- Subject autocomplete via GET /api/permission/subjects (users + derived groups).
- User profile page (/profile) and username in the navbar; /api/user/me now
  returns merged/local/external groups.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 10:54:15 -04:00
wmantly a2d194f855 Add dynamic DNS: keep A records pointed at the current public IP
For deployments on WAN DHCP, operators can declare A records in the DNS section
that the app updates to this box's current public IP every 4 hours (and
immediately on create).

- utils/public_ip.js: getPublicIp() queries external echo services (ipify +
  fallbacks, configurable) with pure isIPv4/extractIp helpers.
- utils/dns_records.js: pure planARecordUpdate() reconciliation decision.
- models/dns_provider.js: Domain.upsertARecord(name, ip) — provider-agnostic
  upsert via getRecords + deleteRecordById + createRecord (createRecord alone is
  not a reliable cross-provider upsert). Apex ('@') handling added to each
  provider (CloudFlare uses the domain name, Porkbun an empty name, DigitalOcean
  '@') via a new DnsApi.apexName().
- models/dynamic_record.js: DynamicRecord model (deterministic id per host,
  apply()/refreshAll()), registered + ModelPs-wrapped for live UI updates.
- services/dynamic_dns.js + conf: 4h scheduler mirroring host_scheduler.
- routes/dns.js: /dynamic CRUD + /dynamic/ip, gated to domain managers/admins.
- views/dns.ejs: "Dynamic A Records (WAN IP)" card with add form + list.
- test/unit/dynamic_record.test.js: public-IP parsing + reconciliation logic.

Verified end-to-end against a live Porkbun domain (create, idempotent, IP-change,
cleanup) plus unit suite (111 pass).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 23:55:56 -04:00
wmantly 6092468901 Add per-host reverse-proxy controls (rate limit, cache, headers, IP ACL)
Every proxied request flows through one shared OpenResty location whose
behavior is chosen at request time from the host's Redis hash. Add per-host
controls as new Host fields enforced in Lua rather than static nginx config
(which can't key off a per-request variable):

- Rate limiting: per-client-IP token bucket via resty.limit.req
  (ratelimit_enabled/rate/burst), backed by a new `ratelimit` shared dict.
- Response caching: opt-in per host via a global proxy_cache zone gated by
  $skip_cache (respcache_enabled). Off by default; upstream Cache-Control
  still honored.
- Custom/security headers: req_headers (upstream) + resp_headers (client) and
  hsts_enabled, applied in access/header_filter phases.
- IP allow/deny CIDR lists via resty.ipmatcher (deny wins; non-empty allow is
  default-deny).

New ops/nginx_conf/hostfeatures.lua holds the enforcement; proxy.conf's
access_by_lua string becomes a block that calls it, plus a header_filter block.
nodejs/utils/host_features.js is the pure, unit-tested normalize/validate layer
(header/CIDR parsing, range clamping, injection-safe values) applied in
routes/host.js and mirrored by the hosts.ejs edit form. install.sh gains the
ipmatcher rock, the cache dir, and the hostfeatures.lua symlink.

Per-host cache TTL is intentionally deferred (global default only) — see the
plan's limitations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 22:10:55 -04:00
wmantly cff816fa06 Address CodeQL findings on the OIDC auth flow
- Open redirect / client-side XSS (app-base.js): the post-login `redirect`
  read from the URL fragment was assigned straight to window.location. Add a
  same-origin guard (safeInternalPath) that rejects absolute URLs,
  protocol-relative "//host"/"/\\host", and scheme targets like
  "javascript:". Apply it in consumeTokenFragment and logInRedirect.
- Server-side defense in depth: sanitize `redirect` when storing OidcState
  and when building the callback fragment (utils/safe_redirect.js, shared +
  unit-tested).
- Missing rate limiting: throttle the unauthenticated auth endpoints
  (/login, /oidc/start, /oidc/callback) with express-rate-limit (60/IP/15m).
  Set `trust proxy: 1` so req.ip reflects the real client behind OpenResty.

Adds test/unit/safe_redirect.test.js; unit suite 77 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 13:22:12 -04:00
wmantly 10abd36340 Add OIDC login and per-domain authorization
Authentication previously implied full authorization: any valid token
could manage every host, DNS provider, domain, and user. This adds SSO
login and a per-domain rights model.

OIDC login (authorization_code + PKCE):
- conf.oidc + conf.auth blocks; clientSecret in (gitignored) secrets.js.
- utils/oidc.js (state/PKCE, code exchange, userinfo) using global fetch.
- models/oidc_state.js: short-lived state store, auto-expiring via
  model-redis 1.5 per-key TTL.
- routes/auth.js: GET /auth/oidc/start + /auth/oidc/callback; JIT-provisions
  a local user, mints an AuthToken carrying the SSO groups, hands the token to
  the browser via a URL fragment. "Log in with SSO" button on the login page.

Authorization (groups + app overrides, per-domain, with ownership):
- models/grant.js + utils/roles.js (pure, unit-tested): effective rights from
  conf.auth (admin users/groups, group->role map), Grant records
  (user|group -> global|domain -> viewer|manager|admin), and ownership
  (created_by). Roles rank admin > manager(owner) > viewer.
- AuthToken stores session groups; middleware/auth.js exposes req.groups.
- middleware/authz.js: requireAdmin, requireDomainRole(minRole, resolveDomain),
  filterViewable. Applied across routes: host mutations need manager on the
  host's domain; reads are filtered to visible domains; DNS providers, user
  management, and grant management are global-admin-only; certs need viewer.
- routes/grant.js: admin CRUD for grants. Anti-lockout via conf.auth.adminUsers
  plus migrations/grant_bootstrap.js.

Frontend: /me returns effective rights; nav gates Users/Grants to admins;
grants management page; OIDC token-fragment handling in app-base.js.

Tests: utils/roles and utils/oidc unit-tested (no redis); wired into the test
scripts. Full suite 89 pass. Also verified end-to-end against redis (grant
resolution, middleware allow/deny/403, list filtering) and the OIDC pure flow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 12:17:05 -04:00
wmantly 56c2fb1a5c Fixed issue with matching wild card 2026-07-10 00:13:40 -04:00
wmantly 0a63e05028 Import path fix 2026-02-25 12:35:21 -05:00
wmantly 6cb2880131 Using the simpleworks conf now 2025-12-31 19:28:26 -05:00
wmantly 9c762e0e0c Updated versions 2025-12-31 19:08:56 -05:00
wmantly 5dd07edf1a Moved jq-repeat to use NPM package 2025-12-31 16:01:34 -05:00
wmantly ef3fffdef2 Updated DNS-01 validation 2025-12-31 16:00:21 -05:00
wmantly d944ca6957 Cleaned up file layout 2025-12-31 15:57:33 -05:00
wmantly c418dd39a8 removed unneeded route 2024-08-15 22:09:17 -04:00
wmantly ce814c306b Fixed title whe in dev env 2024-08-15 18:50:52 -04:00
wmantly b27e1b1617 Added relations to redis ORM 2024-08-13 15:18:47 -04:00
wmantly e86de04a69 Better UI for DNS 2024-08-11 22:57:18 -04:00
wmantly cb87f22d98 DNS API error message 2024-08-11 12:24:42 -04:00
wmantly 3df3341235 DNS stuff 2024-08-10 23:07:32 -04:00
wmantly 0547e8efdf Fixed issue with form ajax 2024-08-07 14:52:02 -04:00
wmantly 41919db8f8 wild card UI done 2024-08-06 17:52:00 -04:00
wmantly 84db245f4a fixed issue with redis models 2024-08-06 13:11:55 -04:00
wmantly fb9a19d5f0 Changed .add to .create 2024-07-31 20:02:51 -04:00
wmantly 441e372987 Added socket.io 2024-07-31 19:01:55 -04:00
wmantly fc66225cf5 new front end #33 2024-07-30 20:47:46 -04:00
wmantly da1f1a4127 consolidate front end routes to single file 2024-07-30 20:40:02 -04:00
wmantly b32b2de001 pull SSL certs via API 2024-02-07 00:07:51 -05:00
wmantly 41325f7a55 updated files to ES6 2023-08-17 12:28:32 -04:00
wmantly 18f1f19211 better match for host param 2021-01-12 17:19:14 -05:00
wmantly bce23ab9fe update lookup object on edit 2020-12-22 21:27:17 -05:00
wmantly 2279918ae3 backed tracked some changes 2020-12-21 21:31:33 -05:00
wmantly 9c3d29bb7b wild card socket lookup 2020-12-21 21:28:59 -05:00
wmantly 607a5f33af ldap user 2020-05-01 17:57:25 -04:00
wmantly 7d9ea08ec9 fixed issue with user creation 2020-04-12 19:24:55 -04:00
wmantly ab108ebec8 user redis 2020-04-12 19:19:36 -04:00
wmantly 5266aec2b1 gui 2020-04-10 17:04:50 -04:00
wmantly 255d6568fd new model model 2020-04-09 22:22:54 -04:00
wmantly 9b0f60d30f host route next() for errors and removed all vaildation 2019-12-20 20:25:59 -05:00
wmantly 408c86f86a host detail fixes 2019-12-20 13:46:05 -05:00
wmantly a9026f037e added routes for hosts 2019-12-20 13:38:31 -05:00
wmantly 696afb63b2 route for current user info 2019-12-20 13:38:21 -05:00
wmantly 42700d8d0a vagrant up 2019-12-10 16:50:32 -05:00
wmantly 729b6ecaba Merge branch 'force_SSL' of https://github.com/wmantly/proxy into vagrant 2019-12-10 01:40:21 -05:00
wmantly 236753b383 moved all files to code directory 2019-09-02 11:26:48 -04:00