- System Context diagram (C4 Level 1) - Container diagram (C4 Level 2) - Flask component diagram (C4 Level 3) - Deployment architecture with NPM proxy - Database schema (PostgreSQL) - External integrations (Gemini AI, Brave Search, PageSpeed) - Network topology (INPI infrastructure) - Security architecture - API endpoints reference - Troubleshooting guide - Data flow diagrams (auth, search, AI chat, SEO audit, news monitoring) All diagrams use Mermaid.js and render automatically on GitHub. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
37 KiB
Network Topology Diagram
Document Version: 1.0 Last Updated: 2026-01-10 Status: Production LIVE Diagram Type: Network Topology / Infrastructure Network
Overview
This document provides a network-centric view of the Norda Biznes Hub 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 - Infrastructure and service deployment view
- Container Diagram - Application container architecture
- HTTP Request Flow - Complete request path through network layers
Network Topology Diagram
graph TB
%% External network
subgraph "Public Internet (Untrusted)"
Users["👥 External Users<br/>Website Visitors"]
DNS_OVH["🌐 OVH DNS<br/>nordabiznes.pl<br/>→ 85.237.177.83"]
end
%% Perimeter security
subgraph "Network Perimeter"
Fortigate["🛡️ FORTIGATE FIREWALL<br/><br/>WAN: 85.237.177.83<br/>LAN: 10.22.68.1<br/><br/>NAT Rules:<br/>• 85.237.177.83:443 → 10.22.68.250:443<br/>• 85.237.177.83:80 → 10.22.68.250:80<br/><br/>Firewall Policy:<br/>• Allow HTTPS (443) from ANY<br/>• Allow HTTP (80) from ANY<br/>• Allow SSH (22) from ADMIN_NET<br/>• Default: DENY ALL"]
end
%% Internal network zones
subgraph "INPI Internal Network (10.22.68.0/24)"
%% DMZ Zone
subgraph "DMZ Zone (Semi-Trusted)"
NPM_Server["🖥️ R11-REVPROXY-01<br/><br/>IP: 10.22.68.250<br/>VM ID: 119<br/>Hostname: r11-revproxy-01<br/><br/>Services:<br/>• NPM (Docker) :443, :80, :81<br/>• SSH :22<br/><br/>Gateway: 10.22.68.1<br/>DNS: 10.22.68.1"]
end
%% Application Zone
subgraph "Application Zone (Trusted)"
App_Server["🖥️ NORDABIZ-01<br/><br/>IP: 10.22.68.249<br/>VM ID: 249<br/>Hostname: nordabiz-01<br/>DNS: nordabiznes.inpi.local<br/><br/>Services:<br/>• Flask/Gunicorn :5000<br/>• PostgreSQL :5432 (localhost)<br/>• Nginx :80, :443 (system)<br/>• SSH :22<br/><br/>Gateway: 10.22.68.1<br/>DNS: 10.22.68.1"]
end
%% Internal Services Zone
subgraph "Internal Services Zone (Trusted)"
Git_Server["🖥️ r11-git-inpi<br/><br/>IP: 10.22.68.180<br/>Hostname: r11-git-inpi<br/><br/>Services:<br/>• Gitea :3000 (HTTPS)<br/>• SSH :22<br/><br/>Gateway: 10.22.68.1"]
end
%% Internal DNS
Internal_DNS["🔍 Internal DNS<br/>Zone: inpi.local<br/><br/>Records:<br/>• nordabiznes.inpi.local → 10.22.68.249<br/>• git.inpi.local → 10.22.68.180"]
end
%% External services
subgraph "External APIs (Internet)"
API_Google["☁️ Google Cloud APIs<br/><br/>• Gemini AI API<br/>• PageSpeed Insights API<br/>• Places API<br/><br/>Auth: API Keys<br/>HTTPS only"]
API_MS["☁️ Microsoft Graph API<br/><br/>• Email sending<br/><br/>Auth: OAuth 2.0<br/>HTTPS only"]
API_Brave["☁️ Brave Search API<br/><br/>• News search<br/><br/>Auth: API Key<br/>HTTPS only"]
API_KRS["🏛️ KRS Open API<br/><br/>• Company registry<br/><br/>Auth: Public<br/>HTTPS only"]
Web_Scrapers["🌐 Web Scraping<br/><br/>• ALEO.com (NIP)<br/>• rejestr.io (Connections)<br/><br/>HTTPS only"]
end
%% Network flows - User traffic
Users -->|"DNS Query<br/>nordabiznes.pl"| DNS_OVH
DNS_OVH -->|"DNS Response<br/>85.237.177.83"| Users
Users -->|"HTTPS :443<br/>HTTP :80"| Fortigate
%% Firewall to DMZ
Fortigate -->|"NAT + Route<br/>:443 → 10.22.68.250:443<br/>:80 → 10.22.68.250:80"| NPM_Server
%% DMZ to Application Zone
NPM_Server ==>|"⚠️ CRITICAL<br/>HTTP :5000<br/>(NOT port 80!)"| App_Server
NPM_Server -.->|"Internal DNS Query<br/>nordabiznes.inpi.local"| Internal_DNS
%% Application to External APIs
App_Server -->|"HTTPS<br/>API Requests"| API_Google
App_Server -->|"HTTPS<br/>OAuth 2.0"| API_MS
App_Server -->|"HTTPS<br/>API Requests"| API_Brave
App_Server -->|"HTTPS<br/>API Requests"| API_KRS
App_Server -->|"HTTPS<br/>Web Scraping"| Web_Scrapers
%% Git deployment
App_Server -.->|"git pull<br/>HTTPS :3000<br/>Deployment only"| Git_Server
%% Admin SSH access
Fortigate -.->|"SSH :22<br/>Admin only"| NPM_Server
Fortigate -.->|"SSH :22<br/>Admin only"| App_Server
Fortigate -.->|"SSH :22<br/>Admin only"| Git_Server
%% Styling
classDef public fill:#f9f,stroke:#333,stroke-width:2px
classDef perimeter fill:#f96,stroke:#333,stroke-width:3px
classDef dmz fill:#ff9,stroke:#333,stroke-width:2px
classDef app fill:#9f9,stroke:#333,stroke-width:2px
classDef internal fill:#99f,stroke:#333,stroke-width:2px
classDef external fill:#ccc,stroke:#333,stroke-width:1px
class Users,DNS_OVH public
class Fortigate perimeter
class NPM_Server dmz
class App_Server app
class Git_Server,Internal_DNS internal
class API_Google,API_MS,API_Brave,API_KRS,Web_Scrapers 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 (Firewall)
Purpose: Network security boundary, NAT, and traffic filtering
Components:
- Fortigate Firewall
- Model: (Infrastructure-specific)
- WAN IP: 85.237.177.83
- LAN IP: 10.22.68.1 (gateway for internal network)
Security Level: Perimeter - First line of defense
NAT Configuration:
| Public Address | Public Port | Internal Address | Internal Port | Protocol | Purpose |
|---|---|---|---|---|---|
| 85.237.177.83 | 443 | 10.22.68.250 | 443 | TCP | HTTPS to NPM |
| 85.237.177.83 | 80 | 10.22.68.250 | 80 | TCP | HTTP to NPM (redirect) |
Firewall Rules:
| Priority | Source | Destination | Port | Action | Purpose |
|---|---|---|---|---|---|
| 1 | ANY | 85.237.177.83 | 443 | ALLOW | Public HTTPS access |
| 2 | ANY | 85.237.177.83 | 80 | ALLOW | HTTP (redirect to HTTPS) |
| 3 | ADMIN_NET | 10.22.68.0/24 | 22 | ALLOW | SSH administration |
| 4 | 10.22.68.0/24 | ANY | 443 | ALLOW | Outbound HTTPS (APIs) |
| 5 | 10.22.68.0/24 | ANY | 80 | ALLOW | Outbound HTTP (fallback) |
| 99 | ANY | ANY | ANY | DENY | Default deny all |
Notes:
- Stateful firewall maintains connection state
- Return traffic automatically allowed for established connections
- No inbound SSH from public internet (admin access via internal network only)
Zone 3: DMZ Zone (Semi-Trusted)
Purpose: SSL termination, reverse proxy, and public-facing services
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
Security Level: Semi-Trusted - Exposed to internet traffic, hardened configuration
Inbound Traffic:
- From Internet (via Fortigate NAT): HTTPS :443, HTTP :80
- From ADMIN_NET: SSH :22
Outbound Traffic:
- To Application Zone (10.22.68.249): 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:
# NPM Proxy Host Configuration (Host ID: 27)
domain_names:
- nordabiznes.pl
forward_host: 10.22.68.249
forward_port: 5000 # ⚠️ MUST be 5000, NOT 80!
ssl:
certificate_id: 27
force_ssl: true
http2_support: true
hsts_enabled: true
Common Misconfiguration:
# ❌ WRONG - Causes infinite redirect loop
forward_port: 80 # This forwards to nginx on NORDABIZ-01, which redirects to HTTPS
Correct Configuration:
# ✅ CORRECT - Forwards directly to Flask/Gunicorn
forward_port: 5000 # Direct connection to application server
Verification:
# Test from external network
curl -I https://nordabiznes.pl/health
# Expected: HTTP/2 200 (success)
# Test internal routing (from NPM server)
curl -I http://10.22.68.249:5000/health
# Expected: HTTP/1.1 200 (success)
Incident Reference: See INCIDENT_REPORT_20260102.md for port 80 vs 5000 misconfiguration incident.
Zone 4: Application Zone (Trusted)
Purpose: Application hosting, business logic processing
Network: 10.22.68.249/32 (single host)
Components:
- NORDABIZ-01 (VM 249)
- IP: 10.22.68.249
- Internal DNS: nordabiznes.inpi.local
- Services: Flask/Gunicorn :5000, PostgreSQL :5432 (localhost), Nginx :80/443 (unused), SSH :22
Security Level: Trusted - Internal zone, application processing
Inbound Traffic:
- From DMZ (10.22.68.250): HTTP :5000 (NPM → Gunicorn)
- From ADMIN_NET: SSH :22
Outbound Traffic:
- To External APIs: HTTPS :443 (Google, Microsoft, Brave, KRS)
- To Git Server (10.22.68.180): HTTPS :3000 (deployment)
- 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: 10.22.68.249/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 (10.22.68.249): 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 (10.22.68.249)
→ 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 |
| 10.22.68.249 | 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 | NORDABIZ-01 | 10.22.68.249 | 5000 | TCP | Internal only | Web application |
| PostgreSQL | NORDABIZ-01 | 127.0.0.1 | 5432 | TCP | Localhost only | Database |
| Nginx (unused) | NORDABIZ-01 | 10.22.68.249 | 80 | TCP | Internal only | HTTP redirect (not used) |
| Nginx (unused) | NORDABIZ-01 | 10.22.68.249 | 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 | NORDABIZ-01 | 10.22.68.249 | 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 | 10.22.68.249:5000 | HTTP | HTTPS requests → Flask | ✅ CORRECT |
| NPM :80 | 10.22.68.249:5000 | HTTP | HTTP requests → Flask | ✅ CORRECT |
| NPM :443 | 10.22.68.249:80 | HTTP | HTTPS requests → Nginx | ❌ WRONG - Redirect loop! |
Why Port 5000 is Critical:
- NPM terminates SSL at port 443
- NPM forwards decrypted HTTP to backend
- Backend must accept HTTP (not redirect to HTTPS)
- Flask/Gunicorn on port 5000 accepts HTTP ✅
- 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 | 85.237.177.83 | 3600 | Main website |
| A | www.nordabiznes.pl | 85.237.177.83 | 3600 | WWW subdomain |
| MX | nordabiznes.pl | (Not configured) | - | No email hosting |
| TXT | nordabiznes.pl | (SPF, DKIM if configured) | - | Email authentication |
DNS Propagation:
# Check DNS resolution
dig nordabiznes.pl +short
# Expected: 85.237.177.83
# Check from Google DNS
dig @8.8.8.8 nordabiznes.pl +short
# Expected: 85.237.177.83
# 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 | 10.22.68.249 | 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:
# /etc/resolv.conf on NORDABIZ-01
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 (NORDABIZ-01 Example)
# 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 10.22.68.249 # 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 → 85.237.177.83
↓ HTTPS :443
Fortigate WAN (85.237.177.83:443)
↓ NAT translation
Fortigate LAN → NPM (10.22.68.250:443)
↓ SSL termination, proxy
NPM → Flask/Gunicorn (10.22.68.249:5000)
↓ HTTP request processing
Flask → PostgreSQL (127.0.0.1:5432)
↓ SQL query
PostgreSQL → Flask (result set)
↓ HTML rendering
Flask → NPM (HTTP response)
↓ SSL encryption
NPM → Fortigate LAN (HTTPS)
↓ NAT reverse
Fortigate WAN → User Browser (85.237.177.83:443)
Total Hops: 6 (external) + 3 (internal) = 9 hops Typical Latency: 150-250ms total
Example 2: Application Calls External API (Gemini AI)
Flask Application (10.22.68.249)
↓ 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 (10.22.68.249)
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 (10.22.68.249)
↓ 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
-
Perimeter Security (Fortigate)
- Firewall rules
- NAT
- DDoS protection
-
DMZ Security (NPM)
- SSL/TLS termination
- Reverse proxy filtering
- Access logging
-
Application Security (Flask)
- Authentication (Flask-Login)
- Authorization (@login_required decorators)
- CSRF protection (Flask-WTF)
- Input validation
- Rate limiting (Flask-Limiter)
-
Data Security (PostgreSQL)
- Localhost-only access
- Password authentication
- SQL injection prevention (SQLAlchemy ORM)
- Connection pooling limits
Network Monitoring
Health Checks
External Health Check:
# 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:
# From NORDABIZ-01, 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:
# 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.plfails
Diagnosis:
# 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://10.22.68.249:5000/health
# If fails: Gunicorn service down
Resolution:
- Restart NPM:
docker restart nginx-proxy-manager(on R11-REVPROXY-01) - Restart Gunicorn:
sudo systemctl restart nordabiznes(on NORDABIZ-01) - 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:
# Check certificate expiry
openssl s_client -connect nordabiznes.pl:443 -servername nordabiznes.pl | openssl x509 -noout -dates
# Look for notBefore and notAfter dates
Resolution:
- Login to NPM admin UI (http://10.22.68.250:81)
- Navigate to SSL Certificates → nordabiznes.pl
- Click "Renew" (Let's Encrypt auto-renewal)
- 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:
# Check network latency
ping -c 10 10.22.68.249
# 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:
- Restart Gunicorn to clear memory:
sudo systemctl restart nordabiznes - Check for slow queries:
SELECT * FROM pg_stat_activity WHERE state = 'active'; - 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:
# 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:
- Restart PostgreSQL:
sudo systemctl restart postgresql - 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
- Should have:
- Check postgresql.conf:
sudo cat /etc/postgresql/14/main/postgresql.conf- Should have:
listen_addresses = 'localhost'
- Should have:
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:
# 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://10.22.68.249:XXXX
# XXXX should be 5000, NOT 80
Resolution:
- Login to NPM admin UI (http://10.22.68.250:81)
- Navigate to Proxy Hosts → nordabiznes.pl (Host ID 27)
- Edit → Details tab
- Change "Forward Port" from 80 to 5000
- Save
- Test:
curl -I https://nordabiznes.pl/health
Prevention:
- Document critical port configuration
- Regular configuration backups
- Monitoring for redirect errors
Reference: INCIDENT_REPORT_20260102.md
Network Diagrams - Additional Views
Physical Network Diagram (Layer 2/3)
graph LR
subgraph "Layer 2/3 Network Topology"
Internet["Internet<br/>(Layer 3)"]
FW_WAN["Fortigate WAN<br/>85.237.177.83<br/>(Layer 3)"]
FW_LAN["Fortigate LAN<br/>10.22.68.1<br/>(Layer 3)"]
Switch["Switch<br/>(Layer 2)<br/>10.22.68.0/24"]
NPM_NIC["NPM NIC<br/>10.22.68.250<br/>MAC: xx:xx:xx:xx:xx:01"]
APP_NIC["NORDABIZ-01 NIC<br/>10.22.68.249<br/>MAC: xx:xx:xx:xx:xx:02"]
GIT_NIC["Git Server NIC<br/>10.22.68.180<br/>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
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 (10.22.68.249)
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<br/>→ 10.22.68.250:443
FW->>NPM: HTTPS :443 (10.22.68.250)
Note over NPM: SSL termination<br/>Decrypt HTTPS → HTTP
NPM->>App: HTTP :5000 (10.22.68.249)
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)
sequenceDiagram
participant User as User Browser
participant FW as Fortigate
participant NPM as NPM (10.22.68.250)
participant Nginx as Nginx (10.22.68.249:80)
User->>FW: HTTPS :443
FW->>NPM: HTTPS :443
Note over NPM: SSL termination<br/>Decrypt HTTPS → HTTP
NPM->>Nginx: ❌ HTTP :80 (WRONG!)
Note over Nginx: Nginx config:<br/>Redirect HTTP → HTTPS
Nginx-->>NPM: 301 Redirect to https://nordabiznes.pl
Note over NPM: Receives HTTPS request<br/>Terminates SSL again
NPM->>Nginx: HTTP :80 (loop iteration 2)
Nginx-->>NPM: 301 Redirect to https://nordabiznes.pl
Note over NPM,Nginx: Loop continues...<br/>(20 iterations max)
NPM-->>User: ❌ ERR_TOO_MANY_REDIRECTS
Maintenance and Change Management
Network Change Procedure
Before Making Changes:
- Document current configuration
- Create backup of NPM configuration
- Test in development environment (if possible)
- Schedule maintenance window
- Notify stakeholders
During Changes:
- Make one change at a time
- Test after each change
- Monitor logs for errors
- Verify health checks
After Changes:
- Update documentation
- Verify all services operational
- Update incident response procedures
- Review monitoring alerts
Configuration Backups
NPM Configuration:
# 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:
# Backup PostgreSQL config (from NORDABIZ-01)
sudo tar czf postgresql-config-backup-$(date +%Y%m%d).tar.gz /etc/postgresql/14/main/
Network Configuration:
# Backup network config (from NORDABIZ-01)
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 - External actors and system boundary
- Container Diagram - Major containers and their interactions
- Deployment Architecture - Infrastructure deployment view
Component Architecture
- Flask Components - Application component architecture
- Database Schema - Database entity relationships
- External Integrations - External API integrations
Network and Security
- Current Document: Network Topology Diagram
- Critical Configurations - NPM, SSL, port mappings (to be created)
- Security Architecture - Security boundaries and auth flows (to be created)
Data Flows
- Authentication Flow - User login and session management
- Search Flow - Company search implementation
- AI Chat Flow - AI chat context and API integration
- SEO Audit Flow - SEO audit process
- News Monitoring Flow - News monitoring workflow
- HTTP Request Flow - 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)