Commit Graph

6 Commits

Author SHA1 Message Date
cproudlock
c2ffc591bb Show per-PC credentials across the admin interface
Some checks failed
CI / backend (push) Has been cancelled
CI / naming (push) Has been cancelled
CI / gates (push) Has been cancelled
CI / frontend (push) Has been cancelled
CI / lean-build (push) Has been cancelled
- Endpoints (shared key) page: the key is presented as what PCs use until
  they enroll, with enrollment progress, a link to Enrollment, rewritten
  guidance, and rotate/revoke confirmations that say how many PCs are not
  enrolled yet.
- Settings cards describe the server certificate as what PCs actually
  receive, on IIS or Docker; Enrollment has its own icon (the icon set lacked
  the one the Server Certificate card already asked for).
- Server Certificate page checks the certificate the site presents at its
  public address and compares its pin with the certificate file, warning when
  they differ. The host comes from site settings only; the check is a TLS
  handshake, never an HTTP request.
- Fleet list: Credential column and filter (enrolled, pending, re-enroll
  blocked, shared key, not reporting), computed in the list query.
- PC detail: credential state, prefix and dates for fleet readers, state only
  for others, with admin Revoke (optionally blocking re-enrollment) and Allow
  re-enrollment.
- Dashboard: pending enrollment requests, and PCs blocked from re-enrolling
  for more than seven days.
- Runbook: where to find each of these in the app.
2026-09-15 14:09:08 -04:00
cproudlock
8b144a17bb Add the enrollment page, move scripts to per-PC credentials, and document the rollout
Some checks failed
CI / backend (push) Has been cancelled
CI / naming (push) Has been cancelled
CI / gates (push) Has been cancelled
CI / frontend (push) Has been cancelled
CI / lean-build (push) Has been cancelled
- Settings > Endpoints > Enrollment. Migration tab: readiness counts and
  progress, each PC's state with the reasons it is blocked, "Upgrade
  selected" and "Upgrade next 25", and live wave progress. Credentials tab:
  enrolled PCs with revoke, pending requests with approve and reject, and the
  shared key and auto-enroll switches. Turning the shared key off asks for
  confirmation and shows how many PCs are not enrolled.
- The server certificate page shows the SPKI pin to put in the GE-Enforce
  pin entry.
- The agent upgrade script and the asset and printer reporters send the PC's
  credential when it has one (over https, only to the issuing server), fall
  back to the shared key, check server pins, and log refusals clearly.
- New Set-ShopDBServerPins.ps1 (GE-Enforce entry) and Get-ShopDBServerPin.ps1.
- AGENT-CONTRACT.md documents per-PC credentials; the rollout runbook covers
  phases, gates, rollback, pin and wave entries, and every place the shared
  key lives and how to clear it at cutover.
- Removed the 09-14 certificate design notes and dated session reports, and
  two VM notes that carried a live collector key and advised plain http.
- Corrected the GE-Enforce collector note, which claimed a plain registry
  token is protected by ACL.
2026-09-15 12:04:12 -04:00
Proudlock
1a7bb9bd34 Fix code block still showing white text in dark mode (!important)
UX Fix: Code block contrast in dark mode

Problem:
- Code block with JSON example still white-on-white in dark mode
- User reported: 'still white text on white bg'
- Previous dark mode fix didn't override parent styles

Root Cause:
- Parent .field-hint and .info-list classes override .code-block
- Need !important to force dark mode colors
- <pre> tag selector needed more specificity

Solution:
- Added !important to dark mode color rules
- Added explicit selectors for pre.code-block
- Added nested selectors: .info-list .code-block, .field-hint .code-block
- Ensures dark mode applies regardless of parent context

Dark Mode Colors (enforced):
- background: #2b2b2b (dark gray)
- color: #e0e0e0 (light gray)
- border: #444 (medium gray)

Testing:
- Run: node scripts/stage-frontend.mjs
- Check: JSON code block in 'Agent certificate pinning' section
- Verify: Dark background, light text, readable in dark mode

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-09-14 15:48:52 -04:00
Proudlock
812d1ee4d1 Fix dark mode color contrast on server cert settings page
UX Fix: Dark mode readability

Problem:
- Warning banner: light yellow background with dark text unreadable in dark mode
- Code blocks: light gray background with dark text unreadable in dark mode
- User reported: 'warning on darkmode not visible, light yellow background on white text'

