Multi-domain SSL z certbot — SAN, wildcard i auto-renewal
Opublikowano: 10 kwietnia 2026 · Kategoria: Bezpieczeństwo
Jeden serwer, kilka domen, jeden certyfikat SSL. Certyfikaty SAN (Subject Alternative Name) pozwalają zabezpieczyć wiele domen i subdomen jednym certyfikatem Let's Encrypt — bezpłatnie, automatycznie odnawianym. Ten przewodnik pokazuje konfigurację certbot dla różnych scenariuszy: wiele domen, wildcard, kombinacja wildcard + SAN, deploy-hooks i monitoring wygaśnięcia.
Certbot — instalacja i podstawy
# Ubuntu / Debian — instalacja certbot i pluginu Nginx sudo apt update && sudo apt install -y certbot python3-certbot-nginx # CentOS / AlmaLinux / Rocky sudo dnf install -y epel-release sudo dnf install -y certbot python3-certbot-nginx # Alternatywnie przez snap (zalecane przez Let's Encrypt) sudo snap install --classic certbot sudo ln -s /snap/bin/certbot /usr/bin/certbot # Sprawdź wersję certbot --version
Certyfikat SAN — wiele domen jednym poleceniem
Każda flaga -d dodaje kolejną domenę do pola SAN certyfikatu. Certbot automatycznie
konfiguruje Nginx i ustawia przekierowanie HTTP → HTTPS:
# Certyfikat SAN dla głównej domeny i subdomen sudo certbot --nginx \ -d mojadomena.pl \ -d www.mojadomena.pl \ -d sklep.mojadomena.pl \ -d blog.mojadomena.pl \ --email [email protected] \ --agree-tos \ --no-eff-email # Certyfikat SAN dla kilku różnych domen (multi-domain) sudo certbot --nginx \ -d mojadomena.pl \ -d www.mojadomena.pl \ -d innafirma.com \ -d www.innafirma.com \ --email [email protected] \ --agree-tos # Sprawdź wystawione certyfikaty sudo certbot certificates # Output: # Certificate Name: mojadomena.pl # Domains: mojadomena.pl www.mojadomena.pl sklep.mojadomena.pl blog.mojadomena.pl # Expiry Date: 2026-07-10 (VALID: 89 days) # Certificate Path: /etc/letsencrypt/live/mojadomena.pl/fullchain.pem # Private Key Path: /etc/letsencrypt/live/mojadomena.pl/privkey.pem
Wildcard SSL — certyfikat dla wszystkich subdomen
Certyfikat wildcard (*.mojadomena.pl) obejmuje wszystkie subdomeny jednego
poziomu. Wymaga weryfikacji przez DNS-01 (nie HTTP-01) — musisz dodać rekord TXT do DNS.
Wiele providerów DNS ma pluginy certbot do automatyzacji:
# Wildcard + domena główna (oba naraz) # Uwaga: *.mojadomena.pl NIE obejmuje mojadomena.pl — dodaj osobno sudo certbot certonly \ --manual \ --preferred-challenges dns \ -d "*.mojadomena.pl" \ -d "mojadomena.pl" \ --email [email protected] \ --agree-tos # Certbot wyświetli rekord TXT do dodania w DNS: # Please deploy a DNS TXT record under the name: # _acme-challenge.mojadomena.pl # with the following value: # ABC123XYZ... # Dodaj rekord → poczekaj na propagację → naciśnij Enter # Automatyczna weryfikacja DNS przez plugin Cloudflare sudo pip install certbot-dns-cloudflare # Plik z API token Cloudflare (chmod 600!) cat > /etc/letsencrypt/cloudflare.ini <<'EOF' dns_cloudflare_api_token = TWOJ_API_TOKEN_CLOUDFLARE EOF sudo chmod 600 /etc/letsencrypt/cloudflare.ini sudo certbot certonly \ --dns-cloudflare \ --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \ -d "*.mojadomena.pl" \ -d "mojadomena.pl" \ --email [email protected] \ --agree-tos
Kombinacja wildcard + SAN — elastyczna konfiguracja
Wildcard nie obejmuje subdomen wielopoziomowych (*.*.mojadomena.pl nie jest obsługiwane
przez Let's Encrypt). Dla takich przypadków łącz wildcard z konkretnymi wpisami SAN:
# Wildcard + konkretne subdomeny drugiego poziomu sudo certbot certonly \ --dns-cloudflare \ --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \ -d "*.mojadomena.pl" \ -d "mojadomena.pl" \ -d "api.sklep.mojadomena.pl" \ -d "admin.sklep.mojadomena.pl" # Wynikowy certyfikat SAN: # DNS: *.mojadomena.pl # DNS: mojadomena.pl # DNS: api.sklep.mojadomena.pl # DNS: admin.sklep.mojadomena.pl # Użyj certyfikatu w Nginx dla wszystkich wirtualnych hostów # /etc/nginx/snippets/ssl-mojadomena.conf ssl_certificate /etc/letsencrypt/live/mojadomena.pl/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/mojadomena.pl/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/mojadomena.pl/chain.pem; # Include w każdym vhoście # include /etc/nginx/snippets/ssl-mojadomena.conf;
Deploy-hooks — akcje po odnowieniu certyfikatu
# Deploy-hook uruchamiany po KAŻDYM udanym odnowieniu
# Umieść skrypt w /etc/letsencrypt/renewal-hooks/deploy/
cat > /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh <<'EOF'
#!/bin/bash
# Reload Nginx po odnowieniu certyfikatu
systemctl reload nginx
# Jeśli używasz HAProxy lub innych serwerów
# systemctl reload haproxy
# Jeśli certyfikat jest kopiowany do innej lokalizacji
# cp /etc/letsencrypt/live/mojadomena.pl/fullchain.pem /etc/haproxy/certs/
# cat /etc/letsencrypt/live/mojadomena.pl/privkey.pem >> /etc/haproxy/certs/mojadomena.pl.pem
# Powiadomienie Discord
WEBHOOK_URL="https://discord.com/api/webhooks/ID/TOKEN"
curl -s -X POST ${WEBHOOK_URL} \
-H "Content-Type: application/json" \
-d "{\"content\": \"SSL cert renewed for ${RENEWED_DOMAINS} — valid 90 more days\"}"
EOF
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh
# Test odnowienia (dry-run)
sudo certbot renew --dry-run
# Sprawdź status timera (systemd)
systemctl list-timers | grep certbot
systemctl status snap.certbot.renew.timer Monitoring wygaśnięcia certyfikatów
# Skrypt monitorujący wygaśnięcie — /usr/local/bin/ssl-check.sh
#!/bin/bash
DOMAINS="mojadomena.pl innafirma.com sklep.mojadomena.pl"
WARN_DAYS=14
ALERT_WEBHOOK="https://discord.com/api/webhooks/ID/TOKEN"
for DOMAIN in ${DOMAINS}; do
EXPIRY=$(echo | openssl s_client -connect ${DOMAIN}:443 \
-servername ${DOMAIN} 2>/dev/null | \
openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "${EXPIRY}" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (${EXPIRY_EPOCH} - ${NOW_EPOCH}) / 86400 ))
echo "${DOMAIN}: ${DAYS_LEFT} days until expiry (${EXPIRY})"
if [ ${DAYS_LEFT} -lt ${WARN_DAYS} ]; then
curl -s -X POST ${ALERT_WEBHOOK} \
-H "Content-Type: application/json" \
-d "{\"content\": \"ALERT: SSL cert for ${DOMAIN} expires in ${DAYS_LEFT} days!\"}"
fi
done
# Uruchamiaj codziennie przez cron
# 0 9 * * * /usr/local/bin/ssl-check.sh >> /var/log/ssl-check.log 2>&1
# Sprawdź certyfikat lokalnie (z pliku PEM)
openssl x509 -in /etc/letsencrypt/live/mojadomena.pl/cert.pem -noout -dates -text | grep -E "Subject:|DNS:|Not After" Konwersja formatów certyfikatów
| Format | Rozszerzenia | Używany przez | Polecenie konwersji z PEM |
|---|---|---|---|
| PEM | .pem, .crt, .cer, .key | Nginx, Apache, Linux | — (format natywny certbot) |
| DER | .der, .cer | Java, Android, niektóre urządzenia | openssl x509 -in cert.pem -outform DER -out cert.der |
| PKCS#12 / PFX | .pfx, .p12 | Windows IIS, Azure, .NET | openssl pkcs12 -export -out cert.pfx -inkey privkey.pem -in cert.pem -certfile chain.pem |
| PKCS#7 | .p7b, .p7c | Windows, Java keystores | openssl crl2pkcs7 -nocrl -certfile fullchain.pem -out cert.p7b |
# Konwersja PEM → PFX (dla IIS / Azure)
DOMAIN="mojadomena.pl"
LIVE="/etc/letsencrypt/live/${DOMAIN}"
openssl pkcs12 -export \
-out /tmp/${DOMAIN}.pfx \
-inkey ${LIVE}/privkey.pem \
-in ${LIVE}/cert.pem \
-certfile ${LIVE}/chain.pem \
-name "${DOMAIN}"
# Hasło eksportu: wpisz i zapamiętaj
# Konwersja PEM → DER (dla Java / Android)
openssl x509 \
-in ${LIVE}/cert.pem \
-outform DER \
-out /tmp/${DOMAIN}.der
# Weryfikacja certyfikatu PFX
openssl pkcs12 -in /tmp/${DOMAIN}.pfx -noout -info