
به اشتراک بگذارید

به اشتراک بگذارید
از ۱ اوت ۲۰۲۱ و نسخه 3.0، هر گواهی که acme.sh بدون پارامتر اضافه صادر کند از ZeroSSL میآید، چون این مرجع در پیشفرض بهجای Let’s Encrypt نشست. برای صدور بدون پورت 80، همین اسکریپت پوستهای چالش DNS-01 را با API سرویس DNS حل میکند و گواهی را با --install-cert به nginx میدهد.
مسیر در ~/.acme.sh | چه چیزی آنجاست | حساس است؟ |
|---|---|---|
account.conf | ایمیل حساب و مرجع پیشفرض، بهاضافه متغیرهای API سرویس DNS | بله؛ توکن DNS همینجاست |
ca/.../account.key | کلید حساب ACME، جدا برای هر مرجع | بله |
example.com_ecc/example.com.key | کلید خصوصی گواهی | بله |
example.com_ecc/example.com.conf | مسیرهای نصب و فرمان reload | گاهی توکن Cloudflare هم دارد |
💡 گواهی Wildcard بدون هیچ پورت ورودی
اسکریپتی پوستهای برای پروتکل ACME که مالکیت دامنه را با ساختن رکورد TXT از راه API سرویس DNS ثابت میکند.
✅ پیش از اولین صدور، مرجع را با --set-default-ca تعیین کنید و توکن را به یک zone محدود کنید.
✅ گواهی را فقط با --install-cert به وبسرور بدهید.
هر چیزی که یک بار به این اسکریپت بگویید، از ایمیل حساب تا فرمان reload، زیر ~/.acme.sh میماند و در تمدیدهای بعدی دوباره خوانده میشود. فاصله تمدید تا انقضا را با حاشیهای که تمدید ssl در قطعی اینترنت لازم دارد هماهنگ کنید، چون cron این اسکریپت فقط وقتی گواهی تازه میگیرد که سرور به مرجع صدور برسد.
📌 نقشه پوشه و فرمانهایی که آن را میسازند
◀️ نقشه پوشه بعد از نصب
◀️ مرجع صدور گواهی را چه کسی انتخاب میکند؟
◀️ چالش DNS با API سرویسدهنده یا حالت alias کار میکند
◀️ تحویل گواهی به وبسرور

نصبکننده رسمی اسکریپت را در ~/.acme.sh/ کپی میکند و برایش alias میسازد؛ یک کار cron هم برای بررسی تمدید میگذارد. به گفته README پروژه، گواهیها هم در همین پوشه قرار میگیرند.
curl https://get.acme.sh | sh -s email=my@example.com
در README نصب با root توصیه شده است. صفحه sudo ویکی میگوید بهجای sudo اول با su به root بروید، چون sudo بدون تنظیم درست در cron به خطای مجوز و تمدید نشدن میرسد. بعد از نصب، ترمینال را دوباره باز کنید تا alias فعال شود؛ ایمیل با نام ACCOUNT_EMAIL در account.conf مینشیند.
درخت زیر مسیرهای اصلی نسخه 3.1.7 را نشان میدهد، یعنی شاخه master که نصب آنلاین میگیرد (آخرین انتشار برچسبخورده 3.1.6). کلید پیشفرض ec-256 است و پسوند _ecc از همینجا میآید.
~/.acme.sh/
├── acme.sh # اسکریپت اصلی؛ alias به آن اشاره دارد
├── acme.sh.env # alias و LE_WORKING_DIR
├── account.conf # حساس: ایمیل، CA پیشفرض، توکن DNS
├── dnsapi/ # افزونههای DNS مثل dns_cf.sh
├── deploy/ # هوکهای استقرار
├── notify/ # هوکهای اعلان
├── ca/
│ └── acme.zerossl.com/v2/DV90/
│ ├── account.key # حساس: کلید حساب ACME نزد ZeroSSL
│ └── ca.conf
└── example.com_ecc/ # کلید ec-256
├── example.com.key # حساس: کلید خصوصی گواهی
├── example.com.cer # گواهی دامنه
├── ca.cer # گواهی میانی
├── fullchain.cer # گواهی بهاضافه میانی
└── example.com.conf # مسیرهای نصب و Le_ReloadCmd
ساختار این پوشه، به گفته README، ممکن است در نسخههای بعد عوض شود؛ هیچ سرویسی را مستقیم به آن وصل نکنید. کلید خصوصی و توکن DNS مثل سند خانه، شناسنامهها و قبضها در یک پوشه مدارک کنار هم نشستهاند؛ راحت است، تا روزی که یک پشتیبان رمزنشده از home هر دو را با هم لو بدهد.
وقتی --server را نمیدهید، خود اسکریپت مرجع را انتخاب میکند و متغیر DEFAULT_CA در کد به ZeroSSL اشاره دارد. صفحه Change default CA to ZeroSSL در ویکی پروژه این تغییر را از نسخه 3.0 ثبت کرده است. برای اینکه acme.sh از این به بعد سراغ Let’s Encrypt برود، یک فرمان کافی است:
acme.sh --set-default-ca --server letsencrypt
تابع setdefaultca این انتخاب را با نام DEFAULT_ACME_SERVER در account.conf مینویسد. برای گرفتن فقط یک دامنه از مرجع دیگر، --server را در همان فرمان صدور بنویسید.
| نام کوتاه | مرجع | کاربرد |
|---|---|---|
zerossl | ZeroSSL | پیشفرض از نسخه 3.0 |
letsencrypt | Let’s Encrypt | محیط اصلی صدور |
letsencrypt_test | Let’s Encrypt | محیط آزمایشی برای تمرین فرمانها |
کد کلید حساب هر مرجع را زیر ca/ در پوشهای به نام نشانی همان مرجع نگه میدارد؛ عوض کردن مرجع یعنی یک account.key تازه.

