Angie
مرور کلی
Angie یک فورک از nginx با مجموعه ماژولهای گستردهتر، کلاینت ACME داخلی و پشتیبانی از HTTP/3 است. برای پنل XLADA همانند nginx استفاده میشود: TLS را ترمینیت میکند و درخواستها را به پورتهای loopback پنل و صفحه اشتراک پروکسی میکند.
پنل و صفحه اشتراک باید هدرهای X-Forwarded-For و
X-Forwarded-Proto: https را دریافت کنند. در کد آنها میانافزاری وجود دارد که
اگر هدرها نباشند اتصال را قطع میکند: بدون آنها پنل پشت پروکسی راهاندازی نمیشود.
IP واقعی کاربر در این حالت برای محدودیتهای HWID، ممیزی اقدامات مدیران و
محافظت از ورود در برابر حمله جستوجوی فراگیر لازم است.
در مثالها دامنههای panel.example.com و sub.example.com، آپاستریمهای
127.0.0.1:3000 (پنل) و 127.0.0.1:3010 (صفحه اشتراک) پورتهای
کانتینرهای xpanel و xpanel-subpage روی loopback میزبان هستند.
به یک دامنه ثبتشده نیاز دارید. تا زمانی که دامنه به سرور شما اشاره نکند، صدور گواهی Let's Encrypt ممکن نیست.
DNS
آدرس IP سرور را بررسی کنید (ترجیحاً استاتیک) و رکوردها را نزد ارائهدهنده DNS یا ثبتکننده ایجاد کنید:
| نوع | نام | مقدار |
|---|---|---|
A | panel | آدرس IPv4 سرور |
A | sub | آدرس IPv4 سرور |
AAAA | panel | آدرس IPv6 سرور |
AAAA | sub | آدرس IPv6 سرور |
در Cloudflare رکوردها در بخش DNS → Records → Create record ساخته میشوند.
- ابر خاکستری — ترافیک مستقیم میرود و Angie IP واقعی کاربر را میبیند؛ اگر Cloudflare در دسترس نیست یا مزاحمت ایجاد میکند از این حالت استفاده کنید.
- ابر نارنجی — ترافیک از شبکه Cloudflare عبور میکند: IP سرور پنهان میشود و
محافظت در برابر حملات فعال است. در این حالت Angie آدرسهای Cloudflare را
میبیند، بنابراین برای IP صحیح ماژول
realipو اعتماد به هدرCF-Connecting-IPرا فعال کنید.
گواهیهای TLS
دایرکتوریهایی برای گواهیها و بررسیهای ACME بسازید:
sudo mkdir -p /etc/angie/ssl /var/www/acme
گزینه ۱: certbot
sudo apt-get update && sudo apt-get install -y certbot
sudo certbot certonly --webroot -w /var/www/acme \
-d panel.example.com -d sub.example.com \
--email admin@example.com --agree-tos --no-eff-email
sudo ln -sf /etc/letsencrypt/live/panel.example.com/fullchain.pem /etc/angie/ssl/fullchain.pem
sudo ln -sf /etc/letsencrypt/live/panel.example.com/privkey.pem /etc/angie/ssl/privkey.key
گزینه ۲: acme.sh
curl https://get.acme.sh | sh -s email=admin@example.com
acme.sh --issue --webroot /var/www/acme \
-d panel.example.com -d sub.example.com \
--key-file /etc/angie/ssl/privkey.key \
--fullchain-file /etc/angie/ssl/fullchain.pem \
--reloadcmd "docker exec angie angie -s reload"
هر دو کلاینت گواهی را بهصورت خودکار تمدید میکنند: certbot با تایمر systemd خود،
acme.sh با یک وظیفه در cron هر ۶۰ روز. پورت 80 باید باز باشد و به
مسیر بررسی ACME که در پیکربندی زیر توضیح میدهیم پاسخ دهد.
Angie کلاینت ACME اختصاصی خود را نیز دارد: گواهی را بدون certbot و acme.sh صادر میکند. پیکربندی کامل این حالت در زیربخش «گزینه با ACME داخلی» آمده است.
پیکربندی کامل angie.conf
sudo mkdir -p /opt/angie && cd /opt/angie
sudo nano angie.conf
# ─── آپاستریمها: کانتینرهای xpanel و xpanel-subpage ──────────────────
upstream xpanel_panel {
server 127.0.0.1:3000;
keepalive 32;
}
upstream xpanel_subpage {
server 127.0.0.1:3010;
keepalive 16;
}
# WebSocket: هدر Connection را فقط هنگام Upgrade تنظیم میکنیم
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# ─── Gzip برای همه میزبانهای مجازی ────────────────────────────────
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types
application/atom+xml
application/geo+json
application/javascript
application/json
application/ld+json
application/manifest+json
application/rdf+xml
application/rss+xml
application/xhtml+xml
application/xml
application/wasm
font/eot
font/otf
font/ttf
image/svg+xml
text/css
text/javascript
text/plain
text/xml;
# ─── HTTP: بررسی ACME و ریدایرکت به HTTPS ─────────────────────────
server {
listen 80;
listen [::]:80;
server_name panel.example.com sub.example.com;
location /.well-known/acme-challenge/ {
root /var/www/acme;
}
location / {
return 301 https://$host$request_uri;
}
}
# ─── پنل XLADA ────────────────────────────────────────────────────
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name panel.example.com;
# TLS (Mozilla Intermediate)
ssl_certificate /etc/angie/ssl/fullchain.pem;
ssl_certificate_key /etc/angie/ssl/privkey.key;
ssl_trusted_certificate /etc/angie/ssl/fullchain.pem;
ssl_protocols TLSv1.2 TLSv1.3;
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;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=60s;
resolver_timeout 2s;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options SAMEORIGIN always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
# اندازه بدنه درخواست — با حاشیه اطمینان برای بارگذاری فایلها از رابط کاربری
client_max_body_size 25m;
# استاتیک رابط کاربری: هش در نام فایل امکان کش طولانیمدت آن را میدهد
location ^~ /assets/ {
proxy_http_version 1.1;
proxy_pass http://xpanel_panel;
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 https;
proxy_set_header Connection "";
expires 365d;
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
location / {
proxy_http_version 1.1;
proxy_pass http://xpanel_panel;
# پنل بدون این دو هدر اتصال را قطع میکند
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 https;
proxy_set_header X-Forwarded-Host $host;
# WebSocket — ترمینال SSH به نودها
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_connect_timeout 10s;
proxy_send_timeout 86400s;
proxy_read_timeout 86400s;
proxy_buffering off; # استریم و SSE
proxy_cache off;
}
}
# ─── صفحه اشتراک ───────────────────────────────────────────────
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name sub.example.com;
ssl_certificate /etc/angie/ssl/fullchain.pem;
ssl_certificate_key /etc/angie/ssl/privkey.key;
ssl_trusted_certificate /etc/angie/ssl/fullchain.pem;
ssl_protocols TLSv1.2 TLSv1.3;
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;
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=60s;
resolver_timeout 2s;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
client_max_body_size 10m;
# استاتیک بیلد صفحه اشتراک
location ^~ /assets/ {
proxy_http_version 1.1;
proxy_pass http://xpanel_subpage;
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 https;
proxy_set_header Connection "";
expires 365d;
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
location / {
proxy_http_version 1.1;
proxy_pass http://xpanel_subpage;
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 https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_connect_timeout 10s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_buffering off;
}
}
# ─── قطع TLS handshake برای IP و دامنههای ناشناخته ──────────────
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name _;
ssl_reject_handshake on;
}
هنگام نصب روی میزبان، فایل را به /etc/angie/http.d/xlada.conf کپی کنید. هنگام
اجرا در Docker، همانطور که در زیر نشان داده شده، کل فایل در کانتینر mount میشود.
از دایرکتیو reuseport استفاده نمیکنیم: با چند
میزبان مجازی روی یک آدرس و پورت واحد سازگار نیست — Angie با خطای
duplicate listen options خاتمه مییابد.
گزینه با ACME داخلی
اگر به کلاینتهای خارجی نیازی نیست، Angie خودش گواهی را صادر میکند. پیکربندی زیر
خودکفاست: نیازی به صدور از قبل نیست؛ Angie گواهی را در
اولین اجرا دریافت میکند و آن را بهصورت خودکار تمدید میکند. احراز مالکیت دامنه
از طریق TLS-ALPN روی پورت 443 انجام میشود، بنابراین پورت 80 فقط ریدایرکت میکند. وضعیت
ACME در /var/lib/angie/acme/ ذخیره میشود — این دایرکتوری باید از
راهاندازی مجدد کانتینر جان سالم به در ببرد.
# ─── آپاستریمها: کانتینرهای xpanel و xpanel-subpage ──────────────────
upstream xpanel_panel {
server 127.0.0.1:3000;
keepalive 32;
}
upstream xpanel_subpage {
server 127.0.0.1:3010;
keepalive 16;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# ─── ACME داخلی ─────────────────────────────────────────────────
resolver 1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 valid=60s;
resolver_timeout 2s;
acme_client acme_le https://acme-v02.api.letsencrypt.org/directory;
# ─── Gzip برای همه میزبانهای مجازی ────────────────────────────────
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types
application/atom+xml
application/geo+json
application/javascript
application/json
application/ld+json
application/manifest+json
application/rdf+xml
application/rss+xml
application/xhtml+xml
application/xml
application/wasm
font/eot
font/otf
font/ttf
image/svg+xml
text/css
text/javascript
text/plain
text/xml;
# ─── HTTP: ریدایرکت به HTTPS ─────────────────────────────────────────
server {
listen 80;
listen [::]:80;
server_name panel.example.com sub.example.com;
location / {
return 301 https://$host$request_uri;
}
}
# ─── پنل XLADA ────────────────────────────────────────────────────
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name panel.example.com;
acme acme_le;
ssl_certificate $acme_cert_acme_le;
ssl_certificate_key $acme_cert_key_acme_le;
ssl_protocols TLSv1.2 TLSv1.3;
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;
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:ACME_TLS:10m;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options SAMEORIGIN always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
client_max_body_size 25m;
location ^~ /assets/ {
proxy_http_version 1.1;
proxy_pass http://xpanel_panel;
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 https;
proxy_set_header Connection "";
expires 365d;
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
location / {
proxy_http_version 1.1;
proxy_pass http://xpanel_panel;
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 https;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_connect_timeout 10s;
proxy_send_timeout 86400s;
proxy_read_timeout 86400s;
proxy_buffering off;
proxy_cache off;
}
}
# ─── صفحه اشتراک ───────────────────────────────────────────────
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name sub.example.com;
acme acme_le;
ssl_certificate $acme_cert_acme_le;
ssl_certificate_key $acme_cert_key_acme_le;
ssl_protocols TLSv1.2 TLSv1.3;
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;
ssl_prefer_server_ciphers off;
ssl_session_timeout 1d;
ssl_session_cache shared:ACME_TLS:10m;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
client_max_body_size 10m;
location ^~ /assets/ {
proxy_http_version 1.1;
proxy_pass http://xpanel_subpage;
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 https;
proxy_set_header Connection "";
expires 365d;
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
location / {
proxy_http_version 1.1;
proxy_pass http://xpanel_subpage;
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 https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_connect_timeout 10s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_buffering off;
}
}
# ─── قطع TLS handshake برای IP و دامنههای ناشناخته ──────────────
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name _;
ssl_reject_handshake on;
}
پیکربندی را بررسی و Angie را دوباره بارگذاری کنید:
sudo angie -t && sudo angie -s reload
docker compose exec angie angie -t && docker compose exec angie angie -s reload
اجرا در Docker
services:
angie:
image: docker.angie.software/angie:1.9.0
container_name: angie
restart: unless-stopped
# حالت host تا Angie بتواند 127.0.0.1:3000 و 127.0.0.1:3010 را ببیند
network_mode: host
environment:
- TZ=UTC
volumes:
- ./angie.conf:/etc/angie/http.d/default.conf:ro
- /etc/angie/ssl:/etc/angie/ssl:ro # گواهیهای Let's Encrypt
- /var/www/acme:/var/www/acme:ro # دایرکتوری بررسیهای ACME
- angie-ssl-data:/var/lib/angie/acme/ # وضعیت ACME داخلی
volumes:
angie-ssl-data:
name: angie-ssl-data
cd /opt/angie
docker compose up -d
docker compose logs -f -t
حالت Host عمداً انتخاب شده است: کانتینر همان پورتهای loopback
127.0.0.1:3000 و 127.0.0.1:3010 را میبیند که میزبان میبیند، بنابراین آپاستریمهای
angie.conf را نیازی نیست به نام کانتینرها بازنویسی کنید. گواهیها و دایرکتوری
بررسیهای ACME در این حالت روی میزبان باقی میمانند.
بررسی و بارگذاری مجدد
# بررسی سینتکس
sudo angie -t
docker compose exec angie angie -t
# دامپ کامل پیکربندی نهایی
sudo angie -T
# بارگذاری مجدد نرم بدون قطع اتصالها
sudo angie -s reload
docker compose exec angie angie -s reload
در لاگها به چه چیزهایی توجه کنیم
journalctl -u angie -f # نصب روی میزبان
docker compose logs -f -t angie
[emerg]— خطای پیکربندی؛angie -tخط را نشان میدهد؛connect() failed (111: Connection refused)— پنل یا صفحه اشتراک روی پورت مورد انتظار اجرا نشده است؛upstream timed out— برای عملیات طولانیproxy_read_timeoutرا افزایش دهید؛SSL_do_handshake() failed— اسکنرهای معمول؛ نیازی به واکنش ندارند.
curl -I https://panel.example.com
curl -I https://sub.example.com
هر دو پاسخ باید با HTTP/2 200 شروع شوند. اگر در ممیزی پنل آدرسهای
Cloudflare دیده میشود، اعتماد به CF-Connecting-IP را از طریق ماژول realip فعال کنید.