# Trojan + Let's Encrypt Production Deployment

Complete workflow for deploying Trojan with real SSL certificates for long-term GFW bypass.

## Overview

This guide documents the full production deployment process from initial Trojan setup through Let's Encrypt certificate integration.

## Prerequisites

- Ubuntu 20.04+ server with root access
- Domain name (e.g., hot13399.com) configured with DNS A record
- Port 443 available (not used by other services)
- Port 80 available (for Let's Encrypt ACME challenge)

## Workflow

### Phase 1: DNS Configuration

**Cloudflare DNS Configuration:**

1. Login to Cloudflare Dashboard
2. Select domain (e.g., hot13399.com)
3. Add A record:
   - Type: A
   - Name: @ (or leave blank)
   - IPv4 address: [SERVER_IP]
   - Proxy status: **DNS only** (grey cloud ⚠️ CRITICAL)
   - TTL: Auto

**Verify DNS:**
```bash
dig +short A hot13399.com
# Should return: [SERVER_IP]
```

⚠️ **Critical**: Do not enable Cloudflare proxy (orange cloud). Let's Encrypt requires direct server access on port 80.

### Phase 2: Trojan Installation

```bash
# Install Trojan
curl -fsSL https://raw.githubusercontent.com/trojan-gfw/trojan-quickstart/master/trojan-quickstart.sh -o /tmp/trojan-quickstart.sh
sudo bash /tmp/trojan-quickstart.sh

# Generate self-signed certificate (temporary)
sudo mkdir -p /etc/trojan-cert
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
  -keyout /etc/trojan-cert/trojan.key \
  -out /etc/trojan-cert/trojan.crt \
  -subj "/CN=[SERVER_IP]"
sudo chmod 600 /etc/trojan-cert/trojan.key
sudo chmod 644 /etc/trojan-cert/trojan.crt

# Generate password
openssl rand -base64 32
```

**Trojan Configuration (Self-Signed):**
```json
{
    "run_type": "server",
    "local_addr": "0.0.0.0",
    "local_port": 443,
    "remote_addr": "127.0.0.1",
    "remote_port": 80,
    "password": ["GENERATED_PASSWORD"],
    "ssl": {
        "cert": "/etc/trojan-cert/trojan.crt",
        "key": "/etc/trojan-cert/trojan.key",
        "sni": "[SERVER_IP]"
    }
}
```

### Phase 3: Let's Encrypt Certificate

```bash
# Install Certbot
sudo apt update
sudo apt install -y certbot

# Stop conflicting services
sudo systemctl stop trojan
sudo wg-quick down wg0  # If WireGuard uses port 80

# Request certificate
sudo certbot certonly --standalone \
  -d hot13399.com \
  --non-interactive \
  --agree-tos \
  --email admin@hot13399.com \
  --preferred-challenges http

# Verify certificate
sudo openssl x509 -in /etc/letsencrypt/live/hot13399.com/fullchain.pem -noout -subject -issuer -enddate

# Start fallback web server (required by Trojan)
python3 -m http.server 80 --bind 127.0.0.1 &
```

### Phase 4: Update Trojan Configuration

```json
{
    "run_type": "server",
    "local_addr": "0.0.0.0",
    "local_port": 443,
    "remote_addr": "127.0.0.1",
    "remote_port": 80,
    "password": ["GENERATED_PASSWORD"],
    "ssl": {
        "cert": "/etc/letsencrypt/live/hot13399.com/fullchain.pem",
        "key": "/etc/letsencrypt/live/hot13399.com/privkey.pem",
        "verify_hostname": true
    }
}
```

**Apply changes:**
```bash
# Set correct permissions
sudo chmod 644 /etc/letsencrypt/live/hot13399.com/fullchain.pem
sudo chmod 600 /etc/letsencrypt/live/hot13399.com/privkey.pem

# Restart Trojan
sudo systemctl restart trojan
sudo systemctl status trojan

# Restart WireGuard (if using as backup)
sudo wg-quick up wg0
```

### Phase 5: Auto-Renewal Configuration

```bash
# Configure cron job for automatic renewal
(crontab -l 2>/dev/null | grep -v "certbot renew"; \
 echo "0 3 * * * certbot renew --quiet --post-hook 'systemctl reload trojan'") | crontab -

# Verify cron
crontab -l
```

**Auto-renewal details:**
- Runs daily at 3:00 AM
- Certbot checks if certificate needs renewal (within 30 days of expiry)
- If renewal needed: automatically renews certificate
- Post-hook reloads Trojan service to apply new certificate

### Phase 6: Client Configurations

**Self-Signed Cert (Testing):**
```json
{
    "run_type": "client",
    "remote_addr": "[SERVER_IP]",
    "remote_port": 443,
    "password": ["GENERATED_PASSWORD"],
    "ssl": {
        "verify": false,
        "verify_hostname": false,
        "sni": "[SERVER_IP]"
    }
}
```

**Let's Encrypt Cert (Production):**
```json
{
    "run_type": "client",
    "remote_addr": "hot13399.com",
    "remote_port": 443,
    "password": ["GENERATED_PASSWORD"],
    "ssl": {
        "verify": true,
        "verify_hostname": true,
        "sni": "hot13399.com"
    }
}
```

**iOS (Shadowrocket):**
```
Type: Trojan
Address: hot13399.com
Port: 443
Password: [GENERATED_PASSWORD]
SNI: hot13399.com
Skip Cert Verify: OFF (production), ON (self-signed)
```

**Android:**
```
Protocol: Trojan
Server: hot13399.com
Port: 443
Password: [GENERATED_PASSWORD]
SNI: hot13399.com
Skip Cert Verify: OFF (production), ON (self-signed)
```

**Clash:**
```yaml
proxies:
  - name: "Trojan-Production"
    type: trojan
    server: hot13399.com
    port: 443
    password: [GENERATED_PASSWORD]
    sni: hot13399.com
    skip-cert-verify: false  # Production: false, Self-signed: true
    udp: true
```

## Troubleshooting

### Certificate Issues

**Error: "Could not bind TCP port 80"**
```bash
# Check what's using port 80
sudo ss -tlnp | grep ":80"

# Stop conflicting service
sudo systemctl stop [SERVICE_NAME]

# Or use different challenge method
sudo certbot certonly --standalone -d hot13399.com --preferred-challenges dns
```

**Error: "DNS did not resolve"**
```bash
# Verify DNS propagation
dig +short A hot13399.com

# Check from multiple servers
dig @8.8.8.8 +short A hot13399.com
dig @1.1.1.1 +short A hot13399.com

# If not resolving, wait 5-10 minutes for DNS propagation
```

**Error: "Invalid response from https://acme-v02.api.letsencrypt.org"**
- Check Cloudflare proxy status (must be DNS only)
- Verify firewall allows port 80 (HTTP) and 443 (HTTPS)
- Ensure port 80 not blocked by ISP

### Trojan Issues

**Error: "Handshake failed"**
```bash
# Check Trojan status
sudo systemctl status trojan

# Check port 443
sudo ss -tlnp | grep 443

# Verify certificate
sudo openssl x509 -in /etc/letsencrypt/live/hot13399.com/fullchain.pem -noout -text | grep -A 2 "Validity"

# Test TLS handshake
echo | timeout 3 openssl s_client -connect 127.0.0.1:443 -servername hot13399.com </dev/null 2>/dev/null | grep "Verify return code"
```

**Client: "Certificate verify failed"**
- Verify server certificate is using Let's Encrypt (not self-signed)
- Check client configuration has correct SNI (must match domain)
- Ensure client has correct domain (not IP address)

### Verification Commands

```bash
# Service status
sudo systemctl status trojan
sudo systemctl is-active trojan

# Port listening
sudo ss -tlnp | grep -E "443|80"

# Certificate details
sudo openssl x509 -in /etc/letsencrypt/live/hot13399.com/fullchain.pem -noout \
  -subject -issuer -dates

# TLS handshake test
echo | openssl s_client -connect hot13399.com:443 -servername hot13399.com 2>/dev/null | \
  grep -E "Verify return code|Protocol|Cipher"

# Certificate expiry
sudo certbot certificates

# Test renewal (dry run)
sudo certbot renew --dry-run
```

## Comparison: Self-Signed vs Let's Encrypt

| Feature | Self-Signed | Let's Encrypt |
|----------|--------------|---------------|
| Trust Level | ⚠️ Not trusted | ✅ Globally trusted |
| Client Config | `skip-cert-verify: true` required | `skip-cert-verify: false` |
| Security | ⚠️ Medium | ✅ High |
| Certificate Source | Self-generated | Let's Encrypt (trusted CA) |
| Validity | 10 years (manual renewal) | 90 days (auto-renewal) |
| Detection Risk | ⚠️ Higher (non-standard) | ✅ Lower (standard HTTPS) |
| Setup Time | ~5 minutes | ~15 minutes |

## Production Migration Checklist

- [ ] DNS A record configured (DNS only, not proxied)
- [ ] DNS propagated (verified with `dig`)
- [ ] Certbot installed
- [ ] Let's Encrypt certificate requested successfully
- [ ] Trojan config updated with Let's Encrypt paths
- [ ] Trojan service restarted
- [ ] TLS handshake verified (`Verify return code: 0`)
- [ ] Auto-renewal cron configured
- [ ] Client configs updated (domain, not IP)
- [ ] Client skip-cert-verify disabled
- [ ] Test connection from client device
- [ ] Verify actual traffic flows (e.g., access Google)

## File Locations

| File | Path |
|-------|------|
| Trojan config | `/usr/local/etc/trojan/config.json` |
| Let's Encrypt cert | `/etc/letsencrypt/live/hot13399.com/fullchain.pem` |
| Let's Encrypt key | `/etc/letsencrypt/live/hot13399.com/privkey.pem` |
| Certbot logs | `/var/log/letsencrypt/letsencrypt.log` |
| Cron jobs | `crontab -l` |

## References

- [Trojan GitHub](https://github.com/trojan-gfw/trojan)
- [Let's Encrypt Documentation](https://letsencrypt.org/docs/)
- [Certbot User Guide](https://eff.org/certbot)
- [Trojan Config Reference](https://trojan-gfw.github.io/trojan/config.html)
