[object Object]

← back to AbramsOS

deploy: abramsos vhost with fleet Basic Auth (admin/DW2024!) + SSL + ACME exemption

b2fd5b15176d3776ed12e4b11e78256986daa184 · 2026-08-18 15:02:52 -0700 · Steve

Files touched

Diff

commit b2fd5b15176d3776ed12e4b11e78256986daa184
Author: Steve <steve@designerwallcoverings.com>
Date:   Tue Aug 18 15:02:52 2026 -0700

    deploy: abramsos vhost with fleet Basic Auth (admin/DW2024!) + SSL + ACME exemption
---
 deploy/abramsos.agentabrams.com.nginx.conf | 23 +++++++++++++++++------
 middleware/auth.js                         | 10 +++++++---
 2 files changed, 24 insertions(+), 9 deletions(-)

diff --git a/deploy/abramsos.agentabrams.com.nginx.conf b/deploy/abramsos.agentabrams.com.nginx.conf
index 37e13e0..dbe0875 100644
--- a/deploy/abramsos.agentabrams.com.nginx.conf
+++ b/deploy/abramsos.agentabrams.com.nginx.conf
@@ -1,13 +1,24 @@
 # abramsos.agentabrams.com — Kamatera nginx reverse-proxy to AbramsOS on Steve's Mac (tailnet).
-# AbramsOS enforces its OWN requireAuth (session + TOTP + Google OAuth), so the personal
-# settlement-claims data (name/address/claims) stays behind login even though the domain is public.
-# certbot --nginx injects the acme-challenge handler + the 443 block.
+# Fleet HTTP Basic Auth (admin / DW2024!) guards the public door; the app then trusts the gate
+# and acts as the owner (SINGLE_USER_AUTOLOGIN). ACME challenge is exempt so certbot can renew.
 server {
   listen 45.61.58.125:80;
   server_name abramsos.agentabrams.com;
-  # ACME challenge served locally (NOT proxied) so certbot can issue — the app's
-  # requireAuth would otherwise redirect the challenge to /signin and fail issuance.
-  location ^~ /.well-known/acme-challenge/ { root /var/www/certbot; default_type "text/plain"; }
+  location ^~ /.well-known/acme-challenge/ { root /var/www/certbot; default_type "text/plain"; auth_basic off; }
+  location / { return 301 https://$host$request_uri; }
+}
+server {
+  listen 45.61.58.125:443 ssl;
+  server_name abramsos.agentabrams.com;
+  ssl_certificate     /etc/letsencrypt/live/abramsos.agentabrams.com/fullchain.pem;
+  ssl_certificate_key /etc/letsencrypt/live/abramsos.agentabrams.com/privkey.pem;
+  include /etc/letsencrypt/options-ssl-nginx.conf;
+  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
+
+  auth_basic "AbramsOS";
+  auth_basic_user_file /etc/nginx/.htpasswd-abramsos;
+
+  location ^~ /.well-known/acme-challenge/ { root /var/www/certbot; default_type "text/plain"; auth_basic off; }
   location / {
     proxy_pass http://100.82.17.107:9774;   # Steve's Mac (tailnet), AbramsOS :9774
     proxy_http_version 1.1;
diff --git a/middleware/auth.js b/middleware/auth.js
index 92b999e..5d638e2 100644
--- a/middleware/auth.js
+++ b/middleware/auth.js
@@ -3,17 +3,21 @@ const auth = require('../lib/auth');
 // Hydrate req.session + req.userId from the cookie
 async function loadSessionMiddleware(req, _res, next) {
   const sid = req.cookies?.[auth.SESSION_COOKIE] || req.signedCookies?.[auth.SESSION_COOKIE];
-  if (!sid) return next();
+  if (!sid) { if (process.env.SINGLE_USER_AUTOLOGIN) req.userId = process.env.SINGLE_USER_AUTOLOGIN; return next(); }
   const session = await auth.loadSession(sid);
-  if (!session) return next();
+  if (!session) { if (process.env.SINGLE_USER_AUTOLOGIN) req.userId = process.env.SINGLE_USER_AUTOLOGIN; return next(); }
   req.session = session;
   req.userId = session.user_id;
   next();
 }
 
-// Reject unauthenticated requests; redirect HTML to /signin, JSON gets 401
+// Reject unauthenticated requests; redirect HTML to /signin, JSON gets 401.
+// FLEET BASIC-AUTH MODE: when SINGLE_USER_AUTOLOGIN is set, the app is deployed behind
+// nginx HTTP Basic Auth (admin/DW2024!) — the door is already guarded, so trust the
+// request and act as the single owner instead of showing the app's own email/TOTP login.
 function requireAuth(req, res, next) {
   if (req.userId) return next();
+  if (process.env.SINGLE_USER_AUTOLOGIN) { req.userId = process.env.SINGLE_USER_AUTOLOGIN; return next(); }
   if (req.accepts('html')) return res.redirect('/signin?next=' + encodeURIComponent(req.originalUrl));
   return res.status(401).json({ error: 'unauthenticated' });
 }

← 6106895 deploy: serve ACME challenge locally in abramsos vhost (prox  ·  back to AbramsOS  ·  amazon-orders: 30-min Gmail poller (George bridge) → purchas f450f59 →