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

34 KiB

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:


Table of Contents

  1. NPM Reverse Proxy Configuration
  2. Port Mappings Reference
  3. SSL/TLS Configuration
  4. Environment Variables
  5. Database Configuration
  6. Gunicorn WSGI Configuration
  7. Systemd Service Configuration
  8. Git Repository Configuration
  9. Critical File Paths
  10. Firewall and Network Rules
  11. Backup and Recovery Locations
  12. Configuration Management
  13. 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

Complete NPM Configuration

{
  "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

# 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!

# 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

# 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)

# 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

# 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):

# 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

# 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

# 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

# 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):

# 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):

# 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):

# 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:

# /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:

# 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

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

# 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

[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

# 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

# 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):

[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

# 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

# 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:

# 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:

# 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:

# 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:

# 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:
    sudo apt update
    sudo apt install -y python3 python3-venv python3-pip postgresql-14 git nginx
    
  3. Restore database:
    sudo -u postgres createdb nordabiz
    sudo -u postgres psql nordabiz < /path/to/backup.sql
    
  4. Clone application:
    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:
    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:
    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:
    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:
    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

    # 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

    # 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

    # 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

    # 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)

    # 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!)

# ⚠️ 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

# 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

# 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

# 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

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

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