SSL Setup - luckydeva03/desa_karangrejo GitHub Wiki

🔐 SSL Setup

Panduan lengkap setup SSL certificate untuk Website Desa Karangrejo.

🎯 Overview

SSL (Secure Socket Layer) atau TLS (Transport Layer Security) adalah teknologi keamanan yang mengenkripsi koneksi antara browser dan server. Panduan ini menjelaskan cara setup SSL certificate untuk website desa.

🔒 Mengapa SSL Penting?

Keamanan Data

  • Enkripsi data dalam transit
  • Perlindungan terhadap man-in-the-middle attacks
  • Keamanan login dan form submissions
  • Perlindungan informasi sensitif warga

SEO & Trust

  • Google ranking factor - website HTTPS mendapat prioritas
  • Trust indicator - browser menampilkan ikon gembok
  • User confidence - pengunjung lebih percaya
  • Modern standard - semua website seharusnya menggunakan HTTPS

Compliance

  • Standar keamanan pemerintah
  • Best practices untuk website resmi
  • GDPR compliance jika ada data personal
  • PCI DSS jika ada payment processing

📜 Jenis SSL Certificate

1. Domain Validation (DV) - Recommended

✅ Gratis dengan Let's Encrypt
✅ Proses otomatis
✅ Validasi cepat (menit)
✅ Cocok untuk website desa
❌ Tidak menampilkan nama organisasi

2. Organization Validation (OV)

💰 Berbayar ($50-200/tahun)
🔍 Validasi organisasi required
⏰ Proses 1-3 hari
✅ Menampilkan nama organisasi
✅ Lebih terpercaya untuk bisnis

3. Extended Validation (EV)

💰💰 Mahal ($200-1000/tahun)
🔍🔍 Validasi ketat organisasi
⏰ Proses 1-2 minggu
✅ Address bar berwarna hijau
✅ Nama organisasi di browser
❌ Overkill untuk website desa

🆓 Let's Encrypt SSL (Recommended)

Apa itu Let's Encrypt?

  • Certificate Authority gratis dan terbuka
  • Automated certificate issuance dan renewal
  • Widely trusted - didukung semua browser modern
  • 90-day validity dengan auto-renewal
  • Perfect untuk website desa

Prerequisites

# Pastikan domain sudah pointing ke server
dig karangrejo.desa.id +short
# Harus menampilkan IP server Anda

# Pastikan port 80 dan 443 terbuka
sudo ufw status
sudo netstat -tlnp | grep :80
sudo netstat -tlnp | grep :443

🚀 Setup Let's Encrypt dengan Certbot

1. Install Certbot

# Ubuntu/Debian
sudo apt update
sudo apt install -y certbot python3-certbot-nginx

# CentOS/RHEL
sudo yum install -y certbot python3-certbot-nginx

# Verify installation
certbot --version

2. Obtain SSL Certificate

# Method 1: Nginx Plugin (Automatic)
sudo certbot --nginx -d karangrejo.desa.id -d www.karangrejo.desa.id

# Method 2: Webroot (Manual)
sudo certbot certonly --webroot -w /var/www/desa-karangrejo/public -d karangrejo.desa.id -d www.karangrejo.desa.id

# Method 3: Standalone (Temporary server)
sudo systemctl stop nginx
sudo certbot certonly --standalone -d karangrejo.desa.id -d www.karangrejo.desa.id
sudo systemctl start nginx

3. Certificate Installation Process

# Certbot akan meminta informasi:
Enter email address (used for urgent renewal and security notices): [email protected]

Please read the Terms of Service at https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf
(A)gree/(C)ancel: A

Would you be willing to share your email address with the Electronic Frontier Foundation
(Y)es/(N)o: N

# Certbot akan otomatis:
# 1. Verify domain ownership
# 2. Generate certificate
# 3. Update Nginx configuration
# 4. Test HTTPS access

4. Verify SSL Installation

# Check certificate details
sudo certbot certificates

# Test HTTPS access
curl -I https://karangrejo.desa.id

# Check SSL labs rating (online)
# https://www.ssllabs.com/ssltest/analyze.html?d=karangrejo.desa.id

⚙️ Manual Nginx SSL Configuration

1. Basic SSL Configuration

