Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Production moved from on-prem VM 249 (10.22.68.249) to OVH VPS (57.128.200.27, inpi-vps-waw01). Updated ALL documentation, slash commands, memory files, architecture docs, and deploy procedures. Added |local_time Jinja filter (UTC→Europe/Warsaw) and converted 155 .strftime() calls across 71 templates so timestamps display in Polish timezone regardless of server timezone. Also includes: created_by_id tracking, abort import fix, ICS calendar fix for missing end times, Pros Poland data cleanup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1087 lines
35 KiB
Markdown
1087 lines
35 KiB
Markdown
# Network Topology Diagram
|
|
|
|
**Document Version:** 1.0
|
|
**Last Updated:** 2026-04-04
|
|
**Status:** Production LIVE
|
|
**Diagram Type:** Network Topology / Infrastructure Network
|
|
|
|
---
|
|
|
|
> **UWAGA (2026-04-04):** Produkcja przeniesiona z OVH VPS inpi-vps-waw01 (VM 249, 57.128.200.27) na **OVH VPS (57.128.200.27, hostname inpi-vps-waw01)**. Diagramy Mermaid poniżej odzwierciedlają starą architekturę — ruch produkcyjny nie przechodzi już przez Fortigate/NPM. Staging (10.22.68.248) i NPM (10.22.68.250) bez zmian.
|
|
|
|
## 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<br/>Website Visitors"]
|
|
DNS_OVH["🌐 OVH DNS<br/>nordabiznes.pl<br/>→ 57.128.200.27"]
|
|
end
|
|
|
|
%% Production (OVH VPS - direct internet access)
|
|
subgraph "OVH Cloud (Warsaw) — PRODUCTION"
|
|
VPS["🖥️ OVH VPS inpi-vps-waw01<br/><br/>IP: 57.128.200.27<br/><br/>Services:<br/>• Nginx :443 (SSL termination)<br/>• Nginx :80 (redirect)<br/>• Gunicorn :5000 (localhost)<br/>• PostgreSQL :5432 (localhost)<br/>• SSH :22"]
|
|
end
|
|
|
|
%% Staging (on-prem via FortiGate + NPM)
|
|
subgraph "INPI Internal Network (10.22.68.0/24) — STAGING"
|
|
Fortigate["🛡️ FORTIGATE<br/>WAN: 85.237.177.83<br/>LAN: 10.22.68.1<br/><br/>NAT for staging:<br/>• :443 → 10.22.68.250:443"]
|
|
|
|
NPM_Server["🖥️ R11-REVPROXY-01<br/>10.22.68.250<br/>NPM (staging proxy)"]
|
|
|
|
Staging_Server["🖥️ NORDABIZ-STAGING-01<br/>10.22.68.248<br/>Staging app + DB"]
|
|
|
|
Git_Server["🖥️ r11-git-inpi<br/>10.22.68.180<br/>Gitea :3000"]
|
|
end
|
|
|
|
%% External services
|
|
subgraph "External APIs (Internet)"
|
|
API_Google["☁️ Google Cloud APIs<br/>Gemini AI, PageSpeed, Places"]
|
|
API_MS["☁️ Microsoft Graph API<br/>Email sending"]
|
|
API_Brave["☁️ Brave Search API"]
|
|
API_KRS["🏛️ KRS Open API"]
|
|
end
|
|
|
|
%% Production traffic (direct to OVH VPS)
|
|
Users -->|"DNS Query<br/>nordabiznes.pl"| DNS_OVH
|
|
DNS_OVH -->|"DNS Response<br/>57.128.200.27"| Users
|
|
Users -->|"HTTPS :443"| VPS
|
|
|
|
%% Staging traffic (via FortiGate)
|
|
Fortigate -.->|"staging NAT<br/>→ 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 | 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:**
|
|
```bash
|
|
# 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 | 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 → 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 (57.128.200.27: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 (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<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["OVH VPS inpi-vps-waw01 NIC<br/>57.128.200.27<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
|
|
|
|
```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<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 (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<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:**
|
|
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)
|