Skip to content

Conversation

@hselomein
Copy link

@hselomein hselomein commented Oct 18, 2025

Fix --days flag to properly calculate renewal time with --valid-to

Summary

When using --valid-to with --days, the renewal time was incorrectly set to 1 day before certificate expiry instead of respecting the user's --days value. This fix ensures renewal is scheduled at the specified number of days after issuance, as intended.

Background & Business Need

Our institution is proactively preparing for the industry-wide transition to shorter certificate lifetimes ahead of the 2029 deadline. We've moved to 47-day certificates now to:

  1. Avoid yearly revisits - Rather than adjusting our processes each time the industry reduces maximum validity periods, we're implementing our target lifetime today
  2. Test automation early - This gives us years to validate our renewal automation works reliably with short-lived certificates
  3. Reduce future disruption - As an educational institution, we want to avoid scrambling to adjust our certificate infrastructure during the academic year when validity limits change

Original Problem

The --days flag in acme.sh was being ignored for short-lived certificates:

  • Normal certificates (90+ days): --days 60 worked as expected
  • Short-lived certificates (5-50 days): --days value was ignored, renewal defaulted to 1 day before expiration

Root Cause: The renewal calculation logic had separate code paths where short-lived certificates (when _notAfter is set) ignored Le_RenewalDays and used hardcoded fallback logic instead of respecting user preferences.

Update: Simplified Approach

After real-world testing, I simplified the fix:

Removed:

  • Parameter validation with artificial 398-day limit (let the CA enforce validity limits)
  • Complex safety checks

Reasoning:

  • Different CAs have different limits (Let's Encrypt: 90 days, Sectigo with profiles: varies)
  • The 398-day limit is a moving target (will be 200, then 100, then 47 by 2029)
  • Better to let the CA reject invalid requests rather than artificially limiting users

The Solution

Modified the renewal calculation logic to match the behavior when --valid-to is NOT used:

  1. Calculate Le_UserRenewTime as Le_CertCreateTime + Le_RenewalDays * 86400
  2. Compare against Le_CertExpireTime to validate renewal time is before expiry
  3. Use user-specified renewal time if valid
  4. Fall back to 1 day before expiry only if certificate would expire before scheduled renewal
# Calculate renewal time based on user's --days setting
Le_UserRenewTime=$(_math "$Le_CertCreateTime" + "$Le_RenewalDays" \* 24 \* 60 \* 60)

# Check if this would be after certificate expiration
if [ "$Le_UserRenewTime" -ge "$Le_CertExpireTime" ]; then
  # User's setting would renew after expiration, use fallback
  Le_NextRenewTime=$(_math $Le_CertExpireTime - 86400)
  _info "Certificate expires in less than $Le_RenewalDays days, setting renewal to 1 day before expiration"
else
  # User's setting is valid, use it
  Le_NextRenewTime="$Le_UserRenewTime"
  _info "Using user-specified renewal time: $Le_RenewalDays days after issuance"
fi

When using --valid-to with --days, the renewal time was incorrectly
set to 1 day before certificate expiry instead of respecting the
user's --days value.

This fix ensures that:
- Renewal is scheduled at 'issuance + days' as intended
- Falls back to 1 day before expiry only if cert expires before renewal
- Matches the behavior when --valid-to is not specified

Example: With --valid-to '+47d' --days 42:
- Before: Renewal at day 46 (1 day before expiry)
- After: Renewal at day 42 (as specified)
@hselomein hselomein force-pushed the fix-days-flag-short-lived-certs branch from 1e785a6 to b115c47 Compare November 12, 2025 20:09
@hselomein
Copy link
Author

Update: Simplified Fix Based on Testing

What Changed

After real-world testing, I discovered the original fix had an issue and have simplified the approach:

Original PR approach:

  • Added parameter validation (1-398 days range)
  • Added complex safety checks
  • Calculated renewal time and then checked if it exceeded expiry

Updated approach (current):

  • Removed artificial 398-day validation limit (let the CA enforce validity limits)
  • Simplified logic to match the existing else branch behavior
  • Calculate renewal as issuance_time + days, only fall back if it would exceed expiry

Bug Found During Testing

The original fix still had a logic issue. When testing with --valid-to "+47d" --days 42:

  • Expected: Renewal on day 42 (5 days before expiry)
  • Actual: Renewal on day 46 (1 day before expiry)

The problem was in how the renewal time was being calculated and compared. The original code was still falling back to "1 day before expiry" even when the user's --days value was valid.

The Corrected Fix

Now the logic is much simpler and matches what happens when --valid-to is NOT used:

# Calculate renewal time based on user's --days setting
Le_UserRenewTime=$(_math "$Le_CertCreateTime" + "$Le_RenewalDays" \* 24 \* 60 \* 60)

# Check if this would be after certificate expiration
if [ "$Le_UserRenewTime" -ge "$Le_CertExpireTime" ]; then
  # User's setting would renew after expiration, use fallback
  Le_NextRenewTime=$(_math $Le_CertExpireTime - 86400)
  _info "Certificate expires in less than $Le_RenewalDays days, setting renewal to 1 day before expiration"
else
  # User's setting is valid, use it
  Le_NextRenewTime="$Le_UserRenewTime"
  _info "Using user-specified renewal time: $Le_RenewalDays days after issuance"
fi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants