install.fairie/docs/scripts/before/run_onchange_before_11-install-docker.sh.tmpl.md
2023-11-06 09:56:49 +00:00

10 KiB

title description sidebar_label slug githubLocation scriptLocation repoLocation
Docker Install Ensures Docker is installed, ensures the user can access Docker without sudo, and ensures Docker is configured to use gVisor 11 Docker Install /scripts/before/run_onchange_before_11-install-docker.sh.tmpl https://github.com/megabyte-labs/install.doctor/blob/master/home/.chezmoiscripts/universal/run_onchange_before_11-install-docker.sh.tmpl https://github.com/megabyte-labs/install.doctor/raw/master/home/.chezmoiscripts/universal/run_onchange_before_11-install-docker.sh.tmpl home/.chezmoiscripts/universal/run_onchange_before_11-install-docker.sh.tmpl

Docker Install

Ensures Docker is installed, ensures the user can access Docker without sudo, and ensures Docker is configured to use gVisor

Overview

This script ensures Docker is installed and then adds the provisioning user to the docker group so that they can access Docker without sudo. It also installs and configures gVisor for use with Docker.

gVisor

gVisor is included with our Docker setup because it improves the security of Docker. gVisor is an application kernel, written in Go, that implements a substantial portion of the Linux system call interface. It provides an additional layer of isolation between running applications and the host operating system. It has gained a lot of attention, perhaps partly, because it is maintained by Google.

Source Code

{{- if ne .host.distro.family "windows" -}}
#!/usr/bin/env bash
# @file Docker Install
# @brief Ensures Docker is installed, ensures the user can access Docker without sudo, and ensures Docker is configured to use gVisor
# @description
#     This script ensures Docker is installed and then adds the provisioning user to the `docker` group so that they can
#     access Docker without `sudo`. It also installs and configures gVisor for use with Docker.
#
#     ## gVisor
#
#     gVisor is included with our Docker setup because it improves the security of Docker. gVisor is an application kernel, written in Go,
#     that implements a substantial portion of the Linux system call interface. It provides an additional layer of isolation between running
#     applications and the host operating system. It has gained a lot of attention, perhaps partly, because it is maintained by Google.

{{ includeTemplate "universal/profile-before" }}
{{ includeTemplate "universal/logg-before" }}

### Install Docker
if [ -d /Applications ] && [ -d /System ]; then
    # macOS
    if [ ! -d /Applications/Docker.app ]; then
        logg info 'Installing Docker on macOS via Homebrew cask'
        brew install --cask docker
    else
        logg info 'Docker appears to be installed already'
    fi
    logg info 'Opening the Docker for Desktop app so that the Docker engine starts running'
    # Note: At one point `--install-privileged-components` was also a flag at the end below but it no longer appears to work
    open --background -a Docker --args --accept-license --unattended
elif command -v apt-get > /dev/null; then
    . /etc/os-release
    if [ "$ID" == 'ubuntu' ]; then
        logg info 'Installing Docker on Ubuntu'
    else
        logg info 'Installing Docker on Debian'
    fi
    sudo apt-get update
    sudo apt-get install -y ca-certificates curl gnupg lsb-release
    sudo mkdir -p /etc/apt/keyrings
    curl -fsSL "https://download.docker.com/linux/$ID/gpg" | sudo gpg --dearmor --yes -o /etc/apt/keyrings/docker.gpg
    sudo chmod a+r /etc/apt/keyrings/docker.gpg
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$ID $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    sudo apt-get update
    sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
elif command -v dnf > /dev/null; then
    . /etc/os-release
    if [ "$ID" == 'centos' ]; then
        logg info 'Installing Docker on CentOS'
    elif [ "$ID" == 'fedora' ]; then
        logg info 'Installing Docker on Fedora'
    else
        logg error 'Unknown OS - cannot install Docker' && exit 1
    fi
    sudo dnf -y install dnf-plugins-core
    sudo dnf config-manager --add-repo "https://download.docker.com/linux/$ID/docker-ce.repo"
    sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
elif command -v yum > /dev/null; then
    # CentOS
    logg info 'Installing Docker on CentOS'
    sudo yum install -y yum-utils
    sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    sudo yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
elif command -v apk > /dev/null; then
    # Alpine
    logg info 'Installing Docker on Alpine'
    sudo apk add --update docker
elif command -v pacman > /dev/null; then
    # Archlinux
    logg info 'Installing Docker on Archlinux'
    sudo pacman -Syu
    sudo pacman -S docker
elif command -v zypper > /dev/null; then
    # OpenSUSE
    logg info 'Installing Docker on OpenSUSE'
    sudo zypper addrepo https://download.docker.com/linux/sles/docker-ce.repo
    sudo zypper install docker-ce docker-ce-cli containerd.io docker-compose-plugin
fi

### Add Docker group on Linux
if command -v groupadd > /dev/null; then
    # Linux
    if ! cat /etc/group | grep docker > /dev/null; then
        logg info 'Creating Docker group'
        sudo groupadd docker
    fi
    logg info 'Adding user to Docker group'
    sudo usermod -aG docker "$USER"
fi

### Boot Docker on start with systemd on Linux machines
if command -v systemctl > /dev/null; then
    # Systemd Linux
    sudo systemctl start docker.service
    sudo systemctl enable docker.service
    sudo systemctl enable containerd.service
