SSL/TLS dengan Let's Encrypt — DevOps

Mengapa HTTPS? SSL/TLS mengenkripsi komunikasi antara browser dan server. Tanpa HTTPS, data dikirim dalam plain text dan bisa di-intercept (man-in-the-middle…

HTTPS adalah HTTP yang dienkripsi dengan TLS (dulu namanya SSL): isi request dan response diacak di jalan, jadi password dan cookie gak bisa dibaca orang di WiFi yang sama. Supaya browser mau tersambung lewat HTTPS, server butuh certificate: kartu identitas digital yang ditandatangani Certificate Authority (CA). Let's Encrypt adalah CA gratis yang mengeluarkan certificate secara otomatis, dan certbot adalah program di server yang memintanya dan memasangnya. Bayangin bikin kartu anggota perpustakaan: petugas mengirim surat ke alamat yang kamu tulis, dan kamu harus menunjukkan surat itu untuk membuktikan alamatnya benar milikmu. Kartunya juga cuma berlaku beberapa bulan, jadi harus rajin diperpanjang.

🧪 Tebakan cepat: firewall web-1 cuma membuka port 22 (SSH) dan 443 (HTTPS). Port 80 sengaja ditutup, toh semuanya mau HTTPS. Kamu menjalankan sudo certbot --nginx -d catatan.example.com. Berhasil atau gagal? Tebak dulu, jawabannya ada di bagian "Challenge HTTP-01: membuktikan domainnya milikmu".

Cara kerja TLS di dalamnya (handshake, rantai certificate, HSTS) sudah dibahas di pelajaran HTTPS & TLS di track Security. Di sini fokusnya praktik: memasang certificate di nginx yang sudah jadi reverse proxy catatan-api. Satu catatan: catatan.example.com cuma nama contoh. Let's Encrypt menolak domain example.com karena dicadangkan untuk dokumentasi, jadi di server sungguhan pakai domainmu sendiri.

Meminta certificate dengan certbot

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d catatan.example.com -d www.catatan.example.com

Kalau berhasil, ini yang tercetak (di Lab, ditambah bingkai ajakan donasi di bawahnya):

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for catatan.example.com and www.catatan.example.com

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/catatan.example.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/catatan.example.com/privkey.pem
This certificate expires on 2027-01-07.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

Deploying certificate
Successfully deployed certificate for catatan.example.com to /etc/nginx/sites-enabled/catatan
Successfully deployed certificate for www.catatan.example.com to /etc/nginx/sites-enabled/catatan
Congratulations! You have successfully enabled HTTPS on https://catatan.example.com and https://www.catatan.example.com

Challenge HTTP-01: membuktikan domainnya milikmu

Let's Encrypt gak kenal kamu. Sebelum mengeluarkan certificate, dia minta bukti bahwa kamu menguasai server di balik nama itu, lewat challenge HTTP-01:

  1. certbot meminta certificate untuk dua nama tadi.
  2. Let's Encrypt memberi satu token (teks acak) per nama: "taruh jawaban untuk token ini di http://catatan.example.com/.well-known/acme-challenge/<token>".
  3. certbot menyuruh nginx menjawab path itu untuk sementara.
  4. Server Let's Encrypt, dari beberapa lokasi di dunia, mencari IP catatan.example.com lewat DNS (203.0.113.10), lalu membuka URL tadi lewat port 80.
  5. Jawabannya cocok? Certificate dikeluarkan, certbot menyimpannya dan memasangnya ke nginx.

Port 80 tertutup, jadi langkah 4 gak pernah tersambung, dan certbot gagal. Itu jawaban tebakannya. Pesannya:

Certbot failed to authenticate some domains (authenticator: nginx). The Certificate Authority reported these problems:
  Domain: catatan.example.com
  Type:   connection
  Detail: 203.0.113.10: Fetching http://catatan.example.com/.well-known/acme-challenge/oxq7J0ax22How6lDMneZGC4fAjOGM9lE40rw65FFR2o: Timeout during connect (likely firewall problem)

Buka port 80 dan 443 dengan sudo ufw allow 'Nginx Full', lalu ulangi. Penyebab gagal yang lain: DNS masih menunjuk server lama (pesannya Invalid response from ...: 404), atau nama itu belum punya record A (pesannya DNS problem: NXDOMAIN looking up A for ...).

Yang ditulis certbot ke config nginx

certbot menambahkan baris bertanda # managed by Certbot. Hasilnya setara dengan config tulisan tangan ini:

server {
    listen 80;
    listen [::]:80;
    server_name catatan.example.com www.catatan.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name catatan.example.com www.catatan.example.com;
    ssl_certificate /etc/letsencrypt/live/catatan.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/catatan.example.com/privkey.pem;

    # location /lampiran/ dan location / dari pelajaran Reverse Proxy
}

Header HSTS yang memaksa browser selalu HTTPS dibahas di pelajaran Security Headers.

90 hari, lalu diperpanjang otomatis

Certificate Let's Encrypt berlaku 90 hari, dan Let's Encrypt sudah mengumumkan masa berlakunya bakal makin pendek beberapa tahun ke depan. Memperpanjang dengan tangan pasti suatu hari lupa, jadi certbot dari apt memasang systemd timer certbot.timer yang menjalankan certbot renew dua kali sehari. Certificate baru benar-benar diperpanjang kalau sisa umurnya 30 hari atau kurang:

Hari Kejadian
0 (9 Okt 2026) certificate dikeluarkan, berlaku sampai 7 Jan 2027
1 sampai 59 timer jalan, certbot renew bilang "not due for renewal yet"
60 (8 Des 2026) sisa 30 hari: challenge diulang, certificate baru berlaku sampai 8 Mar 2027, nginx di-reload
90 tanpa renewal browser menolak: NET::ERR_CERT_DATE_INVALID

Uji jalur perpanjangannya sekarang dengan sudo certbot renew --dry-run, jangan menunggu hari ke-60. --dry-run menjalankan seluruh proses ke server uji (staging) Let's Encrypt tanpa mengganti certificate-mu. Kalau di sini gagal, renewal sungguhan juga bakal gagal. Let's Encrypt juga sudah berhenti mengirim email pengingat kedaluwarsa sejak 2025, jadi pastikan timer-nya jalan.

Membaca URL di kode

Di latihan, kamu membedah URL jadi { protocol, isSecure, domain, port }. Port yang gak ditulis berarti port bawaan: 80 untuk http, 443 untuk https.

URL protocol isSecure domain port
https://catatan.example.com "https" true "catatan.example.com" 443
http://catatan.example.com "http" false "catatan.example.com" 80
http://localhost:3000 "http" false "localhost" 3000
function analyzeUrl(url) {
  const u = new URL(url);
  const protocol = u.protocol.replace(":", ""); // "https:" jadi "https"
  const isSecure = protocol === "https";
  const port = u.port ? Number(u.port) : isSecure ? 443 : 80;
  return { protocol, isSecure, domain: u.hostname, port };
}

⚠️ Jebakan yang sering ketemu

TL;DR: HTTPS mengenkripsi HTTP dengan TLS, dan butuh certificate dari CA; Let's Encrypt memberikannya gratis lewat certbot: sudo certbot --nginx -d catatan.example.com -d www.catatan.example.com. Challenge HTTP-01 membuktikan kepemilikan: Let's Encrypt membuka http://nama/.well-known/acme-challenge/<token> lewat port 80, jadi DNS harus mengarah ke server dan port 80 harus terbuka. certbot menyimpan fullchain.pem dan privkey.pem di /etc/letsencrypt/live/<nama>/, menambahkan listen 443 ssl dan redirect 301 ke HTTPS, dan memasang certbot.timer yang memperpanjang certificate 90 hari itu saat sisanya 30 hari. Uji dengan sudo certbot renew --dry-run. Di URL, port bawaan http 80 dan https 443.

Yang akan kamu pelajari