در چالش DNS-01 مرجع صدور رکورد TXT را در _acme-challenge.<دامنه> میخواند و پورت 80 سرور نقشی ندارد. طبق مستندات Let’s Encrypt، گواهی Wildcard را هم فقط همین چالش صادر میکند. افزونههای پوشه dnsapi این رکورد را با API سرویس DNS میسازند و بعد پاک میکنند.
صفحه dnsapi در ویکی برای Cloudflare توکنی با دسترسی Zone > DNS > Edit فقط روی zoneهای لازم را توصیه میکند؛ آن را در پروفایل Cloudflare از بخش User API Tokens بسازید. کلید سراسری حساب هم کار میکند، اما نشتش کل حساب را لو میدهد. مقدارهای زیر نمونهاند:
export CF_Token="PLACEHOLDER_CLOUDFLARE_API_TOKEN"
export CF_Account_ID="PLACEHOLDER_CLOUDFLARE_ACCOUNT_ID"
acme.sh --issue -d example.com -d '*.example.com' --dns dns_cf
پس از صدور موفق، acme.sh این متغیرها را ذخیره میکند تا در هر تمدید دوباره بخواند؛ ویکی محل آنها را account.conf میگوید. کد dns_cf.sh استثنایی دارد که در ویکی نیامده است: با CF_Zone_ID، توکن در example.com.conf پوشه دامنه ذخیره میشود.
⚠️ توکن DNS در یک فایل متنی ساده
اسکریپت account.conf را با مجوز 600 میسازد؛ اگر ls -l چیز دیگری نشان داد، برگردانید. خواننده این فایل یا پشتیبانش میتواند رکوردهای zone شما را تغییر دهد و Let’s Encrypt هم نگه داشتن اعتبارنامه کامل API روی وبسرور را پرخطر میداند.
سرویس DNS دامنه اصلی گاهی API ندارد، یا نمیخواهید توکن zone اصلی روی سرور بماند. صفحه DNS alias mode برای هر دو حالت یک CNAME ثابت پیشنهاد میکند، از _acme-challenge دامنه اصلی به دامنه دومی که DNS آن API دارد.
_acme-challenge.example.com IN CNAME _acme-challenge.aliasDomainForValidationOnly.com.
این رکورد در پنل DNS دامنه اصلی ثبت میشود، کنار رکوردهایی که مدیریت رکوردهای DNS دامنه یکییکی معرفی میکند. فرمان زیر روی سرور اجرا میشود و توکن آن باید روی zone دامنه دوم دسترسی Edit داشته باشد:
acme.sh --issue -d example.com --challenge-alias aliasDomainForValidationOnly.com --dns dns_cf
☁️ یک CNAME بهجای توکن zone اصلی
توکن روی سرور فقط به دامنه دوم دسترسی دارد و تمدید بدون دست زدن به DNS اصلی جلو میرود. مستندات Let’s Encrypt هم واگذاری پاسخ چالش با CNAME را ممکن میداند.
برای دامنهای که API ندارد ولی zone آن را خودتان ویرایش میکنید، صفحه dns-manual-mode ویکی حالا اول حالت DNS persist را پیشنهاد میکند. یک رکورد TXT ماندگار در _validation-persist.example.com یک بار ثبت میشود و تمدیدها دیگر ویرایش DNS نمیخواهند. مقدار رکورد را فرمان اول چاپ میکند:
acme.sh --make-dns-persist-value -d example.com
acme.sh --issue -d example.com --dns-persist
مقدار رکورد به حساب ACME شما (accounturi) گره خورده است و با گم شدن account.key بیاثر میشود. برای Wildcard، گزینه --dns-persist-wildcard عبارت policy=wildcard را اضافه میکند. این حالت پیشنویس draft-ietf-acme-dns-persist-01 را پیاده میکند و ویکی میخواهد پیش از استفاده مطمئن شوید مرجع شما آن را پشتیبانی میکند. بدون API و بدون persist، حالت دستی میماند:
acme.sh --issue -d example.com --dns \
--yes-I-know-dns-manual-mode-enough-go-ahead-please
acme.sh --renew -d example.com \
--yes-I-know-dns-manual-mode-enough-go-ahead-please
حالت دستی خودکار تمدید نمیشود، چون TXT در هر تمدید باید دوباره ثبت شود.

