Skip to content

feat(ssl): Add per-certificate renewal and auto-renewal#116

Merged
nfebe merged 3 commits intomainfrom
feat/cert-renewal
Apr 11, 2026
Merged

feat(ssl): Add per-certificate renewal and auto-renewal#116
nfebe merged 3 commits intomainfrom
feat/cert-renewal

Conversation

@nfebe
Copy link
Copy Markdown
Contributor

@nfebe nfebe commented Apr 11, 2026

Certificates can now be renewed individually by domain, or in bulk for all domains belonging to a deployment. Each certificate tracks its own auto-renew preference, and a background worker automatically renews certificates approaching expiry. Certificates listed in the API are linked back to their owning deployment.

nfebe added 3 commits April 9, 2026 14:23
Certificates can now be renewed individually by domain, or in bulk
for all domains belonging to a deployment. Each certificate tracks
its own auto-renew preference, and a background worker automatically
renews certificates approaching expiry. Certificates listed in the
API are linked back to their owning deployment.
@sourceant
Copy link
Copy Markdown

sourceant bot commented Apr 11, 2026

Code Review Summary

This PR introduces individual certificate renewal, auto-renewal capabilities, and better API linking between certificates and deployments. It includes a background worker to handle certificates approaching expiration.

🚀 Key Improvements

  • Added a background Renewer in internal/ssl/autorenew.go to automate maintenance.
  • Implemented per-domain renewal via Certbot in internal/ssl/manager.go.
  • Updated API to allow toggling auto_renew status per certificate.

💡 Minor Suggestions

  • Refactor domain collection logic to reduce duplication.
  • Use a more specific context for the background renewal loop.

Copy link
Copy Markdown

@sourceant sourceant bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review complete. See the overview comment for a summary.

Comment on lines +634 to +645
if s.config.Certbot.Enabled && s.config.Certbot.AutoRenewalEnabled {
s.certRenewer = ssl.NewRenewer(
s.proxyOrchestrator.SSLManager(),
s.config.Certbot.RenewalThresholdDays,
s.config.Certbot.RenewalCheckInterval,
func(domain string) {
if err := s.proxyOrchestrator.NginxManager().Reload(); err != nil {
log.Printf("auto-renew: failed to reload nginx after %s: %v", domain, err)
}
},
)
s.certRenewer.Start(context.Background())
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting a background worker in ListenAndServe (which is typically called in a goroutine or as a blocking call) is okay, but context.Background() here means the renewer isn't bound to the application's shutdown context. If the server is stopped, the loop might persist until the process exits.

Suggested change
if s.config.Certbot.Enabled && s.config.Certbot.AutoRenewalEnabled {
s.certRenewer = ssl.NewRenewer(
s.proxyOrchestrator.SSLManager(),
s.config.Certbot.RenewalThresholdDays,
s.config.Certbot.RenewalCheckInterval,
func(domain string) {
if err := s.proxyOrchestrator.NginxManager().Reload(); err != nil {
log.Printf("auto-renew: failed to reload nginx after %s: %v", domain, err)
}
},
)
s.certRenewer.Start(context.Background())
s.certRenewer.Start(context.Background()) // Consider passing a context that is cancelled on app shutdown

return nil
}

f, err := os.Create(marker)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When disabling auto-renew, the code creates an empty file. It is good practice to ensure the file is closed immediately to release the descriptor, which you are doing, but adding a check to see if it already exists before creating could avoid unnecessary IO.

Suggested change
f, err := os.Create(marker)
if _, err := os.Stat(marker); os.IsNotExist(err) {
f, err := os.Create(marker)
if err != nil {
return fmt.Errorf("failed to disable auto-renew: %w", err)
}
return f.Close()
}
return nil

@nfebe nfebe merged commit 37e0a9c into main Apr 11, 2026
5 checks passed
@nfebe nfebe deleted the feat/cert-renewal branch April 11, 2026 22:22
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.

1 participant