littleshop/DEPLOYMENT_NGINX_GUIDE.md
SysAdmin a2247d7c02
Some checks failed
Build and Deploy LittleShop / Build TeleBot Docker Image (push) Failing after 11s
Build and Deploy LittleShop / Build LittleShop Docker Image (push) Failing after 15s
Build and Deploy LittleShop / Deploy to Production VPS (Manual Only) (push) Has been skipped
Build and Deploy LittleShop / Deploy to Pre-Production (CT109) (push) Has been skipped
feat: Add customer management, payments, and push notifications with security enhancements
Major Feature Additions:
- Customer management: Full CRUD with data export and privacy compliance
- Payment management: Centralized payment tracking and administration
- Push notification subscriptions: Manage and track web push subscriptions

Security Enhancements:
- IP whitelist middleware for administrative endpoints
- Data retention service with configurable policies
- Enhanced push notification security documentation
- Security fixes progress tracking (2025-11-14)

UI/UX Improvements:
- Enhanced navigation with improved mobile responsiveness
- Updated admin dashboard with order status counts
- Improved product CRUD forms
- New customer and payment management interfaces

Backend Improvements:
- Extended customer service with data export capabilities
- Enhanced order service with status count queries
- Improved crypto payment service with better error handling
- Updated validators and configuration

Documentation:
- DEPLOYMENT_NGINX_GUIDE.md: Nginx deployment instructions
- IP_STORAGE_ANALYSIS.md: IP storage security analysis
- PUSH_NOTIFICATION_SECURITY.md: Push notification security guide
- UI_UX_IMPROVEMENT_PLAN.md: Planned UI/UX enhancements
- UI_UX_IMPROVEMENTS_COMPLETED.md: Completed improvements

Cleanup:
- Removed temporary database WAL files
- Removed stale commit message file

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-16 19:33:02 +00:00

449 lines
12 KiB
Markdown

