Files
password-manager-server-app…/installer.sh
2025-07-11 13:51:53 -04:00

381 lines
11 KiB
Bash
Executable File

#!/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 openssl
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 <<EOF
CREATE DATABASE ${DB_NAME};
CREATE USER '${DB_USER}'@'localhost' IDENTIFIED BY '${DB_PASS}';
GRANT ALL PRIVILEGES ON ${DB_NAME}.* TO '${DB_USER}'@'localhost';
FLUSH PRIVILEGES;
EOF
echo "Verifying MariaDB connection..."
mysql -u "$DB_USER" -p"$DB_PASS" -e "USE $DB_NAME;" >/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 <<EOF > /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 <<EOF > /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
# HTTPS self-signed cert
mkdir -p /etc/ssl/private
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/vaultwarden-selfsigned.key -out /etc/ssl/certs/vaultwarden-selfsigned.crt -subj "/CN=localhost"
# NGINX vhost config
cat <<"EOF" > /etc/nginx/sites-available/vaultwarden
server {
listen 80 default_server;
listen [::]:80 default_server;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name _;
ssl_certificate /etc/ssl/certs/vaultwarden-selfsigned.crt;
ssl_certificate_key /etc/ssl/private/vaultwarden-selfsigned.key;
root /var/www/html/vaultinfo;
index index.php;
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
# Serve PHP files
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
# First-Run Activation
location /activate {
root /var/www/html;
index index.php;
location ~ ^/activate/.*\.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
}
# 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";
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
}
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 php-bcmath
cd "$INSTALLER_DIR"
cp -R ./activate /var/www/html/
chown -R www-data:www-data /var/www/html/activate
# Welcome page with /setup condition check
cat <<"EOF" > /var/www/html/vaultinfo/index.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Password Management Appliance</title>
<style>
body { font-family: sans-serif; background: #111; color: #eee; padding: 2rem; text-align: center; }
h1 { color: #6cf; }
a { color: #9cf; }
</style>
</head>
<body>
<h1>Password Management Appliance</h1>
<p>This server is running <strong>Vaultwarden</strong> on a Password Management Appliance.</p>
<p>✓ Installation Complete</p>
<p><?php if (file_exists('/var/lib/vaultwarden/.setup-complete')) { echo '✓ Activated'; } else { echo '✗ Activated'; } ?></p>
<?php if (file_exists('/var/lib/vaultwarden/.setup-complete')) { echo '<p>Access the admin panel at: <a href="/admin">/admin</a></p>'; } else { echo '<p>Continue to <a href="/setup/">activation</a>.</p>'; } ?>
<p style="font-size: 0.9em; color: #666;">An Open Source Appliance from Robbie Ferguson</p>
</body>
</html>
EOF
echo "Setup system ready. Visit http://<appliance-ip>/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://<your-appliance-ip>/setup to begin configuration."