From dd9841cd94db448c512ba7d9b4e393486bfbec61 Mon Sep 17 00:00:00 2001 From: Jamil Date: Thu, 20 Oct 2022 13:43:31 -0700 Subject: [PATCH] Polish install, migration script, docs (#1050) * Checkpoint * Checkpoint * checkpoint * fix typo * Update migrate with changes * UID and GID * fix perms * Remove custom user * Fix migrate script typos * fix grep * remove cleverness * migrate final fixes --- docker-compose.prod.yml | 18 +- docs/docs/administer/uninstall.mdx | 22 ++- docs/docs/deploy/README.mdx | 15 +- docs/docs/deploy/docker/README.mdx | 2 +- .../firezone/templates/sv-phoenix-finish.erb | 2 +- scripts/docker_migrate.sh | 167 +++++++++++------- scripts/install.sh | 96 ++++++---- 7 files changed, 202 insertions(+), 120 deletions(-) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 61adfc12c..a3d639bb1 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,5 +1,11 @@ -# Example compose deployment - +# Example compose file for production deployment. +# +# Note: This file is meant to serve as a template. Please modify it +# according to your needs. Read more about Docker Compose: +# +# https://docs.docker.com/compose/compose-file/ +# +# x-deploy: &default-deploy restart_policy: condition: on-failure @@ -15,7 +21,7 @@ services: caddy: image: caddy:2 volumes: - - ./caddy:/data/caddy + - ${FZ_INSTALL_DIR:-.}/caddy:/data/caddy ports: - 80:80 - 443:443 @@ -30,12 +36,12 @@ services: env_file: # This should contain a list of env vars for configuring Firezone. # See https://docs.firezone.dev/reference/env-vars for more info. - - .env + - ${FZ_INSTALL_DIR:-.}/.env volumes: # IMPORTANT: Persists WireGuard private key and other data. If # /var/firezone/private_key exists when Firezone starts, it is # used as the WireGuard private. Otherwise, one is generated. - - ./firezone:/var/firezone + - ${FZ_INSTALL_DIR:-.}/firezone:/var/firezone cap_add: # Needed for WireGuard and firewall support. - NET_ADMIN @@ -53,7 +59,7 @@ services: postgres: image: postgres:15 volumes: - - ./postgres:/var/lib/postgresql/data + - ${FZ_INSTALL_DIR:-.}/postgres:/var/lib/postgresql/data environment: POSTGRES_DB: ${DATABASE_NAME:-firezone} POSTGRES_USER: ${DATABASE_USER:-postgres} diff --git a/docs/docs/administer/uninstall.mdx b/docs/docs/administer/uninstall.mdx index f01d106c7..d78a11d3a 100644 --- a/docs/docs/administer/uninstall.mdx +++ b/docs/docs/administer/uninstall.mdx @@ -3,6 +3,21 @@ title: Uninstall sidebar_position: 5 --- +Firezone can be uninstalled using the steps below. + +:::warning +This will irreversibly destroy ALL Firezone data and can't be undone. +::: + + + + +For docker-based deployments, simply delete the working directory where +you installed the Firezone docker files (`$HOME/.firezone` by default). + + + + To completely remove Omnibus-based deployments of Firezone run the [uninstall.sh script](https://github.com/firezone/firezone/blob/master/scripts/uninstall.sh): @@ -10,8 +25,5 @@ script](https://github.com/firezone/firezone/blob/master/scripts/uninstall.sh): sudo /bin/bash -c "$(curl -fsSL https://github.com/firezone/firezone/raw/master/scripts/uninstall.sh)" ``` -**Warning**: This will irreversibly destroy ALL Firezone data and can't be -undone. - -For docker-based deployments, simply delete the working directory where -you installed the Firezone docker files (`/data/firezone` by default). + + diff --git a/docs/docs/deploy/README.mdx b/docs/docs/deploy/README.mdx index 7c53db3b9..d0584fe11 100644 --- a/docs/docs/deploy/README.mdx +++ b/docs/docs/deploy/README.mdx @@ -3,8 +3,8 @@ title: Deploy sidebar_position: 2 --- -Firezone can be deployed on most Linux servers in minutes. Read more -below to get started. +Firezone can be deployed on most Docker-supported platforms in about a minute. +Read more below to get started. ## Deployment Methods @@ -19,8 +19,8 @@ preferred method of deployment. :::note Chef Infra Client, the configuration system Chef Omnibus relies on, has been [scheduled for End-of-Life in 2024](https://docs.chef.io/versions/). As such, -Omnibus-based deployments will be deprecated in a future version of Firezone. -To transition to Docker from Omnibus today, follow our [migration guide +support for Omnibus-based deployments will be removed in a future version of +Firezone. To transition to Docker from Omnibus today, follow our [migration guide ](../administer/migrate). ::: @@ -87,13 +87,6 @@ See the [configuration file reference](../reference/configuration-file) for details. -:::warning -Firezone modifies the kernel netfilter and routing tables. Other -programs that modify the Linux routing table or firewall may interfere with -Firezone's operation. For help troubleshooting connectivity issues, see -[troubleshoot](../administer/troubleshoot). -::: - ### Resource Requirements We recommend **starting with 1 vCPU and 1 GB of RAM and scaling up** as the diff --git a/docs/docs/deploy/docker/README.mdx b/docs/docs/deploy/docker/README.mdx index 6604a7624..6a3772268 100644 --- a/docs/docs/deploy/docker/README.mdx +++ b/docs/docs/deploy/docker/README.mdx @@ -44,7 +44,7 @@ After prerequisites are satisfied, you're ready to install the Firezone Server. The easiest way to deploy Firezone with Docker is the automatic install script: ```bash -curl -fsSL https://github.com/firezone/firezone/raw/master/scripts/install.sh | bash -E +bash <(curl -fsSL https://github.com/firezone/firezone/raw/master/scripts/install.sh) ``` This will ask you a few questions regarding initial configuration, then proceed diff --git a/omnibus/cookbooks/firezone/templates/sv-phoenix-finish.erb b/omnibus/cookbooks/firezone/templates/sv-phoenix-finish.erb index 3e38d23e6..fca1cc6df 100644 --- a/omnibus/cookbooks/firezone/templates/sv-phoenix-finish.erb +++ b/omnibus/cookbooks/firezone/templates/sv-phoenix-finish.erb @@ -32,7 +32,7 @@ if [ $1 -eq "1" ]; then count=`cat count` if [ $count -eq "5" ]; then rm -f count - echo "Firezone detected a service crash loop. Taking service down. For support please email support@firez.one and include a copy of these crash logs." + echo "Firezone detected a service crash loop. Taking service down. For support please email support@firezone.dev and include a copy of these crash logs." echo 'd' > supervise/control else new_count=$((count+1)) diff --git a/scripts/docker_migrate.sh b/scripts/docker_migrate.sh index 8f72a49e7..ba75fcedd 100755 --- a/scripts/docker_migrate.sh +++ b/scripts/docker_migrate.sh @@ -1,37 +1,54 @@ #!/bin/bash set -eE -trap 'handler' ERR +trap "handler" ERR handler () { echo - echo 'An error occurred running this migration. Your existing Firezone installation has not been affected.' - rm .env + echo "An error occurred running this migration. Your existing Firezone installation has not been affected." + echo exit 1 } curlCheck () { if ! type curl > /dev/null; then - echo 'curl not found. Please install curl to use this script.' + echo "curl not found. Please install curl to use this script." exit 1 fi } dockerCheck () { if ! command -v docker > /dev/null; then - echo 'docker not found. Please install docker and try again.' + echo "docker not found. Please install docker and try again." + exit 1 + fi + + if command -v docker-compose &> /dev/null; then + dc='docker-compose' + else + dc='docker compose' + fi + + $dc version | grep -q "v2" + if [ $? -ne 0 ]; then + echo "Error: Automatic migration is only supported with Docker Compose version 2 or higher." exit 1 fi } prompt () { - echo 'This script will copy Omnibus-based Firezone configuration to Docker-based Firezone configuration.' - echo 'It operates non-destructively and leaves your current Firezone services running.' - read -p 'Proceed? (Y/n): ' migrate + echo "This script will copy Omnibus-based Firezone configuration to Docker-based Firezone configuration." + echo "It operates non-destructively and leaves your current Firezone services running." + read -p "Proceed? (Y/n): " migrate case $proceed in - n|N) echo 'Aborted' ;; - *) migrate + n|N) + echo "Aborted" + exit + ;; + *) + migrate + ;; esac } @@ -43,39 +60,47 @@ condIns () { val=$(cat $dir/$file) val=$(echo $val | sed 's/"/\\"/g') if [ $file = "EXTERNAL_URL" ]; then - val=$(echo $val | sed 's:/*$::') + val=$(echo $val | sed "s:/*$::") fi - echo "$file=\"$val\"" >> .env + echo "$file=\"$val\"" >> $installDir/.env + fi +} + +promptInstallDir() { + defaultInstallDir="${HOME}/.firezone" + read -p "Enter the desired installation directory ($defaultInstallDir): " installDir + if [ -z "$installDir" ]; then + installDir=$defaultInstallDir + fi + if ! test -d $installDir; then + mkdir $installDir fi } migrate () { + export FZ_INSTALL_DIR=$installDir + promptInstallDir env_files=/opt/firezone/service/phoenix/env - cwd=`pwd` - read -p "Enter the desired installation directory ($cwd): " installDir - if [ -z "$installDir" ]; then - installDir=$cwd - fi - cd $installDir - if ! test -f docker-compose.yml; then - curl -fsSL https://raw.githubusercontent.com/firezone/firezone/master/docker-compose.prod.yml -o docker-compose.yml - fi - # setup data dir - mkdir -p /data/firezone/firezone + if ! test -f $installDir/docker-compose.yml; then + curl -fsSL https://raw.githubusercontent.com/firezone/firezone/master/docker-compose.prod.yml -o $installDir/docker-compose.yml + fi # copy tid - cp $env_files/TELEMETRY_ID /data/firezone/firezone/.tid + mkdir -p $installDir/firezone/ + cp $env_files/TELEMETRY_ID $installDir/firezone/.tid # copy private key - cp /var/opt/firezone/cache/wg_private_key /data/firezone/firezone/private_key - chown root:root /data/firezone/firezone/private_key - chmod 0600 /data/firezone/firezone/private_key + cp /var/opt/firezone/cache/wg_private_key $installDir/firezone/private_key + chown $(id -u):$(id -g) $installDir/firezone/private_key + chmod 0600 $installDir/firezone/private_key # generate .env - if test -f ".env"; then - echo 'Existing .env detected! Remove and try again.' - exit 1 + if test -f "$installDir/.env"; then + echo + echo "Existing .env detected! Moving to .env.bak and continuing..." + echo + mv $installDir/.env $installDir/.env.bak fi # BEGIN env vars that matter @@ -89,8 +114,8 @@ migrate () { condIns $env_files "COOKIE_ENCRYPTION_SALT" condIns $env_files "DATABASE_NAME" # These shouldn't change - echo "DATABASE_HOST=postgres" >> .env - echo "DATABASE_PORT=5432" >> .env + echo "DATABASE_HOST=postgres" >> $installDir/.env + echo "DATABASE_PORT=5432" >> $installDir/.env condIns $env_files "DATABASE_POOL" condIns $env_files "DATABASE_SSL" condIns $env_files "DATABASE_SSL_OPTS" @@ -124,68 +149,90 @@ migrate () { condIns $env_files "CONNECTIVITY_CHECKS_ENABLED" condIns $env_files "CONNECTIVITY_CHECKS_INTERVAL" - # optional vars if test -f $env_files/DATABASE_PASSWORD; then db_pass=$(cat $env_files/DATABASE_PASSWORD) else db_pass=$(/opt/firezone/embedded/bin/openssl rand -base64 12) fi - echo "DATABASE_PASSWORD=\"${db_pass}\"" >> .env + echo "DATABASE_PASSWORD=\"${db_pass}\"" >> $installDir/.env if test -f $env_files/DEFAULT_ADMIN_PASSWORD; then - echo "DEFAULT_ADMIN_PASSWORD=\"$(cat $env_files/DEFAULT_ADMIN_PASSWORD)\"" >> .env + echo "DEFAULT_ADMIN_PASSWORD=\"$(cat $env_files/DEFAULT_ADMIN_PASSWORD)\"" >> $installDir/.env fi # END env vars that matter } doDumpLoad () { - if command -v docker-compose &> /dev/null; then - dc='docker-compose' - else - dc='docker compose' - fi - - echo 'Dumping existing database to ./firezone.sql' + echo "Dumping existing database to $installDir/firezone_omnibus_backup.sql" db_host=$(cat /opt/firezone/service/phoenix/env/DATABASE_HOST) db_port=$(cat /opt/firezone/service/phoenix/env/DATABASE_PORT) db_name=$(cat /opt/firezone/service/phoenix/env/DATABASE_NAME) db_user=$(cat /opt/firezone/service/phoenix/env/DATABASE_USER) - /opt/firezone/embedded/bin/pg_dump -h $db_host -p $db_port -d $db_name -U $db_user > firezone.sql + /opt/firezone/embedded/bin/pg_dump -h $db_host -p $db_port -d $db_name -U $db_user > $installDir/firezone_omnibus_backup.sql - echo 'Loading existing database into docker...' - DATABASE_PASSWORD=$db_pass $dc up -d postgres + echo "Loading existing database into docker..." + DATABASE_PASSWORD=$db_pass $dc -f $installDir/docker-compose.yml up -d postgres sleep 5 - $dc exec postgres psql -U postgres -h 127.0.0.1 -c "ALTER ROLE postgres WITH PASSWORD '${db_pass}'" - $dc exec postgres dropdb -U postgres -h 127.0.0.1 --if-exists $db_name - $dc exec postgres createdb -U postgres -h 127.0.0.1 $db_name - $dc exec -T postgres psql -U postgres -h 127.0.0.1 -d $db_name < firezone.sql - rm firezone.sql + $dc -f $installDir/docker-compose.yml exec postgres psql -U postgres -h 127.0.0.1 -c "ALTER ROLE postgres WITH PASSWORD '${db_pass}'" + $dc -f $installDir/docker-compose.yml exec postgres dropdb -U postgres -h 127.0.0.1 --if-exists $db_name + $dc -f $installDir/docker-compose.yml exec postgres createdb -U postgres -h 127.0.0.1 $db_name + $dc -f $installDir/docker-compose.yml exec -T postgres psql -U postgres -h 127.0.0.1 -d $db_name < $installDir/firezone_omnibus_backup.sql + rm $installDir/firezone_omnibus_backup.sql } dumpLoadDb () { - echo 'Would you like Firezone to attempt to migrate your existing database to Dockerized Postgres too?' - echo 'We only recommend this for Firezone installations using the default bundled Postgres.' - read -p 'Proceed? (Y/n): ' dumpLoad + echo "Would you like Firezone to attempt to migrate your existing database to Dockerized Postgres too?" + echo "We only recommend this for Firezone installations using the default bundled Postgres." + read -p "Proceed? (Y/n): " dumpLoad case $dumpLoad in - n|N) echo 'Aborted' ;; - *) doDumpLoad + n|N) + echo "Aborted" + exit + ;; + *) + doDumpLoad + ;; esac } doBoot () { + echo "Stopping Omnibus Firezone..." firezone-ctl stop + + echo "Tearing down network..." firezone-ctl teardown-network - $dc up -d + + echo "Disabling systemd unit..." + systemctl disable firezone-runsvdir-start.service + + echo "Bringing Docker services up..." + $dc -f $installDir/docker-compose.yml up -d } printSuccess () { - echo 'Done! Would you like to stop Omnibus Firezone and start Docker Firezone now?' - read -p 'Proceed? (y/N): ' boot + echo "Done! Would you like to stop Omnibus Firezone and start Docker Firezone now?" + read -p "Proceed? (y/N): " boot case $boot in - y|Y) doBoot ;; - *) echo "Aborted. Run 'firezone-ctl stop && firezone-ctl teardown-network && docker-compose up -d' to stop Omnibus Firezone and start Docker Firezone when you're ready." + y|Y) + doBoot + ;; + *) +cat << EOF +Aborted. Run the following to stop Omnibus Firezone and start Docker Firezone when you're ready. + + sudo firezone-ctl stop + sudo firezone-ctl teardown-network + docker-compose up -d + +You may also want to disable the systemd unit: + + sudo systemctl disable firezone-runsvdir-start.service + +EOF + exit + ;; esac } diff --git a/scripts/install.sh b/scripts/install.sh index da3f8d505..7f889d837 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -3,19 +3,27 @@ set -e dockerCheck () { if ! type docker > /dev/null; then - echo 'docker not found. Please install docker and try again.' + echo "docker not found. Please install docker and try again." exit fi if command -v docker-compose &> /dev/null; then - dc='docker-compose' + dc="docker-compose" else - dc='docker compose' + dc="docker compose" + fi + + + $dc version | grep -q "v2" + if [ $? -ne 0 ]; then + echo "Error: Automatic installation is only supported with Docker Compose version 2 or higher." + echo "Please upgrade Docker Compose or use the manual installation method: https://docs.firezone.dev/deploy/docker" + exit 1 fi } curlCheck () { if ! type curl > /dev/null; then - echo 'curl not found. Please install curl to use this script.' + echo "curl not found. Please install curl to use this script." exit fi } @@ -25,7 +33,7 @@ capture () { if [ ! -z "$telemetry_id" ]; then curl -s -XPOST \ -m 5 \ - -H 'Content-Type: application/json' \ + -H "Content-Type: application/json" \ -d "{ \"api_key\": \"phc_ubuPhiqqjMdedpmbWpG2Ak3axqv5eMVhFDNBaXl9UZK\", \"event\": \"$1\", @@ -45,12 +53,15 @@ promptInstallDir() { if [ -z "$installDir" ]; then installDir=$defaultInstallDir fi + if ! test -d $installDir; then + mkdir $installDir + fi } promptExternalUrl() { read -p "$1" externalUrl # Remove trailing slash if present - externalUrl=$(echo $externalUrl | sed 's:/*$::') + externalUrl=$(echo $externalUrl | sed "s:/*$::") if [ -z "$externalUrl" ]; then externalUrl=$defaultExternalUrl fi @@ -59,49 +70,64 @@ promptExternalUrl() { promptEmail() { read -p "$1" adminEmail case $adminEmail in - *@*) adminUser=$adminEmail;; - *) promptEmail "Please provide a valid email: " + *@*) + adminUser=$adminEmail + ;; + *) + promptEmail "Please provide a valid email: " + ;; esac } promptContact() { - read -p 'Could we email you to ask for product feedback? Firezone depends heavily on input from users like you to steer development. (Y/n): ' contact + read -p "Could we email you to ask for product feedback? Firezone depends heavily on input from users like you to steer development. (Y/n): " contact case $contact in - n|N);; - *) capture "contactOk" $adminUser + n|N) + ;; + *) + capture "contactOk" $adminUser + ;; esac } promptACME() { - read -p 'Would you like to enable automatic SSL cert provisioning? Requires a valid DNS record and port 80 to be reachable. (Y/n): ' acme + read -p "Would you like to enable automatic SSL cert provisioning? Requires a valid DNS record and port 80 to be reachable. (Y/n): " acme case $acme in - n|N) export CADDY_OPTS="--internal-certs";; + n|N) + export CADDY_OPTS="--internal-certs" + ;; *) + export CADDY_OPTS="" + ;; esac } firezoneSetup() { - installDir=${installDir/\~/$HOME} - cd $installDir - if ! test -f docker-compose.yml; then - curl -fsSL https://raw.githubusercontent.com/firezone/firezone/master/docker-compose.prod.yml -o docker-compose.yml + export FZ_INSTALL_DIR=$installDir + + if ! test -f $installDir/docker-compose.yml; then + curl -fsSL https://raw.githubusercontent.com/firezone/firezone/master/docker-compose.prod.yml -o $installDir/docker-compose.yml fi db_pass=$(od -vN "8" -An -tx1 /dev/urandom | tr -d " \n" ; echo) - docker run --rm firezone/firezone bin/gen-env > .env - sed -i.bak "s/ADMIN_EMAIL=.*/ADMIN_EMAIL=$1/" .env - sed -i.bak "s~EXTERNAL_URL=.*~EXTERNAL_URL=$2~" .env - sed -i.bak "s/DATABASE_PASSWORD=.*/DATABASE_PASSWORD=$db_pass/" .env + docker run --rm firezone/firezone bin/gen-env > "$installDir/.env" + sed -i.bak "s/ADMIN_EMAIL=.*/ADMIN_EMAIL=$1/" "$installDir/.env" + sed -i.bak "s~EXTERNAL_URL=.*~EXTERNAL_URL=$2~" "$installDir/.env" + sed -i.bak "s/DATABASE_PASSWORD=.*/DATABASE_PASSWORD=$db_pass/" "$installDir/.env" + + echo "UID=$(id -u)" >> $installDir/.env + echo "GID=$(id -g)" >> $installDir/.env + # Set DATABASE_PASSWORD explicitly here in case the user has this var set in their shell - DATABASE_PASSWORD=$db_pass $dc up -d postgres - echo 'Waiting for DB to boot...' + DATABASE_PASSWORD=$db_pass $dc -f $installDir/docker-compose.yml up -d postgres + echo "Waiting for DB to boot..." sleep 5 - $dc logs postgres - echo 'Resetting DB password...' - $dc exec postgres psql -p 5432 -U postgres -d firezone -h 127.0.0.1 -c "ALTER ROLE postgres WITH PASSWORD '${db_pass}'" - $dc up -d firezone caddy - echo 'Waiting for app to boot before creating admin...' + $dc -f $installDir/docker-compose.yml logs postgres + echo "Resetting DB password..." + $dc -f $installDir/docker-compose.yml exec postgres psql -p 5432 -U postgres -d firezone -h 127.0.0.1 -c "ALTER ROLE postgres WITH PASSWORD '${db_pass}'" + $dc -f $installDir/docker-compose.yml up -d firezone caddy + echo "Waiting for app to boot before creating admin..." sleep 15 - $dc exec firezone bin/create-or-reset-admin + $dc -f $installDir/docker-compose.yml exec firezone bin/create-or-reset-admin displayLogo @@ -111,12 +137,10 @@ Installation complete! You should now be able to log into the Web UI at $externalUrl with the following credentials: -`grep ADMIN_EMAIL .env` -`grep DEFAULT_ADMIN_PASSWORD .env` +`grep ADMIN_EMAIL $installDir/.env` +`grep DEFAULT_ADMIN_PASSWORD $installDir/.env` EOF - - cd - } displayLogo() { @@ -151,10 +175,10 @@ EOF } main() { - defaultInstallDir=`pwd` defaultExternalUrl="https://$(hostname)" - adminUser='' - externalUrl='' + adminUser="" + externalUrl="" + defaultInstallDir="$HOME/.firezone" promptEmail "Enter the administrator email you'd like to use for logging into this Firezone instance: " promptInstallDir "Enter the desired installation directory ($defaultInstallDir): " promptExternalUrl "Enter the external URL that will be used to access this instance. ($defaultExternalUrl): "