About Podman container

Hi,

since firezone will change to containerized type (docker), however which is not the prefer tool in my common Linux support (Redhat / Fedora).

here I have make test on podman, but no luck in podman-compose tool, seems it is not fully compatible.

so I try to modify it, I am not a programmer so the script may not perfect as expect, but should able to execute in a fresh installation.

here I would share the script for those who want to use podman for testing. thanks

I also share in google drive for easy download
https://drive.google.com/file/d/18jnK50crP337Dr5Rgq9ngumdwRfwQl0q/view

Regard,
Lawes

#!/bin/bash
set -e

#####################################
# This script is for podman only
# support in firezone 0.67 fresh deployment

# version 2.5
# - support podman 4.x
# - support firezone 0.67
# - support fresh installation only, not for data migration
# - support Linux, test on Fedora 35, 36
# - create pod
# - create firezone-network
# - create host container caddy
# - create bridge container firezone, postgres
# - assign static ip to firezone
# - fix caddy syntax error now
# Remark:
# - caddy process not running in PID 1, therefore podman stop command will not take effect
# ##################################


podmanCheck () {
  if ! type podman > /dev/null; then
    echo "podman not found. Please install podman and try again."
    exit 1
  fi

  set +e
  podman version | grep -q "Version:      4"
  if [ $? -ne 0 ]; then
    echo "Error: Automatic installation is only supported with Podman version 4 or higher."
    echo "Please upgrade Podman Version"
    exit 1
  fi
  set -e
}

curlCheck () {
  if ! type curl > /dev/null; then
    echo "curl not found. Please install curl to use this script."
    exit 1
  fi
}

capture () {
  if type curl > /dev/null; then
    if [ ! -z "$telemetry_id" ]; then
      curl -s -XPOST \
        -m 5 \
        -H "Content-Type: application/json" \
        -d "{
          \"api_key\": \"phc_ubuPhiqqjMdedpmbWpG2Ak3axqv5eMVhFDNBaXl9UZK\",
          \"event\": \"$1\",
          \"properties\": {
            \"distinct_id\": \"$telemetry_id\",
            \"email\": \"$2\"
          }
        }" \
        https://telemetry.firez.one/capture/ > /dev/null \
        || true
    fi
  fi
}

