Instalacja Certbot na Ubuntu 24.04 dla Nginx
Instrukcja instalacji Certbot przez apt lub snap na Ubuntu 24.04. Wyjaśnienie różnic w odnawianiu certyfikatów Let's Encrypt oraz rozwiązanie problemu timeout na porcie 80.
Instalacja Certbot: apt lub snap
W systemie Ubuntu 24.04 pakiet sudo apt install certbot python3-certbot-nginx zapewnia działającego klienta Certbot, który wystawia rzeczywiste, publicznie zaufane certyfikaty Let's Encrypt. Oficjalna dokumentacja Certbot zaleca użycie pakietu snap; różnica jest niewielka – snap śledzi wydania upstream, natomiast pakiet z repozytorium systemowego otrzymuje wersję dostarczoną wraz z wydaniem LTS oraz poprawki bezpieczeństwa.
Należy wybrać jedną metodę. Dwie kopie programu Certbot oznaczają dwa osobne timery odnawiania skierowane do tego samego drzewa /etc/letsencrypt, co prowadzi do problemów z zapomnianą instancją.
Ścieżka apt:
sudo apt update
sudo apt install certbot python3-certbot-nginxInstalacja ta dostarcza /usr/bin/certbot, wtyczkę dla nginx, parę certbot.service + certbot.timer oraz wpis /etc/cron.d/certbot, który w systemie systemd nie wykonuje żadnych operacji.
Ścieżka snap:
sudo apt remove certbot python3-certbot-nginx
sudo snap install core && sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbotPakiet snap zawiera własny timer, snap.certbot.renew.timer. Przed instalacją wersji snap należy usunąć pakiet apt.
Obie metody instalacji działają w ten sam sposób. Certbot 2.x domyślnie używa kluczy ECDSA (P-256); flagę --key-type rsa należy przekazać tylko w przypadku klienta, który nie obsługuje ECDSA. Cały stan przechowywany jest w /etc/letsencrypt: archive/ zawiera właściwe pliki kluczy i certyfikatów, live/ przechowuje dowiązania symboliczne do aktualnych plików, renewal/ zawiera jeden plik konfiguracyjny na certyfikat, a accounts/ przechowuje klucz konta ACME.
Na czym polega wyzwanie HTTP-01 i dlaczego port 80 jest wymagany
Wyzwanie HTTP-01 to mechanizm typu callback. Użytkownik prosi Let's Encrypt o certyfikat dla domeny example.com; urząd certyfikacji sprawdza nazwę w publicznym systemie DNS, nawiązuje połączenie z portem 80 pod znalezionym adresem i żąda pliku http://example.com/.well-known/acme-challenge/<token>. Serwer odpowiada dokładnie taką treścią tokena, jaką Certbot zapisał na dysku. To cały mechanizm. Wynikają z niego trzy konsekwencje, które odpowiadają za większość nieudanych prób wydania certyfikatu.
- Port 80 musi być dostępny z publicznego Internetu, a nie tylko z poziomu sieci lokalnej. Reguła
ufw, grupa zabezpieczeń u dostawcy chmurowego lub firewall w konsoli VPS, który otwiera wyłącznie port 443, uniemożliwia wydanie certyfikatu oraz każde przyszłe odnowienie. - Rekordy DNS muszą już wskazywać na ten serwer. Serwer walidacyjny wykonuje własne zapytanie z zewnątrz; wpisy w
/etc/hostsoraz pamięć podręczna przeglądarki nie mają dla niego znaczenia. - Jeśli opublikowano rekord AAAA, w pierwszej kolejności podejmowana jest próba połączenia przez IPv6. Let's Encrypt ponawia próbę przez IPv4, jeśli połączenie IPv6 całkowicie zawiedzie, ale nieaktualny rekord AAAA wskazujący na hosta, który akceptuje połączenie i serwuje inną treść, powoduje błąd krytyczny.
Przekierowania są dozwolone: walidacja podąża za przekierowaniem HTTP na HTTPS i nie weryfikuje, czy certyfikat po drugiej stronie jest nieobecny, wygasły lub podpisany samodzielnie. Proces nie rozpocznie się jednak nigdzie indziej niż na porcie 80. Certbot nie posiada implementacji TLS-ALPN-01, więc „użycie portu 443” nie stanowi obejścia tego wymogu.
Wybór metody uwierzytelniania: --nginx, --webroot, --standalone
--nginx jest właściwym wyborem domyślnym, gdy Nginx już działa i obsługuje daną domenę. Certbot analizuje konfigurację, wstrzykuje tymczasową lokalizację wyzwania (challenge), przeładowuje Nginx, przeprowadza walidację, a następnie zapisuje dyrektywy TLS w bloku serwera. Brak przestojów.
sudo certbot --nginx -d example.com -d www.example.comWersja skryptowa dla świeżego systemu:
sudo certbot --nginx \
-d example.com -d www.example.com \
--agree-tos -m ops@example.com --no-eff-email \
--redirect --non-interactive--webroot jest odpowiedni, gdy konfiguracja Nginx ma pozostać nienaruszona przez Certbot, na przykład gdy jest generowana z szablonu, przechowywana w git lub wdrażana przez Ansible. Certbot zapisuje jedynie plik wyzwania w katalogu, który jest już serwowany.
sudo certbot certonly --webroot -w /var/www/example.com \
-d example.com -d www.example.com \
--deploy-hook "systemctl reload nginx"--standalone jest właściwy, gdy port 80 jest wolny: w przypadku serwera pocztowego, API komunikującego się wyłącznie przez 443 lub skryptu inicjującego, który uruchamia się przed instalacją Nginx. Certbot samodzielnie wiąże port 80 na kilka sekund. Jeśli Nginx działa, proces zakończy się niepowodzeniem; należy go zatrzymać na czas wykonania polecenia:
sudo certbot certonly --standalone -d mail.example.com \
--pre-hook "systemctl stop nginx" \
--post-hook "systemctl start nginx"Te operacje (hooki) są zapisywane w konfiguracji odnawiania certyfikatu, dzięki czemu to samo zatrzymanie i uruchomienie usług odbywa się automatycznie podczas odnawiania.
Blok serwera działający przed i po wygenerowaniu certyfikatu
Problem jajka i kury: nginx odmawia uruchomienia, gdy ssl_certificate wskazuje na nieistniejący plik, a Certbot nie może przeprowadzić walidacji, gdy nginx nie działa. Najpierw należy uruchomić witrynę na porcie 80.
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
location ^~ /.well-known/acme-challenge/ {
root /var/www/example.com;
default_type "text/plain";
try_files $uri =404;
}
location / {
try_files $uri $uri/ =404;
}
}Uruchom sudo nginx -t && sudo systemctl reload nginx, upewnij się, że curl -I http://example.com/ odpowiada spoza serwera, a następnie wystaw certyfikat. Po tym kroku:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/example.com;
default_type "text/plain";
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}Prefiks ^~ w lokalizacji ACME jest niezbędny: zapobiega on przejęciu żądania weryfikacyjnego przez blok return 301. Utrzymanie tej lokalizacji na porcie 80 gwarantuje, że odnawianie certyfikatów będzie działać nawet po wymuszeniu HTTPS dla reszty witryny.
Oba powyższe bloki serwują pliki z dysku; jeśli nginx pełni rolę frontendu dla aplikacji, location / staje się blokiem proxy_pass, a blok serwera reverse proxy, wiersz po wierszu opisuje nagłówki wymagane przez aplikację, podczas gdy lokalizacja ACME i dyrektywy TLS pozostają bez zmian.
Składnia HTTP/2 zależy od wersji nginx, a mieszanie obu form powoduje błąd przy starcie. Ubuntu 24.04 dostarcza nginx 1.24, który wymaga zapisu w jednej linii, listen 443 ssl http2;. Debian 13 dostarcza nowszą wersję nginx, która wymaga oddzielnej dyrektywy http2 on;. Sprawdź najpierw nginx -v.
Wskaż nginx na live/, nigdy na archive/. Dowiązania symboliczne live/ są aktualizowane przy każdym odnowieniu; użycie ścieżki bezpośredniej do archive/ spowoduje przypisanie do certyfikatu, który wygaśnie bez wiedzy systemu.
Certyfikaty wildcard wymagają DNS-01, a DNS-01 wymaga wtyczki
Certyfikatu typu wildcard (*.example.com) nie można zweryfikować metodą HTTP-01, ponieważ nie istnieje pojedyncza nazwa hosta, z której można pobrać plik. Jedyną drogą jest DNS-01: kontrolę nad domeną potwierdza się poprzez publikację rekordu TXT _acme-challenge.example.com. Aby Certbot mógł wykonać to automatycznie, wymaga danych uwierzytelniających API dostawcy DNS – właśnie do tego służą wtyczki dostawców. Pełny przewodnik po certyfikatach wildcard omawia mechanikę rekordów TXT oraz pułapkę odnawiania w trybie ręcznym; poniżej znajduje się skrócona wersja dla Cloudflare.
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflareW przypadku instalacji przez apt należy użyć sudo apt install python3-certbot-dns-cloudflare. Dane uwierzytelniające umieszcza się w pliku dostępnym tylko dla użytkownika root:
# /root/.secrets/cloudflare.ini
# then: sudo chmod 600 /root/.secrets/cloudflare.ini
dns_cloudflare_api_token = your_scoped_token_hereOgranicz uprawnienia tokena do edycji DNS tylko dla wybranej strefy. Jest to klucz do Twojego DNS; traktuj go odpowiednio.
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
-d example.com -d '*.example.com'Użyj cudzysłowu dla nazwy wildcard, aby powłoka nie zinterpretowała jej jako wzorca (glob). Metoda DNS-01 rozwiązuje również problemy, z którymi HTTP-01 sobie nie radzi: certyfikaty dla hostów bez publicznego portu 80, usług wewnętrznych, maszyn dostępnych wyłącznie przez własny serwer WireGuard VPN na VPS lub paneli administracyjnych na prywatnym interfejsie.
Odnowienie: 90 dni, timer, hook wdrażania
Certyfikaty Let’s Encrypt są ważne przez 90 dni. Certbot dokonuje odnowienia, gdy pozostaje mniej niż 30 dni ważności, co zapewnia 30-dniowe okno, w którym nieudane odnowienie jest możliwą do naprawienia niedogodnością, a nie awarią. Let’s Encrypt nie wysyła już powiadomień e-mail o wygasaniu, nikt nie przypomni o tym fakcie, więc monitorowanie leży po stronie administratora.
Sprawdź timer dostarczony z instalacją:
systemctl list-timers 'certbot*' 'snap.certbot*'
sudo certbot certificatescertbot renew przegląda każdą konfigurację w /etc/letsencrypt/renewal/, pomija wszystko spoza 30-dniowego okna i odnawia resztę, używając dokładnie tych samych flag, co przy pierwszym uruchomieniu. Dlatego pierwsze uruchomienie jest kluczowe: to ono zostaje zapisane.
Samo odnowienie pliku na dysku niczego nie zmienia, nginx kontynuuje serwowanie starego certyfikatu z pamięci, dopóki nie otrzyma polecenia przeładowania. Skonfiguruj hook wdrażania jednorazowo:
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh >/dev/null <<'EOF'
#!/bin/sh
set -e
nginx -t && systemctl reload nginx
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.shKażdy plik wykonywalny w renewal-hooks/deploy/ uruchamia się po każdym udanym odnowieniu. Flaga --deploy-hook wykonuje to samo zadanie dla pojedynczego certyfikatu, przechowując renew_hook = ... w jego konfiguracji odnowienia. certbot --nginx wykonuje przeładowanie automatycznie; konfiguracje --webroot oraz --standalone tego nie robią. Brak hooka jest bezpośrednią przyczyną sytuacji, w której witryna serwuje wygasły certyfikat, podczas gdy certbot certificates radośnie raportuje posiadanie świeżego. Każdy inny proces odczytujący certyfikat przy starcie wymaga tego samego hooka; aplikacja kontenerowa, taka jak instalacja Nextcloud na VPS z Docker, TLS i kopiami zapasowymi, również wymaga tutaj skonfigurowania kroku restartu lub przeładowania.
Testowanie odnawiania w warunkach rzeczywistych
sudo certbot renew --dry-runPolecenie to uruchamia pełną procedurę weryfikacji w środowisku testowym (staging) Let's Encrypt: ta sama ścieżka kodu, ta sama konfiguracja firewalla, ten sam DNS, brak limitów zapytań i brak zapisu na dysku. Jeśli test zakończy się powodzeniem, automatyczne odnawianie za 60 dni również przebiegnie pomyślnie, przy założeniu, że konfiguracja serwera nie ulegnie zmianie.
Testowe uruchomienie (dry run) nie potwierdza poprawnego wywołania skryptu przeładowania (reload hook), ponieważ zachowanie to różni się w zależności od wersji Certbot. Należy przetestować ten element ręcznie: uruchomić skrypt bezpośrednio, potwierdzić, że systemctl reload nginx kończy się powodzeniem, a następnie sprawdzić sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.
Błędy, które faktycznie wystąpią
Could not bind to IPv4 or IPv6., --standalone podczas gdy nginx już zajmuje port 80. Użyj --nginx lub --webroot albo zatrzymaj nginx na czas uruchomienia. Potwierdź proces zajmujący port za pomocą sudo ss -lntp | grep ':80'.
Timeout during connect (likely firewall problem), Let's Encrypt nie może uzyskać dostępu do portu 80. Sprawdź po kolei: sudo ufw status (otwórz go za pomocą sudo ufw allow 'Nginx Full'), następnie własny firewall dostawcy VPS, a na końcu DNS. Przetestuj połączenie z lokalizacji innej niż serwer: curl -sSv http://example.com/.well-known/acme-challenge/test. Nieaktualny rekord AAAA powoduje ten sam komunikat.
unauthorized :: Invalid response from http://example.com/.well-known/acme-challenge/xyz: 404, port 80 jest osiągalny, ale token nie jest serwowany. Żądanie trafiło do innego bloku serwera (sprawdź, który z nich obsługuje default_server) lub katalog wskazany w -w nie jest tym, z którego korzysta nginx. Umieść plik w /var/www/example.com/.well-known/acme-challenge/test i pobierz go z zewnątrz; jeśli otrzymasz błąd 404, problemem nigdy nie był certyfikat.
DNS problem: NXDOMAIN looking up A for example.com, nazwa nie rozwiązuje się publicznie. Przyczyną są nowe rekordy, które nie zostały jeszcze rozpropagowane, lub rekord w strefie, której nie obsługuje Twój rejestrator.
too many certificates already issued for: example.com, limit zapytań, na który trafiają osoby debugujące w pętli. Let's Encrypt ogranicza liczbę duplikatów certyfikatów (ten sam zestaw nazw) do pięciu na tydzień oraz pozwala na 50 nowych certyfikatów na zarejestrowaną domenę tygodniowo; żadne działanie nie odblokuje tego poza upływem czasu. Debuguj przy użyciu środowiska staging z flagą --dry-run.
nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/example.com/fullchain.pem": No such file or directory, nginx jest skonfigurowany dla certyfikatu, który nigdy nie został wydany lub został usunięty za pomocą certbot delete. Zakomentuj blok serwera TLS, uruchom nginx, wydaj certyfikat, a następnie przywróć blok.
open() "/etc/letsencrypt/options-ssl-nginx.conf" failed, ten plik jest dostarczany wraz z pakietem wtyczki nginx. Na systemie certonly bez python3-certbot-nginx należy albo dodać wtyczkę, albo zastąpić linię include własnymi ustawieniami ssl_protocols oraz ssl_ciphers.
Zarządzanie na dużą skalę
Jeden certyfikat może zawierać do 100 nazw, a pojedynczy certbot --nginx -d a.example.com -d b.example.com ... bywa kuszący, dopóki jeden nieaktualny rekord DNS nie spowoduje niepowodzenia walidacji, co unieważni wszystkie pozostałe nazwy w tym certyfikacie. Osobne certyfikaty dla każdej witryny ulegają awarii niezależnie, co jest pożądanym zachowaniem na serwerze obsługującym więcej niż kilka usług. Powyżej kilku witryn, reverse proxy z obsługą ACME staje się opłacalne: Traefik reverse proxy uruchamiający wiele aplikacji w Docker Compose samodzielnie żąda i odnawia certyfikaty, a Certbot przestaje być potrzebny. Wybór odpowiedniego proxy zależy od indywidualnych potrzeb, a porównanie Nginx, Caddy i Traefik sprowadza się głównie do tego, jak dużą część konfiguracji certyfikatów i poszczególnych aplikacji ma przejąć proxy.
Wykonuj kopie zapasowe /etc/letsencrypt w całości, sudo tar -czf letsencrypt-$(date +%F).tar.gz -C /etc letsencrypt, z zachowaniem dowiązań symbolicznych. To drzewo katalogów zawiera accounts/, klucz konta ACME, którego nie można wygenerować ponownie w identycznej postaci. Przeniesienie na nowy VPS sprowadza się do: synchronizacji drzewa za pomocą -a, instalacji Certbot, przekierowania DNS i uruchomienia certbot renew --dry-run przed przełączeniem ruchu.
Po przebudowie serwera lub migracji do nowego wydania LTS, timer odnawiania nie przenosi się automatycznie. Po każdej migracji, przywróceniu migawki lub aktualizacji dystrybucji, uruchom systemctl list-timers 'certbot*' oraz jedno --dry-run. Pominięcie tego kroku sprawia, że witryna przestaje działać 89 dni później, o 3 nad ranem, z powodu certyfikatu, co do którego zakładano, że odnawia się automatycznie.
Wszystkie powyższe kroki zakładają kontrolę nad maszyną z publicznym adresem IP i otwartym portem 80, czyli w praktyce VPS. Mechanizmy te są identyczne dla każdego z nich.
Te same kroki dotyczące certyfikatów mają zastosowanie w przypadku Apache zamiast nginx, a gdy certyfikat publiczny nie jest opcją, certyfikat z podpisem własnym na Ubuntu zabezpiecza usługi wewnętrzne.
FAQ
Czy muszę otwierać port 80, jeśli moja witryna obsługuje tylko HTTPS?
Tak, ze względu na wyzwanie HTTP-01. Let’s Encrypt zawsze rozpoczyna żądanie weryfikacji na porcie 80, a Certbot nie posiada implementacji TLS-ALPN-01. Zapora sieciowa otwierająca tylko port 443 zablokuje zarówno pierwsze wydanie certyfikatu, jak i każde kolejne automatyczne odnowienie. Przekierowanie z portu 80 na HTTPS jest dopuszczalne, weryfikacja podąży za nim. Jedynym sposobem na całkowite pominięcie portu 80 jest użycie metody DNS-01 z odpowiednią wtyczką dostawcy.
apt czy snap, którą wersję Certbot zainstalować dla nginx na Ubuntu 24.04?
Należy użyć apt. sudo apt install certbot python3-certbot-nginx dostarcza Certbot 2.9.0 na Ubuntu 24.04, co jest wersją wystarczającą dla wszystkich kroków w tym poradniku. Pozwala to na otrzymywanie poprawek bezpieczeństwa przez unattended-upgrades i nie wymaga instalacji snapd. Wybierz snap tylko wtedy, gdy potrzebujesz najnowszego wydania natychmiast lub wtyczki DNS dystrybuowanej wyłącznie w tym formacie. Wybierz tylko jedną metodę: dwie instalacje oznaczają dwa timery odnawiania wskazujące na to samo drzewo /etc/letsencrypt, a zapomniana instalacja spowoduje błędy w przyszłości.
Czy Certbot może wydać certyfikat typu wildcard dla nginx?
Tylko poprzez DNS-01. Certyfikat wildcard, taki jak *.example.com, nie posiada pojedynczej nazwy hosta, z której można pobrać plik wyzwania, więc metody --nginx, --webroot oraz --standalone nie mają zastosowania. Zainstaluj wtyczkę dla swojego dostawcy DNS, umieść ograniczony token API w pliku z uprawnieniami tylko dla root i uruchom certbot certonly --dns-cloudflare -d example.com -d '*.example.com', ujmując wildcard w cudzysłów, aby powstrzymać powłokę przed interpretacją znaku wieloznacznego.
Dlaczego nginx nadal serwuje stary certyfikat po udanym odnowieniu?
nginx przechowuje certyfikat w pamięci i nie wykrywa nowego pliku na dysku, dopóki nie zostanie przeładowany. certbot --nginx wykonuje przeładowanie automatycznie, ale polecenia --webroot oraz --standalone tego nie robią. W efekcie odnowienie może zakończyć się sukcesem, podczas gdy przeglądarka nadal widzi wygasający certyfikat. Umieść wykonywalny skrypt w /etc/letsencrypt/renewal-hooks/deploy/, który uruchamia nginx -t && systemctl reload nginx; zostanie on wywołany po każdym udanym odnowieniu.
Czy certbot renew --dry-run gwarantuje, że odnowienie zadziała?
W większości przypadków. Uruchamia ono rzeczywiste wyzwanie w środowisku testowym (staging), z tą samą zaporą, tym samym DNS i tą samą ścieżką kodu, bez limitów zapytań i bez zapisu na dysk. Sukces oznacza, że warstwa sieciowa jest poprawna. Nie potwierdza to jednak w pełni działania skryptu wdrożeniowego (deploy hook). Należy przetestować go oddzielnie: uruchom skrypt ręcznie i sprawdź sudo grep renew_hook /etc/letsencrypt/renewal/example.com.conf.