feat: implement SSL support with Let's Encrypt and update Nginx configuration
This commit is contained in:
Binary file not shown.
@@ -122,13 +122,23 @@ services:
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
- ./certbot/www:/var/www/certbot:ro
|
||||
- ./certbot/conf:/etc/letsencrypt:ro
|
||||
depends_on:
|
||||
- backend
|
||||
- frontend
|
||||
restart: unless-stopped
|
||||
|
||||
# --- Certbot (Let's Encrypt) ---
|
||||
certbot:
|
||||
image: certbot/certbot
|
||||
volumes:
|
||||
- ./certbot/www:/var/www/certbot
|
||||
- ./certbot/conf:/etc/letsencrypt
|
||||
|
||||
volumes:
|
||||
redis_data:
|
||||
upload_data:
|
||||
|
||||
273
fixed-ssl.md
Normal file
273
fixed-ssl.md
Normal file
@@ -0,0 +1,273 @@
|
||||
سؤالك ممتاز—وده بالضبط الفرق بين **حل “يشتغل وخلاص”** وحل **Production حقيقي**.
|
||||
|
||||
خليني أشرح لك الأول **ليه الطريقة الأولى (certbot --nginx) مش مناسبة** عندك، وبعدين أديك **الحل الصحيح الجاهز (Nginx داخل Docker + SSL + Auto renew)** copy/paste.
|
||||
|
||||
---
|
||||
|
||||
# لماذا `certbot --nginx` مش مناسب عندك؟
|
||||
|
||||
أنت عندك:
|
||||
|
||||
```text
|
||||
Nginx شغال داخل Docker (saas-pdf-nginx-1)
|
||||
```
|
||||
|
||||
لكن الأمر:
|
||||
|
||||
```bash
|
||||
certbot --nginx
|
||||
```
|
||||
|
||||
بيفترض إن:
|
||||
|
||||
```text
|
||||
Nginx مثبت على السيرفر (host)
|
||||
```
|
||||
|
||||
📌 النتيجة:
|
||||
|
||||
* Certbot حاول يتحكم في Nginx الخارجي
|
||||
* لكن البورت 80 مستخدم بالفعل بواسطة Docker
|
||||
* حصل الخطأ: `Address already in use`
|
||||
|
||||
---
|
||||
|
||||
# هل الحل اللي عملناه “مؤقت”؟
|
||||
|
||||
لا 👇
|
||||
|
||||
## عندنا 3 مستويات حلول:
|
||||
|
||||
### 🥉 حل بدائي
|
||||
|
||||
تشغل Certbot خارج Docker → كل مرة توقف Nginx
|
||||
❌ غير عملي
|
||||
|
||||
---
|
||||
|
||||
### 🥈 حل متوسط
|
||||
|
||||
Certbot standalone + manual config
|
||||
✔ شغال
|
||||
❌ لكن renewal مش أوتوماتيك بسهولة
|
||||
|
||||
---
|
||||
|
||||
### 🥇 الحل الاحترافي (اللي هنطبقه الآن)
|
||||
|
||||
```text
|
||||
Nginx + Certbot داخل Docker
|
||||
```
|
||||
|
||||
✔ Auto renew
|
||||
✔ scalable
|
||||
✔ نفس طريقة الشركات الكبيرة
|
||||
|
||||
---
|
||||
|
||||
# المشاكل لو ما عملتش الحل الصحيح
|
||||
|
||||
لو كملت بالحل القديم:
|
||||
|
||||
* ❌ SSL هينتهي كل 90 يوم
|
||||
* ❌ الموقع هيقع فجأة
|
||||
* ❌ Google هيقلل ترتيبك
|
||||
* ❌ AdSense ممكن يرفضك
|
||||
* ❌ صعب التوسعة لاحقًا
|
||||
|
||||
---
|
||||
|
||||
# الآن الحل الاحترافي (جاهز للتطبيق)
|
||||
|
||||
هنعمل 3 حاجات:
|
||||
|
||||
```text
|
||||
1. nginx config احترافي
|
||||
2. docker-compose فيه SSL
|
||||
3. auto renew
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 1️⃣ تعديل nginx config
|
||||
|
||||
ادخل:
|
||||
|
||||
```bash
|
||||
cd ~/SaaS-PDF/nginx
|
||||
nano default.conf
|
||||
```
|
||||
|
||||
واستبدل كله بهذا:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name dociva.io www.dociva.io;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name dociva.io www.dociva.io;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/dociva.io/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/dociva.io/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://frontend:5173;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 2️⃣ تعديل docker-compose.yml
|
||||
|
||||
افتح:
|
||||
|
||||
```bash
|
||||
nano docker-compose.yml
|
||||
```
|
||||
|
||||
وابحث عن nginx وعدله:
|
||||
|
||||
```yaml
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx:/etc/nginx/conf.d
|
||||
- ./certbot/www:/var/www/certbot
|
||||
- ./certbot/conf:/etc/letsencrypt
|
||||
depends_on:
|
||||
- frontend
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 3️⃣ إضافة certbot container
|
||||
|
||||
في نفس الملف أضف:
|
||||
|
||||
```yaml
|
||||
certbot:
|
||||
image: certbot/certbot
|
||||
volumes:
|
||||
- ./certbot/www:/var/www/certbot
|
||||
- ./certbot/conf:/etc/letsencrypt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 4️⃣ تشغيل المشروع
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 5️⃣ إصدار الشهادة (مرة واحدة)
|
||||
|
||||
```bash
|
||||
docker compose run --rm certbot certonly \
|
||||
--webroot \
|
||||
--webroot-path=/var/www/certbot \
|
||||
-d dociva.io -d www.dociva.io \
|
||||
--email your@email.com \
|
||||
--agree-tos --no-eff-email
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 6️⃣ إعادة تشغيل Nginx
|
||||
|
||||
```bash
|
||||
docker compose restart nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# 7️⃣ اختبار
|
||||
|
||||
```text
|
||||
https://dociva.io
|
||||
```
|
||||
|
||||
🔥 المفروض يشتغل
|
||||
|
||||
---
|
||||
|
||||
# 8️⃣ auto renew (مهم جداً)
|
||||
|
||||
أضف cron job:
|
||||
|
||||
```bash
|
||||
crontab -e
|
||||
```
|
||||
|
||||
وأضف:
|
||||
|
||||
```bash
|
||||
0 3 * * * docker compose run --rm certbot renew && docker compose restart nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# النتيجة النهائية
|
||||
|
||||
✔ HTTPS شغال
|
||||
✔ Auto renew
|
||||
✔ Production ready
|
||||
✔ مناسب لـ SEO + AdSense
|
||||
|
||||
---
|
||||
|
||||
# خلاصة بسيطة
|
||||
|
||||
| الطريقة | هل مناسبة؟ |
|
||||
| ---------------- | ---------- |
|
||||
| certbot --nginx | ❌ لا |
|
||||
| standalone | ⚠️ مؤقت |
|
||||
| Docker SSL setup | ✅ الأفضل |
|
||||
|
||||
---
|
||||
|
||||
# رأيي كمستشار
|
||||
|
||||
أنت حالياً وصلت لمستوى:
|
||||
|
||||
```text
|
||||
Junior → Mid-level DevOps 🚀
|
||||
```
|
||||
|
||||
ولو كملت بالشكل ده مشروعك فعلاً ممكن يتحول لمنتج حقيقي.
|
||||
|
||||
---
|
||||
|
||||
# الخطوة التالية بعد SSL
|
||||
|
||||
بعد ما نخلص:
|
||||
|
||||
1. Google Search Console
|
||||
2. Sitemap
|
||||
3. SEO pages
|
||||
4. Analytics
|
||||
|
||||
---
|
||||
|
||||
لو حابب، الخطوة الجاية أعملك:
|
||||
|
||||
👉 **إعداد SEO كامل يجلب أول 10,000 زيارة**
|
||||
بطريقة عملية مش كلام نظري.
|
||||
@@ -6,12 +6,35 @@ upstream frontend {
|
||||
server frontend:5173;
|
||||
}
|
||||
|
||||
# ── HTTP: ACME challenge + redirect to HTTPS ──
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost 127.0.0.1 178.104.57.123 dociva.io www.dociva.io;
|
||||
server_name dociva.io www.dociva.io;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# ── HTTPS ──
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name dociva.io www.dociva.io;
|
||||
client_max_body_size 100M;
|
||||
|
||||
# SSL certificates (Let's Encrypt)
|
||||
ssl_certificate /etc/letsencrypt/live/dociva.io/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/dociva.io/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
|
||||
|
||||
# Security headers
|
||||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
@@ -33,7 +56,7 @@ server {
|
||||
proxy_connect_timeout 60s;
|
||||
}
|
||||
|
||||
# Frontend (Vite dev server in dev, static in prod)
|
||||
# Frontend (Vite dev server)
|
||||
location / {
|
||||
proxy_pass http://frontend;
|
||||
proxy_set_header Host $host;
|
||||
|
||||
102
scripts/init-letsencrypt.sh
Normal file
102
scripts/init-letsencrypt.sh
Normal file
@@ -0,0 +1,102 @@
|
||||
#!/bin/bash
|
||||
# ────────────────────────────────────────────────────────────
|
||||
# init-letsencrypt.sh
|
||||
# Bootstrap Let's Encrypt certificates for dociva.io
|
||||
#
|
||||
# This script solves the chicken-and-egg problem:
|
||||
# Nginx needs SSL certs to start on 443,
|
||||
# but Certbot needs Nginx running on 80 to verify the domain.
|
||||
#
|
||||
# Solution: create a temporary self-signed cert → start Nginx →
|
||||
# obtain the real cert → reload Nginx.
|
||||
#
|
||||
# Usage: chmod +x scripts/init-letsencrypt.sh
|
||||
# sudo ./scripts/init-letsencrypt.sh
|
||||
# ────────────────────────────────────────────────────────────
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DOMAINS=(dociva.io www.dociva.io)
|
||||
DATA_PATH="./certbot"
|
||||
EMAIL="admin@dociva.io" # ← replace with your real email
|
||||
RSA_KEY_SIZE=4096
|
||||
STAGING=0 # Set to 1 to test against staging (no rate limits)
|
||||
|
||||
# ── colour helpers ──
|
||||
info() { echo -e "\n\033[1;34m▶ $*\033[0m"; }
|
||||
ok() { echo -e "\033[1;32m✔ $*\033[0m"; }
|
||||
fail() { echo -e "\033[1;31m✖ $*\033[0m"; exit 1; }
|
||||
|
||||
# ── Pre-flight checks ──
|
||||
command -v docker > /dev/null 2>&1 || fail "docker is not installed"
|
||||
command -v docker compose > /dev/null 2>&1 && COMPOSE="docker compose" || COMPOSE="docker-compose"
|
||||
|
||||
# ── Step 1: Create required directories ──
|
||||
info "Creating certbot directories …"
|
||||
mkdir -p "$DATA_PATH/conf" "$DATA_PATH/www"
|
||||
ok "Directories ready"
|
||||
|
||||
# ── Step 2: Download recommended TLS parameters ──
|
||||
if [ ! -e "$DATA_PATH/conf/options-ssl-nginx.conf" ]; then
|
||||
info "Downloading recommended TLS parameters …"
|
||||
curl -sSf https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf \
|
||||
-o "$DATA_PATH/conf/options-ssl-nginx.conf"
|
||||
curl -sSf https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem \
|
||||
-o "$DATA_PATH/conf/ssl-dhparams.pem"
|
||||
ok "TLS parameters saved"
|
||||
fi
|
||||
|
||||
# ── Step 3: Create temporary self-signed certificate ──
|
||||
LIVE_DIR="$DATA_PATH/conf/live/dociva.io"
|
||||
if [ ! -e "$LIVE_DIR/fullchain.pem" ]; then
|
||||
info "Creating temporary self-signed certificate …"
|
||||
mkdir -p "$LIVE_DIR"
|
||||
openssl req -x509 -nodes -newkey rsa:2048 -days 1 \
|
||||
-keyout "$LIVE_DIR/privkey.pem" \
|
||||
-out "$LIVE_DIR/fullchain.pem" \
|
||||
-subj "/CN=dociva.io" 2>/dev/null
|
||||
ok "Temporary certificate created"
|
||||
fi
|
||||
|
||||
# ── Step 4: Start Nginx (uses the temp cert) ──
|
||||
info "Starting Nginx …"
|
||||
$COMPOSE up -d nginx
|
||||
ok "Nginx is running"
|
||||
|
||||
# ── Step 5: Remove the temporary certificate ──
|
||||
info "Removing temporary certificate …"
|
||||
rm -rf "$LIVE_DIR"
|
||||
ok "Temporary certificate removed"
|
||||
|
||||
# ── Step 6: Request real certificate from Let's Encrypt ──
|
||||
info "Requesting Let's Encrypt certificate …"
|
||||
|
||||
DOMAIN_ARGS=""
|
||||
for d in "${DOMAINS[@]}"; do
|
||||
DOMAIN_ARGS="$DOMAIN_ARGS -d $d"
|
||||
done
|
||||
|
||||
STAGING_ARG=""
|
||||
if [ "$STAGING" -eq 1 ]; then
|
||||
STAGING_ARG="--staging"
|
||||
fi
|
||||
|
||||
$COMPOSE run --rm certbot certonly --webroot \
|
||||
--webroot-path=/var/www/certbot \
|
||||
$DOMAIN_ARGS \
|
||||
--email "$EMAIL" \
|
||||
--agree-tos \
|
||||
--no-eff-email \
|
||||
--force-renewal \
|
||||
$STAGING_ARG
|
||||
|
||||
ok "Certificate obtained successfully"
|
||||
|
||||
# ── Step 7: Reload Nginx with the real certificate ──
|
||||
info "Reloading Nginx …"
|
||||
$COMPOSE exec nginx nginx -s reload
|
||||
ok "Nginx reloaded with Let's Encrypt certificate"
|
||||
|
||||
echo ""
|
||||
ok "HTTPS is now active for ${DOMAINS[*]}"
|
||||
echo " Test: curl -I https://dociva.io"
|
||||
Reference in New Issue
Block a user