Browse Source

Merge 7d904a4d8c into 08246f7005

pull/6572/merge
hselomein 2 days ago
committed by GitHub
parent
commit
78d8aee40f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 150
      ISSUE_DESCRIPTION.md
  2. 48
      acme.sh

150
ISSUE_DESCRIPTION.md

@ -0,0 +1,150 @@
# 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:
1. **When `_notAfter` is NOT set** (normal certs): Uses `Le_RenewalDays` from `--days` flag
2. **When `_notAfter` IS set** (short-lived certs): Ignores `Le_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:**
```bash
./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):
```bash
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):
```bash
# 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
1. **Early Validation**: Check `--days` parameter for reasonable range (1-398 days)
2. **User Preference**: Calculate renewal time based on user's `--days` setting
3. **Safety Check**: Verify user's setting doesn't cause renewal after expiration
4. **Fallback Logic**: Use safe defaults only when user's setting is invalid
5. **Clear Feedback**: Inform user which logic was applied and why
### Behavior Examples
**Valid User Settings (respected):**
```bash
--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):**
```bash
--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**
```bash
./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**
```bash
./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**
```bash
./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.

48
acme.sh

@ -5351,6 +5351,15 @@ $_authorizations_map"
if [ -z "$Le_RenewalDays" ] || [ "$Le_RenewalDays" -lt "0" ]; then
Le_RenewalDays="$DEFAULT_RENEW"
else
# Add reasonable validation for --days parameter based on current industry standards
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
_savedomainconf "Le_RenewalDays" "$Le_RenewalDays"
fi
@ -5386,26 +5395,41 @@ $_authorizations_map"
_cleardomainconf Le_ForceNewDomainKey
fi
if [ "$_notAfter" ]; then
Le_NextRenewTime=$(_date2time "$_notAfter")
Le_CertExpireTime=$(_date2time "$_notAfter")
Le_NextRenewTimeStr="$_notAfter"
if [ "$_valid_to" ] && ! _startswith "$_valid_to" "+"; then
_info "The domain is set to be valid until: $_valid_to"
_info "It cannot be renewed automatically"
_info "See: $_VALIDITY_WIKI"
Le_NextRenewTime="$Le_CertExpireTime"
else
_now=$(_time)
_debug2 "_now" "$_now"
_lifetime=$(_math $Le_NextRenewTime - $_now)
_debug2 "_lifetime" "$_lifetime"
if [ $_lifetime -gt 86400 ]; then
#if lifetime is logner than one day, it will renew one day before
Le_NextRenewTime=$(_math $Le_NextRenewTime - 86400)
Le_NextRenewTimeStr=$(_time2str "$Le_NextRenewTime")
# Calculate renewal time based on user's --days setting first
Le_UserRenewTime=$(_math "$Le_CertCreateTime" + "$Le_RenewalDays" \* 24 \* 60 \* 60)
# Check if user's renewal time is after certificate expiration
if [ "$Le_UserRenewTime" -ge "$Le_CertExpireTime" ]; then
# User's setting would renew after expiration, use fallback logic
_err "Warning: --days $Le_RenewalDays is greater than certificate validity period."
_info "Certificate expires before your requested renewal time."
_now=$(_time)
_debug2 "_now" "$_now"
_lifetime=$(_math $Le_CertExpireTime - $_now)
_debug2 "_lifetime" "$_lifetime"
if [ $_lifetime -gt 86400 ]; then
#if lifetime is longer than one day, it will renew one day before
Le_NextRenewTime=$(_math $Le_CertExpireTime - 86400)
_info "Setting renewal to 1 day before expiration as fallback"
else
#if lifetime is less than 24 hours, it will renew one hour before
Le_NextRenewTime=$(_math $Le_CertExpireTime - 3600)
_info "Setting renewal to 1 hour before expiration as fallback"
fi
else
#if lifetime is less than 24 hours, it will renew one hour before
Le_NextRenewTime=$(_math $Le_NextRenewTime - 3600)
Le_NextRenewTimeStr=$(_time2str "$Le_NextRenewTime")
# User's setting is valid, use it
Le_NextRenewTime="$Le_UserRenewTime"
_info "Using user-specified renewal time: $Le_RenewalDays days after issuance"
fi
Le_NextRenewTimeStr=$(_time2str "$Le_NextRenewTime")
fi
else
Le_NextRenewTime=$(_math "$Le_CertCreateTime" + "$Le_RenewalDays" \* 24 \* 60 \* 60)

Loading…
Cancel
Save