NewsPortal Deploy - Docker, nginx, VPS

Partea 4 din 7

Actualizat 2026-05-24Sursă pe GitHub ↗Cod start

Parte 3 - Reverse proxy, DNS, TLS

Cele trei piese care fac diferența între “rulează pe localhost” și “e accesibil pe internet la https://numele-vostru.dev”.

Reverse proxy: ce e și de ce

Un reverse proxy e un server care primește request-uri și le direcționează către alte servere din spate. Dintr-un singur punct de intrare (portul 443) gestionează:

  • TLS termination - decriptează HTTPS, rulează HTTP intern către containere
  • Routing - /api/* la API, /* la frontend, /admin la alt serviciu
  • Rate limiting - blochează request-uri abuzive
  • Headers manipulation - adaugă X-Forwarded-For, security headers
  • Compression - gzip pentru text/css/js
  • Static caching - servește singur fișiere statice frecvente

În stack-ul vostru, proxy-ul nginx primește tot traficul, decide unde să-l trimită:

Reverse proxy routing: nginx-ul de pe 443 face TLS termination și rutare prefix-based către api sau frontend

Config nginx pentru proxy

nginx/nginx.conf (top-level config):

worker_processes auto;
events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    sendfile on;
    keepalive_timeout 65;
    server_tokens off;             # nu expune versiunea nginx

    gzip on;
    gzip_min_length 1024;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;

    access_log /dev/stdout;        # docker logs proxy va arata access log
    error_log  /dev/stderr warn;

    include /etc/nginx/conf.d/*.conf;
}

nginx/conf.d/default.conf (regulile de proxy):

# HTTP -> redirectionare permanenta la HTTPS
server {
    listen 80 default_server;
    server_name _;
    return 301 https://$host$request_uri;
}

# HTTPS - serve frontend + proxy /api/* la api
server {
    listen 443 ssl;
    http2 on;
    server_name localhost;

    ssl_certificate     /etc/nginx/certs/server.crt;
    ssl_certificate_key /etc/nginx/certs/server.key;

    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    location /api/ {
        proxy_pass http://api:8080;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /swagger {
        proxy_pass http://api:8080;
        proxy_set_header Host $host;
    }

    # Pagini MVC (Login/Register din Lab12 sunt servite tot de API container)
    location /Auth/ {
        proxy_pass http://api:8080;
        proxy_set_header Host $host;
    }

    # Imagini din wwwroot/images servite de API
    location /images/ {
        proxy_pass http://api:8080;
        proxy_set_header Host $host;
    }

    # Tot restul - frontend Angular static
    location / {
        proxy_pass http://frontend:80;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

location se evaluează pe baza prefixului, în ordine de specificitate. location /api/ e mai specific decât location /, deci /api/articles se duce la API, nu la frontend.

proxy_pass http://api:8080 - aici api e numele serviciului din compose, rezolvat prin DNS-ul intern Docker. Ăsta e magia bridge-network: dintr-un container, alt container e accesibil prin nume.

TLS pe scurt

HTTPS = HTTP + TLS. TLS face două lucruri:

  1. Criptarea - tot ce trece între client și server e criptat. Nimeni în mijloc (ISP, sniffer wifi) nu poate citi.
  2. Autentificarea serverului - clientul verifică că site-ul e cine zice că e (printr-un certificat semnat de o autoritate de încredere - CA).

TLS handshake: ClientHello, ServerHello cu certificat, verificare chain-of-trust, apoi date criptate

Un certificat TLS conține:

  • Domeniul pentru care e valid (ex *.example.com)
  • Cheia publică a serverului
  • Semnătura unei autorități (CA) care garantează că cheia aparține domeniului

Browserele au pre-instalate ~150 CA-uri de încredere (Mozilla CA list). Dacă un cert e semnat de unul dintre ele, browserul acceptă. Altfel, warning “connection not private”.

Self-signed pentru dev local

Pentru dev local nu aveți nevoie de un cert “real” - merge unul self-signed (semnat de voi, nu de o CA). Browserul va da warning, dar pentru localhost e ok.

Generați cu OpenSSL:

openssl req -x509 -newkey rsa:4096 \
  -keyout server.key \
  -out server.crt \
  -days 365 \
  -nodes \
  -subj "/CN=localhost"

Plasați server.crt și server.key în certs/. Compose-ul mountează ./certs:/etc/nginx/certs:ro -> nginx îl folosește.

Let’s Encrypt pentru producție

Pentru un domeniu real, folosiți Let’s Encrypt - o CA gratuită care emite certificate prin protocolul ACME. Tool-ul standard: certbot.

Two challenges:

  • HTTP-01 - certbot pune un fișier la http://yourdomain/.well-known/acme-challenge/..., Let’s Encrypt face GET, vede valoarea așteptată. Necesită ca domeniul să fie deja accesibil pe portul 80.
  • DNS-01 - certbot pune o înregistrare TXT în DNS-ul vostru. Mai complex, dar funcționează pentru wildcard certs (*.example.com).

În producție, certbot rulează în container, într-un loop care reînnoiește certul la fiecare 12h:

certbot certonly --webroot -w /var/www/certbot -d yourdomain.com -m admin@yourdomain.com --agree-tos
# certificatele aterizeaza in /etc/letsencrypt/live/yourdomain.com/

VPS-ul cursului face asta automat - voi nu vă ocupați de cert. URL-ul https://<numele-vostru>.student-dev.ro are certificat valid emis de Let’s Encrypt, fără warning de browser.

DNS pe scurt

DNS = traducerea de la nume domeniu la IP. Când introduceți https://example.com, browserul întreabă un resolver: “ce IP are example.com?”. Resolver-ul întreabă autoritățile, se întoarce cu un IP, browserul deschide TCP la IP-ul ăla.

DNS resolution: browser cere resolver-ului care întreabă root, TLD și authoritative până obține IP-ul; cu cache la fiecare nivel

Tipuri de înregistrări (records)

Tip Ce face Exemplu
A Mapează nume -> IPv4 example.com -> 1.2.3.4
AAAA Mapează nume -> IPv6 example.com -> 2001:db8::1
CNAME Alias la alt nume www.example.com -> example.com
MX Server email example.com -> mail.example.com priority=10
TXT Date arbitrare (SPF, verificări) example.com -> "v=spf1 include:..."
NS Care servere DNS sunt autoritate example.com -> ns1.cloudflare.com

Pentru proiect, aveți nevoie doar de A record: subdomeniul vostru -> IP-ul VPS-ului. În lab-ul nostru, toate subdomeniile *.student-dev.ro sunt deja mapate la 178.105.43.73 via wildcard (vedeți mai jos).

Wildcard records

Pentru *.example.com -> 1.2.3.4, orice subdomeniu se rezolvă la IP-ul respectiv. Ăsta e ce face VPS-ul cursului: un singur record *.student-dev.ro mapează toate <student-x>.student-dev.ro, <student-y>.student-dev.ro etc. la IP-ul serverului.

*.student-dev.ro       300    A    178.105.43.73

(300 = TTL în secunde - cât timp poate fi cached).

Tools utile

dig +short newsportal.student-dev.ro   # ce IP rezolva
dig newsportal.student-dev.ro any      # toate inregistrarile
nslookup newsportal.student-dev.ro     # alt query DNS
whois student-dev.ro                   # cine e proprietar, cand expira

Cumpărat domeniu

Registrari uzuali:

  • Spaceship - cei mai ieftini overall, panou OK
  • Cloudflare Registrar - vinde la cost (renew identic cu price-ul wholesale), DNS gratis bun
  • Namecheap - rezonabil, multe TLD-uri
  • Evitați: GoDaddy (prețuri umflate la renew), domain parker (revând cu marjă)

TLD recomandate pentru proiecte tehnice (~$10-15/an):

  • .dev - HSTS preload (forțează HTTPS automat)
  • .app - similar
  • .codes, .tech - mai “creative”
  • .io - popular dar scump (~$30/an)

Evitați TLD-urile gratuite (.tk, .ml, .ga) - sunt instabile și reputației lor le pasă Google și email providers, multe ajung blocate.

Ce ați învățat

  • Reverse proxy = single point of entry, TLS termination, routing
  • nginx config: server blocks, location, proxy_pass, headers
  • TLS: criptare + autentificare, CA-uri, self-signed pentru dev
  • Let’s Encrypt + certbot pentru producție (cert real, gratuit, auto-renew)
  • DNS: A/CNAME/MX/TXT, wildcard records, tools (dig, nslookup)
  • Registrari: cum cumpărați un domeniu

În secțiunea următoare combinăm toate astea pentru un deploy real pe VPS.