Metadata-Version: 2.4
Name: lightsail-wordpress-migrator
Version: 1.0.0
Summary: Automated migration tool for upgrading AWS Lightsail Bitnami WordPress instances to modern Amazon Linux 2023 / Debian Lightsail blueprints.
Author: Jonathan Glass
License: MIT
Project-URL: Homepage, https://github.com/jonathanbglass/lightsail-wordpress-migrator
Project-URL: Repository, https://github.com/jonathanbglass/lightsail-wordpress-migrator.git
Project-URL: Issues, https://github.com/jonathanbglass/lightsail-wordpress-migrator/issues
Keywords: aws,lightsail,wordpress,bitnami,migration,cloudwatch,fail2ban,devops
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: System Administrators
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: System :: Systems Administration
Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: boto3>=1.28.0
Requires-Dist: paramiko>=3.0.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Dynamic: license-file

# 🚀 AWS Lightsail WordPress Migrator

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

An automated migration tool designed to upgrade legacy **Bitnami WordPress** Amazon Lightsail instances to modern **Amazon Linux 2023 / Debian Native Lightsail Blueprints** (`wordpress_ls_1_0`).

---

## 📖 The Problem

AWS Lightsail deprecated its legacy Bitnami WordPress blueprint in favor of a modern native stack running Debian/AL2023 with systemd, native MariaDB, and standard Apache. 

Migrating manually is painful and error-prone because:
1. **Incompatible Directory Layouts**: Legacy Bitnami uses `/opt/bitnami/...` and `ctlscript.sh`, whereas native instances use standard `/var/www/html` and `systemctl`.
2. **Database Collation Incompatibilities**: MySQL 8.0 exports using `utf8mb4_0900_ai_ci` fail on target MariaDB databases without translation.
3. **Apache Permalinks / `.htaccess` 404s**: Bitnami disables `.htaccess` runtime reading by default. When migrating to native Apache, missing `.htaccess` files break all subpages and return `404 Not Found`.
4. **Dynamic Firewall Restrictions**: Managing temporary port 22 access without leaving SSH open permanently to `0.0.0.0/0`.
5. **No IAM Instance Profiles on Lightsail**: Streaming server and application logs to CloudWatch requires provisioning dedicated minimal IAM credentials.

**Lightsail WordPress Migrator automates this entire lifecycle with zero manual database editing.**

---

## ✨ Features

- 🔄 **Full End-to-End Migration**: Automates database dump, `wp-content` archival, target instance provisioning, and data import.
- ⚡ **Direct S3 Transfer**: Uses presigned S3 URLs so high-volume assets transfer directly between Lightsail instances without routing through your local laptop.
- 🛡️ **Zero-Exposure Firewall Automation**: Dynamically opens port 22 for the runner's exact `/32` public IP, and **guarantees closure** via `finally` blocks upon completion or failure.
- 🌐 **Automated Static IP Cutover (`--reassign-static-ip`)**: Automatically discovers static IPs attached to the source, reassigns them to the target upon completion, and updates WordPress URLs for seamless DNS cutover.
- 🔧 **Collation Translation**: Automatically converts MySQL 8 collations (`utf8mb4_0900_ai_ci` / `utf8mb4_0900_as_cs`) to MariaDB-compatible `utf8mb4_unicode_520_ci`.
- 📄 **Automatic `.htaccess` Generation**: Configures standard WordPress rewrite rules and `www-data` ownership to ensure flawless subpage permalink routing.
- 🔒 **Optional Security Hardening (`--harden-security`)**:
  - **Fail2ban**: Deploys jails for SSH brute-force (`sshd`), WordPress login/XML-RPC defense (`apache-wplogin`), and vulnerability probe/traversal blocking (`apache-scanprobe`).
  - **Apache Availability Hardening**: Deploys timeout and keepalive limits to protect small instances from slowloris / connection exhaustion attacks.
- 📊 **Optional CloudWatch Ingestion (`--cloudwatch`)**: Provisions a dedicated least-privilege IAM user and configures `amazon-cloudwatch-agent` to stream Apache access/error logs, PHP-FPM, WordPress debug logs, and Fail2ban logs.
- 🔍 **Interactive Discovery & Dry-Run Mode**: Discovers running Lightsail instances and validates credentials, disk space, static IPs, and SSH keys before making any changes.

---

## 🛠️ Architecture

```
                                  ┌──────────────────────────┐
                                  │      S3 Staging Bucket   │
                                  │  (Presigned Direct Link) │
                                  └─────────────▲────────────┘
                                                │
                          1. Export DB & Assets │ 2. Direct S3 Pull
                                                │
┌───────────────────────────────┐               │              ┌───────────────────────────────┐
│     Source Lightsail Host     │───────────────┴─────────────▶│     Target Lightsail Host     │
│   (Legacy Bitnami / MySQL)    │                              │  (Native AL2023/Debian/Maria) │
│                               │                              │                               │
│ • Bitnami stack detection     │                              │ • Collation normalization     │
│ • Database dump via socket/TCP│                              │ • Apache .htaccess generation │
│ • wp-content tarball          │                              │ • File permissions (www-data) │
│ • Pre-migration snapshot      │                              │ • Static IP cutover           │
│ • Attached Static IP discovery│                              │ • Fail2ban & Security tuning  │
└───────────────────────────────┘                              │ • CloudWatch Logs agent       │
                                                               └───────────────────────────────┘
```

