WIUNIX ACADEMY
آموزش HAProxy و Let’s Encrypt با تمدید امن گواهی
صفحهٔ 2 از 4؛ مراحل قبلی و پیشنیازها را پیش از ادامه مرور کن.
آموزش متناسب با محیط تو
کجا اجرا میکنی؟
سیستمعامل و نسخه را انتخاب کن تا مراحل، دستورها و راهحلها برای همان محیط نمایش داده شوند.
آموزش متناسب با مقادیر تو
تنظیمات این آموزش
مقادیر نمونه را برای محیط خودت تنظیم کن؛ متن و دستورهای مرتبط با هم تغییر میکنند. رمز، کلید خصوصی و توکن وارد نکن. مقادیر فقط در همین زبانه نگهداری میشوند و به سرور یا لینک اشتراکگذاری فرستاده نمیشوند. پیش از اجرا، DNS، گواهیها و سیاست شبکهٔ واقعی را بررسی کن.
آیپی، دامنه یا پورت مثالها با محیط تو فرق دارد؟ دکمهٔ زیر را باز کن، مقادیر را تغییر بده و «اعمال در آموزش» را بزن؛ سپس دستورهای بهروزشده را کپی کن.
تنظیم مقادیر برای محیط من7 مقدار قابلتغییر
قدم بعدی
پیش از ACME مسیر challenge را ثابت کنید
پس از اعتبارسنجی پیکربندی، دو سرویس اختصاصی را شروع کنید. از یک میزبان اینترنتی مستقل نیز همین probe را بدون resolve روی همهٔ مسیرهای A و AAAA بررسی کنید؛ پاسخ باید دقیق و بدون ریدایرکت باشد. مسیر عادی ۳۰۸ میدهد و مقصد آن دامنهٔ ثابت است، نه Host دلخواه کاربر. وبسرور Python در این آزمایش فقط loopback و فقط برای توکن عمومی است؛ سرویس عملیاتی باید پاسخگوی استاتیک نگهداریشده داشته باشد.
Bash روی VM اختصاصی؛ مورد بیرونی در متن مشخص است
set -eu
sudo haproxy -c -f '/srv/academy-haproxy-tls/haproxy.cfg'
sudo systemctl daemon-reload
sudo systemctl start academy-acme-webroot.service academy-haproxy.service
printf '%s\n' academy-http01-probe | sudo tee '/srv/academy-haproxy-tls/webroot/.well-known/acme-challenge/academy-probe' >/dev/null
curl --fail --resolve 'app.example.com:80:192.0.2.10' 'http://app.example.com/.well-known/acme-challenge/academy-probe'
curl --head --resolve 'app.example.com:80:192.0.2.10' 'http://app.example.com/'
dig +short A 'app.example.com'
dig +short AAAA 'app.example.com'
متن academy-http01-probe برمیگردد و مسیر عادی به HTTPS همان دامنه ریدایرکت ۳۰۸ میشود.
DNS، IPv6 و شرطهای صدور را بررسی کنید
تمام A و AAAA عمومی باید روی پورت ۸۰ به همین پاسخگو برسند. Let’s Encrypt معمولاً IPv6 را مقدم میداند؛ پاسخ اشتباه AAAA با IPv4 لزوماً جبران نمیشود. نمونهٔ حاضر فقط IPv4 دارد و نباید برایش AAAA منتشر کنید؛ IPv6 به bind صریح و آزمایش مسیر مستقل نیاز دارد. DNS مرجع، CAA، DNSSEC، خروجی HTTPS، ساعت و NAT/فایروال ارائهدهنده را پیش از سفارش بررسی کنید. HTTP-01 برای wildcard مناسب نیست.
مرحلهٔ بعد به CA عمومی وصل میشود و پس از مطالعهٔ شرایط ممکن است حساب بسازد. فقط دامنهٔ متعلق به خودتان را وارد کنید. برای این مقاله حساب یا گواهی عمومی صادر نشده است. خطا را پنهان نکنید؛ پس از محدودیت نرخ، Retry-After و راهنمای جاری را رعایت کنید.
قدم بعدی
staging را در مسیر مستقل امتحان کنید
شرایط CA را در فرایند تعاملی بخوانید و ایمیل قابلپیگیری را همانجا وارد کنید؛ ایمیل در فرم مقاله ذخیره نمیشود. حساب و گواهی staging مستقل و نامعتبر برای کاربران است و از این مسیر چیزی deploy نمیشود. موفقیت این مرحله به شبکه و CA واقعی وابسته است و پیشنمایش مقاله شاهد آن نیست.
Bash روی VM اختصاصی؛ مورد بیرونی در متن مشخص است
sudo certbot certonly --dry-run --webroot -w '/srv/academy-haproxy-tls/webroot' -d 'app.example.com' --cert-name 'app.example.com' \
--config-dir '/srv/academy-haproxy-tls/acme/staging-config' \
--work-dir '/srv/academy-haproxy-tls/acme/staging-work' \
--logs-dir '/srv/academy-haproxy-tls/acme/staging-logs'
Certbot صدور آزمایشی موفق را گزارش میکند و PEM سرویس تغییری نمیکند.
قدم بعدی
گواهی اصلی را یکبار درخواست کنید
فقط پس از پاسخ درست probe بیرونی و staging ادامه دهید. خروجی و وضعیت خطای Certbot را حفظ کنید. نام lineage با cert-name صریح ثابت میماند. دستور certonly پیکربندی HAProxy را تغییر نمیدهد.
Bash روی VM اختصاصی؛ مورد بیرونی در متن مشخص است
sudo certbot certonly --webroot -w '/srv/academy-haproxy-tls/webroot' -d 'app.example.com' --cert-name 'app.example.com' \
--config-dir '/srv/academy-haproxy-tls/acme/config' \
--work-dir '/srv/academy-haproxy-tls/acme/work' \
--logs-dir '/srv/academy-haproxy-tls/acme/logs'
sudo certbot certificates --config-dir '/srv/academy-haproxy-tls/acme/config' --work-dir '/srv/academy-haproxy-tls/acme/work' --logs-dir '/srv/academy-haproxy-tls/acme/logs'
lineage اصلی همان نام را دارد و فایلهای cert، chain، fullchain و کلید خصوصی محفوظ ایجاد شدهاند.
قدم بعدی
اولین PEM را با اعتبارسنجی بسازید
ابتدا fullchain و سپس کلید متناظر در PEM قرار میگیرد. نام دامنه، کاربرد سرور، زنجیرهٔ اعتماد و بیش از یک روز اعتبار کنترل میشوند. فایل موقت ۰۶۰۰ در همان فایلسیستم بهصورت اتمیک جایگذاری میشود. برای دسترسی HAProxy مجوز درخت خصوصی ACME را باز نکنید.
Bash روی VM اختصاصی؛ مورد بیرونی در متن مشخص است
set -eu
sudo python3 - <<'PY'
import os, subprocess, tempfile
from pathlib import Path
os.umask(0o077)
root=Path('/srv/academy-haproxy-tls')
lineage=root/'acme/config/live/app.example.com'
target=root/'certs/app.example.com.pem'
assert not target.exists() and not target.is_symlink()
def run(*args):
return subprocess.run(args,check=True,capture_output=True).stdout
run('openssl','x509','-in',str(lineage/'cert.pem'),'-checkend','86400','-noout')
run('openssl','verify','-CAfile','/etc/ssl/certs/ca-certificates.crt',
'-untrusted',str(lineage/'chain.pem'),'-purpose','sslserver',
'-verify_hostname','app.example.com',str(lineage/'cert.pem'))
assert run('openssl','x509','-in',str(lineage/'cert.pem'),'-pubkey','-noout') == run('openssl','pkey','-in',str(lineage/'privkey.pem'),'-pubout')
fd,name=tempfile.mkstemp(dir=target.parent,prefix='.initial-')
with os.fdopen(fd,'wb') as f:
f.write((lineage/'fullchain.pem').read_bytes()+b'\n'+(lineage/'privkey.pem').read_bytes())
f.flush(); os.fsync(f.fileno())
os.replace(name,target)
print('Initial protected PEM assembled; TLS config not yet loaded')
PY
PEM با مالک root و مجوز ۰۶۰۰ ساخته شده و bootstrap همچنان challenge را پاسخ میدهد.
قدم بعدی
کاندیدای TLS را جدا ذخیره کنید
هر دو بکاند باید همان اپلیکیشن را اجرا کنند و health/ready تنها هنگام آمادگی واقعی وابستگیها ۲۰۰ بدهد. Host برابر app.example.com باید پذیرفته شود. مدیریت نشست و داده مسئولیت اپلیکیشن است. فایل بعدی را در کاندیدای تازه وارد کنید؛ bootstrap در حال اجرا حفظ میشود. در این نمونه HSTS preload، includeSubDomains و سوکت مدیریتی فعال نیست.
Bash روی VM اختصاصی؛ مورد بیرونی در متن مشخص است
set -eu
curl --fail -H 'Host: app.example.com' 'http://10.77.0.21:8080/health/ready'
curl --fail -H 'Host: app.example.com' 'http://10.77.0.22:8080/health/ready'
test ! -e '/srv/academy-haproxy-tls/haproxy.tls.cfg'
test ! -L '/srv/academy-haproxy-tls/haproxy.tls.cfg'
sudo install -m 0644 /dev/null '/srv/academy-haproxy-tls/haproxy.tls.cfg'
sudo nano '/srv/academy-haproxy-tls/haproxy.tls.cfg'
هر دو readiness پاسخ ۲۰۰ دارند و کاندیدای مستقل برای ویرایش آماده است.
قدم بعدی
TLS، هدر قابلاعتماد و readiness
این پنل کل محتوای کاندیداست. Forwarded حذف میشود و X-Forwarded-* از اتصال مستقیم ساخته میشود. option forwardfor را روی این سیاست اضافه نکنید. بکاند باید فقط IP مبدأ واقعی HAProxy را بپذیرد، نه تمام شبکههای خصوصی را.
فایل روی VM اختصاصی
global
log stdout format raw local0
user haproxy
group haproxy
ssl-default-bind-options ssl-min-ver TLSv1.2
defaults
log global
mode http
option httplog
timeout connect 5s
timeout client 30s
timeout server 30s
timeout http-request 10s
frontend public_http
bind 192.0.2.10:80
acl known_host hdr(host) -i app.example.com app.example.com:80
http-request deny deny_status 421 unless known_host
acl acme_path path_beg /.well-known/acme-challenge/
http-request redirect prefix https://app.example.com code 308 unless acme_path
use_backend acme_webroot if acme_path
backend acme_webroot
server challenge 127.0.0.1:8888 check
frontend public_https
bind 192.0.2.10:443 ssl crt /srv/academy-haproxy-tls/certs/app.example.com.pem alpn h2,http/1.1
acl known_host hdr(host) -i app.example.com app.example.com:443
http-request deny deny_status 421 unless known_host
http-request del-header Forwarded
http-request del-header X-Forwarded-For
http-request del-header X-Forwarded-Proto
http-request del-header X-Forwarded-Host
http-request del-header X-Forwarded-Port
http-request del-header X-Real-IP
http-request set-header X-Forwarded-For %[src]
http-request set-header X-Forwarded-Proto https
http-request set-header X-Forwarded-Host app.example.com
http-request set-header X-Forwarded-Port 443
default_backend app_nodes
backend app_nodes
balance roundrobin
option httpchk
http-check send meth GET uri /health/ready ver HTTP/1.1 hdr Host app.example.com
http-check expect status 200
server app_a 10.77.0.21:8080 check inter 2s fall 2 rise 2
server app_b 10.77.0.22:8080 check inter 2s fall 2 rise 2
کاندیدا شامل دو ورودی عمومی، بکاند محلی challenge و دو نود اپلیکیشن با health check است.