# /etc/nginx/sites-available/karangrejo.desa.id

server {
    listen 80;
    server_name karangrejo.desa.id www.karangrejo.desa.id;
    
    # Redirect HTTP to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name karangrejo.desa.id www.karangrejo.desa.id;
    root /var/www/desa-karangrejo/public;

    # SSL Configuration
    ssl_certificate /etc/letsencrypt/live/karangrejo.desa.id/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/karangrejo.desa.id/privkey.pem;
    
    # SSL Security Settings
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets off;
    
    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/karangrejo.desa.id/chain.pem;
    
    # Security Headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;
    
    # Rest of your configuration...
    index index.php;
    charset utf-8;
    
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

2. Advanced SSL Configuration

# /etc/nginx/conf.d/ssl.conf

# SSL Configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;

# SSL Session Settings
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;

# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

# Security Headers Template
map $scheme $hsts_header {
    https   "max-age=31536000; includeSubDomains; preload";
}

add_header Strict-Transport-Security $hsts_header always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;

🔄 Auto-Renewal Setup

1. Test Renewal

# Dry run (test without actually renewing)
sudo certbot renew --dry-run

# Expected output:
# Congratulations, all renewals succeeded

2. Setup Automatic Renewal

# Option 1: Cron Job
sudo crontab -e

# Add this line (runs twice daily):
0 12 * * * /usr/bin/certbot renew --quiet
# Option 2: Systemd Timer (modern approach)
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer

# Check timer status
sudo systemctl status certbot.timer

3. Renewal with Nginx Reload

# Create renewal hook script
sudo nano /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh
#!/bin/bash
# /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh

# Test nginx configuration
nginx -t

# If test passes, reload nginx
if [ $? -eq 0 ]; then
    systemctl reload nginx
    echo "$(date): Nginx reloaded after SSL renewal" >> /var/log/ssl-renewal.log
else
    echo "$(date): Nginx configuration test failed" >> /var/log/ssl-renewal.log
    exit 1
fi
# Make script executable
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh

🛡️ SSL Security Testing

1. Online SSL Tests

# SSL Labs Test (Comprehensive)
https://www.ssllabs.com/ssltest/analyze.html?d=karangrejo.desa.id

# Target Grade: A or A+

# Mozilla Observatory
https://observatory.mozilla.org/analyze/karangrejo.desa.id

# Target Score: 90+

2. Command Line Testing

# Test SSL certificate
openssl s_client -connect karangrejo.desa.id:443 -servername karangrejo.desa.id

# Check certificate expiry
echo | openssl s_client -connect karangrejo.desa.id:443 2>/dev/null | \
openssl x509 -noout -dates

# Test specific TLS version
openssl s_client -connect karangrejo.desa.id:443 -tls1_2 -servername karangrejo.desa.id

3. Browser Testing

# Check in different browsers:
# - Chrome: Look for green padlock
# - Firefox: Look for green padlock
# - Safari: Look for green padlock
# - Edge: Look for green padlock

# Check certificate details:
# Click on padlock → Certificate → Details

📱 Mobile & App SSL

For Mobile Apps (if any)

# Certificate Pinning (for mobile apps)
# Get certificate fingerprint
openssl x509 -in /etc/letsencrypt/live/karangrejo.desa.id/cert.pem -pubkey -noout | \
openssl rsa -pubin -outform der | \
openssl dgst -sha256 -binary | \
openssl enc -base64

PWA Considerations

// Service Worker HTTPS requirement
// PWA features only work over HTTPS

// manifest.json requires HTTPS
{
  "name": "Website Desa Karangrejo",
  "start_url": "https://karangrejo.desa.id/",
  "display": "standalone"
}

🚨 Troubleshooting SSL Issues

Common Problems & Solutions

1. Mixed Content Warnings

// Problem: HTTP resources on HTTPS page
// Solution: Update all URLs to HTTPS

// Bad
<img src="http://example.com/image.jpg">
<script src="http://example.com/script.js"></script>

// Good
<img src="https://example.com/image.jpg">
<script src="https://example.com/script.js"></script>

// Laravel: Force HTTPS URLs
// In AppServiceProvider
public function boot()
{
    if (config('app.env') === 'production') {
        URL::forceScheme('https');
    }
}

2. Certificate Chain Issues

# Problem: Incomplete certificate chain
# Check chain
openssl s_client -connect karangrejo.desa.id:443 -showcerts

# Solution: Use fullchain.pem instead of cert.pem
ssl_certificate /etc/letsencrypt/live/karangrejo.desa.id/fullchain.pem;

3. Renewal Failures

# Check renewal logs
sudo journalctl -u certbot.timer

# Common issues:
# - Port 80 blocked
# - Webroot path incorrect
# - DNS not pointing to server

# Manual renewal with verbose output
sudo certbot renew --verbose

4. Nginx Configuration Errors

# Test nginx configuration
sudo nginx -t

# Common SSL errors:
# - Wrong certificate path
# - Missing ssl_protocols
# - Conflicting server blocks

# Reload nginx after fixing
sudo systemctl reload nginx

Emergency SSL Recovery

#!/bin/bash
# emergency-ssl-fix.sh

echo "🚨 Emergency SSL Recovery"

# 1. Stop nginx
sudo systemctl stop nginx

# 2. Obtain new certificate
sudo certbot certonly --standalone -d karangrejo.desa.id -d www.karangrejo.desa.id --force-renewal

# 3. Update nginx configuration
sudo nano /etc/nginx/sites-available/karangrejo.desa.id

# 4. Test configuration
sudo nginx -t

# 5. Start nginx
if [ $? -eq 0 ]; then
    sudo systemctl start nginx
    echo "✅ SSL recovery completed"
else
    echo "❌ Nginx configuration error"
    exit 1
fi

📊 SSL Monitoring

1. Certificate Expiry Monitoring

# Check certificate expiry script
#!/bin/bash
# ssl-check.sh

DOMAIN="karangrejo.desa.id"
DAYS_WARNING=30

# Get certificate expiry date
EXPIRY_DATE=$(echo | openssl s_client -connect $DOMAIN:443 -servername $DOMAIN 2>/dev/null | \
    openssl x509 -noout -enddate | cut -d= -f2)

# Convert to epoch time
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
CURRENT_EPOCH=$(date +%s)

# Calculate days until expiry
DAYS_UNTIL_EXPIRY=$(( (EXPIRY_EPOCH - CURRENT_EPOCH) / 86400 ))

if [ $DAYS_UNTIL_EXPIRY -le $DAYS_WARNING ]; then
    echo "⚠️ SSL certificate expires in $DAYS_UNTIL_EXPIRY days"
    # Send alert email
    echo "SSL certificate for $DOMAIN expires in $DAYS_UNTIL_EXPIRY days" | \
    mail -s "SSL Certificate Expiry Warning" [email protected]
else
    echo "✅ SSL certificate valid for $DAYS_UNTIL_EXPIRY days"
fi

2. SSL Health Dashboard

// In AdminController
public function sslStatus()
{
    $domain = config('app.url');
    $parsed = parse_url($domain);
    $hostname = $parsed['host'];
    
    // Get SSL certificate info
    $context = stream_context_create([
        "ssl" => [
            "capture_peer_cert" => true,
        ],
    ]);
    
    $socket = stream_socket_client(
        "ssl://{$hostname}:443",
        $errno,
        $errstr,
        30,
        STREAM_CLIENT_CONNECT,
        $context
    );
    
    if ($socket) {
        $cert = stream_context_get_params($socket)['options']['ssl']['peer_certificate'];
        $certInfo = openssl_x509_parse($cert);
        
        $sslData = [
            'issuer' => $certInfo['issuer']['CN'] ?? 'Unknown',
            'valid_from' => date('Y-m-d H:i:s', $certInfo['validFrom_time_t']),
            'valid_to' => date('Y-m-d H:i:s', $certInfo['validTo_time_t']),
            'days_until_expiry' => ceil(($certInfo['validTo_time_t'] - time()) / 86400),
            'subject' => $certInfo['subject']['CN'] ?? 'Unknown',
        ];
        
        fclose($socket);
    } else {
        $sslData = ['error' => "Unable to connect to {$hostname}:443"];
    }
    
    return view('admin.ssl.status', compact('sslData'));
}

SSL certificate adalah fondasi keamanan website modern 🔐

⚠️ **GitHub.com Fallback** ⚠️