promptInstallDir() {
  read -p "$1" installDir
  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:/*$::")
  if [ -z "$externalUrl" ]; then
    externalUrl=$defaultExternalUrl
  fi
}

promptEmail() {
  read -p "$1" adminEmail
  case $adminEmail in
    *@*)
      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
  case $contact in
    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
  case $acme in
    n|N)
      tlsOpts=" tls internal {
                        on_demand
                }"
      ;;
    *)
      tlsOpts=" tls {
                        on_demand
                }"
      ;;
  esac
}

promptTelemetry() {
  read -p "Firezone collects crash and performance logs to help us improve the product. Would you like to disable this? (N/y): " telem
  case $telem in
    y|Y)
      telemEnabled="false"
      ;;
    *)
      telemEnabled="true"
      ;;
  esac
}

firezoneSetup() {
  export FZ_INSTALL_DIR=$installDir

  db_pass=$(od -vN "8" -An -tx1 /dev/urandom | tr -d " \n" ; echo)
  # podman, if use shortname for image search may return error, here use full image_path
  podman run --rm  docker.io/firezone/firezone:latest bin/gen-env > "$installDir/.env"
  # search and update .env variable
  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 "TLS_OPTS=$3" >> "$installDir/.env"
  echo "TELEMETRY_ENABLED=$telemEnabled" >> "$installDir/.env"

  # XXX: This causes perms issues on macOS with postgres
  # 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
  #
  # - f compse.yml file
  # - d backend execute
  DATABASE_PASSWORD=$db_pass
  echo "Show DATABASE_PASSWORD..."
  echo $DATABASE_PASSWORD
  #DATABASE_PASSWORD=$db_pass $dc -f $installDir/podman-compose.yml up -d postgres
  #
  # check does pod_firezone and firezone_network exist
  podExist=$(podman pod exists pod-firezone; echo $?)
  networkExist=$(podman network exists firezone-network; echo $?)
  caddyExist=$(podman container exists caddy; echo $?)
  #
  if [ $podExist -eq 0 ]; then
    echo " "
    echo "stopping and remove pod pod-firezone..."
    podman pod stop pod-firezone
    podman pod rm -i pod-firezone
    sleep 5
    REpodExist=$(podman pod exists pod-firezone; echo $?)
    if [ $REpodExist -gt 1 ]; then
      echo "something wrong during remove pod pod-firezone or container..."
      #echo "please manually correct It (podman system reset)"
      exit 1
    fi
  fi

  if [ $networkExist -eq 0 ]; then
    echo " "
    echo "remove pod firezone-network..."
    podman network rm firezone-network
    sleep 5
    REnetworkExist=$(podman network exists firezone-network; echo $?)
    if [ $REnetworkExist -gt 1 ]; then
      echo "something wrong during remove network firezone-network..."
      #echo "please manually correct It (podman system reset)"
      exit 1
    fi
  fi

  if [ $caddyExist -eq 0 ]; then
    echo " "
    echo "stop and remove container caddy..."
    podman container stop caddy
    sleep 5
    podman container rm -i caddy
    sleep 5
    REcaddyExist=$(podman container exists caddy; echo $?)
    if [ $REcaddyExist -gt 1 ]; then
      echo "something wrong during remove container caddy..."
      #echo "please manually correct It (podman system reset)"
      exit 1
    fi
  fi

  REpodExist=$(podman pod exists pod-firezone; echo $?)
  REnetworkExist=$(podman network exists firezone-network; echo $?)
  REcaddyExist=$(podman container exists caddy; echo $?)
  #
  if [[ $REpodExist -eq 1 && $REnetworkExist -eq 1 && $REcaddyExist -eq 1 ]]; then
          echo " "
          echo "creating pod pod-firezone..."
          podman pod create --name pod-firezone -p 51820:51820/udp
          sleep 3
          echo " "
          echo "creating network firezone-network..."
          podman network create firezone-network --subnet 172.25.0.0/16 --gateway 172.25.0.1 --label firezone-network
          sleep 5
  fi
  mkdir -p ${FZ_INSTALL_DIR}/postgres-data
  echo " "
  echo "creating container postgres..."
  # the following script run once only, if ${FZ_INSTALL_DIR:-.}/postgres-data is empty
  podman run --pod=pod-firezone --name postgres -dt --net=firezone-network -e POSTGRES_DB=${DATABASE_NAME:-firezone} -e POSTGRES_USER=${DATABASE_USER:-postgres} -e POSTGRES_PASSWORD=${DATABASE_PASSWORD:?err} -v ${FZ_INSTALL_DIR}/postgres-data/:/var/lib/postgresql/data/:Z docker.io/library/postgres:15
  echo " "
  echo "waiting for DB to boot..."
  sleep 5
  podman logs postgres
  echo " "
  echo "Resetting DB password..."
  sleep 5
  #
  # The following is the method to reset ROLE postgres passowrd ...
  # first, create a file in postgres home_directory /var/lib/postgresql/, named password_reset
  podman container exec postgres /bin/su - postgres -c 'touch password_reset'
  #
  # create content
  podman container exec postgres /bin/bash -c "echo -e ALTER ROLE postgres WITH PASSWORD \'${DATABASE_PASSWORD}\' > /var/lib/postgresql/password_reset"

  # then execute the reset process
  podman container exec postgres /bin/su - postgres -c 'psql -p 5432 -U postgres -d firezone -h localhost --file=password_reset'
  #
  # execute container_firezone
  # change to static ip, although podman support container alias, change it for compatible in future
  mkdir -p ${FZ_INSTALL_DIR:-.}/firezone
  podman run --pod=pod-firezone --name firezone -dt --net=firezone-network --ip=172.25.0.100 --requires postgres --cap-add NET_ADMIN,SYS_MODULE --sysctl net.ipv6.conf.all.disable_ipv6=0 --sysctl net.ipv4.ip_forward=1 --sysctl net.ipv6.conf.all.forwarding=1 --env-file=${FZ_INSTALL_DIR:-.}/.env -v ${FZ_INSTALL_DIR:-.}/firezone:/var/firezone:Z docker.io/firezone/firezone:latest
  #
  # execute container caddy
  mkdir -p ${FZ_INSTALL_DIR:-.}/caddy
  #
  externalFQDN=$externalUrl
  #remove http:// or https://
  externalFQDN=${externalFQDN#*//}
  #remove /*
  externalFQDN=${externalFQDN%/*}
  #
  # -execute run - caddy cli during container init
  #podman run --name caddy -dt --network=host --privileged -v ${FZ_INSTALL_DIR:-.}/caddy:/data/caddy:Z docker.io/library/caddy:2 caddy reverse-proxy --from ${externalFQDN} --to 172.25.0.100:13000
  #
  # -execute exec - bin/sh -c method
  #podman container exec caddy /bin/sh -c "cat <<@EOF > /etc/caddy/Caddyfile && cd /etc/caddy && caddy fmt --overwrite && caddy run --config /etc/caddy/Caddyfile
  #
  # -execute run - entrypoint method, interactive mode
  #podman run --name caddy -it --network=host --privileged --entrypoint=/bin/sh -v ${FZ_INSTALL_DIR:-.}/caddy:/data/caddy:Z docker.io/library/caddy:2 -i
  #
  # - execute run - entrypoint method, detach mode
  podman run --name caddy -dt --network=host --privileged --entrypoint "/bin/sh" -v ${FZ_INSTALL_DIR:-.}/caddy:/data/caddy:Z docker.io/library/caddy:2 -c "cat <<@EOF > /etc/caddy/Caddyfile && ( cd /etc/caddy; caddy fmt --overwrite; caddy run --config /etc/caddy/Caddyfile --adapter caddyfile )

${externalFQDN} {
        log
        reverse_proxy 172.25.0.100:13000
        ${tlsOpts:-}
        }
"@EOF

  echo " "
  echo "Waiting for app to boot before creating admin..."
  sleep 15
  podman exec firezone bin/create-or-reset-admin

  displayLogo

cat << EOF
Installation complete!

You should now be able to log into the Web UI at $externalUrl with the
following credentials:

`grep ADMIN_EMAIL $installDir/.env`
`grep DEFAULT_ADMIN_PASSWORD $installDir/.env`

EOF
}

displayLogo() {
cat << EOF

                                      ::
                                       !!:
                                       .??^
                                        ~J?^
                                        :???.
                                        .??J^
                                        .??J!
                                        .??J!
                                        ^J?J~
                                        !???:
                                       .???? ::
                                       ^J?J! :~:
                                       7???: :~~
                                      .???7  ~~~.
                                      :??J^ :~~^
                                      :???..~~~:
    .............                     .?J7 ^~~~        ....
 ..        ......::....                ~J!.~~~^       ::..
                  ...:::....            !7^~~~^     .^: .
                      ...:::....         ~~~~~~:. .:~^ .
                         ....:::....      .~~~~~~~~~:..
                             ...::::....   .::^^^^:...
                                .....:::.............
                                    .......:::.....

EOF
}

main() {
  defaultExternalUrl="https://$(hostname)"
  adminUser=""
  externalUrl=""
  defaultInstallDir="$HOME/.firezone"
  tlsOpts=""
  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): "
  promptACME
  promptContact
  promptTelemetry
  read -p "Press <ENTER> to install or Ctrl-C to abort."
  if [ $telemEnabled = "true" ]; then
    capture "install" "email-not-collected@dummy.domain"
  fi
  firezoneSetup $adminUser $externalUrl "$tlsOpts"
}

podmanCheck
curlCheck

telemetry_id=$(od -vN "8" -An -tx1 /dev/urandom | tr -d " \n" ; echo)

main

update:

  • this script for runtime only, after reboot the container won’t auto start, here we can issue cli:
    i. podman pod start pod-firezone
    ii. podman container start caddy

  • or use systemd to generate the startup script, but this is other topic, I may update it when free.

thanks
Lawes