#!/bin/bash # This script is run before the box code is switched. This means that we can # put network related/curl downloads here. If the script fails, the old code # will continue to run set -eu -o pipefail if [[ ${EUID} -ne 0 ]]; then echo "This script should be run as root." > /dev/stderr exit 1 fi function log() { echo -e "$(date +'%Y-%m-%dT%H:%M:%S')" "==> installer: $1" } apt_ready="no" function prepare_apt_once() { [[ "${apt_ready}" == "yes" ]] && return log "Making sure apt is in a good state" log "Waiting for all dpkg tasks to finish..." while fuser /var/lib/dpkg/lock; do sleep 1 done # it's unclear what needs to be run first or whether both these command should be run. so keep trying both for count in {1..3}; do # alternative to apt-install -y --fix-missing ? if ! dpkg --force-confold --configure -a; then log "dpkg reconfigure failed (try $count)" dpkg_configure="no" else dpkg_configure="yes" fi if ! apt update -y; then log "apt update failed (try $count)" apt_update="no" else apt_update="yes" fi [[ "${dpkg_configure}" == "yes" && "${apt_update}" == "yes" ]] && break sleep 1 done apt_ready="yes" if [[ "${dpkg_configure}" == "yes" && "${apt_update}" == "yes" ]]; then log "apt is ready" else log "apt is not ready but proceeding anyway" fi } readonly user=yellowtent readonly box_src_dir=/home/${user}/box readonly curl="curl --fail --connect-timeout 20 --retry 10 --retry-delay 2 --max-time 2400" readonly script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly box_src_tmp_dir="$(realpath ${script_dir}/..)" readonly ubuntu_version=$(lsb_release -rs) readonly ubuntu_codename=$(lsb_release -cs) readonly is_update=$(systemctl is-active -q box && echo "yes" || echo "no") log "Updating from $(cat $box_src_dir/VERSION 2>/dev/null) to $(cat $box_src_tmp_dir/VERSION 2>/dev/null)" # https://docs.docker.com/engine/installation/linux/ubuntulinux/ readonly docker_version=20.10.21 readonly containerd_version=1.6.10-1 if ! which docker 2>/dev/null || [[ $(docker version --format {{.Client.Version}}) != "${docker_version}" ]]; then log "installing/updating docker" # create systemd drop-in file already to make sure images are with correct driver mkdir -p /etc/systemd/system/docker.service.d echo -e "[Service]\nExecStart=\nExecStart=/usr/bin/dockerd -H fd:// --log-driver=journald --exec-opt native.cgroupdriver=cgroupfs --storage-driver=overlay2 --experimental --ip6tables" > /etc/systemd/system/docker.service.d/cloudron.conf # there are 3 packages for docker - containerd, CLI and the daemon (https://download.docker.com/linux/ubuntu/dists/jammy/pool/stable/amd64/) $curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/containerd.io_${containerd_version}_amd64.deb" -o /tmp/containerd.deb $curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/docker-ce-cli_${docker_version}~3-0~ubuntu-${ubuntu_codename}_amd64.deb" -o /tmp/docker-ce-cli.deb $curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/docker-ce_${docker_version}~3-0~ubuntu-${ubuntu_codename}_amd64.deb" -o /tmp/docker.deb log "installing docker" prepare_apt_once apt install -y /tmp/containerd.deb /tmp/docker-ce-cli.deb /tmp/docker.deb rm /tmp/containerd.deb /tmp/docker-ce-cli.deb /tmp/docker.deb fi # we want atleast nginx 1.14 for TLS v1.3 support. Ubuntu 20/22 already has nginx 1.18 # Ubuntu 18 OpenSSL does not have TLS v1.3 support, so we use the upstream nginx packages readonly nginx_version=$(nginx -v 2>&1) if [[ "${ubuntu_version}" == "20.04" ]]; then if [[ "${nginx_version}" == *"Ubuntu"* ]]; then log "switching nginx to ubuntu package" prepare_apt_once apt remove -y nginx apt install -y nginx-full fi elif [[ "${ubuntu_version}" == "18.04" ]]; then if [[ "${nginx_version}" != *"1.18."* ]]; then log "installing/updating nginx 1.18" $curl -sL http://nginx.org/packages/ubuntu/pool/nginx/n/nginx/nginx_1.18.0-2~${ubuntu_codename}_amd64.deb -o /tmp/nginx.deb prepare_apt_once # apt install with install deps (as opposed to dpkg -i) apt install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" --force-yes /tmp/nginx.deb rm /tmp/nginx.deb fi fi readonly node_version=16.18.1 if ! which node 2>/dev/null || [[ "$(node --version)" != "v${node_version}" ]]; then log "installing/updating node ${node_version}" mkdir -p /usr/local/node-${node_version} $curl -sL https://nodejs.org/dist/v${node_version}/node-v${node_version}-linux-x64.tar.gz -o /tmp/node.tar.gz tar zxvf /tmp/node.tar.gz --strip-components=1 -C /usr/local/node-${node_version} rm /tmp/node.tar.gz ln -sf /usr/local/node-${node_version}/bin/node /usr/bin/node ln -sf /usr/local/node-${node_version}/bin/npm /usr/bin/npm rm -rf /usr/local/node-16.14.2 fi # obsolete module rm -rf /usr/local/cloudron-syslog # note that rebuild requires the above node for try in `seq 1 10`; do # for reasons unknown, the dtrace package will fail. but rebuilding second time will work # We need --unsafe-perm as we run as root and the folder is owned by root, # however by default npm drops privileges for npm rebuild # https://docs.npmjs.com/misc/config#unsafe-perm if cd "${box_src_tmp_dir}" && npm rebuild --unsafe-perm; then break; fi log "Failed to rebuild, trying again" sleep 5 done if [[ ${try} -eq 10 ]]; then log "npm rebuild failed, giving up" exit 4 fi log "downloading new addon images" images=$(node -e "let i = require('${box_src_tmp_dir}/src/infra_version.js'); console.log(i.baseImages.map(function (x) { return x.tag; }).join(' '), Object.keys(i.images).map(function (x) { return i.images[x].tag; }).join(' '));") log "\tPulling docker images: ${images}" if ! curl -s --fail --connect-timeout 2 --max-time 2 https://ipv4.api.cloudron.io/api/v1/helper/public_ip; then docker_registry=registry.ipv6.docker.com else docker_registry=registry-1.docker.io fi log "\tUsing ${docker_registry} as the docker registry" for image in ${images}; do while ! docker pull "${docker_registry}/${image}"; do # this pulls the image using the sha256 log "Could not pull ${image}" sleep 5 done while ! docker pull "${docker_registry}/${image%@sha256:*}"; do # this will tag the image for readability log "Could not pull ${image%@sha256:*}" sleep 5 done done log "creating cloudron-support user" if ! id cloudron-support 2>/dev/null; then useradd --system --comment "Cloudron Support (support@cloudron.io)" --create-home --no-user-group --shell /bin/bash cloudron-support fi log "locking the ${user} account" usermod --shell /usr/sbin/nologin "${user}" passwd --lock "${user}" if [[ "${is_update}" == "yes" ]]; then log "stop box service for update" ${box_src_dir}/setup/stop.sh fi # ensure we are not inside the source directory, which we will remove now cd /root log "switching the box code" rm -rf "${box_src_dir}" mv "${box_src_tmp_dir}" "${box_src_dir}" chown -R "${user}:${user}" "${box_src_dir}" log "calling box setup script" "${box_src_dir}/setup/start.sh"