#!/bin/bash # # Password Management Appliance Installer # An Open Source Appliance from Robbie Ferguson # # Licensed under the Apache License, Version 2.0 (the "License"); # You may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 set -e if [[ "$EUID" -ne 0 ]]; then echo "Please run as root." exit 1 fi generate_token() { local length=${1:-64} head -c $((length / 2)) /dev/urandom | xxd -p | tr -d '\n' } # Save the directory where the script was started INSTALLER_DIR="$(pwd)" echo "=== Password Management Appliance Installer ===" echo "An Open Source Appliance from Robbie Ferguson" echo if [[ "$1" == "--purge" ]]; then echo "PURGE command passed on the command line." echo echo "WARNING: This will permanently remove Vaultwarden and all its data." read -r -p "Are you sure you want to delete everything? [y/N]: " confirm # Default to "No" if input is empty or not 'y' or 'Y' if [[ ! "$confirm" =~ ^[Yy]$ ]]; then echo "Purge aborted." exit 0 fi echo "Purging previous Vaultwarden installation..." set +e echo "Removing MariaDB (server + client + common)..." systemctl stop mariadb 2>/dev/null apt purge -y mariadb-server mariadb-client mariadb-common mariadb-backup apt autoremove -y rm -rf /var/lib/mysql /etc/mysql if id "vaultwarden" &>/dev/null; then echo "Attempting to stop and remove vaultwarden user..." echo "Killing any processes owned by vaultwarden..." pkill -u vaultwarden 2>/dev/null || true echo "Stopping Vaultwarden service if running..." systemctl stop vaultwarden 2>/dev/null || true systemctl disable vaultwarden 2>/dev/null || true rm -f /etc/systemd/system/vaultwarden.service echo "Unmounting or clearing home directory if needed..." umount /opt/vaultwarden 2>/dev/null || true rm -rf /opt/vaultwarden /var/lib/vaultwarden rm -f /usr/local/bin/vaultwarden-wrapper echo "Deleting vaultwarden user..." userdel vaultwarden && echo "vaultwarden user removed." || echo "Failed to remove vaultwarden user." else echo "vaultwarden user does not exist. Skipping." fi echo "Purging the vaultgroup gruop" groupdel vaultgroup getent group vaultgroup echo "Cleaning www-data writable config and setup..." rm -f /var/lib/vaultwarden/.env.user rm -f /var/lib/vaultwarden/.setup-complete rm -rf /var/www/html/setup/ echo "Removing sudoers config for www-data..." rm -f /etc/sudoers.d/vaultwarden-www-restart echo "Purge complete. You can now re-run installer.sh safely." exit 0 fi # Update system and install dependencies echo "Installing dependencies..." apt update && apt upgrade -y apt install -y curl gnupg2 software-properties-common apt-transport-https lsb-release mariadb-server mariadb-client nginx unzip ufw git build-essential pkg-config libssl-dev libmariadb-dev libmariadb-dev-compat sudo xxd ufw allow OpenSSH ufw allow 'Nginx Full' ufw --force enable DB_NAME="vaultwarden" DB_USER="vaultuser" #DB_PASS=$(openssl rand -base64 24) DB_PASS=$(generate_token 64) echo "Creating MariaDB database and user..." mysql -u root </dev/null 2>&1 || { echo "ERROR: Cannot verify MariaDB access." exit 1 } echo "MariaDB credentials verified." echo "Installing Rust and Vaultwarden..." curl https://sh.rustup.rs -sSf | sh -s -- -y export PATH="$HOME/.cargo/bin:$PATH" useradd -r -d /opt/vaultwarden -s /usr/sbin/nologin vaultwarden || true mkdir -p /opt/vaultwarden chown vaultwarden:vaultwarden /opt/vaultwarden cd /opt git clone https://github.com/dani-garcia/vaultwarden.git cd vaultwarden echo "Building Vaultwarden with MySQL support..." if ! cargo build --release --locked --features mysql; then echo "Cargo build failed." exit 1 fi echo "Verifying that Vaultwarden binary was built..." if [[ ! -x target/release/vaultwarden ]]; then echo "Vaultwarden binary not found after build." exit 1 fi echo "Checking for MySQL support in the built binary..." if ldd target/release/vaultwarden | grep -qE 'libmysqlclient|libmariadb'; then echo "Vaultwarden binary is linked with MySQL/MariaDB support." if [[ -e /usr/local/bin/vaultwarden ]]; then rm -f /usr/local/bin/vaultwarden fi echo "Placing binary in /usr/local/bin" cp target/release/vaultwarden /usr/local/bin/ chown vaultwarden:vaultwarden /usr/local/bin/vaultwarden else echo "Built Vaultwarden binary does not link MySQL/MariaDB. Something went wrong." exit 1 fi # Ensure vaultwarden data directory exists if [ ! -d /var/lib/vaultwarden ]; then echo "Creating /var/lib/vaultwarden directory..." mkdir -p /var/lib/vaultwarden chown www-data:www-data /var/lib/vaultwarden fi # Ensure .env.user file exists if [ ! -f /var/lib/vaultwarden/.env.user ]; then echo "Creating .env.user file..." touch /var/lib/vaultwarden/.env.user chown www-data:www-data /var/lib/vaultwarden/.env.user fi mkdir -p /opt/vaultwarden/data chown -R vaultwarden:vaultwarden /opt/vaultwarden cat < /opt/vaultwarden/.env DATABASE_URL=mysql://${DB_USER}:${DB_PASS}@localhost:3306/${DB_NAME} DATA_FOLDER=/opt/vaultwarden/data DOMAIN=https://localhost ROCKET_PORT=8080 ROCKET_ADDRESS=0.0.0.0 WEBSOCKET_ENABLED=true WEB_VAULT_FOLDER=/opt/vaultwarden/web-vault EOF chown vaultwarden:vaultwarden /opt/vaultwarden/.env chmod 600 /opt/vaultwarden/.env # Create vaultwarden-wrapper to support layered .env config cat <<"EOF" > /usr/local/bin/vaultwarden-wrapper #!/bin/bash ENV_MAIN="/opt/vaultwarden/.env" ENV_USER="/var/lib/vaultwarden/.env.user" ENV_MERGED="/opt/vaultwarden/.env.merged" cat "$ENV_MAIN" > "$ENV_MERGED" [ -f "$ENV_USER" ] && cat "$ENV_USER" >> "$ENV_MERGED" # Load environment variables from merged file set -a source "$ENV_MERGED" set +a exec /usr/local/bin/vaultwarden EOF chmod +x /usr/local/bin/vaultwarden-wrapper cat < /etc/systemd/system/vaultwarden.service [Unit] Description=Vaultwarden Password Server After=network.target mariadb.service [Service] User=vaultwarden Group=vaultwarden WorkingDirectory=/opt/vaultwarden ExecStart=/usr/local/bin/vaultwarden-wrapper Restart=always RestartSec=5s [Install] WantedBy=multi-user.target EOF systemctl daemon-reexec systemctl enable --now vaultwarden echo "Checking Vaultwarden service..." systemctl is-active --quiet vaultwarden || { echo "Vaultwarden service failed to start." journalctl -u vaultwarden --no-pager | tail -n 10 exit 1 } echo "Vaultwarden service is active." # Remove default nginx site if it exists to avoid duplicate default_server errors if [ -f /etc/nginx/sites-enabled/default ]; then echo "Removing default Nginx site to avoid conflict..." rm -f /etc/nginx/sites-enabled/default fi # Basic NGINX placeholder config cat <<"EOF" > /etc/nginx/sites-available/vaultwarden server { listen 80 default_server; server_name _; root /var/www/html/vaultinfo; index index.php; # Main landing page location / { try_files $uri $uri/ /index.php?$args; } # Serve PHP files location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.2-fpm.sock; } # Setup interface location /setup { root /var/www/html; index index.php; location ~ ^/setup/.*\.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.2-fpm.sock; } } # Vaultwarden Admin Panel location ^~ /admin/ { proxy_pass http://127.0.0.1:8080/admin/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # Vaultwarden static files (referenced from root!) location ~ ^/(bootstrap|admin|vaultwarden|.*\.(css|js|png|ico|woff2?)$) { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Host $host; } # WebSocket location /notifications/hub { proxy_pass http://127.0.0.1:3012; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } } EOF ln -sf /etc/nginx/sites-available/vaultwarden /etc/nginx/sites-enabled/vaultwarden echo "Testing Nginx configuration..." if nginx -t; then echo "Reloading Nginx..." systemctl reload nginx else echo "Nginx config test failed. Please check manually." fi mkdir -p /var/www/html/vaultinfo chown -R www-data:www-data /var/www/html/vaultinfo # Setup a group that allows Vaultwarden and www-data to share configs groupadd vaultgroup usermod -aG vaultgroup vaultwarden usermod -aG vaultgroup www-data # Allow web interface to save initial config touch /var/lib/vaultwarden/.env.user chown www-data:vaultgroup /var/lib/vaultwarden/.env.user chmod 640 /var/lib/vaultwarden/.env.user # Download and deploy setup wizard echo "Installing PHP and deploying setup page..." apt install -y php php-fpm php-cli php-common php-mbstring php-json php-curl php-xml php-zip php-gd cd "$INSTALLER_DIR" cp -R ./setup /var/www/html/ chown -R www-data:www-data /var/www/html/setup # Welcome page with /setup condition check cat <<"EOF" > /var/www/html/vaultinfo/index.php Password Management Appliance

Password Management Appliance

This server is running Vaultwarden on a Password Management Appliance.

✓ Installation Complete

Access the admin panel at: /admin

'; } else { echo '

Continue to activation.

'; } ?>

An Open Source Appliance from Robbie Ferguson

EOF echo "Setup system ready. Visit http:///setup to begin configuration." # Allow www-data to restart Vaultwarden without password echo "www-data ALL=NOPASSWD: /bin/systemctl restart vaultwarden" > /etc/sudoers.d/vaultwarden-www-restart chmod 440 /etc/sudoers.d/vaultwarden-www-restart echo "Configuring default web-vault." cd /opt/vaultwarden curl -L https://github.com/dani-garcia/bw_web_builds/releases/download/v2025.6.0/bw_web_v2025.6.0.tar.gz | tar xz chown -R vaultwarden:vaultwarden web-vault chmod -R 755 web-vault systemctl restart vaultwarden echo echo "Installation complete!" echo "Visit http:///setup to begin configuration."