mirror of
https://github.com/mail-in-a-box/mailinabox.git
synced 2025-05-31 17:40:54 +00:00
Merge fe652a4c4f
into 05c2f3c9a2
This commit is contained in:
commit
0e383189d4
150
tools/migrate.md
Normal file
150
tools/migrate.md
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
# Mail-in-a-Box Migration Script
|
||||||
|
|
||||||
|
**Author:** Ahmad Kouider
|
||||||
|
**Created:** April 3, 2025
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The `migrate-miab.sh` script provides a comprehensive solution for migrating Mail-in-a-Box installations from one server to another. It handles the secure transfer of data, configuration updates, and service management to ensure a smooth migration process.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Secure Data Transfer**: Uses rsync over SSH for efficient and secure file transfer
|
||||||
|
- **Configuration Management**: Automatically updates the Mail-in-a-Box configuration with the new IP address
|
||||||
|
- **Service Control**: Optional stopping and starting of Mail-in-a-Box services during migration
|
||||||
|
- **Error Handling**: Robust error detection and recovery with automatic service restoration
|
||||||
|
- **Partial Transfer Support**: Special handling for partial transfers (common with large installations)
|
||||||
|
- **Dry Run Mode**: Test the migration process without making any changes
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- SSH access to both source and target servers
|
||||||
|
- rsync installed on the source server
|
||||||
|
- Sufficient disk space on the target server
|
||||||
|
- Mail-in-a-Box installed on the source server
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./migrate-miab.sh --username admin --target-host 192.168.1.100 --new-ip 203.0.113.10
|
||||||
|
```
|
||||||
|
|
||||||
|
### All Available Options
|
||||||
|
|
||||||
|
| Option | Description | Default |
|
||||||
|
|--------|-------------|---------|
|
||||||
|
| `--username USERNAME` | SSH username for target server | (required) |
|
||||||
|
| `--target-host HOST` | IP address or domain of target server | (required) |
|
||||||
|
| `--new-ip IP` | Public IP address of the new target machine | (required) |
|
||||||
|
| `--source-path PATH` | Path to Mail-in-a-Box files | /home/user-data |
|
||||||
|
| `--target-path PATH` | Destination path on target server | /home/user-data |
|
||||||
|
| `--config-path PATH` | Path to mailinabox.conf file | /etc/mailinabox.conf |
|
||||||
|
| `--exclude LIST` | Comma-separated list of files/folders to exclude | (none) |
|
||||||
|
| `--ssh-port PORT` | SSH port to use | 22 |
|
||||||
|
| `--stop-services` | Stop Mail-in-a-Box services during transfer | (default: keep running) |
|
||||||
|
| `--ignore-partial` | Continue migration even if some files fail to transfer | (default: prompt user) |
|
||||||
|
| `--dry-run` | Simulate the transfer without making changes | (default: false) |
|
||||||
|
| `--help` | Display help message | |
|
||||||
|
|
||||||
|
### Example Commands
|
||||||
|
|
||||||
|
**Standard Migration:**
|
||||||
|
```bash
|
||||||
|
./migrate-miab.sh --username admin --target-host mail2.example.com --new-ip 203.0.113.10
|
||||||
|
```
|
||||||
|
|
||||||
|
**Migration with Service Stopping:**
|
||||||
|
```bash
|
||||||
|
./migrate-miab.sh --username admin --target-host mail2.example.com --new-ip 203.0.113.10 --stop-services
|
||||||
|
```
|
||||||
|
|
||||||
|
**Excluding Certain Directories:**
|
||||||
|
```bash
|
||||||
|
./migrate-miab.sh --username admin --target-host mail2.example.com --new-ip 203.0.113.10 --exclude 'backup,logs,tmp'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dry Run Test:**
|
||||||
|
```bash
|
||||||
|
./migrate-miab.sh --username admin --target-host mail2.example.com --new-ip 203.0.113.10 --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Migration Process
|
||||||
|
|
||||||
|
1. **Preparation**:
|
||||||
|
- Validates all input parameters
|
||||||
|
- Generates a temporary SSH key pair
|
||||||
|
- Prompts you to add the key to the target server
|
||||||
|
|
||||||
|
2. **Service Management** (if `--stop-services` is used):
|
||||||
|
- Stops all Mail-in-a-Box services on the source server
|
||||||
|
- This helps prevent file corruption during transfer
|
||||||
|
|
||||||
|
3. **Data Transfer**:
|
||||||
|
- Uses rsync to efficiently transfer all Mail-in-a-Box data
|
||||||
|
- Handles partial transfers with interactive prompting
|
||||||
|
|
||||||
|
4. **Configuration Update**:
|
||||||
|
- Updates the mailinabox.conf file with the new IP address
|
||||||
|
- Transfers the updated configuration to the target server
|
||||||
|
|
||||||
|
5. **Service Restoration**:
|
||||||
|
- Restarts any services that were stopped (if applicable)
|
||||||
|
- Ensures the source server returns to its original state
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
### Partial Transfers
|
||||||
|
|
||||||
|
When transferring large Mail-in-a-Box installations, you may encounter "partial transfer" errors (rsync code 23). This typically happens because:
|
||||||
|
|
||||||
|
- Some files are actively being used (open files)
|
||||||
|
- Permission issues prevent access to certain files
|
||||||
|
- Special system files cannot be transferred normally
|
||||||
|
|
||||||
|
The script provides two ways to handle this:
|
||||||
|
1. **Interactive prompt**: Choose whether to continue or abort
|
||||||
|
2. **Automatic continuation**: Use the `--ignore-partial` flag
|
||||||
|
|
||||||
|
### Post-Migration Steps
|
||||||
|
|
||||||
|
After the migration completes successfully, you **MUST**:
|
||||||
|
|
||||||
|
1. **Reinstall Mail-in-a-Box** on the target server:
|
||||||
|
```bash
|
||||||
|
curl -s https://mailinabox.email/setup.sh | sudo bash
|
||||||
|
```
|
||||||
|
This ensures all configurations are properly updated while preserving your transferred data.
|
||||||
|
|
||||||
|
2. **Update DNS records** to point to the new server IP address
|
||||||
|
|
||||||
|
3. **Test mail functionality** on the new server
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
1. **SSH Connection Failures**:
|
||||||
|
- Ensure the public key was added to ~/.ssh/authorized_keys on the target server
|
||||||
|
- Verify the SSH port is correct (default: 22)
|
||||||
|
- Check for firewall rules blocking SSH connections
|
||||||
|
|
||||||
|
2. **File Transfer Errors**:
|
||||||
|
- Consider using the `--stop-services` option to prevent open file issues
|
||||||
|
- Use `--exclude` to skip problematic directories
|
||||||
|
- For partial transfers, use `--ignore-partial` if you're confident in the data integrity
|
||||||
|
|
||||||
|
3. **Configuration File Not Found**:
|
||||||
|
- If your Mail-in-a-Box has a non-standard configuration path, use the `--config-path` option
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
- The script generates a temporary SSH key pair for the migration
|
||||||
|
- Keys are automatically deleted after the migration completes
|
||||||
|
- No passwords are stored or transmitted
|
||||||
|
- All data is transferred over encrypted SSH connections
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This script is provided as-is with no warranty. Use at your own risk.
|
522
tools/migrate.sh
Normal file
522
tools/migrate.sh
Normal file
@ -0,0 +1,522 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# migrate-miab.sh - Mail-in-a-Box Migration Script
|
||||||
|
# Author: Ahmad Kouider
|
||||||
|
# Created: April 3, 2025
|
||||||
|
# Description: Transfers Mail-in-a-Box data from one server to another
|
||||||
|
#
|
||||||
|
# Usage: ./migrate-miab.sh --username USER --target-host HOST --new-ip IP [OPTIONS]
|
||||||
|
#
|
||||||
|
# Required:
|
||||||
|
# --username USER SSH username for target server
|
||||||
|
# --target-host HOST IP address or domain of target server
|
||||||
|
# --new-ip IP Public IP address of the new target machine
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# --source-path PATH Path to Mail-in-a-Box files (default: /home/user-data)
|
||||||
|
# --target-path PATH Destination path on target server (default: /home/user-data)
|
||||||
|
# --config-path PATH Path to mailinabox.conf file (default: /etc/mailinabox.conf)
|
||||||
|
# --exclude LIST Comma-separated list of files/folders to exclude
|
||||||
|
# --ssh-port PORT SSH port to use (default: 22)
|
||||||
|
# --stop-services Stop Mail-in-a-Box services during transfer (default: keep running)
|
||||||
|
# --ignore-partial Continue migration even if some files fail to transfer (rsync code 23)
|
||||||
|
# --dry-run Simulate the transfer without making changes
|
||||||
|
# --help Display help message
|
||||||
|
#
|
||||||
|
# Example: ./migrate-miab.sh --username admin --target-host 192.168.1.100 --new-ip 203.0.113.10
|
||||||
|
#
|
||||||
|
# Note: After migration, you MUST reinstall Mail-in-a-Box on the target server:
|
||||||
|
# curl -s https://mailinabox.email/setup.sh | sudo bash
|
||||||
|
|
||||||
|
# Default values
|
||||||
|
SOURCE_PATH="/home/user-data"
|
||||||
|
TARGET_PATH="/home/user-data"
|
||||||
|
EXCLUDE=""
|
||||||
|
DRY_RUN=false
|
||||||
|
STOP_SERVICES=false
|
||||||
|
SERVICES_STOPPED=false
|
||||||
|
IGNORE_PARTIAL=false
|
||||||
|
SSH_KEY_PATH="/tmp/miab_migration_key"
|
||||||
|
SSH_PORT=22
|
||||||
|
CONFIG_PATH="/etc/mailinabox.conf"
|
||||||
|
|
||||||
|
# Mail-in-a-Box services
|
||||||
|
MIAB_SERVICES=(
|
||||||
|
"nginx"
|
||||||
|
"dovecot"
|
||||||
|
"postfix"
|
||||||
|
"opendkim"
|
||||||
|
"spamassassin"
|
||||||
|
"postgrey"
|
||||||
|
"clamav-daemon"
|
||||||
|
"clamav-freshclam"
|
||||||
|
"fail2ban"
|
||||||
|
"nsd"
|
||||||
|
"php8.1-fpm"
|
||||||
|
"redis-server"
|
||||||
|
"memcached"
|
||||||
|
"rspamd"
|
||||||
|
"unattended-upgrades"
|
||||||
|
"mailinabox-daemon"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Text formatting
|
||||||
|
BOLD="\033[1m"
|
||||||
|
GREEN="\033[0;32m"
|
||||||
|
YELLOW="\033[0;33m"
|
||||||
|
RED="\033[0;31m"
|
||||||
|
NC="\033[0m" # No Color
|
||||||
|
|
||||||
|
# Function to display usage information
|
||||||
|
function show_usage {
|
||||||
|
echo -e "${BOLD}Mail-in-a-Box Migration Script${NC}"
|
||||||
|
echo -e "Transfers Mail-in-a-Box data from one server to another"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Usage:${NC}"
|
||||||
|
echo " $0 [options]"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Options:${NC}"
|
||||||
|
echo " --username USERNAME SSH username for target server (required)"
|
||||||
|
echo " --target-host HOST IP address or domain of target server (required)"
|
||||||
|
echo " --source-path PATH Path to Mail-in-a-Box files (default: /home/user-data)"
|
||||||
|
echo " --target-path PATH Destination path on target server (default: /home/user-data)"
|
||||||
|
echo " --config-path PATH Path to mailinabox.conf file (default: /etc/mailinabox.conf)"
|
||||||
|
echo " --exclude LIST Comma-separated list of files/folders to exclude"
|
||||||
|
echo " --new-ip IP Public IP address of the new target machine (required)"
|
||||||
|
echo " --ssh-port PORT SSH port to use (default: 22)"
|
||||||
|
echo " --stop-services Stop Mail-in-a-Box services during transfer (default: keep running)"
|
||||||
|
echo " --ignore-partial Continue migration even if some files fail to transfer (rsync code 23)"
|
||||||
|
echo " --dry-run Simulate the transfer without making changes"
|
||||||
|
echo " --help Display this help message"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Example:${NC}"
|
||||||
|
echo " $0 --username admin --target-host 192.168.1.100 --new-ip 203.0.113.10 --exclude 'backup,logs'"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check if a command exists
|
||||||
|
function command_exists {
|
||||||
|
command -v "$1" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check required dependencies
|
||||||
|
function check_dependencies {
|
||||||
|
local missing_deps=false
|
||||||
|
|
||||||
|
if ! command_exists rsync; then
|
||||||
|
echo -e "${RED}Error: rsync is not installed. Please install it and try again.${NC}"
|
||||||
|
missing_deps=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command_exists ssh; then
|
||||||
|
echo -e "${RED}Error: ssh is not installed. Please install it and try again.${NC}"
|
||||||
|
missing_deps=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command_exists ssh-keygen; then
|
||||||
|
echo -e "${RED}Error: ssh-keygen is not installed. Please install it and try again.${NC}"
|
||||||
|
missing_deps=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$missing_deps" = true ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to validate IP address
|
||||||
|
function validate_ip {
|
||||||
|
local ip=$1
|
||||||
|
local stat=1
|
||||||
|
|
||||||
|
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
||||||
|
IFS='.' read -r -a ip_segments <<< "$ip"
|
||||||
|
[[ ${ip_segments[0]} -le 255 && ${ip_segments[1]} -le 255 && \
|
||||||
|
${ip_segments[2]} -le 255 && ${ip_segments[3]} -le 255 ]]
|
||||||
|
stat=$?
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $stat
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to generate SSH key pair
|
||||||
|
function generate_ssh_key {
|
||||||
|
echo -e "\n${YELLOW}Generating temporary SSH key pair for migration...${NC}"
|
||||||
|
|
||||||
|
# Remove old keys if they exist
|
||||||
|
rm -f "${SSH_KEY_PATH}" "${SSH_KEY_PATH}.pub"
|
||||||
|
|
||||||
|
# Generate new key pair without passphrase
|
||||||
|
ssh-keygen -t ed25519 -f "${SSH_KEY_PATH}" -N "" -C "miab_migration_$(date +%Y%m%d)" >/dev/null 2>&1
|
||||||
|
|
||||||
|
echo -e "\n${GREEN}SSH key pair generated successfully.${NC}"
|
||||||
|
echo -e "\n${BOLD}Please add the following public key to ~/.ssh/authorized_keys on the target server:${NC}"
|
||||||
|
echo -e "\n${YELLOW}$(cat "${SSH_KEY_PATH}.pub")${NC}\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to test SSH connection
|
||||||
|
function test_ssh_connection {
|
||||||
|
echo -e "${YELLOW}Testing SSH connection to $USERNAME@$TARGET_HOST...${NC}"
|
||||||
|
|
||||||
|
if ssh -o BatchMode=yes -o ConnectTimeout=5 -p "$SSH_PORT" -i "${SSH_KEY_PATH}" "$USERNAME@$TARGET_HOST" exit >/dev/null 2>&1; then
|
||||||
|
echo -e "${GREEN}SSH connection successful.${NC}"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e "${RED}SSH connection failed. Please ensure the public key is added to authorized_keys on the target server.${NC}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to sync files using rsync
|
||||||
|
function sync_files {
|
||||||
|
local rsync_options="-avz --progress"
|
||||||
|
local exclude_options=""
|
||||||
|
|
||||||
|
# Add trailing slash to source path if not present
|
||||||
|
[[ "$SOURCE_PATH" != */ ]] && SOURCE_PATH="${SOURCE_PATH}/"
|
||||||
|
|
||||||
|
# Process exclude list
|
||||||
|
if [ -n "$EXCLUDE" ]; then
|
||||||
|
IFS=',' read -ra EXCLUDE_ARRAY <<< "$EXCLUDE"
|
||||||
|
for item in "${EXCLUDE_ARRAY[@]}"; do
|
||||||
|
exclude_options="$exclude_options --exclude='$item'"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add dry-run option if specified
|
||||||
|
if [ "$DRY_RUN" = true ]; then
|
||||||
|
rsync_options="$rsync_options --dry-run"
|
||||||
|
echo -e "\n${YELLOW}Running in DRY RUN mode. No files will be transferred.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "\n${YELLOW}Starting file synchronization...${NC}"
|
||||||
|
echo -e "From: $SOURCE_PATH"
|
||||||
|
echo -e "To: $USERNAME@$TARGET_HOST:$TARGET_PATH"
|
||||||
|
|
||||||
|
# Execute rsync command
|
||||||
|
eval rsync $rsync_options $exclude_options -e "ssh -p $SSH_PORT -i ${SSH_KEY_PATH}" "$SOURCE_PATH" "$USERNAME@$TARGET_HOST:$TARGET_PATH"
|
||||||
|
local rsync_result=$?
|
||||||
|
|
||||||
|
if [ $rsync_result -eq 0 ]; then
|
||||||
|
echo -e "\n${GREEN}File synchronization completed successfully.${NC}"
|
||||||
|
return 0
|
||||||
|
elif [ $rsync_result -eq 23 ]; then
|
||||||
|
# Error code 23 means partial transfer - some files were not transferred
|
||||||
|
# but the overall transfer was mostly successful
|
||||||
|
echo -e "\n${YELLOW}Warning: Partial file synchronization (code 23).${NC}"
|
||||||
|
echo -e "${YELLOW}Some files or attributes were not transferred, but most files were synchronized successfully.${NC}"
|
||||||
|
echo -e "${YELLOW}This is often due to permission issues, open files, or special files that cannot be transferred.${NC}"
|
||||||
|
echo -e "${YELLOW}Would you like to continue with the migration? (y/n)${NC}"
|
||||||
|
read -p "Continue? (y/n): " -r
|
||||||
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
echo -e "${GREEN}Continuing with migration despite partial transfer...${NC}"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e "${RED}Migration aborted due to partial transfer.${NC}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "\n${RED}File synchronization failed with exit code $rsync_result.${NC}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to update mailinabox.conf file
|
||||||
|
function update_mailinabox_conf {
|
||||||
|
local temp_conf="/tmp/mailinabox.conf.tmp"
|
||||||
|
local source_conf="${CONFIG_PATH}"
|
||||||
|
|
||||||
|
echo -e "\n${YELLOW}Updating mailinabox.conf with new IP address...${NC}"
|
||||||
|
|
||||||
|
# Check if mailinabox.conf exists
|
||||||
|
if [ ! -f "$source_conf" ]; then
|
||||||
|
echo -e "${RED}Error: mailinabox.conf not found at $source_conf${NC}"
|
||||||
|
echo -e "${YELLOW}You can specify the correct path using --config-path option${NC}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create a temporary copy of the config file
|
||||||
|
if ! cp "$source_conf" "$temp_conf"; then
|
||||||
|
echo -e "${RED}Error: Failed to create temporary copy of mailinabox.conf${NC}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update the PUBLIC_IP value
|
||||||
|
if ! sed -i "s/^PUBLIC_IP=.*/PUBLIC_IP=$NEW_IP/" "$temp_conf"; then
|
||||||
|
echo -e "${RED}Error: Failed to update PUBLIC_IP in mailinabox.conf${NC}"
|
||||||
|
rm -f "$temp_conf"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}mailinabox.conf updated locally.${NC}"
|
||||||
|
|
||||||
|
# Transfer the updated config file to the target server
|
||||||
|
if [ "$DRY_RUN" = false ]; then
|
||||||
|
if ! scp -P "$SSH_PORT" -i "${SSH_KEY_PATH}" "$temp_conf" "$USERNAME@$TARGET_HOST:$CONFIG_PATH"; then
|
||||||
|
echo -e "${RED}Failed to transfer updated mailinabox.conf to target server.${NC}"
|
||||||
|
rm -f "$temp_conf"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}Updated mailinabox.conf transferred to target server.${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}DRY RUN: Would transfer updated mailinabox.conf to target server.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up temporary file
|
||||||
|
rm -f "$temp_conf"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to stop Mail-in-a-Box services
|
||||||
|
function stop_miab_services {
|
||||||
|
echo -e "\n${YELLOW}Stopping Mail-in-a-Box services on source server...${NC}"
|
||||||
|
local failed_services=""
|
||||||
|
|
||||||
|
for service in "${MIAB_SERVICES[@]}"; do
|
||||||
|
echo -e "Stopping $service..."
|
||||||
|
if ! systemctl stop "$service" 2>/dev/null; then
|
||||||
|
echo -e "${YELLOW}Warning: Failed to stop $service${NC}"
|
||||||
|
failed_services="$failed_services $service"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$failed_services" ]; then
|
||||||
|
echo -e "${GREEN}All Mail-in-a-Box services stopped successfully.${NC}"
|
||||||
|
SERVICES_STOPPED=true
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Warning: Failed to stop some services:${NC}$failed_services"
|
||||||
|
echo -e "${YELLOW}Continuing anyway...${NC}"
|
||||||
|
SERVICES_STOPPED=true
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to start Mail-in-a-Box services
|
||||||
|
function start_miab_services {
|
||||||
|
echo -e "\n${YELLOW}Starting Mail-in-a-Box services on source server...${NC}"
|
||||||
|
local failed_services=""
|
||||||
|
|
||||||
|
for service in "${MIAB_SERVICES[@]}"; do
|
||||||
|
echo -e "Starting $service..."
|
||||||
|
if ! systemctl start "$service" 2>/dev/null; then
|
||||||
|
echo -e "${YELLOW}Warning: Failed to start $service${NC}"
|
||||||
|
failed_services="$failed_services $service"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$failed_services" ]; then
|
||||||
|
echo -e "${GREEN}All Mail-in-a-Box services started successfully.${NC}"
|
||||||
|
SERVICES_STOPPED=false
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Warning: Failed to start some services:${NC}$failed_services"
|
||||||
|
echo -e "${YELLOW}Some manual intervention may be required.${NC}"
|
||||||
|
SERVICES_STOPPED=false
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to handle errors and cleanup
|
||||||
|
function handle_error {
|
||||||
|
local error_message=$1
|
||||||
|
echo -e "\n${RED}ERROR: $error_message${NC}"
|
||||||
|
|
||||||
|
# Restart services if they were stopped
|
||||||
|
if [ "$STOP_SERVICES" = true ] && [ "$SERVICES_STOPPED" = true ]; then
|
||||||
|
echo -e "\n${YELLOW}Attempting to restart Mail-in-a-Box services due to error...${NC}"
|
||||||
|
start_miab_services || echo -e "${RED}Failed to restart some services. Manual intervention may be required.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up temporary files
|
||||||
|
cleanup
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to clean up temporary files
|
||||||
|
function cleanup {
|
||||||
|
echo -e "\n${YELLOW}Cleaning up temporary files...${NC}"
|
||||||
|
rm -f "${SSH_KEY_PATH}" "${SSH_KEY_PATH}.pub"
|
||||||
|
echo -e "${GREEN}Temporary SSH keys removed.${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse command line arguments
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
key="$1"
|
||||||
|
case $key in
|
||||||
|
--username)
|
||||||
|
USERNAME="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--target-host)
|
||||||
|
TARGET_HOST="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--source-path)
|
||||||
|
SOURCE_PATH="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--target-path)
|
||||||
|
TARGET_PATH="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--config-path)
|
||||||
|
CONFIG_PATH="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--exclude)
|
||||||
|
EXCLUDE="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--new-ip)
|
||||||
|
NEW_IP="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--ssh-port)
|
||||||
|
SSH_PORT="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--stop-services)
|
||||||
|
STOP_SERVICES=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--ignore-partial)
|
||||||
|
IGNORE_PARTIAL=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--dry-run)
|
||||||
|
DRY_RUN=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--help)
|
||||||
|
show_usage
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${RED}Error: Unknown option $1${NC}"
|
||||||
|
show_usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check required parameters
|
||||||
|
if [ -z "$USERNAME" ] || [ -z "$TARGET_HOST" ] || [ -z "$NEW_IP" ]; then
|
||||||
|
echo -e "${RED}Error: Missing required parameters.${NC}"
|
||||||
|
show_usage
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate IP address
|
||||||
|
if ! validate_ip "$NEW_IP"; then
|
||||||
|
echo -e "${RED}Error: Invalid IP address format: $NEW_IP${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check dependencies
|
||||||
|
check_dependencies
|
||||||
|
|
||||||
|
# Display migration information
|
||||||
|
echo -e "\n${BOLD}Mail-in-a-Box Migration${NC}"
|
||||||
|
echo -e "Source path: $SOURCE_PATH"
|
||||||
|
echo -e "Target server: $USERNAME@$TARGET_HOST:$TARGET_PATH"
|
||||||
|
echo -e "Config file: $CONFIG_PATH"
|
||||||
|
echo -e "New IP address: $NEW_IP"
|
||||||
|
if [ -n "$EXCLUDE" ]; then
|
||||||
|
echo -e "Excluding: $EXCLUDE"
|
||||||
|
fi
|
||||||
|
if [ "$STOP_SERVICES" = true ]; then
|
||||||
|
echo -e "Services: ${YELLOW}Will be stopped during transfer${NC}"
|
||||||
|
else
|
||||||
|
echo -e "Services: ${GREEN}Will remain running during transfer${NC}"
|
||||||
|
fi
|
||||||
|
if [ "$IGNORE_PARTIAL" = true ]; then
|
||||||
|
echo -e "Partial transfers: ${YELLOW}Will be ignored${NC}"
|
||||||
|
fi
|
||||||
|
if [ "$DRY_RUN" = true ]; then
|
||||||
|
echo -e "Mode: ${YELLOW}DRY RUN${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate SSH key pair
|
||||||
|
if ! generate_ssh_key; then
|
||||||
|
handle_error "Failed to generate SSH key pair"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Wait for user to add the SSH key to the target server
|
||||||
|
echo -e "\n${BOLD}Please add the above public key to the authorized_keys file on the target server.${NC}"
|
||||||
|
echo -e "You can use the following command on the target server:"
|
||||||
|
echo -e "${YELLOW}echo '$(cat "${SSH_KEY_PATH}.pub")' >> ~/.ssh/authorized_keys${NC}"
|
||||||
|
read -p "Press Enter once you have added the key to continue... " -r
|
||||||
|
|
||||||
|
# Test SSH connection
|
||||||
|
while ! test_ssh_connection; do
|
||||||
|
read -p "Would you like to try again? (y/n): " -r
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
echo -e "${RED}Migration aborted.${NC}"
|
||||||
|
cleanup
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Stop Mail-in-a-Box services on source server if requested
|
||||||
|
if [ "$STOP_SERVICES" = true ]; then
|
||||||
|
if [ "$DRY_RUN" = false ]; then
|
||||||
|
echo -e "\n${BOLD}The script will now stop all Mail-in-a-Box services on the source server.${NC}"
|
||||||
|
read -p "Press Enter to continue or Ctrl+C to abort... " -r
|
||||||
|
if ! stop_miab_services; then
|
||||||
|
handle_error "Failed to stop Mail-in-a-Box services"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "\n${YELLOW}DRY RUN: Would stop all Mail-in-a-Box services on the source server.${NC}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "\n${YELLOW}Note: Mail-in-a-Box services will remain running during transfer.${NC}"
|
||||||
|
echo -e "${YELLOW}If you experience issues, consider using the --stop-services option.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sync files
|
||||||
|
if ! sync_files; then
|
||||||
|
if [ "$IGNORE_PARTIAL" = true ]; then
|
||||||
|
echo -e "${YELLOW}Continuing despite file sync issues (--ignore-partial flag is set)${NC}"
|
||||||
|
else
|
||||||
|
handle_error "Failed to sync files"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update mailinabox.conf
|
||||||
|
if ! update_mailinabox_conf; then
|
||||||
|
handle_error "Failed to update mailinabox.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Start Mail-in-a-Box services on source server if they were stopped
|
||||||
|
if [ "$STOP_SERVICES" = true ] && [ "$SERVICES_STOPPED" = true ]; then
|
||||||
|
if [ "$DRY_RUN" = false ]; then
|
||||||
|
echo -e "\n${BOLD}The script will now restart all Mail-in-a-Box services on the source server.${NC}"
|
||||||
|
read -p "Press Enter to continue or Ctrl+C to abort... " -r
|
||||||
|
if ! start_miab_services; then
|
||||||
|
echo -e "${YELLOW}Warning: Some services failed to start. Manual intervention may be required.${NC}"
|
||||||
|
# Continue execution despite service start failures
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "\n${YELLOW}DRY RUN: Would restart all Mail-in-a-Box services on the source server.${NC}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Final message
|
||||||
|
if [ "$DRY_RUN" = false ]; then
|
||||||
|
echo -e "\n${GREEN}${BOLD}Mail-in-a-Box migration completed successfully!${NC}"
|
||||||
|
echo -e "\n${YELLOW}IMPORTANT: ${RED}You MUST reinstall Mail-in-a-Box on the target server${YELLOW} to ensure proper configuration.${NC}"
|
||||||
|
echo -e "The data has been transferred, but Mail-in-a-Box needs to be reinstalled to update all configurations."
|
||||||
|
echo -e "\n${YELLOW}Next steps:${NC}"
|
||||||
|
echo -e "1. SSH into the target server: ssh $USERNAME@$TARGET_HOST"
|
||||||
|
echo -e "2. Reinstall Mail-in-a-Box on the target server: curl -s https://mailinabox.email/setup.sh | sudo bash"
|
||||||
|
echo -e "3. Update DNS records to point to the new server IP: $NEW_IP"
|
||||||
|
echo -e "4. Test mail functionality on the new server"
|
||||||
|
echo -e "\n${YELLOW}Note:${NC} During reinstallation, Mail-in-a-Box will detect existing data and preserve it."
|
||||||
|
else
|
||||||
|
echo -e "\n${YELLOW}${BOLD}Mail-in-a-Box dry run completed. No changes were made.${NC}"
|
||||||
|
echo -e "Run the command without --dry-run to perform the actual migration."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
cleanup
|
||||||
|
|
||||||
|
exit 0
|
Loading…
Reference in New Issue
Block a user