# Network Topology Diagram **Document Version:** 1.0 **Last Updated:** 2026-04-04 **Status:** Production LIVE **Diagram Type:** Network Topology / Infrastructure Network --- > **NOTE (2026-04-04):** Production migrated to OVH VPS (57.128.200.27). Production traffic no longer goes through FortiGate/NPM. Staging (10.22.68.248) and NPM (10.22.68.250) remain unchanged. ## Overview This document provides a **network-centric view** of the Norda Biznes Partner infrastructure. It focuses on: - **Network layout and zones** (Public Internet, DMZ, Application Zone, Data Zone) - **Fortigate firewall configuration** with NAT rules and port forwarding - **Internal network topology** (10.22.68.0/24 subnet) - **Network routing and traffic flows** - **DNS configuration** (external and internal) - **Network security boundaries** and firewall rules - **IP addressing scheme** and port mappings **Abstraction Level:** Network Infrastructure **Audience:** Network Engineers, Security Team, DevOps, System Administrators **Purpose:** Understanding network architecture, firewall configuration, routing, security boundaries, incident response **Related Documentation:** - [Deployment Architecture](03-deployment-architecture.md) - Infrastructure and service deployment view - [Container Diagram](02-container-diagram.md) - Application container architecture - [HTTP Request Flow](flows/06-http-request-flow.md) - Complete request path through network layers --- ## Network Topology Diagram ```mermaid graph TB %% External network subgraph "Public Internet (Untrusted)" Users["👥 External Users
Website Visitors"] DNS_OVH["🌐 OVH DNS
nordabiznes.pl
→ 57.128.200.27"] end %% Production (OVH VPS - direct internet access) subgraph "OVH Cloud (Warsaw) — PRODUCTION" VPS["🖥️ OVH VPS inpi-vps-waw01

IP: 57.128.200.27

Services:
• Nginx :443 (SSL termination)
• Nginx :80 (redirect)
• Gunicorn :5000 (localhost)
• PostgreSQL :5432 (localhost)
• SSH :22"] end %% Staging (on-prem via FortiGate + NPM) subgraph "INPI Internal Network (10.22.68.0/24) — STAGING" Fortigate["🛡️ FORTIGATE
WAN: 85.237.177.83
LAN: 10.22.68.1

