- Zmiana nazwy: "Norda Biznes Hub" → "Norda Biznes Partner" - Aktualizacja modelu AI: Gemini 2.0 Flash → Gemini 3 Flash - Zachowano historyczne odniesienia w timeline i dokumentacji Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1215 lines
42 KiB
Markdown
1215 lines
42 KiB
Markdown
# Deployment Architecture Diagram
|
|
|
|
**Document Version:** 1.0
|
|
**Last Updated:** 2026-01-10
|
|
**Status:** Production LIVE
|
|
**Diagram Type:** Infrastructure / Deployment View
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
This diagram shows the **physical deployment architecture** of the Norda Biznes Partner system. It illustrates:
|
|
|
|
- **Physical servers** and their infrastructure details (VMs, IPs, ports)
|
|
- **Network topology** and connectivity
|
|
- **Service distribution** across servers
|
|
- **Critical port mappings** and firewall rules
|
|
- **External access points** and NAT configuration
|
|
|
|
**Abstraction Level:** Infrastructure / Deployment
|
|
**Audience:** DevOps, System Administrators, Infrastructure Team
|
|
**Purpose:** Understanding physical deployment, troubleshooting connectivity issues, incident response
|
|
|
|
---
|
|
|
|
## Deployment Architecture Diagram
|
|
|
|
```mermaid
|
|
graph TB
|
|
%% External actors and entry points
|
|
Internet["🌐 INTERNET<br/>External Users"]
|
|
|
|
%% Public gateway
|
|
Fortigate["🛡️ FORTIGATE FIREWALL<br/>Public IP: 85.237.177.83<br/><br/>NAT Rules:<br/>• :443 → 10.22.68.250:443<br/>• :80 → 10.22.68.250:80"]
|
|
|
|
%% Internal network boundary
|
|
subgraph "INPI Internal Network (10.22.68.0/24)"
|
|
|
|
%% Reverse proxy server
|
|
subgraph "R11-REVPROXY-01<br/>VM 119 | 10.22.68.250"
|
|
NPM["🔒 NGINX PROXY MANAGER<br/>(Docker Container)<br/><br/>Ports:<br/>• :443 - HTTPS (SSL termination)<br/>• :80 - HTTP redirect<br/>• :81 - Admin UI (internal)<br/><br/>SSL: Let's Encrypt<br/>Certificate ID: 27<br/>Domains: nordabiznes.pl"]
|
|
end
|
|
|
|
%% Backend application server
|
|
subgraph "NORDABIZ-01<br/>VM 249 | 10.22.68.249"
|
|
subgraph "Application Layer"
|
|
Gunicorn["🌐 GUNICORN + FLASK<br/>Port: 5000<br/>Workers: 4<br/>User: www-data<br/><br/>App: /var/www/nordabiznes<br/>Service: nordabiznes.service<br/>Timeout: 120s"]
|
|
|
|
NginxSys["⚠️ NGINX (System)<br/>Ports: 80, 443<br/><br/>Purpose: HTTP→HTTPS redirect<br/>Status: UNUSED in production<br/><br/>⚠️ NPM should NEVER use this!"]
|
|
end
|
|
|
|
subgraph "Data Layer"
|
|
PostgreSQL["💾 POSTGRESQL 14<br/>Port: 5432<br/>Listen: 127.0.0.1 ONLY<br/><br/>Database: nordabiz<br/>User: nordabiz_app<br/>Tables: 36<br/><br/>⚠️ No external connections!"]
|
|
end
|
|
|
|
subgraph "Background Jobs"
|
|
Scripts["⚙️ PYTHON SCRIPTS<br/>Execution: Cron / Manual<br/><br/>• seo_audit.py<br/>• social_media_audit.py<br/>• gbp_audit.py<br/>• fetch_company_news.py"]
|
|
end
|
|
end
|
|
|
|
%% Git server
|
|
subgraph "r11-git-inpi<br/>Server | 10.22.68.180"
|
|
Gitea["📚 GITEA<br/>Port: 3000 (HTTPS)<br/><br/>Repository: maciejpi/nordabiz<br/>SSL: Self-signed<br/>Users: maciejpi, gitadmin"]
|
|
end
|
|
end
|
|
|
|
%% External services
|
|
subgraph "External APIs (HTTPS)"
|
|
Gemini["🤖 Google Gemini AI<br/>generativelanguage.googleapis.com<br/>Auth: API Key"]
|
|
BraveAPI["🔍 Brave Search API<br/>api.search.brave.com<br/>Auth: API Key"]
|
|
PageSpeed["📊 Google PageSpeed API<br/>googleapis.com/pagespeedonline<br/>Auth: API Key"]
|
|
Places["📍 Google Places API<br/>maps.googleapis.com/maps/api<br/>Auth: API Key"]
|
|
KRS["🏛️ KRS Open API<br/>api-krs.ms.gov.pl<br/>Auth: Public"]
|
|
MSGraph["📧 Microsoft Graph API<br/>graph.microsoft.com<br/>Auth: OAuth 2.0"]
|
|
end
|
|
|
|
%% User traffic flow
|
|
Internet -->|"HTTPS :443<br/>HTTP :80"| Fortigate
|
|
Fortigate -->|"NAT<br/>443→443<br/>80→80"| NPM
|
|
|
|
%% NPM to backend (CRITICAL PATH)
|
|
NPM ==>|"⚠️ CRITICAL!<br/>HTTP :5000<br/>(NOT port 80!)"| Gunicorn
|
|
NPM -.->|"❌ WRONG!<br/>Creates redirect loop"| NginxSys
|
|
|
|
%% Application to database (localhost only)
|
|
Gunicorn <-->|"SQL<br/>localhost:5432<br/>SQLAlchemy ORM"| PostgreSQL
|
|
Scripts <-->|"SQL<br/>127.0.0.1:5432<br/>Direct connection"| PostgreSQL
|
|
|
|
%% External API calls
|
|
Gunicorn -->|"HTTPS<br/>API calls"| Gemini
|
|
Gunicorn -->|"HTTPS<br/>API calls"| BraveAPI
|
|
Gunicorn -->|"HTTPS<br/>API calls"| Places
|
|
Gunicorn -->|"HTTPS<br/>API calls"| KRS
|
|
Gunicorn -->|"HTTPS<br/>OAuth 2.0"| MSGraph
|
|
Scripts -->|"HTTPS<br/>Audit requests"| PageSpeed
|
|
Scripts -->|"HTTPS<br/>News search"| BraveAPI
|
|
|
|
%% Git operations
|
|
Gunicorn -.->|"git pull<br/>(deployment)"| Gitea
|
|
|
|
%% Styling
|
|
classDef serverStyle fill:#2c3e50,stroke:#34495e,color:#ecf0f1,stroke-width:3px
|
|
classDef appStyle fill:#1168bd,stroke:#0b4884,color:#ffffff,stroke-width:3px
|
|
classDef dbStyle fill:#438dd5,stroke:#2e6295,color:#ffffff,stroke-width:3px
|
|
classDef proxyStyle fill:#e74c3c,stroke:#c0392b,color:#ffffff,stroke-width:4px
|
|
classDef firewallStyle fill:#f39c12,stroke:#d68910,color:#ffffff,stroke-width:4px
|
|
classDef externalStyle fill:#95a5a6,stroke:#7f8c8d,color:#ffffff,stroke-width:2px
|
|
classDef warningStyle fill:#e67e22,stroke:#d35400,color:#ffffff,stroke-width:3px
|
|
|
|
class NPM proxyStyle
|
|
class Fortigate firewallStyle
|
|
class Gunicorn,Scripts appStyle
|
|
class PostgreSQL dbStyle
|
|
class NginxSys warningStyle
|
|
class Gitea serverStyle
|
|
class Gemini,BraveAPI,PageSpeed,Places,KRS,MSGraph externalStyle
|
|
```
|
|
|
|
---
|
|
|
|
## Infrastructure Inventory
|
|
|
|
### Production Servers
|
|
|
|
| Server | VM ID | IP Address | Hostname | OS | vCPU | RAM | Disk | Hypervisor |
|
|
|--------|-------|------------|----------|-----|------|-----|------|------------|
|
|
| **NORDABIZ-01** | 249 | 10.22.68.249 | nordabiz-01 | Ubuntu 22.04 | 4 | 8 GB | 100 GB SSD | Proxmox VE |
|
|
| **R11-REVPROXY-01** | 119 | 10.22.68.250 | r11-revproxy-01 | Ubuntu 22.04 | 2 | 4 GB | 50 GB SSD | Proxmox VE |
|
|
| **r11-git-inpi** | - | 10.22.68.180 | r11-git-inpi | Ubuntu 22.04 | 2 | 4 GB | 100 GB SSD | Proxmox VE |
|
|
|
|
---
|
|
|
|
## Server Details
|
|
|
|
### NORDABIZ-01 (Backend Application Server)
|
|
|
|
**Infrastructure:**
|
|
- **VM ID:** 249
|
|
- **IP Address:** 10.22.68.249
|
|
- **Internal DNS:** nordabiznes.inpi.local
|
|
- **OS:** Ubuntu 22.04 LTS
|
|
- **Resources:** 4 vCPU, 8 GB RAM, 100 GB SSD
|
|
- **Hypervisor:** Proxmox VE
|
|
|
|
**Services Running:**
|
|
|
|
| Service | Port | Binding | User | Status | Purpose |
|
|
|---------|------|---------|------|--------|---------|
|
|
| **Gunicorn/Flask** | **5000** | **0.0.0.0** | **www-data** | **Active** | **Main Application** ✓ |
|
|
| PostgreSQL 14 | 5432 | 127.0.0.1 | postgres | Active | Database |
|
|
| Nginx (system) | 80, 443 | 0.0.0.0 | root | Active | HTTP→HTTPS redirect ⚠️ |
|
|
| SSH | 22 | 0.0.0.0 | - | Active | Remote administration |
|
|
|
|
**Application Paths:**
|
|
```
|
|
/var/www/nordabiznes/ # Application root
|
|
├── app.py # Main Flask application (13,144 lines)
|
|
├── database.py # SQLAlchemy models (36 tables)
|
|
├── venv/ # Python virtual environment
|
|
├── templates/ # Jinja2 templates
|
|
├── static/ # CSS, JS, images
|
|
├── scripts/ # Background jobs
|
|
└── .env # Environment variables (secrets)
|
|
|
|
/var/log/nordabiznes/ # Application logs
|
|
├── access.log # Gunicorn access log
|
|
└── error.log # Gunicorn error log
|
|
|
|
/etc/systemd/system/
|
|
└── nordabiznes.service # Systemd unit file
|
|
```
|
|
|
|
**Service Configuration:**
|
|
```bash
|
|
# Service management
|
|
sudo systemctl status nordabiznes
|
|
sudo systemctl restart nordabiznes
|
|
sudo systemctl start nordabiznes
|
|
sudo systemctl stop nordabiznes
|
|
|
|
# View logs
|
|
sudo journalctl -u nordabiznes -f # Follow logs
|
|
sudo journalctl -u nordabiznes -n 100 # Last 100 lines
|
|
tail -f /var/log/nordabiznes/access.log # Access log
|
|
tail -f /var/log/nordabiznes/error.log # Error log
|
|
```
|
|
|
|
**SSH Access:**
|
|
```bash
|
|
ssh maciejpi@10.22.68.249
|
|
# CRITICAL: Always use 'maciejpi' user, NEVER 'root'!
|
|
```
|
|
|
|
**Gunicorn Configuration:**
|
|
```ini
|
|
# /etc/systemd/system/nordabiznes.service
|
|
[Service]
|
|
ExecStart=/var/www/nordabiznes/venv/bin/gunicorn \
|
|
--bind 0.0.0.0:5000 \
|
|
--workers 4 \
|
|
--timeout 120 \
|
|
--access-logfile /var/log/nordabiznes/access.log \
|
|
--error-logfile /var/log/nordabiznes/error.log \
|
|
app:app
|
|
```
|
|
|
|
**⚠️ WARNING - System Nginx:**
|
|
- System nginx on ports 80/443 is for HTTP→HTTPS redirects ONLY
|
|
- **NEVER** configure NPM to use port 80 or 443 on this server
|
|
- Doing so causes infinite redirect loop (ERR_TOO_MANY_REDIRECTS)
|
|
- See: `docs/INCIDENT_REPORT_20260102.md`
|
|
|
|
---
|
|
|
|
### R11-REVPROXY-01 (Reverse Proxy Server)
|
|
|
|
**Infrastructure:**
|
|
- **VM ID:** 119
|
|
- **IP Address:** 10.22.68.250
|
|
- **OS:** Ubuntu 22.04 LTS
|
|
- **Resources:** 2 vCPU, 4 GB RAM, 50 GB SSD
|
|
- **Hypervisor:** Proxmox VE
|
|
|
|
**Services Running:**
|
|
|
|
| Service | Port | Binding | Type | Purpose |
|
|
|---------|------|---------|------|---------|
|
|
| NPM (HTTPS) | 443 | 0.0.0.0 | Docker | Public HTTPS traffic (SSL termination) |
|
|
| NPM (HTTP) | 80 | 0.0.0.0 | Docker | HTTP→HTTPS redirect |
|
|
| NPM Admin UI | 81 | 0.0.0.0 | Docker | NPM management interface (internal only) |
|
|
| SSH | 22 | 0.0.0.0 | System | Remote administration |
|
|
|
|
**Docker Setup:**
|
|
```bash
|
|
# NPM container details
|
|
Container Name: nginx-proxy-manager_app_1
|
|
Image: jc21/nginx-proxy-manager:latest
|
|
Volumes:
|
|
- /docker/npm/data:/data
|
|
- /docker/npm/letsencrypt:/etc/letsencrypt
|
|
|
|
# Container management
|
|
docker ps | grep nginx-proxy-manager # Check status
|
|
docker logs nginx-proxy-manager_app_1 --tail 50 # View logs
|
|
docker exec -it nginx-proxy-manager_app_1 /bin/sh # Shell access
|
|
docker restart nginx-proxy-manager_app_1 # Restart
|
|
```
|
|
|
|
**NPM Configuration Database:**
|
|
- **Location:** `/data/database.sqlite` (inside container)
|
|
- **Access:** SQLite database
|
|
- **Backup:** Manual via docker cp
|
|
|
|
**NPM Web UI:**
|
|
- **URL:** http://10.22.68.250:81
|
|
- **Access:** Internal network only
|
|
- **Authentication:** Admin credentials required
|
|
|
|
**SSH Access:**
|
|
```bash
|
|
ssh maciejpi@10.22.68.250
|
|
```
|
|
|
|
**Critical Proxy Host Configuration (ID: 27):**
|
|
```sql
|
|
-- Query NPM database
|
|
docker exec nginx-proxy-manager_app_1 \
|
|
sqlite3 /data/database.sqlite \
|
|
"SELECT id, domain_names, forward_host, forward_port FROM proxy_host WHERE id = 27;"
|
|
|
|
-- Expected output:
|
|
-- 27|["nordabiznes.pl","www.nordabiznes.pl"]|10.22.68.249|5000
|
|
```
|
|
|
|
**⚠️ CRITICAL NPM CONFIGURATION:**
|
|
|
|
| Parameter | Value | Critical Notes |
|
|
|-----------|-------|----------------|
|
|
| Proxy Host ID | 27 | Fixed identifier |
|
|
| Domain Names | nordabiznes.pl, www.nordabiznes.pl | Both variants |
|
|
| Forward Scheme | http | SSL terminated at NPM |
|
|
| Forward Host | 10.22.68.249 | NORDABIZ-01 IP |
|
|
| **Forward Port** | **5000** | **MUST BE 5000, NOT 80!** ⚠️ |
|
|
| SSL Certificate | Let's Encrypt (ID 27) | Auto-renewal enabled |
|
|
| Force SSL | Yes | HTTP→HTTPS redirect |
|
|
| HTTP/2 | Yes | Performance optimization |
|
|
| HSTS | Yes | max-age=31536000 |
|
|
| Block Exploits | Yes | Security hardening |
|
|
|
|
**Verification After NPM Changes:**
|
|
```bash
|
|
curl -I https://nordabiznes.pl/health
|
|
# Expected: HTTP/2 200 OK
|
|
# If redirect loop: Check forward_port is 5000!
|
|
```
|
|
|
|
---
|
|
|
|
### r11-git-inpi (Git Repository Server)
|
|
|
|
**Infrastructure:**
|
|
- **IP Address:** 10.22.68.180
|
|
- **Hostname:** r11-git-inpi
|
|
- **OS:** Ubuntu 22.04 LTS
|
|
- **Resources:** 2 vCPU, 4 GB RAM, 100 GB SSD
|
|
|
|
**Services Running:**
|
|
|
|
| Service | Port | Protocol | Access | Purpose |
|
|
|---------|------|----------|--------|---------|
|
|
| Gitea | 3000 | HTTPS | Internal | Git repository hosting |
|
|
| SSH | 22 | SSH | Internal | Server administration |
|
|
|
|
**Gitea Configuration:**
|
|
- **Web URL:** https://10.22.68.180:3000/
|
|
- **Repository:** maciejpi/nordabiz
|
|
- **Clone URL:** https://10.22.68.180:3000/maciejpi/nordabiz.git
|
|
- **SSL Certificate:** Self-signed (SSL verification disabled)
|
|
|
|
**User Accounts:**
|
|
- `maciejpi` - Personal account (repository owner)
|
|
- `gitadmin` - Gitea administrator
|
|
|
|
**Production Deployment via Git:**
|
|
```bash
|
|
# On NORDABIZ-01
|
|
cd /var/www/nordabiznes
|
|
sudo -u www-data git -c http.sslVerify=false pull origin master
|
|
sudo systemctl restart nordabiznes
|
|
|
|
# Verify deployment
|
|
curl -I http://localhost:5000/health
|
|
```
|
|
|
|
**Git Remote Configuration:**
|
|
```bash
|
|
# Production git config (on NORDABIZ-01)
|
|
git remote -v
|
|
# inpi https://10.22.68.180:3000/maciejpi/nordabiz.git (fetch)
|
|
# inpi https://10.22.68.180:3000/maciejpi/nordabiz.git (push)
|
|
```
|
|
|
|
---
|
|
|
|
## Network Topology
|
|
|
|
### Network Segments
|
|
|
|
| Segment | CIDR/Address | Purpose | Security Level |
|
|
|---------|--------------|---------|----------------|
|
|
| Public Internet | 0.0.0.0/0 | External user access | Untrusted |
|
|
| WAN (Fortigate) | 85.237.177.83/32 | Public gateway | Perimeter |
|
|
| INPI LAN | 10.22.68.0/24 | Internal services network | Trusted |
|
|
| Localhost | 127.0.0.1/8 | Server-local services | Isolated |
|
|
|
|
### DNS Configuration
|
|
|
|
**External DNS (OVH):**
|
|
|
|
| Type | Name | Value | TTL | Purpose |
|
|
|------|------|-------|-----|---------|
|
|
| A | nordabiznes.pl | 85.237.177.83 | 3600 | Main domain |
|
|
| A | www.nordabiznes.pl | 85.237.177.83 | 3600 | WWW subdomain |
|
|
|
|
**Internal DNS (INPI):**
|
|
|
|
| Type | Name | Value | Purpose |
|
|
|------|------|-------|---------|
|
|
| A | nordabiznes.inpi.local | 10.22.68.249 | Internal access |
|
|
| A | nordabiz-01.inpi.local | 10.22.68.249 | Server hostname |
|
|
| A | revproxy-01.inpi.local | 10.22.68.250 | Reverse proxy |
|
|
| A | git.inpi.local | 10.22.68.180 | Git server |
|
|
|
|
---
|
|
|
|
## Port Mappings
|
|
|
|
### NORDABIZ-01 (10.22.68.249) Port Matrix
|
|
|
|
| Port | Protocol | Service | Binding | Access | Purpose | Security Notes |
|
|
|------|----------|---------|---------|--------|---------|----------------|
|
|
| 22 | TCP | SSH | 0.0.0.0 | Internal only | Server administration | Key-based auth |
|
|
| 80 | TCP | Nginx (system) | 0.0.0.0 | Internal only | HTTP→HTTPS redirect | ⚠️ Not for NPM! |
|
|
| 443 | TCP | Nginx (system) | 0.0.0.0 | Internal only | HTTPS redirect | ⚠️ Not for NPM! |
|
|
| **5000** | **TCP** | **Gunicorn/Flask** | **0.0.0.0** | **Internal only** | **Main Application** | **✓ NPM uses this** |
|
|
| 5432 | TCP | PostgreSQL | 127.0.0.1 | Localhost only | Database | No external access |
|
|
| 5433 | TCP | - | - | Unused | Reserved for dev Docker | - |
|
|
|
|
**⚠️ PORT 5000 - CRITICAL NOTES:**
|
|
- This is the **ONLY** correct port for NPM to connect to
|
|
- Ports 80/443 are for nginx system service (redirects only)
|
|
- Using port 80 in NPM causes infinite redirect loop
|
|
- Always verify after NPM configuration changes
|
|
|
|
---
|
|
|
|
### R11-REVPROXY-01 (10.22.68.250) Port Matrix
|
|
|
|
| Port | Protocol | Service | Binding | Access | Purpose | Security Notes |
|
|
|------|----------|---------|---------|--------|---------|----------------|
|
|
| 22 | TCP | SSH | 0.0.0.0 | Internal only | Server administration | Key-based auth |
|
|
| 80 | TCP | NPM | 0.0.0.0 | Public (via NAT) | HTTP→HTTPS redirect | Auto-redirect |
|
|
| 81 | TCP | NPM Admin UI | 0.0.0.0 | Internal only | NPM management | Auth required |
|
|
| 443 | TCP | NPM | 0.0.0.0 | Public (via NAT) | HTTPS traffic | SSL termination |
|
|
|
|
---
|
|
|
|
### r11-git-inpi (10.22.68.180) Port Matrix
|
|
|
|
| Port | Protocol | Service | Binding | Access | Purpose | Security Notes |
|
|
|------|----------|---------|---------|--------|---------|----------------|
|
|
| 22 | TCP | SSH | 0.0.0.0 | Internal only | Server administration | Key-based auth |
|
|
| 3000 | TCP | Gitea (HTTPS) | 0.0.0.0 | Internal only | Git repository hosting | Self-signed SSL |
|
|
|
|
---
|
|
|
|
### Fortigate Firewall NAT Rules
|
|
|
|
| External Port | Protocol | Internal IP | Internal Port | Purpose | Traffic |
|
|
|---------------|----------|-------------|---------------|---------|---------|
|
|
| 443 | TCP | 10.22.68.250 | 443 | HTTPS public access | Incoming |
|
|
| 80 | TCP | 10.22.68.250 | 80 | HTTP redirect | Incoming |
|
|
|
|
**Firewall Rules:**
|
|
- Allow: Public → 10.22.68.250:443 (HTTPS)
|
|
- Allow: Public → 10.22.68.250:80 (HTTP, redirects to HTTPS)
|
|
- Deny: All other inbound traffic
|
|
- Allow: Internal → External (outbound API calls)
|
|
|
|
---
|
|
|
|
## Network Flow Diagrams
|
|
|
|
### Successful Production Request Flow
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 1. USER BROWSER │
|
|
│ https://nordabiznes.pl │
|
|
└────────────────────────────┬────────────────────────────────────┘
|
|
│ DNS: nordabiznes.pl → 85.237.177.83
|
|
│ HTTPS :443
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 2. FORTIGATE FIREWALL (85.237.177.83) │
|
|
│ NAT: 443 → 10.22.68.250:443 │
|
|
└────────────────────────────┬────────────────────────────────────┘
|
|
│ Forward to proxy
|
|
│ HTTPS :443
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 3. NPM @ R11-REVPROXY-01 (10.22.68.250:443) │
|
|
│ • Accept HTTPS connection │
|
|
│ • TLS handshake (Let's Encrypt certificate) │
|
|
│ • Terminate SSL/TLS │
|
|
│ • Add security headers (HSTS, etc.) │
|
|
│ • Proxy pass to backend │
|
|
└────────────────────────────┬────────────────────────────────────┘
|
|
│ ⚠️ CRITICAL PATH
|
|
│ http://10.22.68.249:5000
|
|
│ (NOT port 80!)
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 4. GUNICORN @ NORDABIZ-01 (10.22.68.249:5000) │
|
|
│ • Receive HTTP request (decrypted) │
|
|
│ • Load balance across 4 workers │
|
|
│ • Pass to Flask application │
|
|
└────────────────────────────┬────────────────────────────────────┘
|
|
│ Process request
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 5. FLASK APP (app.py) │
|
|
│ • Rate limiting check (Flask-Limiter) │
|
|
│ • Session validation (Flask-Login) │
|
|
│ • CSRF protection (Flask-WTF) │
|
|
│ • Route matching (90+ routes) │
|
|
│ • Business logic execution │
|
|
└────────────────────────────┬────────────────────────────────────┘
|
|
│ Database query
|
|
│ localhost:5432
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 6. POSTGRESQL @ localhost:5432 │
|
|
│ • Execute SQL query (SQLAlchemy ORM) │
|
|
│ • Apply constraints and indexes │
|
|
│ • Return result set │
|
|
└────────────────────────────┬────────────────────────────────────┘
|
|
│ Results
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 7. FLASK APP (render response) │
|
|
│ • Jinja2 template rendering │
|
|
│ • JSON serialization (API routes) │
|
|
│ • Apply response headers │
|
|
└────────────────────────────┬────────────────────────────────────┘
|
|
│ HTTP response
|
|
▼
|
|
GUNICORN → NPM (encrypt with TLS) → FORTIGATE → USER BROWSER
|
|
```
|
|
|
|
**Timing Breakdown:**
|
|
- DNS resolution: ~50ms
|
|
- SSL handshake: ~100ms
|
|
- NPM proxy: ~10ms
|
|
- Flask processing: ~50-500ms (depends on query complexity)
|
|
- Database query: ~10-100ms
|
|
- Template rendering: ~20-50ms
|
|
- **Total:** ~240-810ms (typical range)
|
|
|
|
---
|
|
|
|
### Failed Request Flow (Port 80 Misconfiguration)
|
|
|
|
**⚠️ This caused the 2026-01-02 production incident**
|
|
|
|
```
|
|
USER BROWSER
|
|
│
|
|
│ https://nordabiznes.pl
|
|
▼
|
|
FORTIGATE (NAT)
|
|
│
|
|
▼
|
|
NPM @ 10.22.68.250:443
|
|
│ SSL termination
|
|
│ ❌ WRONG: Proxy to http://10.22.68.249:80
|
|
▼
|
|
NGINX (System) @ 10.22.68.249:80
|
|
│
|
|
│ ❌ Return: 301 → https://nordabiznes.pl
|
|
▼
|
|
NPM (receives redirect)
|
|
│ SSL termination again
|
|
│ ❌ Proxy to http://10.22.68.249:80 (LOOP!)
|
|
▼
|
|
... Infinite redirect loop ...
|
|
│
|
|
▼
|
|
BROWSER ERROR: ERR_TOO_MANY_REDIRECTS
|
|
```
|
|
|
|
**Root Cause:** NPM forwarding to port 80 instead of port 5000
|
|
|
|
**Fix:** Change NPM `forward_port` from 80 to 5000
|
|
|
|
**Prevention:** Always verify `forward_port = 5000` after NPM changes
|
|
|
|
---
|
|
|
|
## SSL/TLS Configuration
|
|
|
|
### Let's Encrypt Certificate
|
|
|
|
**Certificate Details:**
|
|
- **Provider:** Let's Encrypt
|
|
- **Managed By:** NPM (Nginx Proxy Manager)
|
|
- **Certificate ID:** 27
|
|
- **Domains:**
|
|
- nordabiznes.pl
|
|
- www.nordabiznes.pl
|
|
- **Key Type:** RSA 2048-bit
|
|
- **Validity:** 90 days (auto-renewed)
|
|
- **Renewal:** Automatic (30 days before expiry)
|
|
|
|
**TLS Configuration:**
|
|
- **Protocols:** TLS 1.2, TLS 1.3 (TLS 1.0/1.1 disabled)
|
|
- **Cipher Suites:** Modern ciphers only
|
|
- **HTTP/2:** Enabled
|
|
- **HSTS:** Enabled (max-age=31536000, includeSubDomains)
|
|
|
|
**Security Headers (Added by NPM):**
|
|
```http
|
|
Strict-Transport-Security: max-age=31536000; includeSubDomains
|
|
X-Frame-Options: SAMEORIGIN
|
|
X-Content-Type-Options: nosniff
|
|
X-XSS-Protection: 1; mode=block
|
|
```
|
|
|
|
**Certificate Verification:**
|
|
```bash
|
|
# Check certificate details
|
|
openssl s_client -connect nordabiznes.pl:443 -servername nordabiznes.pl < /dev/null 2>/dev/null | \
|
|
openssl x509 -noout -dates -subject -issuer
|
|
|
|
# Check expiry date
|
|
curl -vI https://nordabiznes.pl 2>&1 | grep -E "(expire|issuer)"
|
|
|
|
# Test SSL configuration
|
|
curl -I https://nordabiznes.pl/health
|
|
# Expected: HTTP/2 200 OK
|
|
```
|
|
|
|
---
|
|
|
|
## External API Connectivity
|
|
|
|
### Outbound HTTPS Connections
|
|
|
|
| API | Endpoint | Port | Authentication | Purpose | Rate Limit |
|
|
|-----|----------|------|----------------|---------|------------|
|
|
| **Google Gemini AI** | generativelanguage.googleapis.com | 443 | API Key | AI chat, image analysis | Cost-limited |
|
|
| **Brave Search** | api.search.brave.com | 443 | API Key | News monitoring, social discovery | 2000/month |
|
|
| **Google PageSpeed** | googleapis.com/pagespeedonline | 443 | API Key | SEO/performance audits | 25,000/day |
|
|
| **Google Places** | maps.googleapis.com/maps/api | 443 | API Key | Business profiles, reviews | Quota-based |
|
|
| **KRS Open API** | api-krs.ms.gov.pl | 443 | Public | Company verification | Unlimited |
|
|
| **Microsoft Graph** | graph.microsoft.com | 443 | OAuth 2.0 | Email notifications | Tenant-based |
|
|
|
|
**Firewall Requirements:**
|
|
- **Outbound HTTPS (443):** Must be allowed for all external API calls
|
|
- **DNS Resolution:** Must be allowed (port 53)
|
|
- **NTP:** Recommended for time sync (port 123)
|
|
|
|
**API Key Storage:**
|
|
- **Location:** `/var/www/nordabiznes/.env` (production)
|
|
- **Permissions:** `chmod 600` (owner read/write only)
|
|
- **Owner:** `www-data:www-data`
|
|
- **⚠️ NEVER commit to git!** (in `.gitignore`)
|
|
|
|
---
|
|
|
|
## Deployment Workflow
|
|
|
|
### Git-Based Deployment
|
|
|
|
```
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ DEVELOPMENT (Mac) │
|
|
│ │
|
|
│ • Code changes │
|
|
│ • Local testing (localhost:5000) │
|
|
│ • Git commit │
|
|
└───────────────────────────┬────────────────────────────────────┘
|
|
│
|
|
│ git push origin master
|
|
│ git push inpi master
|
|
▼
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ GIT REPOSITORIES │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────┐ │
|
|
│ │ GITEA @ r11-git-inpi (10.22.68.180:3000) │ │
|
|
│ │ Repository: maciejpi/nordabiz │ │
|
|
│ │ Purpose: Production deployment source │ │
|
|
│ └─────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌─────────────────────────────────────────────────────┐ │
|
|
│ │ GITHUB @ github.com │ │
|
|
│ │ Repository: pienczyn/nordabiz │ │
|
|
│ │ Purpose: Cloud backup, collaboration │ │
|
|
│ └─────────────────────────────────────────────────────┘ │
|
|
└───────────────────────────┬────────────────────────────────────┘
|
|
│
|
|
│ ssh + git pull
|
|
▼
|
|
┌────────────────────────────────────────────────────────────────┐
|
|
│ PRODUCTION (NORDABIZ-01) │
|
|
│ │
|
|
│ 1. SSH to server: │
|
|
│ ssh maciejpi@10.22.68.249 │
|
|
│ │
|
|
│ 2. Pull latest code: │
|
|
│ cd /var/www/nordabiznes │
|
|
│ sudo -u www-data git -c http.sslVerify=false pull │
|
|
│ │
|
|
│ 3. Restart service: │
|
|
│ sudo systemctl restart nordabiznes │
|
|
│ │
|
|
│ 4. Verify deployment: │
|
|
│ curl -I http://localhost:5000/health │
|
|
│ curl -I https://nordabiznes.pl/health │
|
|
└────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
**Deployment Checklist:**
|
|
1. ✅ Code tested locally
|
|
2. ✅ Git commit with descriptive message
|
|
3. ✅ Push to both remotes (Gitea + GitHub)
|
|
4. ✅ SSH to production server as `maciejpi`
|
|
5. ✅ Pull latest code as `www-data` user
|
|
6. ✅ Restart `nordabiznes` service
|
|
7. ✅ Verify health endpoint (localhost:5000)
|
|
8. ✅ Verify public endpoint (nordabiznes.pl)
|
|
9. ✅ Check logs for errors (`journalctl -u nordabiznes`)
|
|
10. ✅ Update release notes in `app.py`
|
|
|
|
---
|
|
|
|
## Monitoring and Health Checks
|
|
|
|
### Health Check Endpoint
|
|
|
|
**Endpoint:** `GET /health`
|
|
**Authentication:** None (public)
|
|
**Purpose:** Service availability monitoring
|
|
|
|
**Response (Healthy):**
|
|
```http
|
|
HTTP/2 200 OK
|
|
Content-Type: application/json
|
|
|
|
```
|
|
|
|
**Health Check Commands:**
|
|
```bash
|
|
# External check (via NPM)
|
|
curl -I https://nordabiznes.pl/health
|
|
# Expected: HTTP/2 200 OK
|
|
|
|
# Internal check (direct to Flask)
|
|
curl -I http://10.22.68.249:5000/health
|
|
# Expected: HTTP/1.1 200 OK
|
|
|
|
# Localhost check (from NORDABIZ-01)
|
|
curl -I http://localhost:5000/health
|
|
# Expected: HTTP/1.1 200 OK
|
|
```
|
|
|
|
### Service Status Checks
|
|
|
|
**Application Service:**
|
|
```bash
|
|
# Check systemd service
|
|
sudo systemctl status nordabiznes
|
|
# Active: active (running)
|
|
|
|
# Check process
|
|
ps aux | grep gunicorn
|
|
# Should show 5 processes (1 master + 4 workers)
|
|
|
|
# Check listening ports
|
|
ss -tlnp | grep :5000
|
|
# LISTEN 0 128 0.0.0.0:5000 0.0.0.0:*
|
|
```
|
|
|
|
**Database Service:**
|
|
```bash
|
|
# Check PostgreSQL service
|
|
sudo systemctl status postgresql
|
|
# Active: active (running)
|
|
|
|
# Check database connectivity
|
|
sudo -u postgres psql -c "SELECT version();"
|
|
|
|
# Check active connections
|
|
sudo -u postgres psql -c "SELECT count(*) FROM pg_stat_activity WHERE datname='nordabiz';"
|
|
```
|
|
|
|
**NPM Service:**
|
|
```bash
|
|
# Check Docker container
|
|
docker ps | grep nginx-proxy-manager
|
|
# Should show container running
|
|
|
|
# Check NPM logs
|
|
docker logs nginx-proxy-manager_app_1 --tail 50
|
|
|
|
# Verify proxy configuration
|
|
docker exec nginx-proxy-manager_app_1 \
|
|
sqlite3 /data/database.sqlite \
|
|
"SELECT forward_port FROM proxy_host WHERE id = 27;"
|
|
# Expected: 5000
|
|
```
|
|
|
|
### Planned Monitoring (Not Yet Implemented)
|
|
|
|
**Zabbix Monitoring (Planned):**
|
|
- HTTPS endpoint monitoring (5-minute intervals)
|
|
- Response time tracking
|
|
- SSL certificate expiry alerts (30-day warning)
|
|
- Service availability alerts
|
|
- Database connection monitoring
|
|
- Disk space monitoring
|
|
|
|
---
|
|
|
|
## Backup and Disaster Recovery
|
|
|
|
### PostgreSQL Database Backups
|
|
|
|
**Current Status:** Manual backups only
|
|
**Backup Location:** `/backup/nordabiz/` (on NORDABIZ-01)
|
|
|
|
**Manual Backup Procedure:**
|
|
```bash
|
|
# Create backup
|
|
sudo -u postgres pg_dump nordabiz | gzip > \
|
|
/backup/nordabiz/nordabiz_$(date +%Y%m%d_%H%M%S).sql.gz
|
|
|
|
# Verify backup
|
|
gunzip -c /backup/nordabiz/nordabiz_20260110_120000.sql.gz | head -n 20
|
|
```
|
|
|
|
**Restore Procedure:**
|
|
```bash
|
|
# Stop application
|
|
sudo systemctl stop nordabiznes
|
|
|
|
# Drop existing database (CAUTION!)
|
|
sudo -u postgres psql -c "DROP DATABASE nordabiz;"
|
|
sudo -u postgres psql -c "CREATE DATABASE nordabiz OWNER nordabiz_app;"
|
|
|
|
# Restore from backup
|
|
gunzip -c /backup/nordabiz/nordabiz_20260110_120000.sql.gz | \
|
|
sudo -u postgres psql nordabiz
|
|
|
|
# Grant permissions
|
|
sudo -u postgres psql -d nordabiz -c "GRANT ALL ON ALL TABLES IN SCHEMA public TO nordabiz_app;"
|
|
sudo -u postgres psql -d nordabiz -c "GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO nordabiz_app;"
|
|
|
|
# Restart application
|
|
sudo systemctl start nordabiznes
|
|
|
|
# Verify
|
|
curl -I http://localhost:5000/health
|
|
```
|
|
|
|
**Planned:** Automated daily backups via cron
|
|
|
|
---
|
|
|
|
### VM Snapshots (Proxmox)
|
|
|
|
**Snapshot Schedule:**
|
|
- **Daily:** 7-day retention
|
|
- **Weekly:** 4-week retention
|
|
- **Monthly:** 6-month retention
|
|
|
|
**Snapshot Storage:** Proxmox Backup Server
|
|
|
|
**Restore Procedure:**
|
|
1. Access Proxmox VE web interface
|
|
2. Navigate to VM (249 or 119)
|
|
3. Select "Snapshots" tab
|
|
4. Choose restore point
|
|
5. Confirm snapshot restoration
|
|
6. Start VM after restore
|
|
7. Verify services are running
|
|
|
|
**Recovery Time Objective (RTO):** ~15 minutes
|
|
**Recovery Point Objective (RPO):** ~24 hours (daily snapshots)
|
|
|
|
---
|
|
|
|
### NPM Configuration Backup
|
|
|
|
**Database Location:** `/data/database.sqlite` (inside NPM container)
|
|
|
|
**Manual Backup:**
|
|
```bash
|
|
# Backup NPM database
|
|
docker exec nginx-proxy-manager_app_1 cat /data/database.sqlite > \
|
|
/backup/npm/npm_$(date +%Y%m%d).sqlite
|
|
|
|
# Backup SSL certificates
|
|
docker exec nginx-proxy-manager_app_1 tar czf - /etc/letsencrypt > \
|
|
/backup/npm/letsencrypt_$(date +%Y%m%d).tar.gz
|
|
```
|
|
|
|
**Restore Procedure:**
|
|
```bash
|
|
# Stop NPM container
|
|
docker stop nginx-proxy-manager_app_1
|
|
|
|
# Restore database
|
|
cat /backup/npm/npm_20260110.sqlite | \
|
|
docker exec -i nginx-proxy-manager_app_1 sh -c 'cat > /data/database.sqlite'
|
|
|
|
# Restore SSL certificates
|
|
cat /backup/npm/letsencrypt_20260110.tar.gz | \
|
|
docker exec -i nginx-proxy-manager_app_1 sh -c 'tar xzf - -C /'
|
|
|
|
# Start NPM container
|
|
docker start nginx-proxy-manager_app_1
|
|
|
|
# Verify
|
|
curl -I https://nordabiznes.pl/health
|
|
```
|
|
|
|
---
|
|
|
|
## Security Configuration
|
|
|
|
### Firewall Rules (Fortigate)
|
|
|
|
**Inbound Rules:**
|
|
```
|
|
Priority 1: ALLOW Public (0.0.0.0/0) → 10.22.68.250:443 (HTTPS)
|
|
Priority 2: ALLOW Public (0.0.0.0/0) → 10.22.68.250:80 (HTTP redirect)
|
|
Priority 100: DENY All other inbound traffic
|
|
```
|
|
|
|
**Outbound Rules:**
|
|
```
|
|
Priority 1: ALLOW 10.22.68.0/24 → Internet (HTTPS :443)
|
|
Priority 2: ALLOW 10.22.68.0/24 → Internet (DNS :53)
|
|
Priority 3: ALLOW 10.22.68.0/24 → Internet (NTP :123)
|
|
Priority 100: DENY All other outbound traffic
|
|
```
|
|
|
|
### SSH Access Control
|
|
|
|
**Allowed Users:**
|
|
- `maciejpi` - Administrator account (sudo access)
|
|
|
|
**Authentication:**
|
|
- SSH key-based authentication (required)
|
|
- Password authentication disabled
|
|
|
|
**Firewall:**
|
|
- SSH accessible from internal network only (10.22.68.0/24)
|
|
- No public SSH access
|
|
|
|
**SSH Configuration:**
|
|
```bash
|
|
# /etc/ssh/sshd_config
|
|
PermitRootLogin no
|
|
PasswordAuthentication no
|
|
PubkeyAuthentication yes
|
|
AllowUsers maciejpi
|
|
```
|
|
|
|
### Database Security
|
|
|
|
**PostgreSQL Access Control:**
|
|
```conf
|
|
# postgresql.conf
|
|
listen_addresses = 'localhost' # NO external connections!
|
|
port = 5432
|
|
|
|
# pg_hba.conf
|
|
local nordabiz nordabiz_app md5 # Local socket
|
|
host nordabiz nordabiz_app 127.0.0.1/32 md5 # Localhost only
|
|
```
|
|
|
|
**Connection Restrictions:**
|
|
- ✅ Localhost (127.0.0.1): Allowed
|
|
- ❌ Internal network (10.22.68.0/24): Denied
|
|
- ❌ External network: Denied
|
|
|
|
**User Privileges:**
|
|
```sql
|
|
-- nordabiz_app user (application)
|
|
GRANT CONNECT ON DATABASE nordabiz TO nordabiz_app;
|
|
GRANT USAGE ON SCHEMA public TO nordabiz_app;
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO nordabiz_app;
|
|
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO nordabiz_app;
|
|
|
|
-- postgres user (admin)
|
|
-- Full privileges on all databases
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting Guide
|
|
|
|
### Common Issues and Solutions
|
|
|
|
#### Issue 1: ERR_TOO_MANY_REDIRECTS
|
|
|
|
**Symptoms:**
|
|
- Browser shows "ERR_TOO_MANY_REDIRECTS"
|
|
- Site inaccessible via https://nordabiznes.pl
|
|
- Internal access (localhost:5000) works fine
|
|
|
|
**Cause:** NPM forwarding to port 80 instead of port 5000
|
|
|
|
**Solution:**
|
|
```bash
|
|
# 1. Verify NPM configuration
|
|
ssh maciejpi@10.22.68.250
|
|
docker exec nginx-proxy-manager_app_1 \
|
|
sqlite3 /data/database.sqlite \
|
|
"SELECT id, forward_host, forward_port FROM proxy_host WHERE id = 27;"
|
|
|
|
# 2. If forward_port != 5000, fix it via NPM Web UI:
|
|
# - Access http://10.22.68.250:81
|
|
# - Edit Proxy Host #27
|
|
# - Set Forward Port to 5000
|
|
# - Save
|
|
|
|
# 3. Verify fix
|
|
curl -I https://nordabiznes.pl/health
|
|
# Expected: HTTP/2 200 OK
|
|
```
|
|
|
|
**Prevention:** Always verify `forward_port = 5000` after NPM changes
|
|
|
|
---
|
|
|
|
#### Issue 2: 502 Bad Gateway
|
|
|
|
**Symptoms:**
|
|
- NPM returns 502 Bad Gateway
|
|
- External access fails
|
|
- Internal access may or may not work
|
|
|
|
**Possible Causes:**
|
|
1. Flask/Gunicorn service down
|
|
2. Port 5000 not listening
|
|
3. Network connectivity issue
|
|
|
|
**Diagnosis:**
|
|
```bash
|
|
# 1. Check Gunicorn service
|
|
ssh maciejpi@10.22.68.249
|
|
sudo systemctl status nordabiznes
|
|
# If not running: sudo systemctl start nordabiznes
|
|
|
|
# 2. Check port 5000 is listening
|
|
ss -tlnp | grep :5000
|
|
# Expected: LISTEN 0.0.0.0:5000
|
|
|
|
# 3. Test internal access
|
|
curl -I http://localhost:5000/health
|
|
# Expected: HTTP/1.1 200 OK
|
|
|
|
# 4. Test from NPM server
|
|
ssh maciejpi@10.22.68.250
|
|
curl -I http://10.22.68.249:5000/health
|
|
# Expected: HTTP/1.1 200 OK
|
|
```
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Restart Gunicorn service
|
|
sudo systemctl restart nordabiznes
|
|
|
|
# Check logs for errors
|
|
sudo journalctl -u nordabiznes -n 50
|
|
```
|
|
|
|
---
|
|
|
|
#### Issue 3: Database Connection Errors
|
|
|
|
**Symptoms:**
|
|
- Application shows "Database connection error"
|
|
- Health endpoint returns error
|
|
- Logs show "could not connect to server"
|
|
|
|
**Diagnosis:**
|
|
```bash
|
|
# 1. Check PostgreSQL service
|
|
sudo systemctl status postgresql
|
|
# If not running: sudo systemctl start postgresql
|
|
|
|
# 2. Test database connection
|
|
sudo -u postgres psql -c "SELECT 1;"
|
|
# Expected: 1
|
|
|
|
# 3. Check application database access
|
|
sudo -u www-data psql -h 127.0.0.1 -U nordabiz_app -d nordabiz -c "SELECT 1;"
|
|
# Expected: 1 (if password prompt, check .env)
|
|
|
|
# 4. Check active connections
|
|
sudo -u postgres psql -c "SELECT count(*) FROM pg_stat_activity WHERE datname='nordabiz';"
|
|
```
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Restart PostgreSQL
|
|
sudo systemctl restart postgresql
|
|
|
|
# Verify connection string in .env
|
|
cat /var/www/nordabiznes/.env | grep DATABASE_URL
|
|
# Expected: postgresql://nordabiz_app:PASSWORD@localhost:5432/nordabiz
|
|
```
|
|
|
|
---
|
|
|
|
#### Issue 4: SSL Certificate Expired
|
|
|
|
**Symptoms:**
|
|
- Browser shows "Your connection is not private"
|
|
- SSL certificate warning
|
|
- Cert expired error
|
|
|
|
**Diagnosis:**
|
|
```bash
|
|
# Check certificate expiry
|
|
openssl s_client -connect nordabiznes.pl:443 -servername nordabiznes.pl < /dev/null 2>/dev/null | \
|
|
openssl x509 -noout -dates
|
|
|
|
# Check NPM logs for renewal errors
|
|
docker logs nginx-proxy-manager_app_1 | grep -i letsencrypt
|
|
```
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Manual certificate renewal via NPM
|
|
# 1. Access NPM Web UI: http://10.22.68.250:81
|
|
# 2. Go to SSL Certificates
|
|
# 3. Find certificate ID 27
|
|
# 4. Click "Renew" button
|
|
|
|
# OR restart NPM to trigger auto-renewal
|
|
docker restart nginx-proxy-manager_app_1
|
|
```
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
### Architecture Documentation
|
|
|
|
- **[System Context Diagram](./01-system-context.md)** - High-level system overview
|
|
- **[Container Diagram](./02-container-diagram.md)** - Major containers and technology stack
|
|
- **[Flask Application Components](./04-flask-components.md)** - Internal application structure (planned)
|
|
- **[Database Schema](./05-database-schema.md)** - Entity relationships (planned)
|
|
- **[Network Topology](./07-network-topology.md)** - Network diagram (planned)
|
|
|
|
### Data Flow Documentation
|
|
|
|
- **[HTTP Request Flow](./flows/06-http-request-flow.md)** - Detailed HTTP request path (planned)
|
|
- **[Authentication Flow](./flows/01-authentication-flow.md)** - User login sequence (planned)
|
|
|
|
### Incident Documentation
|
|
|
|
- **[Incident Report 2026-01-02](../INCIDENT_REPORT_20260102.md)** - ERR_TOO_MANY_REDIRECTS incident
|
|
|
|
### Infrastructure Documentation
|
|
|
|
- **[CLAUDE.md](../../CLAUDE.md)** - Complete project documentation
|
|
- **[Infrastructure Analysis](./.auto-claude/specs/003-.../analysis/infrastructure-components.md)** - Detailed infrastructure analysis (internal)
|
|
|
|
---
|
|
|
|
## Maintenance Notes
|
|
|
|
### When to Update This Diagram
|
|
|
|
**✏️ UPDATE when:**
|
|
- New server added to infrastructure
|
|
- IP address changes
|
|
- Port configuration changes
|
|
- Network topology changes (new firewall rules, NAT changes)
|
|
- SSL/TLS configuration updates
|
|
- Deployment procedure changes
|
|
- Critical configuration changes (like NPM proxy settings)
|
|
|
|
**❌ DON'T UPDATE for:**
|
|
- Application code changes (not infrastructure)
|
|
- Database schema changes (covered in database diagram)
|
|
- New Flask routes (covered in component diagram)
|
|
- Dependency updates (same infrastructure)
|
|
|
|
### Review Frequency
|
|
|
|
- **After incidents:** Immediate review and update
|
|
- **After infrastructure changes:** Within 24 hours
|
|
- **Quarterly:** Verify accuracy against actual configuration
|
|
- **Before major deployments:** Review deployment section
|
|
|
|
### Verification Checklist
|
|
|
|
- [ ] All IP addresses are current
|
|
- [ ] All port numbers are correct
|
|
- [ ] NPM proxy configuration verified (port 5000!)
|
|
- [ ] DNS records match actual configuration
|
|
- [ ] SSL certificate status checked
|
|
- [ ] Firewall rules documented accurately
|
|
- [ ] Service status verified on all servers
|
|
- [ ] Health check endpoints tested
|
|
- [ ] Backup procedures tested
|
|
|
|
---
|
|
|
|
## Glossary
|
|
|
|
| Term | Definition |
|
|
|------|------------|
|
|
| **NPM** | Nginx Proxy Manager - Docker-based reverse proxy with web UI |
|
|
| **NAT** | Network Address Translation - maps public IP to internal IPs |
|
|
| **SSL Termination** | Decrypting HTTPS at proxy, forwarding HTTP to backend |
|
|
| **Fortigate** | Enterprise firewall/router device |
|
|
| **Gunicorn** | Python WSGI HTTP server for running Flask applications |
|
|
| **Let's Encrypt** | Free automated SSL/TLS certificate authority |
|
|
| **HSTS** | HTTP Strict Transport Security - forces HTTPS |
|
|
| **Proxmox VE** | Virtualization platform for managing VMs |
|
|
| **Systemd** | Linux init system and service manager |
|
|
| **WSGI** | Web Server Gateway Interface - Python web server standard |
|
|
| **VM** | Virtual Machine |
|
|
| **vCPU** | Virtual CPU core |
|
|
| **ORM** | Object-Relational Mapping (SQLAlchemy) |
|
|
|
|
---
|
|
|
|
**Document Status:** ✅ Complete
|
|
**Diagram Validated:** 2026-01-10
|
|
**Production Verified:** 2026-01-10
|
|
**Mermaid Syntax:** v10.6+
|
|
**Renders in:** GitHub, GitLab, VS Code (with Mermaid extension)
|
|
|
|
---
|
|
|
|
**⚠️ CRITICAL CONFIGURATION REMINDER:**
|
|
|
|
**NPM Proxy Host 27 MUST use:**
|
|
- **Forward Host:** 10.22.68.249
|
|
- **Forward Port:** 5000 (NOT 80!)
|
|
|
|
**Always verify after changes:**
|
|
```bash
|
|
curl -I https://nordabiznes.pl/health
|
|
# Expected: HTTP/2 200 OK
|
|
```
|
|
|
|
**See:** `docs/INCIDENT_REPORT_20260102.md` for details on port 80 incident
|