|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +echo "This script installs a new BookStack instance on a fresh Ubuntu 22.04 server." |
| 4 | +echo "This script does not ensure system security." |
| 5 | +echo "" |
| 6 | + |
| 7 | +# Generate a path for a log file to output into for debugging |
| 8 | +LOGPATH=$(realpath "bookstack_install_$(date +%s).log") |
| 9 | + |
| 10 | +# Get the current user running the script |
| 11 | +SCRIPT_USER="${SUDO_USER:-$USER}" |
| 12 | + |
| 13 | +# Get the current machine IP address |
| 14 | +CURRENT_IP=$(ip addr | grep 'state UP' -A4 | grep 'inet ' | awk '{print $2}' | cut -f1 -d'/') |
| 15 | + |
| 16 | +# Generate a password for the database |
| 17 | +DB_PASS="$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13)" |
| 18 | + |
| 19 | +# The directory to install BookStack into |
| 20 | +BOOKSTACK_DIR="/var/www/bookstack" |
| 21 | + |
| 22 | +# Get the domain from the arguments (Requested later if not set) |
| 23 | +DOMAIN=$1 |
| 24 | + |
| 25 | +# Prevent interactive prompts in applications |
| 26 | +export DEBIAN_FRONTEND=noninteractive |
| 27 | + |
| 28 | +# Echo out an error message to the command line and exit the program |
| 29 | +# Also logs the message to the log file |
| 30 | +function error_out() { |
| 31 | + echo "ERROR: $1" | tee -a "$LOGPATH" 1>&2 |
| 32 | + exit 1 |
| 33 | +} |
| 34 | + |
| 35 | +# Echo out an information message to both the command line and log file |
| 36 | +function info_msg() { |
| 37 | + echo "$1" | tee -a "$LOGPATH" |
| 38 | +} |
| 39 | + |
| 40 | +# Run some checks before installation to help prevent messing up an existing |
| 41 | +# web-server setup. |
| 42 | +function run_pre_install_checks() { |
| 43 | + # Check we're running as root and exit if not |
| 44 | + if [[ $EUID -gt 0 ]] |
| 45 | + then |
| 46 | + error_out "This script must be ran with root/sudo privileges" |
| 47 | + fi |
| 48 | + |
| 49 | + # Check if Apache appears to be installed and exit if so |
| 50 | + if [ -d "/etc/apache2/sites-enabled" ] |
| 51 | + then |
| 52 | + error_out "This script is intended for a fresh server install, existing apache config found, aborting install" |
| 53 | + fi |
| 54 | + |
| 55 | + # Check if MySQL appears to be installed and exit if so |
| 56 | + if [ -d "/var/lib/mysql" ] |
| 57 | + then |
| 58 | + error_out "This script is intended for a fresh server install, existing MySQL data found, aborting install" |
| 59 | + fi |
| 60 | +} |
| 61 | + |
| 62 | +# Fetch domain to use from first provided parameter, |
| 63 | +# Otherwise request the user to input their domain |
| 64 | +function run_prompt_for_domain_if_required() { |
| 65 | + if [ -z "$DOMAIN" ] |
| 66 | + then |
| 67 | + info_msg "" |
| 68 | + info_msg "Enter the domain (or IP if not using a domain) you want to host BookStack on and press [ENTER]." |
| 69 | + info_msg "Examples: my-site.com or docs.my-site.com or ${CURRENT_IP}" |
| 70 | + read -r DOMAIN |
| 71 | + fi |
| 72 | + |
| 73 | + # Error out if no domain was provided |
| 74 | + if [ -z "$DOMAIN" ] |
| 75 | + then |
| 76 | + error_out "A domain must be provided to run this script" |
| 77 | + fi |
| 78 | +} |
| 79 | + |
| 80 | +# Install core system packages |
| 81 | +function run_package_installs() { |
| 82 | + apt update |
| 83 | + apt install -y git unzip apache2 php8.1 curl php8.1-curl php8.1-mbstring php8.1-ldap \ |
| 84 | + php8.1-xml php8.1-zip php8.1-gd php8.1-mysql mysql-server-8.0 libapache2-mod-php8.1 |
| 85 | +} |
| 86 | + |
| 87 | +# Set up database |
| 88 | +function run_database_setup() { |
| 89 | + mysql -u root --execute="CREATE DATABASE bookstack;" |
| 90 | + mysql -u root --execute="CREATE USER 'bookstack'@'localhost' IDENTIFIED WITH mysql_native_password BY '$DB_PASS';" |
| 91 | + mysql -u root --execute="GRANT ALL ON bookstack.* TO 'bookstack'@'localhost';FLUSH PRIVILEGES;" |
| 92 | +} |
| 93 | + |
| 94 | +# Download BookStack |
| 95 | +function run_bookstack_download() { |
| 96 | + cd /var/www || exit |
| 97 | + git clone https://github.com/BookStackApp/BookStack.git --branch release --single-branch bookstack |
| 98 | +} |
| 99 | + |
| 100 | +# Install composer |
| 101 | +function run_install_composer() { |
| 102 | + EXPECTED_CHECKSUM="$(php -r 'copy("https://composer.github.io/installer.sig", "php://stdout");')" |
| 103 | + php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" |
| 104 | + ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" |
| 105 | + |
| 106 | + if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ] |
| 107 | + then |
| 108 | + >&2 echo 'ERROR: Invalid composer installer checksum' |
| 109 | + rm composer-setup.php |
| 110 | + exit 1 |
| 111 | + fi |
| 112 | + |
| 113 | + php composer-setup.php --quiet |
| 114 | + rm composer-setup.php |
| 115 | + |
| 116 | + # Move composer to global installation |
| 117 | + mv composer.phar /usr/local/bin/composer |
| 118 | +} |
| 119 | + |
| 120 | +# Install BookStack composer dependencies |
| 121 | +function run_install_bookstack_composer_deps() { |
| 122 | + cd "$BOOKSTACK_DIR" || exit |
| 123 | + export COMPOSER_ALLOW_SUPERUSER=1 |
| 124 | + php /usr/local/bin/composer install --no-dev --no-plugins |
| 125 | +} |
| 126 | + |
| 127 | +# Copy and update BookStack environment variables |
| 128 | +function run_update_bookstack_env() { |
| 129 | + cd "$BOOKSTACK_DIR" || exit |
| 130 | + cp .env.example .env |
| 131 | + sed -i.bak "s@APP_URL=.*\$@APP_URL=http://$DOMAIN@" .env |
| 132 | + sed -i.bak 's/DB_DATABASE=.*$/DB_DATABASE=bookstack/' .env |
| 133 | + sed -i.bak 's/DB_USERNAME=.*$/DB_USERNAME=bookstack/' .env |
| 134 | + sed -i.bak "s/DB_PASSWORD=.*\$/DB_PASSWORD=$DB_PASS/" .env |
| 135 | + # Generate the application key |
| 136 | + php artisan key:generate --no-interaction --force |
| 137 | +} |
| 138 | + |
| 139 | +# Run the BookStack database migrations for the first time |
| 140 | +function run_bookstack_database_migrations() { |
| 141 | + cd "$BOOKSTACK_DIR" || exit |
| 142 | + php artisan migrate --no-interaction --force |
| 143 | +} |
| 144 | + |
| 145 | +# Set file and folder permissions |
| 146 | +# Sets current user as owner user and www-data as owner group then |
| 147 | +# provides group write access only to required directories. |
| 148 | +# Hides the `.env` file so it's not visible to other users on the system. |
| 149 | +function run_set_application_file_permissions() { |
| 150 | + cd "$BOOKSTACK_DIR" || exit |
| 151 | + chown -R "$SCRIPT_USER":www-data ./ |
| 152 | + chmod -R 755 ./ |
| 153 | + chmod -R 775 bootstrap/cache public/uploads storage |
| 154 | + chmod 740 .env |
| 155 | + |
| 156 | + # Tell git to ignore permission changes |
| 157 | + git config core.fileMode false |
| 158 | +} |
| 159 | + |
| 160 | +# Setup apache with the needed modules and config |
| 161 | +function run_configure_apache() { |
| 162 | + # Enable required apache modules |
| 163 | + a2enmod rewrite |
| 164 | + a2enmod php8.1 |
| 165 | + |
| 166 | + # Set-up the required BookStack apache config |
| 167 | + cat >/etc/apache2/sites-available/bookstack.conf <<EOL |
| 168 | +<VirtualHost *:80> |
| 169 | + ServerName ${DOMAIN} |
| 170 | +
|
| 171 | + ServerAdmin webmaster@localhost |
| 172 | + DocumentRoot /var/www/bookstack/public/ |
| 173 | +
|
| 174 | + <Directory /var/www/bookstack/public/> |
| 175 | + Options -Indexes +FollowSymLinks |
| 176 | + AllowOverride None |
| 177 | + Require all granted |
| 178 | + <IfModule mod_rewrite.c> |
| 179 | + <IfModule mod_negotiation.c> |
| 180 | + Options -MultiViews -Indexes |
| 181 | + </IfModule> |
| 182 | +
|
| 183 | + RewriteEngine On |
| 184 | +
|
| 185 | + # Handle Authorization Header |
| 186 | + RewriteCond %{HTTP:Authorization} . |
| 187 | + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] |
| 188 | +
|
| 189 | + # Redirect Trailing Slashes If Not A Folder... |
| 190 | + RewriteCond %{REQUEST_FILENAME} !-d |
| 191 | + RewriteCond %{REQUEST_URI} (.+)/$ |
| 192 | + RewriteRule ^ %1 [L,R=301] |
| 193 | +
|
| 194 | + # Handle Front Controller... |
| 195 | + RewriteCond %{REQUEST_FILENAME} !-d |
| 196 | + RewriteCond %{REQUEST_FILENAME} !-f |
| 197 | + RewriteRule ^ index.php [L] |
| 198 | + </IfModule> |
| 199 | + </Directory> |
| 200 | +
|
| 201 | + ErrorLog \${APACHE_LOG_DIR}/error.log |
| 202 | + CustomLog \${APACHE_LOG_DIR}/access.log combined |
| 203 | +
|
| 204 | +</VirtualHost> |
| 205 | +EOL |
| 206 | + |
| 207 | + # Disable the default apache site and enable BookStack |
| 208 | + a2dissite 000-default.conf |
| 209 | + a2ensite bookstack.conf |
| 210 | + |
| 211 | + # Restart apache to load new config |
| 212 | + systemctl restart apache2 |
| 213 | +} |
| 214 | + |
| 215 | +info_msg "This script logs full output to $LOGPATH which may help upon issues." |
| 216 | +sleep 1 |
| 217 | + |
| 218 | +run_pre_install_checks |
| 219 | +run_prompt_for_domain_if_required |
| 220 | +info_msg "" |
| 221 | +info_msg "Installing using the domain or IP \"$DOMAIN\"" |
| 222 | +info_msg "" |
| 223 | +sleep 1 |
| 224 | + |
| 225 | +info_msg "[1/9] Installing required system packages... (This may take several minutes)" |
| 226 | +run_package_installs >> "$LOGPATH" 2>&1 |
| 227 | + |
| 228 | +info_msg "[2/9] Preparing MySQL database..." |
| 229 | +run_database_setup >> "$LOGPATH" 2>&1 |
| 230 | + |
| 231 | +info_msg "[3/9] Downloading BookStack to ${BOOKSTACK_DIR}..." |
| 232 | +run_bookstack_download >> "$LOGPATH" 2>&1 |
| 233 | + |
| 234 | +info_msg "[4/9] Installing Composer (PHP dependency manager)..." |
| 235 | +run_install_composer >> "$LOGPATH" 2>&1 |
| 236 | + |
| 237 | +info_msg "[5/9] Installing PHP dependencies using composer..." |
| 238 | +run_install_bookstack_composer_deps >> "$LOGPATH" 2>&1 |
| 239 | + |
| 240 | +info_msg "[6/9] Creating and populating BookStack .env file..." |
| 241 | +run_update_bookstack_env >> "$LOGPATH" 2>&1 |
| 242 | + |
| 243 | +info_msg "[7/9] Running initial BookStack database migrations..." |
| 244 | +run_bookstack_database_migrations >> "$LOGPATH" 2>&1 |
| 245 | + |
| 246 | +info_msg "[8/9] Setting BookStack file & folder permissions..." |
| 247 | +run_set_application_file_permissions >> "$LOGPATH" 2>&1 |
| 248 | + |
| 249 | +info_msg "[9/9] Configuring apache server..." |
| 250 | +run_configure_apache >> "$LOGPATH" 2>&1 |
| 251 | + |
| 252 | +info_msg "----------------------------------------------------------------" |
| 253 | +info_msg "Setup finished, your BookStack instance should now be installed!" |
| 254 | +info_msg "- Default login email: admin@admin.com" |
| 255 | +info_msg "- Default login password: password" |
| 256 | +info_msg "- Access URL: http://$CURRENT_IP/ or http://$DOMAIN/" |
| 257 | +info_msg "- BookStack install path: $BOOKSTACK_DIR" |
| 258 | +info_msg "- Install script log: $LOGPATH" |
| 259 | +info_msg "---------------------------------------------------------------" |
0 commit comments