NAT for staging:
• :443 → 10.22.68.250:443"] NPM_Server["🖥️ R11-REVPROXY-01
10.22.68.250
NPM (staging proxy)"] Staging_Server["🖥️ NORDABIZ-STAGING-01
10.22.68.248
Staging app + DB"] Git_Server["🖥️ r11-git-inpi
10.22.68.180
Gitea :3000"] end %% External services subgraph "External APIs (Internet)" API_Google["☁️ Google Cloud APIs
Gemini AI, PageSpeed, Places"] API_MS["☁️ Microsoft Graph API
Email sending"] API_Brave["☁️ Brave Search API"] API_KRS["🏛️ KRS Open API"] end %% Production traffic (direct to OVH VPS) Users -->|"DNS Query
nordabiznes.pl"| DNS_OVH DNS_OVH -->|"DNS Response
57.128.200.27"| Users Users -->|"HTTPS :443"| VPS %% Staging traffic (via FortiGate) Fortigate -.->|"staging NAT
→ NPM"| NPM_Server NPM_Server -.->|"HTTP :5000"| Staging_Server %% API calls from production VPS -->|"HTTPS"| API_Google VPS -->|"HTTPS OAuth 2.0"| API_MS VPS -->|"HTTPS"| API_Brave VPS -->|"HTTPS"| API_KRS %% Styling classDef public fill:#f9f,stroke:#333,stroke-width:2px classDef production fill:#9f9,stroke:#333,stroke-width:3px classDef staging fill:#ff9,stroke:#333,stroke-width:2px classDef external fill:#ccc,stroke:#333,stroke-width:1px class Users,DNS_OVH public class VPS production class Fortigate,NPM_Server,Staging_Server,Git_Server staging class API_Google,API_MS,API_Brave,API_KRS external ``` --- ## Network Zones ### Zone 1: Public Internet (Untrusted) **Purpose:** External user access and public DNS resolution **Components:** - Website visitors (anonymous and authenticated users) - OVH DNS servers (authoritative for nordabiznes.pl) **Security Level:** **Untrusted** - No trust, all traffic inspected at perimeter **Access:** - HTTPS (443) - Public website access - HTTP (80) - Redirects to HTTPS **Protection:** - Fortigate firewall inspection - Rate limiting at application layer - DDoS protection (via OVH) --- ### Zone 2: Network Perimeter (FortiGate) — Staging Only **Purpose:** Network security boundary for staging environment. Production traffic no longer goes through FortiGate. **Components:** - **Fortigate Firewall** - WAN IP: 85.237.177.83 - LAN IP: 10.22.68.1 (gateway for internal network) **Security Level:** **Perimeter** - First line of defense for staging **NAT Configuration (staging only):** | Public Address | Public Port | Internal Address | Internal Port | Protocol | Purpose | |----------------|-------------|------------------|---------------|----------|---------| | 85.237.177.83 | 443 | 10.22.68.250 | 443 | TCP | HTTPS to NPM (staging) | | 85.237.177.83 | 80 | 10.22.68.250 | 80 | TCP | HTTP to NPM (redirect) | **Note:** Production (nordabiznes.pl) DNS now points directly to OVH VPS (57.128.200.27), bypassing FortiGate entirely. --- ### Zone 3: DMZ Zone (Semi-Trusted) — Staging Only **Purpose:** SSL termination and reverse proxy for staging environment only. **Network:** 10.22.68.250/32 (single host) **Components:** - **R11-REVPROXY-01** (VM 119) - IP: 10.22.68.250 - Services: Nginx Proxy Manager (Docker), SSH - Handles: staging.nordabiznes.pl **Security Level:** **Semi-Trusted** - Exposed to staging traffic **Inbound Traffic:** - From Internet (via Fortigate NAT): HTTPS :443, HTTP :80 - From ADMIN_NET: SSH :22 **Outbound Traffic:** - To Application Zone (57.128.200.27): HTTP :5000 (Flask/Gunicorn) - To Internet: HTTPS (for Let's Encrypt ACME challenge, Docker image updates) **Security Controls:** - SSL/TLS termination (Let's Encrypt certificates) - HTTP to HTTPS redirection - Minimal attack surface (only NPM + SSH) - Regular security updates - Docker container isolation **⚠️ CRITICAL CONFIGURATION:** ```yaml # NPM Proxy Host Configuration (Host ID: 27) domain_names: - nordabiznes.pl forward_host: 57.128.200.27 forward_port: 5000 # ⚠️ MUST be 5000, NOT 80! ssl: certificate_id: 27 force_ssl: true http2_support: true hsts_enabled: true ``` **Common Misconfiguration:** ```yaml # ❌ WRONG - Causes infinite redirect loop forward_port: 80 # This forwards to nginx on OVH VPS inpi-vps-waw01, which redirects to HTTPS ``` **Correct Configuration:** ```yaml # ✅ CORRECT - Forwards directly to Flask/Gunicorn forward_port: 5000 # Direct connection to application server ``` **Verification:** ```bash # Test from external network curl -I https://nordabiznes.pl/health # Expected: HTTP/2 200 (success) # Test internal routing (from NPM server) curl -I http://57.128.200.27:5000/health # Expected: HTTP/1.1 200 (success) ``` **Incident Reference:** See [INCIDENT_REPORT_20260102.md](../../docs/INCIDENT_REPORT_20260102.md) for port 80 vs 5000 misconfiguration incident. --- ### Zone 4: Application Zone — Production (OVH VPS) **Purpose:** Application hosting, business logic processing **Network:** 57.128.200.27 (OVH VPS, public IP) **Components:** - **OVH VPS** (inpi-vps-waw01) - IP: 57.128.200.27 - Services: Nginx :443/:80, Gunicorn :5000 (localhost), PostgreSQL :5432 (localhost), SSH :22 **Security Level:** **Production** - Public-facing, secured by nginx + ufw **Inbound Traffic:** - From Internet: HTTPS :443 (nginx SSL termination) - From Internet: HTTP :80 (redirects to HTTPS) - SSH :22 (key-based auth only) **Outbound Traffic:** - To External APIs: HTTPS :443 (Google, Microsoft, Brave, KRS) - To Internet: HTTP/HTTPS (web scraping ALEO.com, rejestr.io) **Localhost Services (127.0.0.1):** - **PostgreSQL** :5432 - Listen address: 127.0.0.1 ONLY - No external connections allowed - Access via Unix socket or localhost TCP - Users: nordabiz_app (application), maciejpi (admin) **Security Controls:** - PostgreSQL confined to localhost (critical data protection) - Flask/Gunicorn behind reverse proxy (no direct internet exposure) - Application-level security (CSRF, authentication, rate limiting) - Regular security updates - SSH key-based authentication only **Network Configuration:** ``` IP Address: 57.128.200.27/24 Gateway: 10.22.68.1 DNS: 10.22.68.1 ``` **Service Ports:** | Port | Service | Listen Address | Access | Purpose | |------|---------|----------------|--------|---------| | 5000 | Gunicorn | 0.0.0.0 | Internal network | Flask application | | 5432 | PostgreSQL | 127.0.0.1 | Localhost only | Database | | 80 | Nginx | 0.0.0.0 | Internal network | HTTP→HTTPS redirect (unused) | | 443 | Nginx | 0.0.0.0 | Internal network | SSL (unused in prod) | | 22 | SSH | 0.0.0.0 | Admin network | Remote administration | --- ### Zone 5: Internal Services Zone (Trusted) **Purpose:** Internal development and deployment tools **Network:** 10.22.68.180/32 (single host) **Components:** - **r11-git-inpi** (Git Server) - IP: 10.22.68.180 - Services: Gitea :3000 (HTTPS), SSH :22 **Security Level:** **Trusted** - Internal services, no public exposure **Inbound Traffic:** - From Application Zone (57.128.200.27): HTTPS :3000 (git pull for deployment) - From ADMIN_NET: SSH :22, HTTPS :3000 (git operations) **Outbound Traffic:** - Minimal (software updates) **Security Controls:** - No direct internet access from public - Self-signed SSL certificate (internal use) - SSH key-based authentication - Repository access control (users: maciejpi, gitadmin) **Git Configuration:** - Repository: maciejpi/nordabiz - URL: https://10.22.68.180:3000/maciejpi/nordabiz.git - SSL verification: disabled for internal self-signed cert - Authentication: username + password (over HTTPS) --- ### Zone 6: External APIs (Internet) **Purpose:** Third-party service integration **Components:** - Google Cloud APIs (Gemini AI, PageSpeed, Places) - Microsoft Graph API (Email) - Brave Search API (News) - KRS Open API (Company registry) - Web scraping targets (ALEO.com, rejestr.io) **Security Level:** **External** - Outside our control **Access Pattern:** - Application Zone initiates outbound HTTPS connections - API key authentication (stored in .env, never committed) - OAuth 2.0 for Microsoft Graph - Rate limiting and quota management **Network Flow:** ``` App Server (57.128.200.27) → Fortigate (10.22.68.1) → Internet Gateway → External API (HTTPS :443) ``` **Security Controls:** - HTTPS/TLS 1.2+ enforced - API keys in environment variables - Rate limiting to prevent quota exhaustion - Error handling and retry logic - Cost tracking and monitoring --- ## IP Addressing Scheme ### Public IP Addresses | IP Address | Type | Purpose | Owner | |------------|------|---------|-------| | 85.237.177.83 | Static Public | Fortigate WAN interface | INPI Infrastructure | ### Internal IP Addresses (10.22.68.0/24) | IP Address | Hostname | VM ID | Purpose | Zone | |------------|----------|-------|---------|------| | 10.22.68.1 | fortigate-lan | N/A | Default gateway | Perimeter | | 10.22.68.180 | r11-git-inpi | N/A | Gitea server | Internal Services | | 57.128.200.27 | nordabiz-01 | 249 | Application + DB server | Application | | 10.22.68.250 | r11-revproxy-01 | 119 | NPM reverse proxy | DMZ | ### Reserved Ranges | Range | Purpose | Status | |-------|---------|--------| | 10.22.68.1-10.22.68.99 | Infrastructure services | Reserved | | 10.22.68.100-10.22.68.199 | Application servers | Available | | 10.22.68.200-10.22.68.254 | Future expansion | Available | --- ## Port Mapping Reference ### Complete Port Matrix | Service | Server | IP | Port | Protocol | Access | Purpose | |---------|--------|-----|------|----------|--------|---------| | **Public-Facing** | | HTTPS | NPM | 10.22.68.250 | 443 | TCP | Public (via NAT) | SSL termination | | HTTP | NPM | 10.22.68.250 | 80 | TCP | Public (via NAT) | Redirect to HTTPS | | **Application Services** | | Flask/Gunicorn | OVH VPS inpi-vps-waw01 | 57.128.200.27 | 5000 | TCP | Internal only | Web application | | PostgreSQL | OVH VPS inpi-vps-waw01 | 127.0.0.1 | 5432 | TCP | Localhost only | Database | | Nginx (unused) | OVH VPS inpi-vps-waw01 | 57.128.200.27 | 80 | TCP | Internal only | HTTP redirect (not used) | | Nginx (unused) | OVH VPS inpi-vps-waw01 | 57.128.200.27 | 443 | TCP | Internal only | SSL (not used) | | **Internal Services** | | Gitea | r11-git-inpi | 10.22.68.180 | 3000 | TCP | Internal only | Git repository | | NPM Admin | NPM | 10.22.68.250 | 81 | TCP | Internal only | NPM web UI | | **Administration** | | SSH | OVH VPS inpi-vps-waw01 | 57.128.200.27 | 22 | TCP | Admin network | Remote admin | | SSH | NPM | 10.22.68.250 | 22 | TCP | Admin network | Remote admin | | SSH | r11-git-inpi | 10.22.68.180 | 22 | TCP | Admin network | Remote admin | ### Critical Port Forwarding (NPM → Application) **⚠️ MOST CRITICAL CONFIGURATION:** | Source | Destination | Protocol | Purpose | Notes | |--------|-------------|----------|---------|-------| | NPM :443 | 57.128.200.27:5000 | HTTP | HTTPS requests → Flask | ✅ CORRECT | | NPM :80 | 57.128.200.27:5000 | HTTP | HTTP requests → Flask | ✅ CORRECT | | NPM :443 | 57.128.200.27:80 | HTTP | HTTPS requests → Nginx | ❌ WRONG - Redirect loop! | **Why Port 5000 is Critical:** 1. NPM terminates SSL at port 443 2. NPM forwards decrypted HTTP to backend 3. Backend must accept HTTP (not redirect to HTTPS) 4. Flask/Gunicorn on port 5000 accepts HTTP ✅ 5. Nginx on port 80 redirects HTTP → HTTPS ❌ (creates loop) **Incident History:** - **2026-01-02:** Misconfiguration set NPM to forward to port 80 - **Result:** ERR_TOO_MANY_REDIRECTS (infinite loop) - **Root Cause:** Nginx on port 80 redirects to HTTPS, NPM receives HTTPS, forwards to Nginx, repeat - **Fix:** Changed NPM forward port from 80 to 5000 - **Prevention:** Documentation, monitoring, configuration backups --- ## DNS Configuration ### External DNS (Public) **Provider:** OVH DNS **Domain:** nordabiznes.pl **Nameservers:** (OVH default nameservers) **DNS Records:** | Record Type | Name | Value | TTL | Purpose | |-------------|------|-------|-----|---------| | A | nordabiznes.pl | 57.128.200.27 | 3600 | Main website (OVH VPS) | | A | www.nordabiznes.pl | 57.128.200.27 | 3600 | WWW subdomain (OVH VPS) | | A | staging.nordabiznes.pl | 85.237.177.83 | 3600 | Staging (via FortiGate) | | MX | nordabiznes.pl | (Not configured) | - | No email hosting | | TXT | nordabiznes.pl | (SPF, DKIM if configured) | - | Email authentication | **DNS Propagation:** ```bash # Check DNS resolution dig nordabiznes.pl +short # Expected: 57.128.200.27 # Check from Google DNS dig @8.8.8.8 nordabiznes.pl +short # Expected: 57.128.200.27 # Check WHOIS whois nordabiznes.pl ``` ### Internal DNS (Private) **Provider:** INPI Internal DNS **Domain:** inpi.local **DNS Server:** 10.22.68.1 (Fortigate or internal DNS server) **DNS Records:** | Record Type | Name | Value | Purpose | |-------------|------|-------|---------| | A | nordabiznes.inpi.local | 57.128.200.27 | Application server | | A | git.inpi.local | 10.22.68.180 | Gitea server | | A | npm.inpi.local | 10.22.68.250 | NPM server | **DNS Resolution Flow:** ``` 1. Application needs to resolve external domain (e.g., generativelanguage.googleapis.com) → Queries internal DNS (10.22.68.1) → Internal DNS forwards to public DNS (8.8.8.8) → Returns public IP 2. Application needs to resolve internal domain (e.g., git.inpi.local) → Queries internal DNS (10.22.68.1) → Internal DNS returns 10.22.68.180 → No external query ``` **Configuration:** ```bash # /etc/resolv.conf on OVH VPS inpi-vps-waw01 nameserver 10.22.68.1 search inpi.local ``` --- ## Network Routing ### Default Gateway All internal servers use **10.22.68.1** (Fortigate LAN interface) as default gateway. ### Routing Table (OVH VPS inpi-vps-waw01 Example) ```bash # ip route show default via 10.22.68.1 dev ens18 # All non-local traffic → Fortigate 10.22.68.0/24 dev ens18 proto kernel scope link src 57.128.200.27 # Local subnet 127.0.0.0/8 dev lo # Localhost ``` ### Network Flow Examples #### Example 1: User Accesses Website ``` User Browser (Internet) ↓ DNS query OVH DNS: nordabiznes.pl → 57.128.200.27 ↓ HTTPS :443 Nginx @ OVH VPS (57.128.200.27:443) ↓ SSL termination, proxy_pass Gunicorn (127.0.0.1:5000) ↓ HTTP request processing Flask → PostgreSQL (127.0.0.1:5432) ↓ SQL query PostgreSQL → Flask (result set) ↓ HTML rendering Flask → Nginx (HTTP response) ↓ SSL encryption Nginx → User Browser (57.128.200.27:443) ``` **Total Hops:** 6 (external) + 3 (internal) = 9 hops **Typical Latency:** 150-250ms total #### Example 2: Application Calls External API (Gemini AI) ``` Flask Application (57.128.200.27) ↓ HTTPS :443 Default Gateway (10.22.68.1) ↓ NAT + Firewall Fortigate WAN → Internet ↓ Route to Google Google Gemini API (generativelanguage.googleapis.com) ↓ API response Internet → Fortigate WAN ↓ NAT reverse Fortigate LAN → Flask Application (57.128.200.27) ``` **Total Hops:** 3 (internal) + variable (internet) + 2 (return) ≈ 15-25 hops **Typical Latency:** 200-500ms (depends on API) #### Example 3: Git Pull for Deployment ``` Flask Application (57.128.200.27) ↓ git pull over HTTPS :3000 Local routing (same subnet) ↓ Direct connection Gitea Server (10.22.68.180:3000) ↓ Repository data Gitea → Flask Application ``` **Total Hops:** 1 (direct local subnet) **Typical Latency:** <5ms --- ## Network Security Boundaries ### Trust Boundaries ``` ┌─────────────────────────────────────────────────────────────┐ │ PUBLIC INTERNET (Untrusted) │ │ • Unknown source IPs │ │ • Potential attack vectors │ │ • DDoS, injection, reconnaissance │ └────────────────────────┬────────────────────────────────────┘ ▼ ╔════════════════════════╗ ║ FORTIGATE FIREWALL ║ ◄── Trust Boundary #1 ║ • Stateful inspection ║ ║ • NAT translation ║ ║ • Rate limiting ║ ╚════════════════════════╝ ▼ ┌─────────────────────────────────────────────────────────────┐ │ DMZ ZONE (Semi-Trusted) │ │ • NPM reverse proxy │ │ • SSL termination │ │ • Limited attack surface │ └────────────────────────┬────────────────────────────────────┘ ▼ ╔════════════════════════╗ ║ NPM PROXY FILTERING ║ ◄── Trust Boundary #2 ║ • Domain validation ║ ║ • Header inspection ║ ║ • Access logging ║ ╚════════════════════════╝ ▼ ┌─────────────────────────────────────────────────────────────┐ │ APPLICATION ZONE (Trusted) │ │ • Flask/Gunicorn application │ │ • Business logic processing │ │ • Authentication & authorization │ └────────────────────────┬────────────────────────────────────┘ ▼ ╔════════════════════════╗ ║ APP-LEVEL SECURITY ║ ◄── Trust Boundary #3 ║ • @login_required ║ ║ • CSRF protection ║ ║ • Input sanitization ║ ╚════════════════════════╝ ▼ ┌─────────────────────────────────────────────────────────────┐ │ DATA ZONE (Critical) │ │ • PostgreSQL database │ │ • Localhost-only access │ │ • Encrypted at rest (planned) │ └─────────────────────────────────────────────────────────────┘ ``` ### Firewall Rules Summary | Source Zone | Destination Zone | Allowed Protocols | Purpose | |-------------|------------------|-------------------|---------| | Internet | DMZ | HTTPS (443), HTTP (80) | Public website access | | DMZ | Application | HTTP (5000) | Reverse proxy to app | | Application | Data (localhost) | PostgreSQL (5432) | Database queries | | Application | Internet | HTTPS (443) | External API calls | | Application | Internal Services | HTTPS (3000), SSH (22) | Git operations | | Admin Network | All Zones | SSH (22) | Remote administration | ### Defense in Depth Layers 1. **Perimeter Security** (Fortigate) - Firewall rules - NAT - DDoS protection 2. **DMZ Security** (NPM) - SSL/TLS termination - Reverse proxy filtering - Access logging 3. **Application Security** (Flask) - Authentication (Flask-Login) - Authorization (@login_required decorators) - CSRF protection (Flask-WTF) - Input validation - Rate limiting (Flask-Limiter) 4. **Data Security** (PostgreSQL) - Localhost-only access - Password authentication - SQL injection prevention (SQLAlchemy ORM) - Connection pooling limits --- ## Network Monitoring ### Health Checks **External Health Check:** ```bash # From internet curl -I https://nordabiznes.pl/health # Expected: HTTP/2 200 OK # Check SSL certificate openssl s_client -connect nordabiznes.pl:443 -servername nordabiznes.pl # Expected: Let's Encrypt certificate, valid dates ``` **Internal Health Checks:** ```bash # From OVH VPS inpi-vps-waw01, check Gunicorn curl -I http://127.0.0.1:5000/health # Expected: HTTP/1.1 200 OK # Check PostgreSQL sudo -u postgres psql -c "SELECT version();" # Expected: PostgreSQL version information # Check network connectivity ping -c 3 10.22.68.1 # Gateway ping -c 3 8.8.8.8 # Internet ``` ### Network Performance Metrics **Typical Latency:** - Internal subnet (10.22.68.x → 10.22.68.y): <2ms - Localhost services (127.0.0.1): <1ms - Internet egress (to Google APIs): 20-50ms - End-to-end user request: 150-250ms **Bandwidth:** - Internal network: 1 Gbps - Internet connection: (Infrastructure-dependent) - NPM → App throughput: ~500 Mbps typical **Connection Limits:** - Gunicorn workers: 4 (handles ~400 concurrent connections) - PostgreSQL max_connections: 100 - Nginx (NPM) connections: Unlimited (memory-limited) ### Monitoring Tools **Network Monitoring:** ```bash # Monitor network traffic sudo tcpdump -i ens18 port 5000 # Watch Gunicorn traffic sudo tcpdump -i ens18 port 443 # Watch HTTPS traffic # Monitor active connections sudo netstat -tulpn # All listening services sudo ss -s # Socket statistics # Monitor bandwidth sudo iftop -i ens18 # Real-time bandwidth usage ``` **Log Locations:** - NPM logs: Docker container logs (`docker logs nginx-proxy-manager`) - Flask logs: `/var/log/nordabiznes/app.log` - Nginx (system) logs: `/var/log/nginx/access.log`, `/var/log/nginx/error.log` - PostgreSQL logs: `/var/log/postgresql/postgresql-14-main.log` - System logs: `journalctl -u nordabiznes -f` --- ## Network Troubleshooting ### Common Network Issues #### Issue 1: Cannot Access Website (ERR_CONNECTION_REFUSED) **Symptoms:** - Browser shows "ERR_CONNECTION_REFUSED" - `curl https://nordabiznes.pl` fails **Diagnosis:** ```bash # Check if Fortigate NAT is working ping 85.237.177.83 # If fails: Network/ISP issue or Fortigate down # Check if NPM is responding curl http://10.22.68.250:443 # If fails: NPM service down # Check if Gunicorn is responding curl http://57.128.200.27:5000/health # If fails: Gunicorn service down ``` **Resolution:** 1. Restart NPM: `docker restart nginx-proxy-manager` (on R11-REVPROXY-01) 2. Restart Gunicorn: `sudo systemctl restart nordabiznes` (on OVH VPS inpi-vps-waw01) 3. Check Fortigate firewall rules (requires admin access) --- #### Issue 2: SSL Certificate Errors **Symptoms:** - Browser shows "Your connection is not private" - Certificate expired or invalid **Diagnosis:** ```bash # Check certificate expiry openssl s_client -connect nordabiznes.pl:443 -servername nordabiznes.pl | openssl x509 -noout -dates # Look for notBefore and notAfter dates ``` **Resolution:** 1. Login to NPM admin UI (http://10.22.68.250:81) 2. Navigate to SSL Certificates → nordabiznes.pl 3. Click "Renew" (Let's Encrypt auto-renewal) 4. Verify renewal success **Prevention:** - NPM has auto-renewal configured - Monitor certificate expiry (30 days before) --- #### Issue 3: Slow Response Times **Symptoms:** - Pages load slowly (>5 seconds) - Timeouts on some requests **Diagnosis:** ```bash # Check network latency ping -c 10 57.128.200.27 # Expected: <2ms average # Check database performance sudo -u postgres psql nordabiz -c "SELECT COUNT(*) FROM pg_stat_activity;" # Look for excessive connections # Check Gunicorn workers ps aux | grep gunicorn # Should show 4 worker processes ``` **Resolution:** 1. Restart Gunicorn to clear memory: `sudo systemctl restart nordabiznes` 2. Check for slow queries: `SELECT * FROM pg_stat_activity WHERE state = 'active';` 3. Review application logs: `sudo journalctl -u nordabiznes -n 100` --- #### Issue 4: Cannot Connect to PostgreSQL **Symptoms:** - Application shows "Database connection failed" - SQLAlchemy errors in logs **Diagnosis:** ```bash # Check if PostgreSQL is running sudo systemctl status postgresql # Check if listening on localhost sudo netstat -tulpn | grep 5432 # Expected: 127.0.0.1:5432 (NOT 0.0.0.0:5432) # Test connection psql -h 127.0.0.1 -U nordabiz_app -d nordabiz # Should prompt for password ``` **Resolution:** 1. Restart PostgreSQL: `sudo systemctl restart postgresql` 2. Check pg_hba.conf: `sudo cat /etc/postgresql/14/main/pg_hba.conf` - Should have: `host nordabiz nordabiz_app 127.0.0.1/32 md5` 3. Check postgresql.conf: `sudo cat /etc/postgresql/14/main/postgresql.conf` - Should have: `listen_addresses = 'localhost'` --- #### Issue 5: ERR_TOO_MANY_REDIRECTS (The Critical Incident) **Symptoms:** - Browser shows "ERR_TOO_MANY_REDIRECTS" - Infinite redirect loop **Root Cause:** NPM forwarding to port 80 instead of port 5000 **Diagnosis:** ```bash # Check NPM configuration (from R11-REVPROXY-01) docker exec -it nginx-proxy-manager cat /data/nginx/proxy_host/27.conf # Look for: proxy_pass http://57.128.200.27:XXXX # XXXX should be 5000, NOT 80 ``` **Resolution:** 1. Login to NPM admin UI (http://10.22.68.250:81) 2. Navigate to Proxy Hosts → nordabiznes.pl (Host ID 27) 3. Edit → Details tab 4. Change "Forward Port" from 80 to 5000 5. Save 6. Test: `curl -I https://nordabiznes.pl/health` **Prevention:** - Document critical port configuration - Regular configuration backups - Monitoring for redirect errors **Reference:** [INCIDENT_REPORT_20260102.md](../../docs/INCIDENT_REPORT_20260102.md) --- ## Network Diagrams - Additional Views ### Physical Network Diagram (Layer 2/3) ```mermaid graph LR subgraph "Layer 2/3 Network Topology" Internet["Internet
(Layer 3)"] FW_WAN["Fortigate WAN
85.237.177.83
(Layer 3)"] FW_LAN["Fortigate LAN
10.22.68.1
(Layer 3)"] Switch["Switch
(Layer 2)
10.22.68.0/24"] NPM_NIC["NPM NIC
10.22.68.250
MAC: xx:xx:xx:xx:xx:01"] APP_NIC["OVH VPS inpi-vps-waw01 NIC
57.128.200.27
MAC: xx:xx:xx:xx:xx:02"] GIT_NIC["Git Server NIC
10.22.68.180
MAC: xx:xx:xx:xx:xx:03"] end Internet <--> FW_WAN FW_WAN <--> FW_LAN FW_LAN <--> Switch Switch <--> NPM_NIC Switch <--> APP_NIC Switch <--> GIT_NIC ``` ### Data Flow - Successful HTTPS Request ```mermaid sequenceDiagram participant User as User Browser participant DNS as OVH DNS participant FW as Fortigate participant NPM as NPM (10.22.68.250) participant App as Flask (57.128.200.27) participant DB as PostgreSQL (127.0.0.1) User->>DNS: DNS query: nordabiznes.pl DNS-->>User: A record: 85.237.177.83 User->>FW: HTTPS :443 (85.237.177.83) Note over FW: NAT: 85.237.177.83:443
→ 10.22.68.250:443 FW->>NPM: HTTPS :443 (10.22.68.250) Note over NPM: SSL termination
Decrypt HTTPS → HTTP NPM->>App: HTTP :5000 (57.128.200.27) Note over App: Flask processes request App->>DB: SQL query (localhost:5432) DB-->>App: Result set App-->>NPM: HTTP response Note over NPM: Encrypt HTTP → HTTPS NPM-->>FW: HTTPS response Note over FW: NAT reverse FW-->>User: HTTPS response ``` ### Data Flow - Failed Request (Port 80 Misconfiguration) ```mermaid sequenceDiagram participant User as User Browser participant FW as Fortigate participant NPM as NPM (10.22.68.250) participant Nginx as Nginx (57.128.200.27:80) User->>FW: HTTPS :443 FW->>NPM: HTTPS :443 Note over NPM: SSL termination
Decrypt HTTPS → HTTP NPM->>Nginx: ❌ HTTP :80 (WRONG!) Note over Nginx: Nginx config:
Redirect HTTP → HTTPS Nginx-->>NPM: 301 Redirect to https://nordabiznes.pl Note over NPM: Receives HTTPS request
Terminates SSL again NPM->>Nginx: HTTP :80 (loop iteration 2) Nginx-->>NPM: 301 Redirect to https://nordabiznes.pl Note over NPM,Nginx: Loop continues...
(20 iterations max) NPM-->>User: ❌ ERR_TOO_MANY_REDIRECTS ``` --- ## Maintenance and Change Management ### Network Change Procedure **Before Making Changes:** 1. Document current configuration 2. Create backup of NPM configuration 3. Test in development environment (if possible) 4. Schedule maintenance window 5. Notify stakeholders **During Changes:** 1. Make one change at a time 2. Test after each change 3. Monitor logs for errors 4. Verify health checks **After Changes:** 1. Update documentation 2. Verify all services operational 3. Update incident response procedures 4. Review monitoring alerts ### Configuration Backups **NPM Configuration:** ```bash # Backup NPM configuration (from R11-REVPROXY-01) docker exec nginx-proxy-manager tar czf /tmp/npm-backup.tar.gz /data docker cp nginx-proxy-manager:/tmp/npm-backup.tar.gz ./npm-backup-$(date +%Y%m%d).tar.gz ``` **PostgreSQL Configuration:** ```bash # Backup PostgreSQL config (from OVH VPS inpi-vps-waw01) sudo tar czf postgresql-config-backup-$(date +%Y%m%d).tar.gz /etc/postgresql/14/main/ ``` **Network Configuration:** ```bash # Backup network config (from OVH VPS inpi-vps-waw01) sudo tar czf network-config-backup-$(date +%Y%m%d).tar.gz /etc/netplan/ /etc/systemd/network/ ``` --- ## Related Documentation ### High-Level Architecture - [System Context Diagram](01-system-context.md) - External actors and system boundary - [Container Diagram](02-container-diagram.md) - Major containers and their interactions - [Deployment Architecture](03-deployment-architecture.md) - Infrastructure deployment view ### Component Architecture - [Flask Components](04-flask-components.md) - Application component architecture - [Database Schema](05-database-schema.md) - Database entity relationships - [External Integrations](06-external-integrations.md) - External API integrations ### Network and Security - **Current Document:** Network Topology Diagram - [Critical Configurations](08-critical-configurations.md) - NPM, SSL, port mappings (to be created) - [Security Architecture](09-security-architecture.md) - Security boundaries and auth flows (to be created) ### Data Flows - [Authentication Flow](flows/01-authentication-flow.md) - User login and session management - [Search Flow](flows/02-search-flow.md) - Company search implementation - [AI Chat Flow](flows/03-ai-chat-flow.md) - AI chat context and API integration - [SEO Audit Flow](flows/04-seo-audit-flow.md) - SEO audit process - [News Monitoring Flow](flows/05-news-monitoring-flow.md) - News monitoring workflow - [HTTP Request Flow](flows/06-http-request-flow.md) - Complete HTTP request path --- ## Glossary | Term | Definition | |------|------------| | **NAT** | Network Address Translation - Maps public IP to internal IPs | | **DMZ** | Demilitarized Zone - Semi-trusted network zone between internet and internal network | | **NPM** | Nginx Proxy Manager - Web-based reverse proxy management | | **Fortigate** | Enterprise firewall appliance (Fortinet) | | **INPI** | InPi Infrastructure - Internal network name | | **SSL Termination** | Decrypting HTTPS traffic at proxy before forwarding to backend | | **Reverse Proxy** | Server that forwards client requests to backend servers | | **Layer 2** | Data Link Layer - MAC addresses, switches | | **Layer 3** | Network Layer - IP addresses, routing | | **Gateway** | Router that connects local network to external networks | | **Subnet** | Subdivision of IP network (e.g., 10.22.68.0/24) | | **CIDR** | Classless Inter-Domain Routing - IP address notation (e.g., /24) | --- ## Maintenance Notes ### When to Update This Document Update this diagram when: - Network topology changes (new servers, IP changes) - Firewall rules modified - NAT configuration updated - DNS records added/changed - New network zones created - Security boundaries redefined ### What NOT to Update Here Do **not** update this document for: - Application code changes (update Flask Components diagram instead) - Database schema changes (update Database Schema diagram instead) - Service versions (update Deployment Architecture instead) - API integrations (update External Integrations instead) ### Review Frequency - **Quarterly:** Review and verify accuracy - **After Major Changes:** Update immediately after network/infrastructure changes - **Incident Response:** Update if incident reveals documentation gaps - **Annual Security Audit:** Comprehensive review of all network documentation --- **Document Status:** ✅ Complete and Accurate (2026-01-10) **Verified By:** Auto-Claude Implementation Task **Next Review Date:** 2026-04-10 (Quarterly)