You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
112 lines
3.3 KiB
112 lines
3.3 KiB
// orly-certs is a certificate management service that obtains and renews |
|
// wildcard SSL certificates from Let's Encrypt using DNS-01 challenges. |
|
// |
|
// It supports multiple DNS providers via the lego library and stores |
|
// certificates at a conventional file path for web apps to consume. |
|
// |
|
// Configuration is via environment variables: |
|
// - ORLY_CERTS_DOMAIN: Wildcard domain (e.g., "*.myapp.com") |
|
// - ORLY_CERTS_EMAIL: Email for Let's Encrypt account |
|
// - ORLY_CERTS_DNS_PROVIDER: DNS provider name (cloudflare, route53, etc.) |
|
// - ORLY_CERTS_OUTPUT_DIR: Certificate output directory (default: /var/cache/orly-certs) |
|
// |
|
// Provider-specific credentials are set via standard lego environment variables. |
|
// See https://go-acme.github.io/lego/dns/ for documentation. |
|
package main |
|
|
|
import ( |
|
"context" |
|
"fmt" |
|
"os" |
|
"os/signal" |
|
"syscall" |
|
"time" |
|
|
|
"lol.mleku.dev" |
|
"lol.mleku.dev/chk" |
|
"lol.mleku.dev/log" |
|
) |
|
|
|
func main() { |
|
cfg := loadConfig() |
|
lol.SetLogLevel(cfg.LogLevel) |
|
|
|
log.I.F("orly-certs starting") |
|
log.I.F(" domain: %s", cfg.Domain) |
|
log.I.F(" email: %s", cfg.Email) |
|
log.I.F(" dns provider: %s", cfg.DNSProvider) |
|
log.I.F(" output dir: %s", cfg.OutputDir) |
|
log.I.F(" acme server: %s", cfg.ACMEServerURL()) |
|
|
|
ctx, cancel := context.WithCancel(context.Background()) |
|
defer cancel() |
|
|
|
// Set up signal handling |
|
sigs := make(chan os.Signal, 1) |
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) |
|
|
|
go func() { |
|
<-sigs |
|
log.I.F("shutdown signal received") |
|
cancel() |
|
}() |
|
|
|
// Create certificate manager |
|
manager, err := NewCertManager(cfg) |
|
if chk.E(err) { |
|
log.F.F("failed to create certificate manager: %v", err) |
|
} |
|
|
|
// Initial certificate check/obtain |
|
if err := manager.EnsureCertificate(); chk.E(err) { |
|
log.F.F("failed to ensure certificate: %v", err) |
|
} |
|
|
|
// Start renewal loop |
|
log.I.F("starting renewal check loop (interval: %s)", cfg.CheckInterval) |
|
ticker := time.NewTicker(cfg.CheckInterval) |
|
defer ticker.Stop() |
|
|
|
for { |
|
select { |
|
case <-ticker.C: |
|
if err := manager.CheckRenewal(); chk.E(err) { |
|
log.E.F("renewal check failed: %v", err) |
|
} |
|
case <-ctx.Done(): |
|
log.I.F("orly-certs shutting down") |
|
return |
|
} |
|
} |
|
} |
|
|
|
func usage() { |
|
fmt.Fprintf(os.Stderr, `orly-certs - DNS-01 wildcard certificate manager |
|
|
|
Usage: orly-certs [options] |
|
|
|
Environment Variables: |
|
ORLY_CERTS_DOMAIN Wildcard domain (e.g., *.myapp.com) [required] |
|
ORLY_CERTS_EMAIL Email for Let's Encrypt account [required] |
|
ORLY_CERTS_DNS_PROVIDER DNS provider name [required] |
|
ORLY_CERTS_OUTPUT_DIR Certificate output directory [default: /var/cache/orly-certs] |
|
ORLY_CERTS_RENEW_DAYS Renew when expiring within N days [default: 30] |
|
ORLY_CERTS_CHECK_INTERVAL Renewal check interval [default: 12h] |
|
ORLY_CERTS_ACME_SERVER ACME server URL [default: production Let's Encrypt] |
|
ORLY_CERTS_LOG_LEVEL Log level [default: info] |
|
|
|
Supported DNS Providers: |
|
cloudflare, route53, hetzner, digitalocean, google, namecheap, godaddy, |
|
ovh, vultr, linode, gandi, dnsimple, duckdns, azure, alidns, and 80+ more. |
|
|
|
Provider credentials are set via standard lego environment variables. |
|
See https://go-acme.github.io/lego/dns/ for documentation. |
|
|
|
Example: |
|
export CF_API_TOKEN="your-cloudflare-api-token" |
|
export ORLY_CERTS_DOMAIN="*.myapp.com" |
|
export ORLY_CERTS_EMAIL="admin@myapp.com" |
|
export ORLY_CERTS_DNS_PROVIDER="cloudflare" |
|
./orly-certs |
|
`) |
|
}
|
|
|