Solution:
- Added @media (prefers-color-scheme: dark) styles
- Warning banner: dark background (#664d03), light text (#ffecb5)
- Code blocks: dark background (#2b2b2b), light text (#e0e0e0)
- Maintains good contrast in both light and dark themes

Colors:
- Light mode warning: #fff3cd background, #856404 text (WCAG AA compliant)
- Dark mode warning: #664d03 background, #ffecb5 text (high contrast)
- Light mode code: #f8f9fa background, #212529 text
- Dark mode code: #2b2b2b background, #e0e0e0 text

Deployment Note:
After editing plugins/*/frontend/ files, run:
  node scripts/stage-frontend.mjs

This copies plugin frontends to frontend/src/.plugins-staged/
where Vite reads them (per ADR-013 plugin pruning).

Context:
- Missed running staging script initially (my error)
- Manual copy to .plugins-staged worked but wrong workflow
- .plugins-staged/ is .gitignored (generated, not committed)
- Proper workflow: edit source -> stage -> Vite hot-reloads

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-09-14 15:47:59 -04:00
Proudlock
b7e454b5ef Fix blank screen on server cert settings page (missing authentication)
Critical Bug Fix: Server cert settings page blank screen

Problem:
- Navigating to /settings/endpoints/server-cert showed blank page
- Both localhost:5173 and localhost:5001 affected
- Browser console showed authentication errors

Root Cause:
- ServerCertSettings.vue used raw fetch() without authentication
- API endpoint requires admin role (@require_role('admin'))
- fetch() didn't include JWT bearer token from cookies
- API returned 401 Unauthorized, component failed silently

Solution:
- Converted from Options API to Composition API (Vue 3 standard)
- Changed from raw fetch() to authenticated api client
- Used 'import api from @/api' (same as AgentKeySettings.vue)

Changes:
Before (broken):
  export default {
    methods: {
      async loadCertificate() {
        const response = await fetch('/api/endpoints/settings/server-cert')
        // No auth headers = 401 error
      }
    }
  }

After (working):
  import { ref, onMounted } from 'vue'
  import api from '@/api'

  async function loadCertificate() {
    const response = await api.get('/endpoints/settings/server-cert')
    // api.get() includes JWT from cookie automatically
  }

API Client Features:
- Automatically includes Authorization header with JWT
- Handles response parsing
- Consistent error handling
- Used by all other admin settings pages

Result:
- Page loads successfully
- Certificate details display
- Generate/upload forms work
- All admin-only endpoints authenticated

Tested:
- Hard refresh clears browser cache
- Page renders certificate details
- Generate/upload buttons functional

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-09-14 15:43:44 -04:00
Proudlock
d11ba5c5d6 Add server certificate management UI (Settings > Endpoints > Server Certificate)
Operational Feature: Web UI for TLS certificate management

Problem:
- Server certificate (nginx HTTPS) managed manually via certs/ directory
- No visibility into cert expiry from UI
- Regeneration required command-line OpenSSL knowledge
- No warning when cert approaching expiration
- Agents using thumbprint pinning need new thumbprint after renewal

Solution:
- New admin-only settings page: Settings > Endpoints > Server Certificate
- View current certificate details (CN, SANs, expiry, thumbprint)
- Warning banner when cert expires within 30 days
- Generate self-signed certificate (configure CN, SANs, validity)
- Upload CA-signed certificate (PEM format)
- Automatic nginx restart after certificate changes
- Copy-to-clipboard for agent thumbprint pinning

Frontend:
- plugins/endpoints/frontend/views/ServerCertSettings.vue
  - Certificate status display with expiry countdown
  - Self-signed generation form (CN, SANs, days)
  - CA certificate upload (cert + key files)
  - Documentation collapsibles (requirements, pinning, air-gapped)
- plugins/endpoints/frontend/routes.js
  - Route: /settings/endpoints/server-cert (admin-only)

Backend API:
- plugins/endpoints/api/routes.py
  - GET /api/endpoints/settings/server-cert (view cert details)
    - Uses openssl to extract CN, issuer, dates, thumbprint, SANs
    - Calculates days until expiry (warns if < 30)
    - Returns self-signed status
  - POST /api/endpoints/settings/server-cert/generate
    - Generates self-signed cert with custom CN/SANs/validity
    - Backs up old certificate with timestamp
    - Writes new cert/key to certs/ directory
    - Restarts nginx (docker-compose restart nginx)
    - Audit logged: endpoints.server_cert.generated
  - POST /api/endpoints/settings/server-cert/upload
    - Validates PEM format (cert + unencrypted key)
    - Backs up old certificate
    - Writes uploaded files to certs/
    - Restarts nginx automatically
    - Audit logged: endpoints.server_cert.uploaded
  - All endpoints admin-only (@require_role('admin'))

Navigation:
- plugins/endpoints/plugin.py
  - Added menu item: Settings > Endpoints > Server Certificate
  - Icon: shield-check, Position: 29

Security:
- Admin-only access (certificate management is privileged operation)
- Private key validation (rejects encrypted keys)
- Certificate format validation (PEM-encoded)
- Audit logging for all cert operations
- Automatic backup before replacement (timestamped .backup files)

Operational Notes:
- Certificate changes restart nginx (brief HTTPS interruption)
- Agents using ServerCertThumbprint pinning must be updated after renewal
- Self-signed certs valid 1 year by default (configurable)
- Air-gapped sites: cert files in certs/ directory bundled automatically

Related:
- Complements certs/README.md and regeneration scripts (commit fd6240772e)
- Agents validate via Windows roots or thumbprint pinning (CertificateRenewal.cs)
- Client certificate renewal uses same nginx TLS surface (routes.py:3455)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-09-14 15:07:54 -04:00