universal zero-downtime migration from legacy WireGuard/Go stack to operator-docker
This commit is contained in:
commit
55cacb0ff4
1 changed files with 385 additions and 0 deletions
385
readme.md
Normal file
385
readme.md
Normal file
|
|
@ -0,0 +1,385 @@
|
|||
# migration-guide
|
||||
|
||||
[🇺🇸English](#english) · [🇪🇸Español](#español)
|
||||
|
||||
---
|
||||
|
||||
# English
|
||||
|
||||
Zero-downtime migration from the legacy WireGuard + Go API stack to operator-docker. Existing VPN clients reconnect automatically — no new configuration required on their end.
|
||||
|
||||
---
|
||||
|
||||
## How it works
|
||||
|
||||
```text
|
||||
Before migration
|
||||
Legacy stack (sp-env-wireguard-ipv6)
|
||||
├── wireguard (linuxserver/wireguard)
|
||||
├── wireguard-api (Go)
|
||||
└── nginx-certbot
|
||||
|
||||
After migration
|
||||
operator-docker
|
||||
├── wireguard (same identity — same public key)
|
||||
├── operator-api (FastAPI)
|
||||
├── marzban
|
||||
├── hysteria2
|
||||
└── mysql
|
||||
```
|
||||
|
||||
The server's WireGuard identity (private key → public key) is preserved.
|
||||
All peers are migrated via the new API. Clients reconnect without any config change.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Legacy stack running (`docker ps` shows `wireguard`)
|
||||
- Domain with DNS pointing to this server
|
||||
- One subdomain per service (`api`, `legacy`, `cdn`, `news` or similar)
|
||||
- SSH key authorized by Simplified Privacy (generated in step 00)
|
||||
- Port 80/TCP reachable externally (required by Let's Encrypt)
|
||||
|
||||
---
|
||||
|
||||
## Migration steps
|
||||
|
||||
| Script | Run as | Description |
|
||||
| :----- | :----- | :---------- |
|
||||
| `00-migrate-root.sh` | root | Backup, shutdown legacy, clean system |
|
||||
| `01-migrate-user.sh` | user | Clone repo, install operator-docker |
|
||||
| `02-migrate-peers.sh` | user | Inject identity, migrate peers |
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
Scripts can be run from any directory:
|
||||
|
||||
```bash
|
||||
sudo bash ~/migration/00-migrate-root.sh
|
||||
bash ~/migration/01-migrate-user.sh
|
||||
bash ~/migration/02-migrate-peers.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 00 — Backup and cleanup
|
||||
|
||||
Run as root:
|
||||
|
||||
```bash
|
||||
sudo bash 00-migrate-root.sh
|
||||
```
|
||||
|
||||
What it does:
|
||||
|
||||
| Action | Detail |
|
||||
| :----- | :----- |
|
||||
| Detects legacy WireGuard container | By image or name |
|
||||
| Backs up private key and public key | Saved to `~/.wg-migration/` |
|
||||
| Backs up all peers | From `wg showconf wg0` |
|
||||
| Backs up Nginx configs and SSL certs | Full `/etc/nginx` and `/etc/letsencrypt` |
|
||||
| Backs up UFW rules | Snapshot only |
|
||||
| Generates SSH key | For repo access |
|
||||
| Shuts down legacy stack | `docker compose down` |
|
||||
| Cleans Nginx | Removes all sites for fresh install |
|
||||
| Verifies port 51820 is free | Fails if still in use |
|
||||
|
||||
At the end, the script prints the SSH public key. Send it to Simplified Privacy before running `01`.
|
||||
|
||||
---
|
||||
|
||||
## Step 01 — Install operator-docker
|
||||
|
||||
Once Simplified Privacy authorizes the SSH key, run as your Linux user:
|
||||
|
||||
```bash
|
||||
bash 01-migrate-user.sh
|
||||
```
|
||||
|
||||
What it does:
|
||||
|
||||
| Action | Detail |
|
||||
| :----- | :----- |
|
||||
| Validates backups exist | Fails early if `00` was not run |
|
||||
| Cleans any failed previous install | Detects corrupt `.env`, incomplete repo |
|
||||
| Clones private repo | Via SSH |
|
||||
| Runs `install.sh` in migration mode | WireGuard is NOT started yet |
|
||||
| Creates `wg0.conf` placeholder | `02` will inject the real private key |
|
||||
| Restores external Nginx configs | e.g. mail server vhosts |
|
||||
|
||||
> `MIGRATION_MODE=1` tells the installer to skip starting WireGuard. The `02` script starts it after injecting the legacy identity.
|
||||
|
||||
---
|
||||
|
||||
## Step 02 — Migrate identity and peers
|
||||
|
||||
```bash
|
||||
bash 02-migrate-peers.sh
|
||||
```
|
||||
|
||||
What it does:
|
||||
|
||||
| Action | Detail |
|
||||
| :----- | :----- |
|
||||
| Stops legacy WireGuard | Via `docker compose stop` in legacy dir |
|
||||
| Injects legacy private key | Into `wg0.conf` and server keyfiles |
|
||||
| Starts new WireGuard | With the legacy identity |
|
||||
| Verifies public key matches | Fails and rolls back if it doesn't |
|
||||
| Verifies API is responding | `GET /wireguard/peers` must return 200 |
|
||||
| Migrates all peers | `POST /wireguard/peers` for each peer |
|
||||
| Destroys private key from disk | `shred` or `rm` |
|
||||
|
||||
---
|
||||
|
||||
## Rollback
|
||||
|
||||
Each script has automatic rollback on failure:
|
||||
|
||||
- `00` — No rollback needed (legacy stays running until `02`)
|
||||
- `01` — Removes `operator-docker`, restores Nginx and certs, restarts legacy
|
||||
- `02` — Stops new WireGuard, restores `wg0.conf`, restarts legacy
|
||||
|
||||
Manual rollback if needed:
|
||||
|
||||
```bash
|
||||
# Restore SSL certificates
|
||||
sudo cp -r ~/.wg-migration/letsencrypt/. /etc/letsencrypt/
|
||||
|
||||
# Restore Nginx
|
||||
sudo cp -r ~/.wg-migration/nginx/sites-available/. /etc/nginx/sites-available/
|
||||
sudo cp -r ~/.wg-migration/nginx/sites-enabled/. /etc/nginx/sites-enabled/
|
||||
sudo systemctl restart nginx
|
||||
|
||||
# Remove operator-docker
|
||||
rm -rf ~/operator-docker
|
||||
|
||||
# Restart legacy stack
|
||||
LEGACY_DIR=$(cat ~/.wg-migration/legacy_compose_dir)
|
||||
cd "$LEGACY_DIR" && docker compose up -d
|
||||
```
|
||||
|
||||
WireGuard identity and peers are never modified in the legacy stack — they remain intact.
|
||||
|
||||
---
|
||||
|
||||
## Backup location
|
||||
|
||||
All backups are stored in `~/.wg-migration/`:
|
||||
|
||||
| File | Content |
|
||||
| :--- | :------ |
|
||||
| `private_key` | WireGuard private key (deleted after `02`) |
|
||||
| `public_key` | WireGuard public key |
|
||||
| `wg0-legacy.conf` | Full peer list |
|
||||
| `wg0-legacy.conf.original` | Unmodified copy |
|
||||
| `legacy.env` | Legacy stack `.env` |
|
||||
| `legacy_compose_dir` | Path to legacy stack |
|
||||
| `nginx/` | Nginx sites and config |
|
||||
| `letsencrypt/` | SSL certificates |
|
||||
| `ufw-snapshot.txt` | UFW rules |
|
||||
| `network_info` | IPv4/IPv6 detection result |
|
||||
| `ssh_pubkey` | SSH public key for repo access |
|
||||
| `migration.log` | Full log of all steps |
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Existing VPN clients reconnect automatically after `02` completes — no config changes needed.
|
||||
- The private key is destroyed from disk after successful peer migration.
|
||||
- If port 80 is blocked externally, SSL certificate issuance will fail at step 01. Contact your VPS provider or use DNS challenge manually.
|
||||
- IPv6 is detected and enabled automatically.
|
||||
|
||||
---
|
||||
|
||||
# Español
|
||||
|
||||
Migración sin tiempo de inactividad desde el stack legacy WireGuard + Go API hacia operator-docker. Los clientes VPN existentes se reconectan automáticamente — no requieren ningún cambio de configuración.
|
||||
|
||||
---
|
||||
|
||||
## Cómo funciona
|
||||
|
||||
```text
|
||||
Antes de la migración
|
||||
Stack legacy (sp-env-wireguard-ipv6)
|
||||
├── wireguard (linuxserver/wireguard)
|
||||
├── wireguard-api (Go)
|
||||
└── nginx-certbot
|
||||
|
||||
Después de la migración
|
||||
operator-docker
|
||||
├── wireguard (misma identidad — misma clave pública)
|
||||
├── operator-api (FastAPI)
|
||||
├── marzban
|
||||
├── hysteria2
|
||||
└── mysql
|
||||
```
|
||||
|
||||
La identidad WireGuard del servidor (clave privada → clave pública) se preserva.
|
||||
Todos los peers se migran vía la nueva API. Los clientes se reconectan sin cambios.
|
||||
|
||||
---
|
||||
|
||||
## Requisitos
|
||||
|
||||
- Stack legacy corriendo (`docker ps` muestra `wireguard`)
|
||||
- Dominio con DNS apuntando a este servidor
|
||||
- Un subdominio por servicio (`api`, `legacy`, `cdn`, `news` o similar)
|
||||
- SSH key autorizada por Simplified Privacy (generada en el paso 00)
|
||||
- Puerto 80/TCP accesible externamente (requerido por Let's Encrypt)
|
||||
|
||||
---
|
||||
|
||||
## Pasos de migración
|
||||
|
||||
| Script | Ejecutar como | Descripción |
|
||||
| :----- | :------------ | :---------- |
|
||||
| `00-migrate-root.sh` | root | Backup, apaga legacy, limpia sistema |
|
||||
| `01-migrate-user.sh` | usuario | Clona repo, instala operator-docker |
|
||||
| `02-migrate-peers.sh` | usuario | Inyecta identidad, migra peers |
|
||||
|
||||
---
|
||||
|
||||
## Uso
|
||||
|
||||
Los scripts se pueden ejecutar desde cualquier directorio:
|
||||
|
||||
```bash
|
||||
sudo bash ~/migration/00-migrate-root.sh
|
||||
bash ~/migration/01-migrate-user.sh
|
||||
bash ~/migration/02-migrate-peers.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Paso 00 — Backup y limpieza
|
||||
|
||||
Ejecutar como root:
|
||||
|
||||
```bash
|
||||
sudo bash 00-migrate-root.sh
|
||||
```
|
||||
|
||||
Acciones:
|
||||
|
||||
| Acción | Detalle |
|
||||
| :----- | :------ |
|
||||
| Detecta el contenedor WireGuard legacy | Por imagen o nombre |
|
||||
| Respalda clave privada y pública | Guardadas en `~/.wg-migration/` |
|
||||
| Respalda todos los peers | Desde `wg showconf wg0` |
|
||||
| Respalda Nginx y certificados SSL | `/etc/nginx` y `/etc/letsencrypt` completos |
|
||||
| Respalda reglas UFW | Solo snapshot |
|
||||
| Genera SSH key | Para acceso al repo |
|
||||
| Apaga el stack legacy | `docker compose down` |
|
||||
| Limpia Nginx | Elimina todos los sites para instalación limpia |
|
||||
| Verifica que el puerto 51820 esté libre | Falla si sigue en uso |
|
||||
|
||||
Al finalizar, el script muestra la clave SSH pública. Envíala a Simplified Privacy antes de ejecutar el `01`.
|
||||
|
||||
---
|
||||
|
||||
## Paso 01 — Instalar operator-docker
|
||||
|
||||
Una vez que Simplified Privacy autorice la SSH key, ejecutar como usuario Linux:
|
||||
|
||||
```bash
|
||||
bash 01-migrate-user.sh
|
||||
```
|
||||
|
||||
Acciones:
|
||||
|
||||
| Acción | Detalle |
|
||||
| :----- | :------ |
|
||||
| Valida que existan los backups | Falla si no se ejecutó el `00` |
|
||||
| Limpia instalación anterior fallida | Detecta `.env` corrupto, repo incompleto |
|
||||
| Clona el repo privado | Vía SSH |
|
||||
| Ejecuta `install.sh` en modo migración | WireGuard NO se inicia todavía |
|
||||
| Crea placeholder de `wg0.conf` | El `02` inyectará la clave privada real |
|
||||
| Restaura configs Nginx externos | Por ejemplo, vhosts del servidor de correo |
|
||||
|
||||
> `MIGRATION_MODE=1` indica al instalador que omita el inicio de WireGuard. El script `02` lo inicia después de inyectar la identidad legacy.
|
||||
|
||||
---
|
||||
|
||||
## Paso 02 — Migrar identidad y peers
|
||||
|
||||
```bash
|
||||
bash 02-migrate-peers.sh
|
||||
```
|
||||
|
||||
Acciones:
|
||||
|
||||
| Acción | Detalle |
|
||||
| :----- | :------ |
|
||||
| Detiene WireGuard legacy | Vía `docker compose stop` en el directorio legacy |
|
||||
| Inyecta la clave privada legacy | En `wg0.conf` y keyfiles del servidor |
|
||||
| Inicia el nuevo WireGuard | Con la identidad legacy |
|
||||
| Verifica que la clave pública coincida | Falla y hace rollback si no coincide |
|
||||
| Verifica que la API responda | `GET /wireguard/peers` debe devolver 200 |
|
||||
| Migra todos los peers | `POST /wireguard/peers` por cada peer |
|
||||
| Destruye la clave privada del disco | `shred` o `rm` |
|
||||
|
||||
---
|
||||
|
||||
## Rollback
|
||||
|
||||
Cada script tiene rollback automático en caso de fallo:
|
||||
|
||||
- `00` — Sin rollback necesario (el legacy sigue corriendo hasta el `02`)
|
||||
- `01` — Elimina `operator-docker`, restaura Nginx y certs, reinicia legacy
|
||||
- `02` — Detiene el nuevo WireGuard, restaura `wg0.conf`, reinicia legacy
|
||||
|
||||
Rollback manual si es necesario:
|
||||
|
||||
```bash
|
||||
# Restaurar certificados SSL
|
||||
sudo cp -r ~/.wg-migration/letsencrypt/. /etc/letsencrypt/
|
||||
|
||||
# Restaurar Nginx
|
||||
sudo cp -r ~/.wg-migration/nginx/sites-available/. /etc/nginx/sites-available/
|
||||
sudo cp -r ~/.wg-migration/nginx/sites-enabled/. /etc/nginx/sites-enabled/
|
||||
sudo systemctl restart nginx
|
||||
|
||||
# Eliminar operator-docker
|
||||
rm -rf ~/operator-docker
|
||||
|
||||
# Reiniciar stack legacy
|
||||
LEGACY_DIR=$(cat ~/.wg-migration/legacy_compose_dir)
|
||||
cd "$LEGACY_DIR" && docker compose up -d
|
||||
```
|
||||
|
||||
La identidad WireGuard y los peers nunca se modifican en el stack legacy — permanecen intactos.
|
||||
|
||||
---
|
||||
|
||||
## Ubicación del backup
|
||||
|
||||
Todos los backups se almacenan en `~/.wg-migration/`:
|
||||
|
||||
| Archivo | Contenido |
|
||||
| :------ | :-------- |
|
||||
| `private_key` | Clave privada WireGuard (eliminada tras el `02`) |
|
||||
| `public_key` | Clave pública WireGuard |
|
||||
| `wg0-legacy.conf` | Lista completa de peers |
|
||||
| `wg0-legacy.conf.original` | Copia sin modificar |
|
||||
| `legacy.env` | `.env` del stack legacy |
|
||||
| `legacy_compose_dir` | Ruta al stack legacy |
|
||||
| `nginx/` | Sites y configuración de Nginx |
|
||||
| `letsencrypt/` | Certificados SSL |
|
||||
| `ufw-snapshot.txt` | Reglas UFW |
|
||||
| `network_info` | Resultado de detección IPv4/IPv6 |
|
||||
| `ssh_pubkey` | Clave SSH pública para acceso al repo |
|
||||
| `migration.log` | Log completo de todos los pasos |
|
||||
|
||||
---
|
||||
|
||||
## Notas
|
||||
|
||||
- Los clientes VPN existentes se reconectan automáticamente al finalizar el `02` — no requieren cambios de configuración.
|
||||
- La clave privada se destruye del disco tras la migración exitosa de peers.
|
||||
- Si el puerto 80 está bloqueado externamente, la emisión de certificados SSL fallará en el paso 01. Contacta a tu proveedor VPS o usa DNS challenge manualmente.
|
||||
- IPv6 se detecta y habilita automáticamente.
|
||||
Loading…
Reference in a new issue