---

## 🚀 Quick Start

### 1. Installation

#### Option A: Clone & Run (Zero install)
```bash
git clone https://github.com/jonathanbglass/lightsail-wordpress-migrator.git
cd lightsail-wordpress-migrator
pip install boto3 paramiko
```

#### Option B: Install via pip
```bash
pip install .
```

---

### 2. Usage

#### Interactive Mode (Recommended)
If you don't pass arguments, the migrator interactively lists your instances, checks for attached static IPs, and prompts for confirmation:
```bash
python migrate.py
# or if installed via pip:
lightsail-wp-migrate
```

#### CLI One-Liner (Non-Interactive with Static IP Reassignment)
```bash
python migrate.py \
  --source "MyWordPressBitnami" \
  --target "MyWordPress-Native" \
  --profile default \
  --region us-east-1 \
  --reassign-static-ip \
  --harden-security \
  --cloudwatch \
  --yes
```

#### Safe Dry-Run (Pre-flight validation)
```bash
python migrate.py \
  --source "MyWordPressBitnami" \
  --target "MyWordPress-Native" \
  --dry-run
```

---

## ⚙️ CLI Options Reference

| Flag | Default | Description |
| :--- | :--- | :--- |
| `--source`, `-s` | `None` | Name of existing Bitnami Lightsail instance (prompted if omitted). |
| `--target`, `-t` | `<source>-2` | Name of target Lightsail instance. |
| `--profile`, `-p` | `$AWS_PROFILE` or `default` | AWS CLI credential profile. |
| `--region`, `-r` | `$AWS_DEFAULT_REGION` or `us-east-1` | AWS region. |
| `--source-key` | Auto-discovered in `~/.ssh` | Path to source SSH private key (`.pem`). |
| `--target-key` | Source key | Path to target SSH private key (`.pem`). |
| `--bundle-id` | `nano_3_0` | Lightsail bundle size (e.g. `nano_3_0`, `micro_3_0`, `small_3_0`). |
| `--s3-bucket` | Auto-derived | S3 bucket for staging transfer artifacts. |
| `--snapshot / --no-snapshot` | `True` | Create a pre-migration snapshot of source. |
| `--reassign-static-ip` | `False` | Automatically detach static IP from source and attach to target upon migration completion. |
| `--static-ip` | `Auto-detect` | Explicit name of Lightsail Static IP to reassign. |
| `--harden-security / --no-harden-security` | `True` | Install and configure Fail2ban and Apache availability protections. |
| `--cloudwatch` | `False` | Provision IAM user and stream logs to CloudWatch. |
| `--dry-run` | `False` | Test credentials, inspect instances, and verify static IPs without making changes. |
| `--yes`, `-y` | `False` | Skip interactive confirmation prompts. |
| `--verbose`, `-v` | `False` | Enable debug logging output. |

---

## 🔒 Security Hardening Details

When `--harden-security` is enabled (default), the following defenses are provisioned on the target:

1. **Fail2ban Jails (`/etc/fail2ban/jail.local`)**:
   - `sshd`: Systemd backend, max 5 retries, 1-hour ban.
   - `apache-wplogin`: Rate limits `wp-login.php` and `xmlrpc.php` brute force (max 8 attempts in 5 mins -> 1-hour ban).
   - `apache-scanprobe`: Blocks automated vulnerability scanners querying `.env`, `.git`, `wp-config.php`, `phpmyadmin`, or directory traversal (5 attempts -> 6-hour ban).
2. **Apache Availability Limits (`/etc/apache2/conf-available/availability-hardening.conf`)**:
   - Caps `Timeout 60`, `KeepAliveTimeout 2`, and `MaxKeepAliveRequests 50` to safeguard low-memory / 2 vCPU Lightsail instances from connection exhaustion.

---

## 📋 Post-Migration Checklist

After migration completes:

1. **Verify Website & Permalinks**: Open `http://<TARGET_IP_OR_STATIC_IP>` and click several subpages to confirm `.htaccess` routing works.
2. **Verify WordPress Admin**: Log into `http://<TARGET_IP_OR_STATIC_IP>/wp-admin` to confirm all plugins, themes, and media uploads are present.
3. **DNS & Static IP**:
   - If `--reassign-static-ip` was used, the static IP is already attached to the new target. Ensure your DNS `A` records point to it.
   - Otherwise, attach a static IP in the AWS Lightsail console and update DNS.
4. **Issue Free SSL via Let's Encrypt**:
   Once your domain resolves to the new instance, run standard Certbot:
   ```bash
   sudo apt-get update
   sudo apt-get install -y certbot python3-certbot-apache
   sudo certbot --apache
   ```
5. **Retire Old Instance**: Once verified, stop or delete the old Bitnami instance.

---

## 🧪 Testing

Run the test suite with Python's built-in `unittest` runner:
```bash
python -m unittest discover -s tests
```

Or with `pytest`:
```bash
pytest tests/
```

---

## 📄 License

This project is licensed under the [MIT License](LICENSE).
