Security Overview — Laundry Management System
| Field |
Value |
| Project |
Laundry Management System |
| Version |
1.0 |
| Language |
English |
| Document Type |
Security Overview |
Table of Contents
- Security Architecture
- Application Security
- Data Protection
- License & Anti-Theft
- Network & Docker Security
- Audit & Monitoring
- 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 |
| 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 |