فرمان --install-cert کلید و زنجیره کامل را به مسیر دلخواه شما کپی میکند و فرمان reload را برای تمدیدهای بعدی نگه میدارد. اسکریپت پوشه مقصد را نمیسازد و /etc/nginx/ssl در nginx پیشفرض Debian و Ubuntu وجود ندارد؛ در نشست root اول آن را بسازید:
mkdir -p /etc/nginx/ssl && chmod 700 /etc/nginx/ssl
acme.sh --install-cert -d example.com \
--key-file /etc/nginx/ssl/example.com.key \
--fullchain-file /etc/nginx/ssl/example.com.fullchain.pem \
--reloadcmd "service nginx force-reload"
این مسیرها و فرمان reload، با نام Le_ReloadCmd و به شکل base64، در example.com.conf نوشته میشوند. مستندات nginx کلید خصوصی را در فایلی با دسترسی محدود میخواهد که فرایند master بخواند، و گواهی میانی را بعد از گواهی اصلی در همان فایل؛ fullchain همین ترتیب را دارد:
ssl_certificate /etc/nginx/ssl/example.com.fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
⚠️ مسیر nginx نباید به پوشه داخلی اسکریپت برود
اشاره ssl_certificate به fullchain.cer داخل پوشه دامنه امروز کار میکند. README اما این فایلها را داخلی میداند و ممکن است در نسخههای بعدی مسیرشان عوض شود.
خطی را که acme.sh هنگام نصب در cron گذاشته است، با crontab -l ببینید. نمونه README:
0 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null
در README این کار روزانه معرفی شده است، اما کد نسخه 3.1.7 دقیقه و ساعت را تصادفی انتخاب میکند و خط را هر شش ساعت اجرا میکند. تمدید هر ۳۰ روز است، یا زودتر اگر مرجع از راه ARI بخواهد؛ سطر Le_NextRenewTimeStr در خروجی این فرمان موعد بعدی را نشان میدهد:
acme.sh --info -d example.com
وقتی نام سرویس DNS شما در فهرست dnsapi نیست و پنلش فقط ویرایش دستی رکورد را میپذیرد، دو راه تمدید بیدخالت هنوز برایتان باز است.
پیشنهاد اول حالت DNS persist است: یک TXT ماندگار و تمدید بدون ویرایش DNS، به شرط پشتیبانی مرجع از dns-persist-01. بعد حالت alias است، یا انتقال nameserverها به سرویسی با API. حالت دستی آخرین گزینه است، چون خودکار تمدید نمیشود.
بله، افزونه dns_arvan کلید را از متغیر Arvan_Token میخواند و صدور با --dns dns_arvan انجام میشود.
دستکم Zone > DNS > Edit روی zoneهای اعتبارسنجی؛ اگر فیلتر IP دارید، نشانی سرور هم باید مجاز باشد.
بله، --server letsencrypt را به همان فرمان --issue بدهید؛ پیشفرض فقط با --set-default-ca عوض میشود.
به پورت ورودی 80 نیازی نیست؛ Let’s Encrypt آن را فقط برای HTTP-01 شرط میداند. دسترسی خروجی به مرجع و API سرویس DNS اما لازم است.
با --remove -d example.com و برای گواهی ECC با --ecc. فایلهای کلید و گواهی روی دیسک میمانند و پاک کردنشان با خودتان است.
فقط اگر پوشه ~/.acme.sh و کلید حساب را برگردانید. با کلید حساب تازه، رکورد قبلی بیاثر است و مقدار جدید را با --make-dns-persist-value میسازید. برای لغو مجوز هم رکورد را پاک کنید.
جمعبندی؛ حافظه acme.sh در دو فایل پیکربندی ایمیل حساب و مرجع پیشفرض در account.conf مینشینند و توکن DNS هم، مگر برای Cloudflare شناسه zone داده باشید، کنار آنهاست. مسیرهای نصب و فرمان reload در فایل .conf پوشه دامنه ثبت میشوند و cron از روی همین مقدارها تمدید و کپی را تکرار میکند. مجوز 600 را نگه دارید، توکن را محدود کنید یا با CNAME به دامنه دوم ببرید و nginx را فقط به مسیرهای --install-cert وصل کنید.
🚀 شبکه رفت، گواهی ماند؟
کار cron هر شش ساعت سراغ مرجع میرود؛ روزی که نرسد، پایش تاریخ انقضای گواهی و یک راه جایگزین باید از قبل چیده شده باشد.
برای دامنه خودتان کدام افزونه پوشه dnsapi را به کار بردید؟
اسنادی که هر مسیر و فرمان از آنها آمده است