Skip to content

Security Overview — Laundry Management System

Document Information

Field Value
Project Laundry Management System
Version 1.0
Language English
Document Type Security Overview

Table of Contents

  1. Security Architecture
  2. Application Security
  3. Data Protection
  4. License & Anti-Theft
  5. Network & Docker Security
  6. Audit & Monitoring
  7. Security Best Practices for Administrators

1. Security Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                         SECURITY LAYERS                               │
│                                                                     │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │   APPLICATION SECURITY                                         │ │
│  │   JWT Auth, BCrypt passwords, Role-Based Access,               │ │
│  │   FluentValidation input sanitization, Global error masking    │ │
│  └───────────────────────────────────────────────────────────────┘ │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │   DATA PROTECTION                                              │ │
│  │   AES-256-GCM license, AES-256-CBC sync files & backups,       │ │
│  │   PostgreSQL ENCRYPTION AT REST (filesystem),                  │ │
│  │   SHA-256 hardware fingerprint                                 │ │
│  └───────────────────────────────────────────────────────────────┘ │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │   LICENSE & ANTI-THEFT                                         │ │
│  │   Hardware-bound license.dat, Offline browser block,           │ │
│  │   7-day grace → read-only hard stop                            │ │
│  └───────────────────────────────────────────────────────────────┘ │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │   NETWORK & DOCKER                                             │ │
│  │   PostgreSQL port internal only, Traefik HTTPS (LE/mkcert),    │ │
│  │   Docker isolated network, API ↔ DB encrypted link             │ │
│  └───────────────────────────────────────────────────────────────┘ │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │   AUDIT & MONITORING                                           │ │
│  │   Audit log (all CRUD+status changes), Seq log aggregation,    │ │
│  │   Health Checks, OpenTelemetry tracing                         │ │
│  └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘

2. Application Security

2.1 Authentication

Element Implementation
Password hashing BCrypt (cost factor 12) via BCrypt.Net-Next
Password policy Min 8 chars, 1 uppercase, 1 number, 1 special. Enforced during admin creation wizard and user management.
Token format JWT (HMAC-SHA256)
Token expiry Configurable (default 8 hours)
Refresh mechanism Not implemented — user re-authenticates after expiry
Login rate limiting ASP.NET Core built-in rate limiter (5 attempts/minute/IP) prevents brute force

2.2 Authorization

Element Implementation
Role-based access 3 roles: Admin, Reception, Accountant. Defined in Permissions_Matrix_EN.md.
Permission enforcement Custom PermissionFilter on every API endpoint
Frontend guard RoleGuard prevents navigation to unauthorized routes
UI element hiding hasPermission directive shows/hides buttons by role

2.3 Input Validation

Element Implementation
All endpoints FluentValidation validates every request DTO before handler execution
SQL injection EF Core parameterized queries + Dapper parameterized queries. No raw string concatenation.
XSS Angular auto-sanitizes templates. No innerHTML without DomSanitizer.
CSRF Not applicable — JWT is sent as Authorization header, not cookies.

2.4 API Protection

Element Implementation
Versioning Asp.Versioning.Mvc — API versioned via URL path (/api/v1/...)
Rate limiting Configurable per endpoint
CORS Configured to allow only the known domain and localhost
Error masking GlobalExceptionMiddleware — 500 errors return generic message, never stack traces in production
Swagger Disabled in production (ASPNETCORE_ENVIRONMENT=Production)

3. Data Protection

3.1 Encryption at Rest

Data Protection
PostgreSQL data files Host filesystem encryption (BitLocker on Windows, LUKS on Linux). Docker volume mapped to encrypted disk.
Database backups AES-256-CBC encrypted. Key derived from license + branch ID.
Sync files (.lndsync) AES-256-CBC encrypted ZIP. Key derived from license + branch ID.
license.dat AES-256-GCM. Master key stored only in vendor's HSM/environment.
Logs (Seq) Seq data stored in Docker volume on encrypted filesystem.

3.2 Encryption in Transit

Connection Protection
Browser ↔ Traefik TLS 1.3 via Let's Encrypt (online) or mkcert certificate (offline)
Traefik ↔ API Internal Docker network — plain HTTP on laundry-api:5000
API ↔ PostgreSQL Internal Docker network — PostgreSQL wire protocol (plain, trusted network)
Offline branch HTTPS mkcert creates valid certificates trusted by the OS. Zero browser warnings.

3.3 Customer Data Handling

Data Classification Handling
Customer name PII Stored as plain text. Necessary for invoices and search.
Customer phone PII Stored as plain text. Used for search and SMS notifications.
Invoice data Business Stored as plain text. GL entries are immutable.
Passwords Credentials BCrypt hashed. Never recoverable.

