nordabiz/docs/architecture/08-critical-configurations.md
Maciej Pienczyn cebe52f303 refactor: Rebranding i aktualizacja modelu AI
- Zmiana nazwy: "Norda Biznes Hub" → "Norda Biznes Partner"
- Aktualizacja modelu AI: Gemini 2.0 Flash → Gemini 3 Flash
- Zachowano historyczne odniesienia w timeline i dokumentacji

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 14:08:39 +01:00

1292 lines
34 KiB
Markdown

# Critical Configurations Reference
**Document Version:** 1.0
**Last Updated:** 2026-01-10
**Status:** Production LIVE
**Diagram Type:** Configuration Reference / Operations Guide
---
## Overview
This document provides a **comprehensive reference** of all critical configurations for the Norda Biznes Partner infrastructure. It serves as the **single source of truth** for:
- **NPM reverse proxy configuration** (includes critical port 5000 warning)
- **Port mappings** across all servers and services
- **SSL/TLS certificate configuration** (Let's Encrypt)
- **Environment variables** and secrets management
- **Database connection strings** and credentials
- **Gunicorn/WSGI configuration** for Flask application
- **Systemd service configuration** for application lifecycle
- **Git repository configuration** for deployment
- **Critical file paths** and directory structure
- **Firewall rules** and network ACLs
**Abstraction Level:** Infrastructure Configuration
**Audience:** System Administrators, DevOps Engineers, Production Support
**Purpose:** Configuration reference, incident response, disaster recovery, new server provisioning
**⚠️ WARNING:** This document contains references to production configurations. Actual secrets are stored in `.env` files and are **never** committed to version control.
**Related Documentation:**
- [Deployment Architecture](03-deployment-architecture.md) - Infrastructure overview
- [Network Topology](07-network-topology.md) - Network layout and zones
- [HTTP Request Flow](flows/06-http-request-flow.md) - Request path details
- [Incident Report 2026-01-02](../INCIDENT_REPORT_20260102.md) - NPM port misconfiguration incident
---
## Table of Contents
1. [NPM Reverse Proxy Configuration](#npm-reverse-proxy-configuration)
2. [Port Mappings Reference](#port-mappings-reference)
3. [SSL/TLS Configuration](#ssltls-configuration)
4. [Environment Variables](#environment-variables)
5. [Database Configuration](#database-configuration)
6. [Gunicorn WSGI Configuration](#gunicorn-wsgi-configuration)
7. [Systemd Service Configuration](#systemd-service-configuration)
8. [Git Repository Configuration](#git-repository-configuration)
9. [Critical File Paths](#critical-file-paths)
10. [Firewall and Network Rules](#firewall-and-network-rules)
11. [Backup and Recovery Locations](#backup-and-recovery-locations)
12. [Configuration Management](#configuration-management)
13. [Verification Checklist](#verification-checklist)
---
## NPM Reverse Proxy Configuration
### ⚠️ CRITICAL: Port 5000 Configuration
**Proxy Host ID:** 27
**Domains:** nordabiznes.pl, www.nordabiznes.pl
> **CRITICAL WARNING:** NPM must forward to **port 5000**, NOT port 80!
>
> Forwarding to port 80 causes an infinite redirect loop (ERR_TOO_MANY_REDIRECTS).
> This was the root cause of the 2026-01-02 production incident.
>
> See: [INCIDENT_REPORT_20260102.md](../INCIDENT_REPORT_20260102.md)
### Complete NPM Configuration
```json
{
"id": 27,
"domain_names": [
"nordabiznes.pl",
"www.nordabiznes.pl"
],
"forward_scheme": "http",
"forward_host": "10.22.68.249",
"forward_port": 5000, // ⚠️ CRITICAL: Must be 5000, NOT 80!
"certificate_id": 27,
"ssl_forced": true,
"http2_support": true,
"block_exploits": true,
"allow_websocket_upgrade": true,
"hsts_enabled": true,
"hsts_subdomains": true,
"advanced_config": "",
"access_list_id": 0,
"meta": {
"letsencrypt_agree": true,
"dns_challenge": false
}
}
```
### NPM Server Details
| Parameter | Value |
|-----------|-------|
| **Server** | R11-REVPROXY-01 |
| **VM ID** | 119 |
| **IP Address** | 10.22.68.250 |
| **NPM Version** | Latest (Docker) |
| **Container Name** | `nginx-proxy-manager_app_1` |
| **Admin Panel** | http://10.22.68.250:81 |
| **Database** | SQLite (`/data/database.sqlite`) |
### Verification Commands
```bash
# 1. Check NPM proxy host configuration
ssh maciejpi@10.22.68.250 "docker exec nginx-proxy-manager_app_1 \
sqlite3 /data/database.sqlite \
\"SELECT id, domain_names, forward_host, forward_port FROM proxy_host WHERE id = 27;\""
# Expected output: 27|["nordabiznes.pl","www.nordabiznes.pl"]|10.22.68.249|5000
# 2. Verify website is accessible
curl -I https://nordabiznes.pl/health
# Expected: HTTP/2 200 OK
# 3. Check NPM logs for errors
ssh maciejpi@10.22.68.250 "docker logs nginx-proxy-manager_app_1 --tail 50"
# 4. Verify SSL certificate
openssl s_client -connect nordabiznes.pl:443 -servername nordabiznes.pl < /dev/null 2>/dev/null | \
openssl x509 -noout -dates -subject -issuer
```
### NPM Configuration Update Procedure
**IMPORTANT:** Always verify port 5000 after making any changes to NPM!
```python
# NPM API configuration update (Python)
import requests
NPM_URL = "http://10.22.68.250:81/api"
NPM_EMAIL = "admin@example.com"
NPM_PASSWORD = "your_npm_password"
# Step 1: Authenticate
auth_response = requests.post(
f"{NPM_URL}/tokens",
json={"identity": NPM_EMAIL, "secret": NPM_PASSWORD}
)
token = auth_response.json()["token"]
headers = {"Authorization": f"Bearer {token}"}
# Step 2: Get current configuration
current = requests.get(
f"{NPM_URL}/nginx/proxy-hosts/27",
headers=headers
).json()
# Step 3: Verify port is 5000
if current["forward_port"] != 5000:
print("⚠️ WARNING: Port is not 5000! Current:", current["forward_port"])
# Update to correct port
current["forward_port"] = 5000
requests.put(
f"{NPM_URL}/nginx/proxy-hosts/27",
headers=headers,
json=current
)
print("✓ Port corrected to 5000")
else:
print("✓ Port is correctly set to 5000")
# Step 4: Verify website works
import subprocess
result = subprocess.run(["curl", "-I", "https://nordabiznes.pl/health"], capture_output=True)
if b"200 OK" in result.stdout:
print("✓ Website is accessible")
else:
print("❌ Website check failed!")
```
---
## Port Mappings Reference
### Complete Port Matrix
| Server | Service | Port | Protocol | Access | Purpose |
|--------|---------|------|----------|--------|---------|
| **Fortigate (WAN)** | Public Gateway | 443 | HTTPS | Public | SSL entry point |
| Fortigate (WAN) | HTTP Redirect | 80 | HTTP | Public | Redirect to HTTPS |
| **R11-REVPROXY-01** | NPM Proxy | 443 | HTTPS | LAN | SSL termination |
| R11-REVPROXY-01 | NPM Proxy | 80 | HTTP | LAN | HTTP → HTTPS redirect |
| R11-REVPROXY-01 | NPM Admin | 81 | HTTP | LAN | Admin panel |
| R11-REVPROXY-01 | SSH | 22 | SSH | Admin | Remote management |
| **NORDABIZ-01** | **Flask/Gunicorn** | **5000** | **HTTP** | **LAN** | **Application (CRITICAL!)** |
| NORDABIZ-01 | PostgreSQL | 5432 | TCP | Localhost | Database |
| NORDABIZ-01 | Nginx (System) | 80 | HTTP | LAN | ⚠️ DO NOT USE (causes redirect loop) |
| NORDABIZ-01 | Nginx (System) | 443 | HTTPS | LAN | ⚠️ DO NOT USE |
| NORDABIZ-01 | SSH | 22 | SSH | Admin | Remote management |
| **r11-git-inpi** | Gitea HTTPS | 3000 | HTTPS | LAN | Git repository |
| r11-git-inpi | SSH | 22 | SSH | Admin | Remote management |
### NAT/Port Forwarding Rules (Fortigate)
```
Public → Internal NAT Mappings:
85.237.177.83:443 → 10.22.68.250:443 (NPM Proxy - HTTPS)
85.237.177.83:80 → 10.22.68.250:80 (NPM Proxy - HTTP)
Internal → Internal Routing:
10.22.68.250:* → 10.22.68.249:5000 (NPM → Flask) ⚠️ CRITICAL PORT
10.22.68.249:* → 127.0.0.1:5432 (Flask → PostgreSQL)
10.22.68.249:* → 10.22.68.180:3000 (Flask → Gitea)
```
### Port Usage by Zone
**Public Internet Zone:**
- Port 443 (HTTPS) - Public website access
- Port 80 (HTTP) - Redirects to HTTPS
**DMZ Zone (R11-REVPROXY-01):**
- Port 443 (HTTPS) - NPM SSL termination
- Port 80 (HTTP) - NPM HTTP redirect
- Port 81 (HTTP) - NPM admin panel (internal only)
**Application Zone (NORDABIZ-01):**
- **Port 5000 (HTTP) - Flask/Gunicorn application** ⚠️ CRITICAL
- Port 5432 (TCP) - PostgreSQL (localhost only)
- Port 80/443 (HTTP/HTTPS) - System nginx (DO NOT USE for app)
**Internal Services Zone:**
- Port 3000 (HTTPS) - Gitea
---
## SSL/TLS Configuration
### Let's Encrypt Certificate
**Certificate Details:**
- **Provider:** Let's Encrypt
- **Managed By:** NPM (Nginx Proxy Manager)
- **Certificate ID:** 27
- **Domains:**
- nordabiznes.pl
- www.nordabiznes.pl
- **Key Type:** RSA 2048-bit
- **Validity:** 90 days
- **Renewal:** Automatic (30 days before expiry)
- **ACME Challenge:** HTTP-01
**Certificate Storage:**
- **Location (NPM):** `/data/letsencrypt/live/npm-27/`
- **Files:**
- `fullchain.pem` - Full certificate chain
- `privkey.pem` - Private key
- `cert.pem` - Certificate only
- `chain.pem` - Intermediate certificates
### TLS Configuration
```nginx
# NPM TLS Configuration (auto-generated)
ssl_certificate /data/letsencrypt/live/npm-27/fullchain.pem;
ssl_certificate_key /data/letsencrypt/live/npm-27/privkey.pem;
# SSL Protocols
ssl_protocols TLSv1.2 TLSv1.3;
# Cipher Suites (Modern configuration)
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
# SSL Session
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
```
### Security Headers (NPM)
```http
# Headers added by NPM for all requests
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
```
### SSL Verification Commands
```bash
# Check certificate expiry
echo | openssl s_client -connect nordabiznes.pl:443 -servername nordabiznes.pl 2>/dev/null | \
openssl x509 -noout -dates
# Test SSL configuration
curl -vI https://nordabiznes.pl 2>&1 | grep -E "(SSL|TLS|expire)"
# Check HSTS header
curl -I https://nordabiznes.pl | grep -i strict
# Test HTTP/2 support
curl -I --http2 https://nordabiznes.pl | head -1
# Comprehensive SSL test (requires ssllabs-scan)
ssllabs-scan --quiet nordabiznes.pl
```
### Certificate Renewal Process
**Automatic Renewal (NPM handles this):**
1. NPM checks certificates 30 days before expiry
2. Initiates ACME HTTP-01 challenge with Let's Encrypt
3. Creates `.well-known/acme-challenge/` endpoint
4. Let's Encrypt validates domain ownership
5. New certificate issued and NPM reloads nginx
6. Zero downtime renewal
**Manual Renewal (if automatic fails):**
```bash
# SSH to NPM server
ssh maciejpi@10.22.68.250
# Force certificate renewal
docker exec nginx-proxy-manager_app_1 \
certbot renew --force-renewal
# Reload nginx
docker exec nginx-proxy-manager_app_1 nginx -s reload
# Verify new certificate
curl -vI https://nordabiznes.pl 2>&1 | grep "expire date"
```
---
## Environment Variables
### Production Environment Variables
**Location:** `/var/www/nordabiznes/.env`
**Owner:** `www-data:www-data`
**Permissions:** `0600` (read/write owner only)
**⚠️ WARNING:** Never commit `.env` to version control!
### Required Variables
```bash
# Flask Configuration
SECRET_KEY=<random-64-character-hex-string>
FLASK_ENV=production
# Server Configuration
PORT=5000
HOST=0.0.0.0
# Database Configuration
DATABASE_URL=postgresql://nordabiz_app:<password>@127.0.0.1:5432/nordabiz
# Google Gemini API
GOOGLE_GEMINI_API_KEY=<gemini-api-key>
# Google PageSpeed Insights API
GOOGLE_PAGESPEED_API_KEY=<pagespeed-api-key>
# Google Places API
GOOGLE_PLACES_API_KEY=<places-api-key>
# Brave Search API
BRAVE_SEARCH_API_KEY=<brave-api-key>
# Microsoft Graph API (OAuth 2.0)
MS_GRAPH_CLIENT_ID=<client-id>
MS_GRAPH_CLIENT_SECRET=<client-secret>
MS_GRAPH_TENANT_ID=<tenant-id>
# Email Configuration (Microsoft Graph)
MAIL_DEFAULT_SENDER=noreply@nordabiznes.pl
# Application URLs
APP_URL=https://nordabiznes.pl
VERIFY_EMAIL_URL=https://nordabiznes.pl/verify-email
```
### Environment Variable Generation
```bash
# Generate SECRET_KEY (64 random hex characters)
python3 -c "import secrets; print(secrets.token_hex(32))"
# Generate random password (32 characters)
openssl rand -base64 32
# Verify .env file permissions
ls -la /var/www/nordabiznes/.env
# Expected: -rw------- 1 www-data www-data ... .env
```
### Development Environment Variables
**Location:** `.env` (in project root, git-ignored)
**Template:** `.env.example`
```bash
# Development configuration (localhost)
DATABASE_URL=postgresql://nordabiz_app:NordaBiz2025Secure@127.0.0.1:5433/nordabiz
APP_URL=http://localhost:5000
FLASK_ENV=development
```
**Note:** Development uses Docker PostgreSQL on port 5433 (not 5432)
---
## Database Configuration
### Production Database
**Server:** NORDABIZ-01 (10.22.68.249)
**DBMS:** PostgreSQL 14
**Database Name:** `nordabiz`
**Port:** 5432 (localhost only)
### Database Users and Roles
| User | Role | Permissions | Purpose |
|------|------|-------------|---------|
| `postgres` | Superuser | ALL | Database administration |
| `nordabiz_app` | Application user | CRUD on all tables | Flask application |
| `nordabiz_readonly` | Read-only | SELECT only | Reporting, backups |
### Connection Strings
**Production (Flask app):**
```
postgresql://nordabiz_app:<password>@127.0.0.1:5432/nordabiz
```
**Development (Docker):**
```
postgresql://nordabiz_app:NordaBiz2025Secure@127.0.0.1:5433/nordabiz
```
**Direct psql (production):**
```bash
# As application user
sudo -u www-data psql -U nordabiz_app -d nordabiz -h 127.0.0.1
# As postgres superuser
sudo -u postgres psql nordabiz
```
### PostgreSQL Configuration Files
**Location:** `/etc/postgresql/14/main/`
**postgresql.conf (Key Settings):**
```conf
# Network Settings
listen_addresses = 'localhost' # ⚠️ CRITICAL: localhost only!
port = 5432
# Memory Settings
shared_buffers = 256MB
effective_cache_size = 1GB
work_mem = 16MB
# Connection Settings
max_connections = 100
# Logging
log_destination = 'stderr'
logging_collector = on
log_directory = '/var/log/postgresql'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_statement = 'mod' # Log all modifications
log_min_duration_statement = 1000 # Log slow queries (>1s)
```
**pg_hba.conf (Access Control):**
```conf
# TYPE DATABASE USER ADDRESS METHOD
# Local connections
local all postgres peer
local nordabiz nordabiz_app md5
# IPv4 local connections
host nordabiz nordabiz_app 127.0.0.1/32 md5
# ⚠️ CRITICAL: No external connections allowed!
# External access is blocked by listening on localhost only
```
### Database Backup Configuration
**Backup Location:** `/var/backups/nordabiz/`
**Owner:** `postgres:postgres`
**Retention:** 7 days
**Daily Backup Cron:**
```bash
# /etc/cron.d/nordabiz-backup
0 2 * * * postgres pg_dump -U nordabiz_app nordabiz | gzip > /var/backups/nordabiz/nordabiz_$(date +\%Y\%m\%d).sql.gz
```
**Manual Backup:**
```bash
# Create backup
sudo -u postgres pg_dump -U nordabiz_app nordabiz > /tmp/nordabiz_backup_$(date +%Y%m%d_%H%M%S).sql
# Create compressed backup
sudo -u postgres pg_dump -U nordabiz_app nordabiz | gzip > /tmp/nordabiz_backup.sql.gz
# Restore from backup
sudo -u postgres psql -U nordabiz_app nordabiz < /tmp/nordabiz_backup.sql
```
---
## Gunicorn WSGI Configuration
### Gunicorn Settings
**Location:** Configured in systemd service file
**Socket:** `0.0.0.0:5000`
**Workers:** 4 (recommended: 2-4 x CPU cores)
**Worker Class:** `sync` (default)
**Timeout:** 120 seconds
**Max Requests:** 1000 (worker restart after 1000 requests)
**Access Log:** `/var/log/nordabiznes/gunicorn_access.log`
**Error Log:** `/var/log/nordabiznes/gunicorn_error.log`
### Gunicorn Command
```bash
gunicorn \
--bind 0.0.0.0:5000 \
--workers 4 \
--timeout 120 \
--max-requests 1000 \
--access-logfile /var/log/nordabiznes/gunicorn_access.log \
--error-logfile /var/log/nordabiznes/gunicorn_error.log \
--log-level info \
app:app
```
### Worker Calculation
```python
# Recommended workers formula
workers = (2 * num_cpu_cores) + 1
# For NORDABIZ-01 (4 vCPUs)
workers = (2 * 4) + 1 = 9 # Maximum
workers = 4 # Current (conservative)
```
### Gunicorn Tuning Considerations
| Parameter | Current | Tuning Notes |
|-----------|---------|--------------|
| Workers | 4 | Increase to 6-8 under high load |
| Timeout | 120s | Sufficient for AI chat (Gemini API ~2-5s) |
| Max Requests | 1000 | Prevents memory leaks |
| Worker Class | sync | Consider `gevent` for WebSocket support |
| Keep-Alive | 5s | Default, no tuning needed |
---
## Systemd Service Configuration
### Service File Location
**File:** `/etc/systemd/system/nordabiznes.service`
**Owner:** `root:root`
**Permissions:** `0644`
### Complete Service Configuration
```ini
[Unit]
Description=Norda Biznes Partner - Flask Application
After=network.target postgresql.service
Requires=postgresql.service
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/nordabiznes
Environment="PATH=/var/www/nordabiznes/venv/bin"
EnvironmentFile=/var/www/nordabiznes/.env
ExecStart=/var/www/nordabiznes/venv/bin/gunicorn \
--bind 0.0.0.0:5000 \
--workers 4 \
--timeout 120 \
--max-requests 1000 \
--access-logfile /var/log/nordabiznes/gunicorn_access.log \
--error-logfile /var/log/nordabiznes/gunicorn_error.log \
--log-level info \
app:app
# Restart policy
Restart=always
RestartSec=10
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=nordabiznes
# Security
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
```
### Service Management Commands
```bash
# Start service
sudo systemctl start nordabiznes
# Stop service
sudo systemctl stop nordabiznes
# Restart service (after code changes)
sudo systemctl restart nordabiznes
# Reload service (graceful restart)
sudo systemctl reload nordabiznes
# Check status
sudo systemctl status nordabiznes
# Enable autostart on boot
sudo systemctl enable nordabiznes
# Disable autostart
sudo systemctl disable nordabiznes
# View logs
sudo journalctl -u nordabiznes -f
# View logs (last 100 lines)
sudo journalctl -u nordabiznes -n 100
# View logs (since 1 hour ago)
sudo journalctl -u nordabiznes --since "1 hour ago"
```
### Service Restart After Changes
```bash
# After modifying .env file
sudo systemctl restart nordabiznes
# After modifying Python code
sudo systemctl restart nordabiznes
# After modifying systemd service file
sudo systemctl daemon-reload
sudo systemctl restart nordabiznes
# After database schema changes
# (Usually no restart needed, but recommended)
sudo systemctl restart nordabiznes
```
---
## Git Repository Configuration
### Git Remotes
| Remote | URL | Purpose |
|--------|-----|---------|
| **inpi** (primary) | `https://10.22.68.180:3000/maciejpi/nordabiz.git` | Internal Gitea (deployment source) |
| **origin** | `git@github.com:pienczyn/nordabiz.git` | GitHub (cloud backup) |
### Production Git Configuration
**Repository Location:** `/var/www/nordabiznes/`
**User:** `www-data`
**Branch:** `master`
**Git Configuration (`/var/www/nordabiznes/.git/config`):**
```ini
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "inpi"]
url = https://10.22.68.180:3000/maciejpi/nordabiz.git
fetch = +refs/heads/*:refs/remotes/inpi/*
[remote "origin"]
url = git@github.com:pienczyn/nordabiz.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = inpi
merge = refs/heads/master
[http]
sslVerify = false # Required for self-signed Gitea cert
```
### Gitea Server Configuration
**Server:** r11-git-inpi
**IP:** 10.22.68.180
**Port:** 3000 (HTTPS)
**URL:** https://10.22.68.180:3000/
**User:** `maciejpi`
**Repository:** `maciejpi/nordabiz`
### Deployment Workflow
```bash
# On development machine (Mac)
git push inpi master # Push to internal Gitea
git push origin master # Backup to GitHub (optional)
# On production server (NORDABIZ-01)
ssh maciejpi@10.22.68.249
cd /var/www/nordabiznes
sudo -u www-data git pull # Pull from Gitea
sudo systemctl restart nordabiznes # Restart application
curl -I https://nordabiznes.pl/health # Verify deployment
```
### Git Commands for Production
```bash
# Check current branch and status
sudo -u www-data git branch
sudo -u www-data git status
# Pull latest changes
sudo -u www-data git pull
# View commit history
sudo -u www-data git log --oneline -10
# Rollback to previous commit (emergency)
sudo -u www-data git reset --hard HEAD~1
# Force pull (discard local changes)
sudo -u www-data git fetch --all
sudo -u www-data git reset --hard inpi/master
```
### SSH Keys for Git Access
**Production Server:**
- Location: `/home/www-data/.ssh/`
- Public key: `id_rsa.pub`
- Private key: `id_rsa`
- Known hosts: `known_hosts` (includes Gitea fingerprint)
**Development Machine:**
- GitHub SSH key: `~/.ssh/id_rsa` (GitHub account: `pienczyn`)
- Gitea HTTP access: Username/password (no SSH key required due to HTTPS)
---
## Critical File Paths
### Application Files
| Path | Description | Owner | Permissions |
|------|-------------|-------|-------------|
| `/var/www/nordabiznes/` | Application root directory | www-data:www-data | 0755 |
| `/var/www/nordabiznes/app.py` | Main Flask application | www-data:www-data | 0644 |
| `/var/www/nordabiznes/.env` | Environment variables (secrets) | www-data:www-data | 0600 |
| `/var/www/nordabiznes/venv/` | Python virtual environment | www-data:www-data | 0755 |
| `/var/www/nordabiznes/static/` | Static assets (CSS, JS, images) | www-data:www-data | 0755 |
| `/var/www/nordabiznes/templates/` | Jinja2 templates | www-data:www-data | 0755 |
| `/var/www/nordabiznes/database.py` | SQLAlchemy models | www-data:www-data | 0644 |
| `/var/www/nordabiznes/scripts/` | Background scripts | www-data:www-data | 0755 |
### Configuration Files
| Path | Description | Owner | Permissions |
|------|-------------|-------|-------------|
| `/etc/systemd/system/nordabiznes.service` | Systemd service file | root:root | 0644 |
| `/etc/postgresql/14/main/postgresql.conf` | PostgreSQL configuration | postgres:postgres | 0644 |
| `/etc/postgresql/14/main/pg_hba.conf` | PostgreSQL access control | postgres:postgres | 0640 |
### Log Files
| Path | Description | Rotation | Owner |
|------|-------------|----------|-------|
| `/var/log/nordabiznes/gunicorn_access.log` | Gunicorn access log | Daily | www-data |
| `/var/log/nordabiznes/gunicorn_error.log` | Gunicorn error log | Daily | www-data |
| `/var/log/postgresql/postgresql-*.log` | PostgreSQL logs | Weekly | postgres |
| `journalctl -u nordabiznes` | Systemd service logs | 30 days | root |
### Backup Locations
| Path | Description | Retention | Owner |
|------|-------------|-----------|-------|
| `/var/backups/nordabiz/` | Daily database backups | 7 days | postgres |
| `/home/maciejpi/backups/` | Manual backups | Manual | maciejpi |
### Temporary Files
| Path | Description | Cleanup | Owner |
|------|-------------|---------|-------|
| `/tmp/` | Temporary files (uploads, etc.) | Reboot | Various |
| `/var/tmp/` | Persistent temp files | 30 days | Various |
---
## Firewall and Network Rules
### Fortigate Firewall Configuration
**WAN Interface:** `wan1` (85.237.177.83)
**LAN Interface:** `internal` (10.22.68.1)
**NAT Rules (Destination NAT / Port Forwarding):**
```
Rule 1: HTTPS
External: 85.237.177.83:443 → Internal: 10.22.68.250:443
Protocol: TCP
Action: DNAT
Rule 2: HTTP
External: 85.237.177.83:80 → Internal: 10.22.68.250:80
Protocol: TCP
Action: DNAT
```
**Firewall Policies:**
```
Policy 1: Allow HTTPS from Internet
Source: all
Destination: 10.22.68.250
Service: HTTPS (443)
Action: ACCEPT
Policy 2: Allow HTTP from Internet
Source: all
Destination: 10.22.68.250
Service: HTTP (80)
Action: ACCEPT
Policy 3: Allow SSH from Admin Network
Source: admin_network (10.22.68.0/24)
Destination: all_internal_servers
Service: SSH (22)
Action: ACCEPT
Policy 4: Allow Internal Traffic
Source: 10.22.68.0/24
Destination: 10.22.68.0/24
Service: any
Action: ACCEPT
Policy 5: Default Deny
Source: all
Destination: all
Service: any
Action: DENY
```
### Linux iptables (NORDABIZ-01)
**Status:** Not actively used (relies on Fortigate firewall)
**Default Policy:**
```bash
# Check iptables status
sudo iptables -L -n -v
# Expected: Mostly ACCEPT policies (Fortigate handles filtering)
```
### PostgreSQL Access Control
**Network Access:** `listen_addresses = 'localhost'` (127.0.0.1 only)
**pg_hba.conf Rules:**
```
# Only allow localhost connections
host nordabiz nordabiz_app 127.0.0.1/32 md5
# Block all other addresses (implicit)
```
---
## Backup and Recovery Locations
### Database Backups
**Automated Daily Backups:**
- **Location:** `/var/backups/nordabiz/`
- **Filename Pattern:** `nordabiz_YYYYMMDD.sql.gz`
- **Schedule:** 2:00 AM daily (cron)
- **Retention:** 7 days (automatic cleanup)
- **Size:** ~5-10 MB compressed
**Manual Backups:**
```bash
# Create manual backup
sudo -u postgres pg_dump -U nordabiz_app nordabiz > \
/home/maciejpi/backups/nordabiz_manual_$(date +%Y%m%d_%H%M%S).sql
# Restore from backup
sudo -u postgres psql -U nordabiz_app nordabiz < \
/home/maciejpi/backups/nordabiz_manual_20260110_120000.sql
```
### Application Backups
**Git Repository:**
- **Primary:** Gitea (10.22.68.180) - internal backup
- **Secondary:** GitHub (github.com/pienczyn/nordabiz) - cloud backup
**VM Snapshots (Proxmox):**
- **Location:** Proxmox Backup Server
- **Schedule:** Weekly
- **Retention:** 4 weeks
- **VM ID:** 249 (NORDABIZ-01)
### Configuration Backups
**NPM Configuration:**
```bash
# Backup NPM database
ssh maciejpi@10.22.68.250 \
"docker cp nginx-proxy-manager_app_1:/data/database.sqlite /tmp/npm_backup.sqlite"
# Copy to local
scp maciejpi@10.22.68.250:/tmp/npm_backup.sqlite ~/backups/
```
**Environment Variables:**
```bash
# Backup .env file (contains secrets!)
sudo cp /var/www/nordabiznes/.env /home/maciejpi/backups/.env.backup
sudo chmod 600 /home/maciejpi/backups/.env.backup
```
### Disaster Recovery Procedure
**Complete Server Rebuild:**
1. **Restore VM from Proxmox snapshot** (or provision new VM)
2. **Install base packages:**
```bash
sudo apt update
sudo apt install -y python3 python3-venv python3-pip postgresql-14 git nginx
```
3. **Restore database:**
```bash
sudo -u postgres createdb nordabiz
sudo -u postgres psql nordabiz < /path/to/backup.sql
```
4. **Clone application:**
```bash
sudo mkdir -p /var/www/nordabiznes
sudo chown www-data:www-data /var/www/nordabiznes
sudo -u www-data git clone https://10.22.68.180:3000/maciejpi/nordabiz.git /var/www/nordabiznes
```
5. **Restore .env file:**
```bash
sudo cp /path/to/.env.backup /var/www/nordabiznes/.env
sudo chown www-data:www-data /var/www/nordabiznes/.env
sudo chmod 600 /var/www/nordabiznes/.env
```
6. **Install Python dependencies:**
```bash
cd /var/www/nordabiznes
sudo -u www-data python3 -m venv venv
sudo -u www-data venv/bin/pip install -r requirements.txt
```
7. **Configure systemd service:**
```bash
sudo cp nordabiznes.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable nordabiznes
sudo systemctl start nordabiznes
```
8. **Reconfigure NPM:**
- Update proxy host 27 to point to new server IP
- Verify `forward_port = 5000`
9. **Verify deployment:**
```bash
curl -I https://nordabiznes.pl/health
# Expected: HTTP/2 200 OK
```
---
## Configuration Management
### Configuration Change Procedure
**⚠️ ALWAYS follow this procedure for production changes:**
1. **Backup current configuration**
```bash
# Database
sudo -u postgres pg_dump nordabiz > /tmp/backup_before_change.sql
# Application
cd /var/www/nordabiznes
sudo -u www-data git status # Ensure clean state
```
2. **Test changes in development**
```bash
# On local machine
# Test with Docker PostgreSQL on port 5433
python3 app.py
# Verify functionality
```
3. **Document the change**
- Update CLAUDE.md if architecture changes
- Update this document if configuration changes
- Add entry to CHANGELOG.md
4. **Apply change to production**
```bash
# SSH to production
ssh maciejpi@10.22.68.249
# Pull changes
cd /var/www/nordabiznes
sudo -u www-data git pull
# Restart service
sudo systemctl restart nordabiznes
```
5. **Verify change**
```bash
# Check service status
sudo systemctl status nordabiznes
# Check application health
curl -I https://nordabiznes.pl/health
# Expected: HTTP/2 200 OK
# Monitor logs
sudo journalctl -u nordabiznes -f
```
6. **Rollback procedure (if needed)**
```bash
# Stop service
sudo systemctl stop nordabiznes
# Rollback code
sudo -u www-data git reset --hard HEAD~1
# Rollback database (if needed)
sudo -u postgres psql nordabiz < /tmp/backup_before_change.sql
# Start service
sudo systemctl start nordabiznes
```
### Configuration Version Control
**Tracked in Git:**
- Application code (`app.py`, `*.py`)
- Templates (`templates/`)
- Static assets (`static/`)
- Requirements (`requirements.txt`)
- Documentation (`docs/`, `CLAUDE.md`)
**NOT tracked in Git (secrets):**
- `.env` (environment variables)
- `*.pyc` (Python bytecode)
- `venv/` (virtual environment)
- `__pycache__/` (Python cache)
- Log files
**Tracked externally (infrastructure as code):**
- NPM configuration (stored in NPM SQLite database)
- Systemd service files (manual versioning)
- PostgreSQL configuration (manual versioning)
---
## Verification Checklist
### Pre-Deployment Verification
- [ ] Code changes tested in development environment
- [ ] Database migrations tested (if applicable)
- [ ] `.env` variables updated (if needed)
- [ ] No secrets committed to Git
- [ ] CHANGELOG.md updated
- [ ] Documentation updated (if architecture changed)
### Post-Deployment Verification
- [ ] **NPM configuration verified: `forward_port = 5000`** ⚠️ CRITICAL
- [ ] Website accessible: `curl -I https://nordabiznes.pl/health` returns 200
- [ ] SSL certificate valid: No browser warnings
- [ ] Database connection working: Query returns data
- [ ] Service running: `systemctl status nordabiznes` shows active
- [ ] No errors in logs: `journalctl -u nordabiznes -n 50`
- [ ] External APIs working: Test chat, SEO audit
- [ ] Authentication working: Test login/logout
- [ ] Static assets loading: Check browser console for 404s
### NPM Configuration Verification (CRITICAL!)
```bash
# ⚠️ ALWAYS run this after ANY NPM changes!
# 1. Check proxy host configuration
ssh maciejpi@10.22.68.250 "docker exec nginx-proxy-manager_app_1 \
sqlite3 /data/database.sqlite \
\"SELECT id, domain_names, forward_host, forward_port FROM proxy_host WHERE id = 27;\""
# Expected output:
# 27|["nordabiznes.pl","www.nordabiznes.pl"]|10.22.68.249|5000
# ^^^^
# MUST BE 5000!
# 2. Test website accessibility
curl -I https://nordabiznes.pl/health
# Expected: HTTP/2 200 OK
# 3. Test from external network (use phone without WiFi)
# Browse to: https://nordabiznes.pl
# Should load without ERR_TOO_MANY_REDIRECTS
```
### Database Verification
```bash
# Check database connectivity
sudo -u www-data psql -U nordabiz_app -d nordabiz -h 127.0.0.1 -c "SELECT COUNT(*) FROM companies;"
# Check recent data
sudo -u www-data psql -U nordabiz_app -d nordabiz -h 127.0.0.1 -c \
"SELECT name, slug FROM companies ORDER BY created_at DESC LIMIT 5;"
# Check database size
sudo -u postgres psql -c "SELECT pg_size_pretty(pg_database_size('nordabiz'));"
```
### Service Health Verification
```bash
# Systemd service status
sudo systemctl status nordabiznes
# Process check
ps aux | grep gunicorn
# Port listening check
sudo netstat -tlnp | grep 5000
# Expected: gunicorn listening on 0.0.0.0:5000
# Application logs (last 50 lines)
sudo journalctl -u nordabiznes -n 50 --no-pager
# Error log check (should be minimal)
sudo journalctl -u nordabiznes -p err -n 20
```
---
## Emergency Contacts and Resources
### Server Access
| Server | IP | SSH User | Purpose |
|--------|-----|----------|---------|
| NORDABIZ-01 | 10.22.68.249 | `maciejpi` | Application server |
| R11-REVPROXY-01 | 10.22.68.250 | `maciejpi` | NPM proxy |
| r11-git-inpi | 10.22.68.180 | `maciejpi` | Gitea repository |
**⚠️ NEVER SSH as root!** Always use `maciejpi` user and `sudo` for elevated privileges.
### Key Commands for Incidents
```bash
# 1. Quick health check
curl -I https://nordabiznes.pl/health
# 2. Check service status
ssh maciejpi@10.22.68.249 "sudo systemctl status nordabiznes"
# 3. Check NPM proxy configuration
ssh maciejpi@10.22.68.250 "docker exec nginx-proxy-manager_app_1 \
sqlite3 /data/database.sqlite \
\"SELECT forward_port FROM proxy_host WHERE id = 27;\""
# 4. View recent errors
ssh maciejpi@10.22.68.249 "sudo journalctl -u nordabiznes -p err -n 20"
# 5. Restart application
ssh maciejpi@10.22.68.249 "sudo systemctl restart nordabiznes"
```
### Documentation Resources
- **This Document:** `docs/architecture/08-critical-configurations.md`
- **Incident Report:** `docs/INCIDENT_REPORT_20260102.md`
- **Deployment Architecture:** `docs/architecture/03-deployment-architecture.md`
- **Network Topology:** `docs/architecture/07-network-topology.md`
- **HTTP Request Flow:** `docs/architecture/flows/06-http-request-flow.md`
- **Main Documentation:** `CLAUDE.md`
---
## Appendix: Quick Reference Cards
### Quick Reference: NPM Proxy
```
Proxy Host ID: 27
Domains: nordabiznes.pl, www.nordabiznes.pl
Backend: 10.22.68.249:5000 ⚠️ PORT 5000 (NOT 80!)
SSL: Let's Encrypt (auto-renew)
```
### Quick Reference: Port Mappings
```
Internet → Fortigate → NPM → Flask
:443 → :443 → :5000
⚠️ CRITICAL: NPM → Flask must use port 5000
```
### Quick Reference: Database
```
Host: 127.0.0.1 (localhost only)
Port: 5432
Database: nordabiz
User: nordabiz_app
```
### Quick Reference: Service Management
```bash
sudo systemctl restart nordabiznes # Restart app
sudo journalctl -u nordabiznes -f # View logs
curl -I https://nordabiznes.pl/health # Test health
```
### Quick Reference: Emergency Rollback
```bash
ssh maciejpi@10.22.68.249
cd /var/www/nordabiznes
sudo systemctl stop nordabiznes
sudo -u www-data git reset --hard HEAD~1
sudo systemctl start nordabiznes
curl -I https://nordabiznes.pl/health
```
---
**Document Maintenance:**
- Update this document after ANY configuration changes
- Verify accuracy quarterly
- Include in onboarding materials for new team members
- Reference in incident response procedures
**Last Verified:** 2026-01-10
**Next Review:** 2026-04-10