5.8 KiB
Fix: Honor --days
flag for short-lived certificates
Issue Description
The --days
flag in acme.sh is currently ignored for short-lived certificates (typically 5-50 days validity), causing unexpected renewal behavior that differs from user expectations and normal certificate handling.
Problem Details
Current Behavior:
- For normal certificates (90+ days):
--days 60
works as expected, scheduling renewal ~60 days after issuance - For short-lived certificates (5-50 days):
--days
value is completely ignored, renewal is automatically set to 1 day before expiration regardless of user preference
Root Cause: The renewal calculation logic has two separate code paths:
- When
_notAfter
is NOT set (normal certs): UsesLe_RenewalDays
from--days
flag - When
_notAfter
IS set (short-lived certs): IgnoresLe_RenewalDays
and uses hardcoded fallback logic
This inconsistency exists in the certificate processing logic around lines 5388-5411, where short-lived certificates follow a different renewal calculation that doesn't consider the user's --days
setting.
Impact
This affects users working with:
- Short-lived certificates from CAs like Sectigo InCommon, DigiCert, etc.
- Testing environments with brief certificate lifespans
- High-security environments requiring frequent certificate rotation
- Upcoming industry standards (200 days by 2026, 100 days by 2027, 47 days by 2029)
Example Issue:
./acme.sh --issue -d example.com --valid-to "+20d" --days 7
# Expected: Renew 7 days after issuance
# Actual: Renews 1 day before expiration (19 days after issuance), ignoring --days 7
Solution
Changes Made
1. Enhanced Renewal Logic for Short-lived Certificates
- Modified the
_notAfter
code path to respect user's--days
setting first - Added safety check to prevent renewal after certificate expiration
- Maintained fallback logic for cases where user's setting is invalid
2. Added Parameter Validation
- Early validation for
--days
parameter (1-398 days range) - Aligned maximum with current industry standard (398 days)
- Clear error messages for invalid values
3. Improved User Feedback
- Added informational messages explaining which renewal logic is being used
- Warning messages when fallback logic is triggered
- Clear indication when user's setting conflicts with certificate validity
Technical Implementation
Core Logic Change (lines ~5388-5427):
if [ "$_notAfter" ]; then
Le_CertExpireTime=$(_date2time "$_notAfter")
# NEW: Calculate renewal time based on user's --days setting first
Le_UserRenewTime=$(_math "$Le_CertCreateTime" + "$Le_RenewalDays" \* 24 \* 60 \* 60)
# NEW: Check if user's renewal time is after certificate expiration
if [ "$Le_UserRenewTime" -ge "$Le_CertExpireTime" ]; then
# Fallback to safe defaults with clear messaging
# (existing logic for 1 day/1 hour before expiration)
else
# NEW: Use user's setting when valid
Le_NextRenewTime="$Le_UserRenewTime"
_info "Using user-specified renewal time: $Le_RenewalDays days after issuance"
fi
fi
Validation Enhancement (lines ~5351-5364):
# NEW: Reasonable validation for --days parameter
if [ "$Le_RenewalDays" -gt "398" ]; then
_err "Invalid --days value: $Le_RenewalDays. Maximum supported is 398 days (current industry standard)."
return 1
fi
if [ "$Le_RenewalDays" -eq "0" ]; then
_err "Invalid --days value: $Le_RenewalDays. Minimum supported is 1 day."
return 1
fi
Logic Flow
- Early Validation: Check
--days
parameter for reasonable range (1-398 days) - User Preference: Calculate renewal time based on user's
--days
setting - Safety Check: Verify user's setting doesn't cause renewal after expiration
- Fallback Logic: Use safe defaults only when user's setting is invalid
- Clear Feedback: Inform user which logic was applied and why
Behavior Examples
Valid User Settings (respected):
--days 7 with 20-day cert → Renews 7 days after issuance ✓
--days 15 with 30-day cert → Renews 15 days after issuance ✓
--days 30 with 90-day cert → Renews 30 days after issuance ✓
Invalid User Settings (fallback with warning):
--days 25 with 20-day cert → Warning + fallback to 1 day before expiration
--days 0 → Error: "Minimum supported is 1 day"
--days 500 → Error: "Maximum supported is 398 days"
Backward Compatibility
- Existing behavior preserved when no
--days
flag is used - Normal certificates maintain existing renewal logic (with 1-day safety buffer)
- Short-lived certificates without
--days
continue using current fallback logic - No breaking changes to configuration file format or cron behavior
Future-Proofing
This fix prepares acme.sh for the industry's transition to shorter certificate lifespans:
- Current: 398 days maximum
- March 2026: 200 days maximum
- March 2027: 100 days maximum
- March 2029: 47 days maximum
The validation and logic gracefully handle these transitions while respecting user intent.
Testing
Test Case 1: Short-lived certificate with valid --days
./acme.sh --issue -d test.example.com --valid-to "+20d" --days 7
Result: Le_RenewalDays='7', renewal scheduled exactly 7 days after issuance ✓
Test Case 2: Short-lived certificate with invalid --days
./acme.sh --issue -d test.example.com --valid-to "+15d" --days 20
Result: Warning message + fallback to 1 day before expiration ✓
Test Case 3: Normal certificate behavior unchanged
./acme.sh --issue -d test.example.com --days 60
Result: Renewal scheduled 59 days after issuance (existing behavior) ✓
This fix ensures the --days
flag works consistently across all certificate types while maintaining safety and providing clear user feedback.