diff --git a/proxmox/README.md b/proxmox/README.md new file mode 100644 index 0000000..4142d38 --- /dev/null +++ b/proxmox/README.md @@ -0,0 +1,304 @@ +# Proxmox LXC Setup for OtterWiki + +This directory contains scripts and documentation for deploying OtterWiki in Proxmox LXC containers. + +## Overview + +The `setup-otterwiki-lxc.sh` script automates the creation and configuration of an LXC container in Proxmox VE for running OtterWiki. It handles everything from container creation to service configuration, providing a ready-to-use OtterWiki installation. + +## Features + +- **Ubuntu 24.04 LTS**: Uses the latest Ubuntu LTS template +- **Official Installation**: Follows [OtterWiki official installation guidelines](https://otterwiki.com/Installation#from-source-as-wsgi-application-with-uwsgi) +- **Source Installation**: Clones from official GitHub repository for latest features +- **Automated Setup**: Complete installation and configuration of OtterWiki +- **Flexible Configuration**: Customizable resources, networking, and authentication +- **Production Ready**: Includes uWSGI with systemd for reliable service management +- **Host DNS Integration**: Automatically uses host DNS settings as default + +## Requirements + +- Proxmox VE 7.0 or later +- Root access to Proxmox host +- Available container ID number +- Network bridge configured (default: vmbr0) +- Storage pool available (default: local-lvm) + +## Quick Start + +### Execute Directly from GitHub + +Run the script directly from the repository without cloning: + +```bash +# Basic usage with DHCP +curl -fsSL https://raw.githubusercontent.com/redimp/otterwiki/main/proxmox/setup-otterwiki-lxc.sh | bash + +# With static IP and SSH key +curl -fsSL https://raw.githubusercontent.com/redimp/otterwiki/main/proxmox/setup-otterwiki-lxc.sh | bash -s -- -i 100 -a 192.168.1.100/24 -g 192.168.1.1 -k ~/.ssh/id_rsa.pub +``` + +### Local Usage + +After cloning or downloading the script locally: + +#### Basic Usage + +Create a container with DHCP networking: + +```bash +./setup-otterwiki-lxc.sh +``` + +#### Advanced Usage + +Create a container with static IP and SSH key authentication: + +```bash +./setup-otterwiki-lxc.sh \ + -i 100 \ + -n "otterwiki-prod" \ + -a 192.168.1.100/24 \ + -g 192.168.1.1 \ + -k ~/.ssh/id_rsa.pub \ + -r https://github.com/user/my-wiki.git \ + -m 4096 \ + -c 4 +``` + +## Command Line Options + +| Option | Description | Default | +|--------|-------------|---------| +| `-i, --id` | Container ID (required) | - | +| `-n, --name` | Container hostname | otterwiki | +| `-t, --template` | Ubuntu template | ubuntu-24.04-standard_24.04-2_amd64.tar.zst | +| `-s, --storage` | Storage pool | auto-detect | +| `-m, --memory` | Memory in MB | 2048 | +| `-c, --cores` | CPU cores | 2 | +| `-d, --disk` | Disk size | 20G | +| `-b, --bridge` | Network bridge | vmbr0 | +| `-a, --ip` | Static IP (CIDR format) | DHCP | +| `-g, --gateway` | Gateway IP | - | +| `-ns, --nameserver` | DNS server | Host DNS | +| `-k, --ssh-key` | SSH public key file | - | +| `-p, --password` | Root password | Interactive prompt | +| `-r, --repo-url` | Git repository URL to clone | - | +| `-h, --help` | Show help message | - | + +## What the Script Does + +1. **Validation**: Checks Proxmox environment and container ID availability +2. **Template Management**: Downloads Ubuntu 24.04 template if not present +3. **Container Creation**: Creates LXC container with specified configuration +4. **System Setup**: Updates packages and installs dependencies +5. **OtterWiki Installation**: Clones from source and sets up Python environment +6. **Service Configuration**: Configures uWSGI with systemd service +7. **Data Setup**: Initializes git repository and configuration files + +## Post-Installation + +### Accessing OtterWiki + +After successful installation: + +- **Web Interface**: `http://[container-ip]:8080` +- **Container Shell**: `pct enter [container-id]` +- **First User**: The first registered user becomes the admin + +### Container Management + +```bash +# Start container +pct start + +# Stop container +pct stop + +# Enter container +pct enter + +# View container status +pct status + +# Delete container +pct destroy +``` + +### Service Management (inside container) + +```bash +# Check service status +systemctl status otterwiki + +# Restart service +systemctl restart otterwiki + +# Stop service +systemctl stop otterwiki + +# View logs +journalctl -u otterwiki -f +``` + +## Configuration Files + +Key configuration files in the container: + +- **OtterWiki Config**: `/opt/otterwiki/settings.cfg` +- **Systemd Service**: `/etc/systemd/system/otterwiki.service` +- **Virtual Environment**: `/opt/otterwiki/venv/` + +## Data Persistence + +- **Wiki Data**: `/opt/otterwiki/app-data/repository` (git repository) +- **Database**: `/opt/otterwiki/app-data/db.sqlite` +- **Configuration**: `/opt/otterwiki/settings.cfg` + +## Networking Examples + +### Static IP Configuration + +```bash +# Single static IP +./setup-otterwiki-lxc.sh -i 100 -a 192.168.1.100/24 -g 192.168.1.1 + +# Custom DNS server +./setup-otterwiki-lxc.sh -i 100 -a 192.168.1.100/24 -g 192.168.1.1 -ns 1.1.1.1 +``` + +### Different Network Bridge + +```bash +# Use custom bridge +./setup-otterwiki-lxc.sh -i 100 -b vmbr1 +``` + +## Troubleshooting + +### Common Issues + +1. **Storage Error** (`no such logical volume`): + - The script now auto-detects available storage + - To manually specify: `./setup-otterwiki-lxc.sh -i 100 -s local` + - Check available storage: `pvesm status -content rootdir` + +2. **Template Download Fails**: Check internet connectivity and Proxmox subscription + +3. **Container ID Exists**: Use a different ID or remove existing container + +4. **Network Issues**: Verify bridge configuration and IP ranges + +5. **SSH Key Not Found**: Ensure path to SSH key file is correct + +### Log Locations + +- **Container Creation**: Script output shows detailed progress +- **Service Logs**: Available via `journalctl -u otterwiki` +- **System Logs**: `/var/log/` in container + +### Getting Help + +For issues with: +- **Script**: Check script output and container logs +- **OtterWiki**: Consult [OtterWiki documentation](https://otterwiki.com/) +- **Proxmox**: Check Proxmox VE documentation + +## Security Considerations + +- Secret key is auto-generated during installation in `/opt/otterwiki/settings.cfg` +- Configure proper firewall rules +- Use SSH key authentication when possible +- Regularly update container packages +- Monitor container resource usage + +## Optional: Git Repository Synchronization + +If you've cloned an existing wiki repository using the `-r` option, you may want to set up automatic synchronization with the remote repository. + +### Setting Up Periodic Sync (Optional) + +To automatically sync changes with the remote repository, you can set up a cron job inside the container: + +1. **Enter the container**: + ```bash + pct enter + ``` + +2. **Switch to the www-data user**: + ```bash + sudo -u www-data -s + ``` + +3. **Navigate to the repository**: + ```bash + cd /opt/otterwiki/app-data/repository + ``` + +4. **Create a sync script**: + ```bash + cat > /opt/otterwiki/sync-repo.sh << 'EOF' + #!/bin/bash + cd /opt/otterwiki/app-data/repository + + # Pull latest changes from remote + git fetch origin + git merge origin/main 2>/dev/null || git merge origin/master 2>/dev/null + + # Push any local changes + git push origin 2>/dev/null || true + + echo "$(date): Repository sync completed" >> /var/log/otterwiki-sync.log + EOF + + chmod +x /opt/otterwiki/sync-repo.sh + chown www-data:www-data /opt/otterwiki/sync-repo.sh + ``` + +5. **Set up the cron job** (as www-data user): + ```bash + crontab -e + ``` + + Add this line to sync every 30 minutes: + ``` + */30 * * * * /opt/otterwiki/sync-repo.sh + ``` + +### Important Notes + +- **Backup First**: Always backup your data before setting up automatic sync +- **Conflict Resolution**: The script handles simple merges but may fail on conflicts +- **Authentication**: Ensure the container has appropriate git credentials configured +- **Monitoring**: Check `/var/log/otterwiki-sync.log` for sync status +- **Testing**: Test the sync script manually before enabling the cron job + +### Alternative: Manual Sync + +For manual synchronization, you can run these commands inside the container: + +```bash +# Enter container and switch to www-data +pct enter +sudo -u www-data -s +cd /opt/otterwiki/app-data/repository + +# Pull changes +git pull origin main # or master + +# Push changes +git push origin main # or master +``` + +## Customization + +The script can be modified to: +- Install additional packages +- Configure different web servers +- Adjust service configurations +- Set up SSL certificates +- Configure backup scripts + +## License + +This script is part of the OtterWiki project and follows the same licensing terms. \ No newline at end of file diff --git a/proxmox/setup-otterwiki-lxc.sh b/proxmox/setup-otterwiki-lxc.sh new file mode 100755 index 0000000..7090b4a --- /dev/null +++ b/proxmox/setup-otterwiki-lxc.sh @@ -0,0 +1,354 @@ +#!/bin/bash + +set -euo pipefail + +SCRIPT_NAME=$(basename "$0") +CONTAINER_ID="" +CONTAINER_NAME="otterwiki" +TEMPLATE="ubuntu-24.04-standard_24.04-2_amd64.tar.zst" +STORAGE="$(pvesm status -content rootdir | awk 'NR>1 && $3=="active" {print $1; exit}' || echo 'local-lvm')" +MEMORY=2048 +CORES=2 +DISK_SIZE="16" # in GB +NETWORK="vmbr0" +IP_ADDRESS="dhcp" +GATEWAY="" +DNS="" +SSH_KEY="" +ROOT_PASSWORD="" +GIT_REPO_URL="" +VERBOSE="false" + +usage() { + cat << EOF +Usage: $SCRIPT_NAME -i CONTAINER_ID [OPTIONS] + +Creates an LXC container in Proxmox for OtterWiki + +Optional: + -i, --id CONTAINER_ID Container ID (e.g., 100) + -n, --name NAME Container name (default: $CONTAINER_NAME) + -t, --template TEMPLATE CT template (default: $TEMPLATE) + -s, --storage STORAGE Storage location (default: auto-detect) + -m, --memory MEMORY Memory in MB (default: $MEMORY) + -c, --cores CORES CPU cores (default: $CORES) + -d, --disk DISK_SIZE Disk size (default: $DISK_SIZE) + -b, --bridge NETWORK Network bridge (default: $NETWORK) + -a, --ip IP_ADDRESS Static IP address (default: $IP_ADDRESS) CIDR format, e.g., 192.168.1.100/24 + -g, --gateway GATEWAY Gateway IP address + -ns, --nameserver NS DNS nameserver(s) (default: host DNS) + -k, --ssh-key SSH_KEY Path to SSH public key file + -p, --password PASSWORD Root password (will prompt if not provided) + -r, --repo-url URL Git repository URL to clone (optional) + -v, --verbose Enable verbose output + -h, --help Show this help message + +Example: + $SCRIPT_NAME -i 100 -a 192.168.1.100/24 -g 192.168.1.1 -k ~/.ssh/id_rsa.pub +EOF +} + +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" +} + +error() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $*" >&2 + exit 1 +} + +debug() { + if [[ "$VERBOSE" == "true" ]]; then + echo "[$(date '+%Y-%m-%d %H:%M:%S')] DBG: $*" + fi +} + +check_proxmox() { + if ! command -v pct &> /dev/null; then + error "This script must be run on a Proxmox host with pct command available" + fi +} + +check_template() { + local template_path="/var/lib/vz/template/cache/$TEMPLATE" + if [[ ! -f "$template_path" ]]; then + log "Template $TEMPLATE not found, downloading..." + pveam update + pveam download local "$TEMPLATE" || error "Failed to download template $TEMPLATE" + fi +} + +check_storage() { + log "Checking storage availability..." + + if ! pvesm status -content rootdir | grep -q "$STORAGE"; then + log "Storage '$STORAGE' not found or not suitable for containers" + log "Available storage options:" + pvesm status -content rootdir | awk 'NR>1 && $3=="active" {print " " $1}' || true + + # Try to auto-detect a suitable storage + local auto_storage + auto_storage=$(pvesm status -content rootdir | awk 'NR>1 && $3=="active" {print $1; exit}') + if [[ -n "$auto_storage" ]]; then + log "Auto-selecting storage: $auto_storage" + STORAGE="$auto_storage" + else + error "No suitable storage found for container rootfs" + fi + fi + + log "Using storage: $STORAGE" +} + +validate_container_id() { + if [[ ! "$CONTAINER_ID" =~ ^[0-9]+$ ]]; then + error "Container ID must be a number" + fi + + if pct status "$CONTAINER_ID" &>/dev/null; then + error "Container with ID $CONTAINER_ID already exists" + fi +} + +create_container() { + local create_cmd=( + pct create "$CONTAINER_ID" + "/var/lib/vz/template/cache/$TEMPLATE" + --hostname "$CONTAINER_NAME" + --memory "$MEMORY" + --cores "$CORES" + --rootfs "$STORAGE:$DISK_SIZE" + --features "nesting=1" + --unprivileged 1 + --onboot 1 + --net0 + ) + + if [[ -n "$GATEWAY" ]]; then + create_cmd[${#create_cmd[@]}]="name=eth0,bridge=$NETWORK,firewall=1,ip=$IP_ADDRESS,gw=$GATEWAY" + else + create_cmd[${#create_cmd[@]}]="name=eth0,bridge=$NETWORK,firewall=1,ip=$IP_ADDRESS" + fi + + if [[ -n "$DNS" ]]; then + create_cmd[${#create_cmd[@]}]="--nameserver "$DNS"" + fi + + if [[ -n "$SSH_KEY" ]]; then + if [[ -f "$SSH_KEY" ]]; then + create_cmd+=(--ssh-public-keys "$SSH_KEY") + else + error "SSH key file not found: $SSH_KEY" + fi + fi + + if [[ -n "$ROOT_PASSWORD" ]]; then + create_cmd+=(--password "$ROOT_PASSWORD") + fi + + log "Creating container $CONTAINER_ID..." + debug "Command: ${create_cmd[*]}" + "${create_cmd[@]}" || error "Failed to create container" +} + +setup_container() { + log "Starting container $CONTAINER_ID..." + pct start "$CONTAINER_ID" || error "Failed to start container" + + log "Waiting for container to be ready..." + sleep 10 + + log "Updating system packages..." + pct exec "$CONTAINER_ID" -- bash -c "apt-get update && apt-get upgrade -y" || error "Failed to update packages" + + log "Installing essential packages..." + pct exec "$CONTAINER_ID" -- bash -c "apt-get install -y curl wget git net-tools netcat-traditional python3 python3-pip python3-venv uwsgi uwsgi-plugin-python3 build-essential python3-dev libjpeg-dev zlib1g-dev libxml2-dev libxslt-dev" || error "Failed to install packages" + + log "Cloning OtterWiki repository..." + pct exec "$CONTAINER_ID" -- bash -c "cd /opt && git clone https://github.com/redimp/otterwiki.git" || error "Failed to clone OtterWiki repository" + + log "Setting up Python virtual environment..." + pct exec "$CONTAINER_ID" -- bash -c "cd /opt/otterwiki && python3 -m venv venv" || error "Failed to create virtual environment" + pct exec "$CONTAINER_ID" -- bash -c "cd /opt/otterwiki && ./venv/bin/pip install -U pip uwsgi" || error "Failed to upgrade pip and install uwsgi" + pct exec "$CONTAINER_ID" -- bash -c "cd /opt/otterwiki && ./venv/bin/pip install ." || error "Failed to install OtterWiki" + + log "Creating app-data directory structure..." + pct exec "$CONTAINER_ID" -- bash -c "cd /opt/otterwiki && mkdir -p app-data/repository" || error "Failed to create app-data directory" + + log "Initializing data repository..." + pct exec "$CONTAINER_ID" -- bash -c "cd /opt/otterwiki/app-data/repository && git init -b main" || error "Failed to initialize repository" + + log "Creating OtterWiki configuration..." + pct exec "$CONTAINER_ID" -- bash -c "cd /opt/otterwiki && python3 -c \" +import secrets +with open('settings.cfg', 'w') as f: + f.write('''REPOSITORY = '/opt/otterwiki/app-data/repository' +SQLALCHEMY_DATABASE_URI = 'sqlite:////opt/otterwiki/app-data/db.sqlite' +SECRET_KEY = '{}' +OTTERWIKI_NAME = 'OtterWiki' +OTTERWIKI_MAIL_DEFAULT_SENDER = 'otterwiki@localhost' +OTTERWIKI_WELCOME_PAGE = 'Home' +'''.format(secrets.token_hex())) +\"" + + if [[ -n "$GIT_REPO_URL" ]]; then + log "Cloning wiki content repository from $GIT_REPO_URL..." + pct exec "$CONTAINER_ID" -- bash -c "cd /opt/otterwiki/app-data && rm -rf repository && git clone '$GIT_REPO_URL' repository" || error "Failed to clone repository" + fi + + log "Setting ownership..." + pct exec "$CONTAINER_ID" -- chown -R www-data:www-data /opt/otterwiki + + log "Creating systemd service for OtterWiki..." + pct exec "$CONTAINER_ID" -- tee /etc/systemd/system/otterwiki.service > /dev/null << 'EOF' +[Unit] +Description=uWSGI server for OtterWiki +After=network.target + +[Service] +User=www-data +Group=www-data +WorkingDirectory=/opt/otterwiki +Environment=OTTERWIKI_SETTINGS=/opt/otterwiki/settings.cfg +ExecStart=/opt/otterwiki/venv/bin/uwsgi --http 0.0.0.0:8080 --master --enable-threads --die-on-term -w otterwiki.server:app +Restart=always +RestartSec=10 +KillMode=mixed +KillSignal=SIGTERM + +[Install] +WantedBy=multi-user.target +EOF + + log "Enabling and starting OtterWiki service..." + pct exec "$CONTAINER_ID" -- systemctl daemon-reload + pct exec "$CONTAINER_ID" -- systemctl enable otterwiki.service + pct exec "$CONTAINER_ID" -- systemctl start otterwiki.service + + log "Container setup completed successfully!" + log "Container ID: $CONTAINER_ID" + log "Container Name: $CONTAINER_NAME" + + if [[ -n "$IP_ADDRESS" ]]; then + log "IP Address: $IP_ADDRESS" + else + local container_ip + container_ip=$(pct exec "$CONTAINER_ID" -- ip route get 1 | awk '{print $7}' | head -1) + log "IP Address: $container_ip (DHCP)" + fi + + log "" + log "To access the container:" + log " pct enter $CONTAINER_ID" + log "" + log "Access OtterWiki at:" + if [[ -n "$IP_ADDRESS" ]]; then + log " http://${IP_ADDRESS%/*}:8080" + else + log " http://[container-ip]:8080" + fi + log "" + log "Service management:" + log " systemctl status otterwiki" + log " systemctl restart otterwiki" + log " systemctl stop otterwiki" + log "" + log "First registered user will become the admin." +} + +while [[ $# -gt 0 ]]; do + case $1 in + -i|--id) + CONTAINER_ID="$2" + shift 2 + ;; + -n|--name) + CONTAINER_NAME="$2" + shift 2 + ;; + -t|--template) + TEMPLATE="$2" + shift 2 + ;; + -s|--storage) + STORAGE="$2" + shift 2 + ;; + -m|--memory) + MEMORY="$2" + shift 2 + ;; + -c|--cores) + CORES="$2" + shift 2 + ;; + -d|--disk) + DISK_SIZE="$2" + shift 2 + ;; + -b|--bridge) + NETWORK="$2" + shift 2 + ;; + -a|--ip) + IP_ADDRESS="$2" + shift 2 + ;; + -g|--gateway) + GATEWAY="$2" + shift 2 + ;; + -ns|--nameserver) + DNS="$2" + shift 2 + ;; + -k|--ssh-key) + SSH_KEY="$2" + shift 2 + ;; + -p|--password) + ROOT_PASSWORD="$2" + shift 2 + ;; + -r|--repo-url) + GIT_REPO_URL="$2" + shift 2 + ;; + -v|--verbose) + VERBOSE="true" + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + error "Unknown option: $1" + ;; + esac +done + +if [[ -z "$CONTAINER_ID" ]]; then + echo -n "Enter the container ID: " + read CONTAINER_ID +fi + +if [[ -z "$CONTAINER_ID" ]]; then + error "Container ID is required!" +fi + +if [[ -z "$ROOT_PASSWORD" && -z "$SSH_KEY" ]]; then + echo -n "Enter root password for the container: " + read -s ROOT_PASSWORD + echo +fi + +log "Starting LXC container creation process..." +check_proxmox +validate_container_id +check_storage +check_template +create_container +setup_container + +log "LXC container creation completed successfully!" \ No newline at end of file