3.4 Data Retention & Deletion

Rule Implementation
Never hard-delete financial records Invoices, payments, journal entries marked as "cancelled" or "reversed" — rows remain.
Audit log retention Indefinite (append-only, cannot be deleted).
Database backup rotation 7 daily + 4 weekly retained.
Seq log retention Configurable. Default 30 days.

4. License & Anti-Theft

Concern Protection
Application copied to another machine Hardware fingerprint bound to license. Partial match (≥2/4 components) → warning. Full mismatch → block.
Application used after license expiry 7-day grace period (full functionality with banner). Day 8 → read-only hard stop.
Browser bypassing Tauri Offline branches: ClientTokenMiddleware rejects all requests without X-Laundry-Client-Token header. Token = HMAC-SHA256(license + branch_id, daily date). Known only to Tauri binary.
license.dat tampering AES-256-GCM with authentication tag. Any modification → decryption fails → app rejects.
Time manipulation System records last-known UTC timestamp. Backward jump beyond last-known time → logged as suspicious.
Docker image tampering Images pulled by SHA-256 digest. ghcr.io provides integrity verification.

5. Network & Docker Security

5.1 Docker Compose Isolation

Network: laundry-net (internal bridge)
┌──────────┐     ┌─────────┐     ┌────────────┐
│ Traefik  │────▶│   API   │────▶│ PostgreSQL │
│  :80,443 │     │  :5000  │     │   :5432    │
│  :8080   │     └─────────┘     └────────────┘
└──────────┘          │
       ▲              ▼
  External       ┌─────────┐
  Traffic        │   Seq   │
                 │  :5341  │
                 └─────────┘
Rule Description
PostgreSQL port is internal Port 5432 not mapped to host. Only API container can connect.
Seq port (online) Secured by Traefik + Let's Encrypt at logs.domain.com.
Seq port (offline) Local only (http://localhost:5341). Not exposed externally.
Docker socket Mounted read-only (:ro) to Traefik only.
Container user API and PostgreSQL run as non-root users inside containers.

5.2 Host OS Security

Requirement Purpose
Firewall Only ports 80, 443, and optionally 22 (SSH) open.
SSH Key-based authentication only. No password SSH.
Automatic security updates Enabled for OS packages (unattended-upgrades on Ubuntu).
Docker daemon Only trusted users in docker group.

6. Audit & Monitoring

6.1 Audit Trail

Every create, update, delete, and status change on operational and financial data is logged:

audit_logs (
    id UUID,
    entity_type VARCHAR(50),   -- 'Invoice', 'Payment', 'Customer', 'User'
    entity_id UUID,
    action VARCHAR(20),         -- 'CREATE', 'UPDATE', 'DELETE', 'STATUS_CHANGE'
    user_id UUID,
    timestamp TIMESTAMPTZ,
    old_value JSONB,
    new_value JSONB,
    branch_id UUID
)

Rules: - Audit log is append-only. Cannot be modified or deleted. - Only Admin role can view the audit log. - Logged events include: price overrides, manual discounts, user permission changes, invoice cancellations.

6.2 Monitoring

Tool What It Monitors
Seq All API errors, accounting events, sync operations, auth failures, Docker health
Health Checks UI PostgreSQL connectivity, Traefik reachability, Seq availability
.NET Metrics (/metrics) Request rate, error rate, GC, memory, thread pool
OpenTelemetry Request tracing (duration, DB queries, downstream calls)

6.3 Alerting

Alert Mechanism
Database down Health Checks UI shows red. Traefik routes to 503 error page.
License expired Admin sees red banner in-app. Logged to Seq.
Sync import failed Logged to Seq. Health report in next sync file contains the error.
Disk space low Seq disk usage metrics visible in Seq dashboard.

7. Security Best Practices for Administrators

Practice Why
Use a strong DB password 32-char random generated by the wizard. Do not reuse.
Encrypt the host disk BitLocker (Windows) or LUKS (Linux). Protects data if the physical machine is stolen.
Keep regular backups Automated daily encrypted backups. Store one copy off-site.
Update Docker images regularly Pull latest patch versions for PostgreSQL, Traefik, Seq.
Restrict SSH access Key-only authentication. Change default SSH port if exposed.
Review audit logs monthly Check for unusual activity: price overrides, invoice deletions, permission changes.
Secure the license.dat file On the host, restrict file permissions to the Docker user only.
Do not share admin credentials Each user should have their own login. The initial admin account is for setup only.
Limit Seq access On online servers, Seq is behind Traefik HTTPS with its own subdomain. Restrict to admin IPs if possible.

Revision History

Date Version Author Changes
2026-05-10 1.0 Security Architect Initial security overview