# Nginx Deployment Guide - Push Notification Security
This guide explains how to properly configure nginx to isolate the admin panel while keeping push notifications functional.
## Architecture Overview
```
Internet
├─── api.littleshop.com (Public API)
│ └─── Only: /api/push/vapid-key, /api/push/subscribe/customer, /api/push/unsubscribe
└─── admin.dark.side (LAN-Only Admin Panel)
└─── Full access: /Admin/*, /api/*, /blazor/*
LittleShop Backend (littleshop:5000)
```
## Complete Nginx Configuration
### Step 1: Create upstream backend
```nginx
# /etc/nginx/conf.d/littleshop-upstream.conf
upstream littleshop_backend {
server littleshop:5000;
keepalive 32;
}
```
### Step 2: Public API Server (Internet-Accessible)
```nginx
# /etc/nginx/sites-available/littleshop-public-api.conf
# Rate limiting zones
limit_req_zone $binary_remote_addr zone=push_vapid:10m rate=60r/m;
limit_req_zone $binary_remote_addr zone=push_subscribe:10m rate=10r/m;
limit_req_zone $binary_remote_addr zone=push_unsubscribe:10m rate=20r/m;
server {
listen 80;
listen [::]:80;
server_name api.littleshop.com;
# Redirect to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name api.littleshop.com;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/api.littleshop.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.littleshop.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Security headers
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# VAPID public key endpoint (required for push notifications)
location = /api/push/vapid-key {
limit_req zone=push_vapid burst=10;
proxy_pass http://littleshop_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# CORS headers for push notifications
add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type" always;
# Preflight requests
if ($request_method = OPTIONS) {
return 204;
}
}
# Customer subscription endpoint
location = /api/push/subscribe/customer {
limit_req zone=push_subscribe burst=5;
proxy_pass http://littleshop_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# CORS headers
add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Methods "POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type" always;
if ($request_method = OPTIONS) {
return 204;
}
}
# Unsubscribe endpoint
location = /api/push/unsubscribe {
limit_req zone=push_unsubscribe burst=10;
proxy_pass http://littleshop_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# CORS headers
add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Methods "POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type" always;
if ($request_method = OPTIONS) {
return 204;
}
}
# Block all other endpoints
location / {
return 403 '{"error": "Access denied", "message": "Admin access is restricted to authorized networks"}';
add_header Content-Type application/json;
}
# Custom error pages
error_page 403 /403.json;
location = /403.json {
internal;
return 403 '{"error": "Access denied", "message": "This endpoint is not publicly accessible"}';
add_header Content-Type application/json;
}
}
```
### Step 3: Admin Panel Server (LAN-Only)
```nginx
# /etc/nginx/sites-available/littleshop-admin.conf
server {
listen 80;
listen [::]:80;
server_name admin.dark.side admin.littleshop.local;
# Redirect to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name admin.dark.side admin.littleshop.local;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/admin.dark.side/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/admin.dark.side/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# LAN-only IP whitelist
# Private IPv4 ranges
allow 10.0.0.0/8; # Class A private network
allow 172.16.0.0/12; # Class B private network
allow 192.168.0.0/16; # Class C private network
allow 127.0.0.1; # Localhost
# Private IPv6 ranges
allow fc00::/7; # Unique local addresses
allow ::1; # IPv6 localhost
# Add your specific office/VPN IPs here
# allow 203.0.113.50; # Example: Office static IP
# allow 198.51.100.0/24; # Example: VPN subnet
# Deny all other IPs
deny all;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;
# Proxy settings
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_buffering off;
# Admin panel
location /Admin/ {
proxy_pass http://littleshop_backend;
}
# API endpoints
location /api/ {
proxy_pass http://littleshop_backend;
}
# Blazor
location /blazor/ {
proxy_pass http://littleshop_backend;
}
# SignalR hubs
location /activityHub {
proxy_pass http://littleshop_backend;
}
location /notificationHub {
proxy_pass http://littleshop_backend;
}
# Static files
location /css/ {
proxy_pass http://littleshop_backend;
}
location /js/ {
proxy_pass http://littleshop_backend;
}
location /lib/ {
proxy_pass http://littleshop_backend;
}
location /uploads/ {
proxy_pass http://littleshop_backend;
}
# Favicon and manifest
location ~ ^/(favicon\.ico|site\.webmanifest|manifest\.json)$ {
proxy_pass http://littleshop_backend;
}
# Health check endpoint
location /health {
proxy_pass http://littleshop_backend;
access_log off;
}
# Root
location / {
proxy_pass http://littleshop_backend;
}
# Logging
access_log /var/log/nginx/littleshop-admin-access.log;
error_log /var/log/nginx/littleshop-admin-error.log warn;
}
```
### Step 4: Enable sites
```bash
# Create symbolic links
sudo ln -s /etc/nginx/sites-available/littleshop-public-api.conf /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/littleshop-admin.conf /etc/nginx/sites-enabled/
# Test configuration
sudo nginx -t
# Reload nginx
sudo systemctl reload nginx
```
## Testing the Configuration
### Test Public Endpoints (Should Work)
```bash
# VAPID key endpoint
curl https://api.littleshop.com/api/push/vapid-key
# Customer subscription (with valid customerId)
curl -X POST https://api.littleshop.com/api/push/subscribe/customer?customerId=<guid> \
-H "Content-Type: application/json" \
-d '{"endpoint":"...","keys":{"p256dh":"...","auth":"..."}}'
# Unsubscribe
curl -X POST https://api.littleshop.com/api/push/unsubscribe \
-H "Content-Type: application/json" \
-d '{"endpoint":"..."}'
```
### Test Blocked Endpoints (Should Return 403)
```bash
# Admin API (should be blocked from internet)
curl https://api.littleshop.com/api/push/subscribe
# Admin panel (should be blocked from internet)
curl https://api.littleshop.com/Admin/Dashboard
```
### Test Admin Panel from LAN (Should Work)
```bash
# From inside LAN
curl https://admin.dark.side/Admin/Dashboard
curl https://admin.dark.side/api/push/subscriptions
```
## Docker Compose Integration
If using Docker Compose with Nginx Proxy Manager:
```yaml
# docker-compose.yml
version: '3.8'
services:
littleshop:
image: littleshop:latest
container_name: littleshop
networks:
- littleshop-network
- nginx-proxy-network
environment:
- ASPNETCORE_URLS=http://+:5000
ports:
- "5000:5000" # Only accessible from docker network
nginx-proxy-manager:
image: 'jc21/nginx-proxy-manager:latest'
container_name: nginx-proxy-manager
networks:
- nginx-proxy-network
ports:
- '80:80'
- '443:443'
- '81:81' # Admin UI
volumes:
- ./nginx-data:/data
- ./nginx-letsencrypt:/etc/letsencrypt
networks:
littleshop-network:
driver: bridge
nginx-proxy-network:
driver: bridge
```
## Monitoring and Alerting
### Monitor Failed Access Attempts
```nginx
# Add to admin server block
location /Admin/ {
access_log /var/log/nginx/admin-access.log combined;
error_log /var/log/nginx/admin-error.log warn;
# Log denied IPs
if ($remote_addr !~* "^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)") {
access_log /var/log/nginx/admin-denied.log combined;
}
proxy_pass http://littleshop_backend;
}
```
### Set up fail2ban for repeated access attempts
```ini
# /etc/fail2ban/filter.d/nginx-littleshop-admin.conf
[Definition]
failregex = ^<HOST> .* "GET /Admin/
^<HOST> .* "POST /api/push/(subscribe|test|broadcast)
ignoreregex =
```
```ini
# /etc/fail2ban/jail.local
[nginx-littleshop-admin]
enabled = true
port = http,https
filter = nginx-littleshop-admin
logpath = /var/log/nginx/admin-denied.log
maxretry = 5
bantime = 3600
findtime = 600
```
## Production Checklist
- [ ] SSL certificates installed and configured
- [ ] Firewall rules updated to allow 80/443
- [ ] Rate limiting configured for public endpoints
- [ ] IP whitelist configured for admin panel
- [ ] Monitoring and logging enabled
- [ ] fail2ban configured for intrusion detection
- [ ] Health checks working
- [ ] DNS records pointing to correct servers
- [ ] Backup procedures in place
- [ ] Team has VPN access for admin panel
## Troubleshooting
### Issue: "502 Bad Gateway"
**Solution**: Check that LittleShop backend is running on port 5000
```bash
docker ps | grep littleshop
curl http://localhost:5000/health
```
### Issue: "403 Forbidden" from LAN
**Solution**: Check IP whitelist includes your LAN subnet
```bash
# Check your IP
ip addr show
# Or on Windows
ipconfig
```
### Issue: Push notifications not working
**Solution**: Verify public endpoints are accessible
```bash
curl -v https://api.littleshop.com/api/push/vapid-key
# Should return 200 OK with public key
```
### Issue: CORS errors in browser
**Solution**: Check CORS headers are present in nginx config
```bash
curl -H "Origin: https://example.com" https://api.littleshop.com/api/push/vapid-key -v
# Look for Access-Control-Allow-Origin header
```