fi

### Installs pre-built gVisor using method recommended on official website
function gVisorPreBuilt() {
    logg info 'Installing gVisor using method recommended on official website'
    set -e
    mkdir /tmp/gvisor && cd /tmp/gvisor
    ARCH=$(uname -m)
    URL="https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}"
    logg info 'Downloading gVisor runsc and containerd-shim-runsc-v1 SHA signatures'
    wget "${URL}/runsc ${URL}/runsc.sha512" "${URL}/containerd-shim-runsc-v1 ${URL}/containerd-shim-runsc-v1.sha512"
    sha512sum -c runsc.sha512 -c containerd-shim-runsc-v1.sha512
    rm -f *.sha512
    chmod a+rx runsc containerd-shim-runsc-v1
    sudo mv runsc containerd-shim-runsc-v1 /usr/local/bin
}

### Installs gVisor using alternate Go method described on the GitHub page
function gVisorGo() {
    # Official build timed out - use Go method
    logg info 'Installing gVisor using the Go fallback method'
    sudo chown -Rf "$(whoami)" /usr/local/src/gvisor
    cd /usr/local/src/gvisor
    echo "module runsc" > go.mod
    GO111MODULE=on go get gvisor.dev/gvisor/runsc@go
    CGO_ENABLED=0 GO111MODULE=on sudo -E go build -o /usr/local/bin/runsc gvisor.dev/gvisor/runsc
    GO111MODULE=on sudo -E go build -o /usr/local/bin/containerd-shim-runsc-v1 gvisor.dev/gvisor/shim
}

### Installs gVisor using the [GitHub developer page method](https://github.com/google/gvisor#installing-from-source). This method requires Docker to be installed
function gVisorSource() {
    ### Ensure sources are cloned / up-to-date
    logg info 'Building gVisor from source'
    if [ -d /usr/local/src/gvisor ]; then
        cd /usr/local/src/gvisor
        sudo git reset --hard HEAD
        sudo git clean -fxd
        sudo git pull origin master
    else
        sudo git clone https://github.com/google/gvisor.git /usr/local/src/gvisor
    fi

    ### Build gVisor
    cd /usr/local/src/gvisor
    sudo mkdir -p bin
    # Wait 5 minutes for build to finish, and if it does not use Go
    # TODO - Generate container-shim-runsc-v1 as well (low priority since this method is not used and is only recommended for development)
    sudo timeout 300 make copy TARGETS=runsc DESTINATION=bin/
    if [ -f ./bin/runsc ]; then
        sudo cp ./bin/runsc /usr/local/bin
    else
        logg error 'Timed out while building runsc from source' && exit 6
    fi
}

### Add gVisor
if [ ! -d /Applications ] || [ ! -d /System ]; then
    # Linux
    if ! command -v runsc > /dev/null; then
        # Install gVisor
        gVisorPreBuilt || PRE_BUILT_EXIT_CODE=$?
        if [ -n "$PRE_BUILT_EXIT_CODE" ]; then
            logg warn 'gVisor failed to install using the pre-built method'
            gVisorGo || GO_METHOD_EXIT_CODE=$?
            if [ -n "$GO_METHOD_EXIT_CODE" ]; then
                logg warn 'gVisor failed to install using the Go fallback method'
                gVisorSource || SOURCE_EXIT_CODE=$?
                if [ -n "$SOURCE_EXIT_CODE" ]; then
                    logg error 'All gVisor installation methods failed' && exit 1
                else
                    logg success 'gVisor installed via source'
                fi
            else
                logg success 'gVisor installed via Go fallback method'
            fi
        else
            logg success 'gVisor installed from pre-built Google-provided binaries'
        fi
    else
        logg info 'runsc is installed'
    fi

    ### Ensure Docker is configured to use runsc
    if [ ! -f /etc/docker/daemon.json ]; then
        # Configure Docker to use gVisor
        # Create /etc/docker/daemon.json
        logg info 'Creating /etc/docker'
        sudo mkdir -p /etc/docker
        if [ -f /usr/local/src/install.doctor/home/dot_config/docker/daemon.json ]; then
            logg info 'Creating /etc/docker/daemon.json'
            sudo cp "/usr/local/src/install.doctor/home/dot_config/docker/daemon.json" /etc/docker/daemon.json
        else
            logg warn '/usr/local/src/install.doctor/home/dot_config/docker/daemon.json is not available so the /etc/docker/daemon.json file cannot be populated'
        fi

        # Restart / enable Docker
        if [[ ! "$(test -d /proc && grep Microsoft /proc/version > /dev/null)" ]] && command -v systemctl > /dev/null; then
            logg info 'Restarting Docker service'
            sudo systemctl restart docker.service
            sudo systemctl restart containerd.service
        fi

        # Test Docker /w runsc
        logg info 'Testing that Docker can load application with runsc'
        docker run --rm --runtime=runsc hello-world || RUNSC_EXIT_CODE=$?
        if [ -n "$RUNSC_EXIT_CODE" ]; then
            logg error 'Failed to run the Docker hello-world container with runsc' && exit 5
        else
            logg success 'Docker successfully ran the hello-world container with runsc'
        fi
    fi
fi

{{ end -}}