815 lines
35 KiB
Bash
815 lines
35 KiB
Bash
#!/usr/bin/env bash
|
|
# @file System Tweaks
|
|
# @brief Applies a set of system tweaks such as ensuring the hostname is set, setting the timezone, and more
|
|
# @description
|
|
# This script applies system tweaks that should be made before the rest of the provisioning process.
|
|
|
|
{{ includeTemplate "universal/profile-before" }}
|
|
{{ includeTemplate "universal/logg-before" }}
|
|
|
|
export VOLTA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/volta"
|
|
export PATH="$VOLTA_HOME/bin:$PATH"
|
|
|
|
# @description
|
|
# This script determines the ideal `/swapfile` size by checking how much RAM is available on the system.
|
|
# It then creates the appropriate `/swapfile` by considering factors such as the file system type. It
|
|
# currently supports BTRFS and regular file systems.
|
|
#
|
|
# After the `/swapfile` is created, it is enabled and assigned the appropriate permissions.
|
|
#
|
|
# ## TODO
|
|
#
|
|
# * Add logic that creates a swapfile for ZFS-based systems
|
|
# * Integrate logic from https://gitlab.com/megabyte-labs/gas-station/-/blob/master/roles/system/common/tasks/linux/swap.yml
|
|
allocateSwap() {
|
|
if [ "{{ .host.distro.family }}" = "linux" ]; then
|
|
if [ ! -f /swapfile ]; then
|
|
### Determine ideal size of /swapfile
|
|
MEMORY_IN_KB="$(grep MemTotal /proc/meminfo | sed 's/.* \(.*\) kB/\1/')"
|
|
MEMORY_IN_GB="$((MEMORY_IN_KB / 1024 / 1024))"
|
|
if [ "$MEMORY_IN_GB" -gt 64 ]; then
|
|
SWAP_SPACE="$((MEMORY_IN_GB / 10))"
|
|
elif [ "$MEMORY_IN_GB" -gt 32 ]; then
|
|
SWAP_SPACE="$((MEMORY_IN_GB / 8))"
|
|
elif [ "$MEMORY_IN_GB" -gt 8 ]; then
|
|
SWAP_SPACE="$((MEMORY_IN_GB / 4))"
|
|
else
|
|
SWAP_SPACE="$MEMORY_IN_GB"
|
|
fi
|
|
|
|
### Create /swapfile
|
|
FS_TYPE="$(df -Th | grep ' /$' | sed 's/[^ ]*\s*\([^ ]*\).*/\1/')"
|
|
if [ "$FS_TYPE" == 'btrfs' ]; then
|
|
logg info 'Creating BTRFS /swapfile'
|
|
sudo btrfs filesystem mkswapfile /swapfile
|
|
elif [ "$FS_TYPE" == 'zfs' ]; then
|
|
logg warn 'ZFS system detected - add logic here to add /swapfile'
|
|
else
|
|
logg info "Creating a $SWAP_SPACE GB /swapfile"
|
|
sudo fallocate -l "${SWAP_SPACE}G" /swapfile
|
|
sudo chmod 600 /swapfile
|
|
sudo mkswap /swapfile
|
|
fi
|
|
|
|
### Enable the /swapfile
|
|
if [ -f /swapfile ]; then
|
|
logg info 'Running sudo swapon /swapfile'
|
|
sudo swapon /swapfile
|
|
if cat /etc/fstab | grep "/swapfile"; then
|
|
sudo sed -i '/\/swapfile/\/swapfile none swap defaults 0 0/' /etc/fstab
|
|
else
|
|
echo "/swapfile none swap defaults 0 0" | sudo tee -a /etc/fstab > /dev/null
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# @description
|
|
# This script imports your publicly hosted GPG key using `pgp.mit.edu` as the key host. It then assigns it
|
|
# the ultimate trust level. It also downloads and configures GPG to use the configuration defined in `.config.gpg`
|
|
# in the `home/.chezmoidata.yaml` file.
|
|
configureGPG() {
|
|
export KEYID="{{ .user.gpg.id }}"
|
|
if [ -n "$KEYID" ] && command -v gpg > /dev/null; then
|
|
if [ ! -d "$HOME/.gnupg" ]; then
|
|
mkdir "$HOME/.gnupg"
|
|
fi
|
|
chown "$(whoami)" "$HOME/.gnupg"
|
|
chmod 700 "$HOME/.gnupg"
|
|
chown -Rf "$(whoami)" "$HOME/.gnupg/"
|
|
find "$HOME/.gnupg" -type f -exec chmod 600 {} \;
|
|
find "$HOME/.gnupg" -type d -exec chmod 700 {} \;
|
|
if [ ! -f "$HOME/.gnupg/gpg.conf" ]; then
|
|
logg 'Downloading hardened gpg.conf file to ~/.gpnupg/gpg.conf'
|
|
curl -sSL --compressed "{{ .config.gpg }}" > "$HOME/.gnupg/gpg.conf"
|
|
chmod 600 "$HOME/.gnupg/gpg.conf"
|
|
fi
|
|
logg info 'Killing dirmngr instance and reloading daemon with standard-resolver' && sudo pkill dirmngr
|
|
dirmngr --daemon --standard-resolver
|
|
KEYID_TRIMMED="$(echo "$KEYID" | sed 's/^0x//')"
|
|
if ! gpg --list-secret-keys --keyid-format=long | grep "$KEYID_TRIMMED" > /dev/null; then
|
|
if [ -f "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/home/private_dot_gnupg/private_public/private_${KEYID}.asc" ]; then
|
|
logg info "Importing GPG key stored in ${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/home/private_dot_gnupg/private_public/private_${KEYID}.asc since its name matches the GPG key ID in .chezmoi.yaml.tmpl"
|
|
gpg --import "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/home/private_dot_gnupg/private_public/private_${KEYID}.asc" && logg success 'Successfully imported master GPG key'
|
|
else
|
|
logg info 'Attempting to download the specified public GPG key ({{ .user.gpg.id }}) from public keyservers'
|
|
gpg --keyserver https://pgp.mit.edu --recv "$KEYID" || EXIT_CODE=$?
|
|
if [ -n "$EXIT_CODE" ]; then
|
|
logg info 'Non-zero exit code received when downloading public GPG key'
|
|
gpg --keyserver hkps://pgp.mit.edu --recv "$KEYID" || EXIT_CODE=$?
|
|
if [ -n "$EXIT_CODE" ]; then
|
|
logg info 'Non-zero exit code received when trying to retrieve public user GPG key on hkps://pgp.mit.edu'
|
|
else
|
|
logg success 'Successfully imported configured public user GPG key'
|
|
fi
|
|
fi
|
|
fi
|
|
else
|
|
logg info 'Key is already in keyring'
|
|
fi
|
|
logg info 'Stopping dirmngr'
|
|
gpgconf --kill dirmngr && logg info 'Stopped dirmngr' || info warn 'Failed to stop dirmngr'
|
|
logg 'Ensuring the trust of the provided public GPG key is set to maximum'
|
|
echo -e "trust\n5\ny" | gpg --command-fd 0 --edit-key "$KEYID"
|
|
else
|
|
logg warn 'gpg appears to be unavailable. Is it installed and on the PATH?'
|
|
fi
|
|
}
|
|
|
|
# @description Disable the creation of `.DS_Store` files on macOS.
|
|
disableDStoreFileCreation() {
|
|
if command -v m > /dev/null; then
|
|
logg info 'Disabling creation of .DS_Store files'
|
|
echo y | m dir dsfiles off > /dev/null
|
|
fi
|
|
}
|
|
|
|
# @description Enables transparent dark-mode on macOS
|
|
enableDarkTransparentMode() {
|
|
if command -v m > /dev/null; then
|
|
logg info 'Enabling dark mode' && m appearance darkmode YES > /dev/null
|
|
logg info 'Enabling theme transparency' && m appearance transparency YES > /dev/null
|
|
fi
|
|
}
|
|
|
|
# @description Helper function for installing Homebrew packages that have a matching package name and binary executable name.
|
|
ensureBrewPackageInstalled() {
|
|
if ! command -v "$1" > /dev/null; then
|
|
if command -v brew; then
|
|
logg info "Installing $1 via Homebrew"
|
|
brew install --quiet "$1" || EXIT_CODE=$?
|
|
if [ -n "$EXIT_CODE" ]; then
|
|
logg error "$1 was not successfully installed via Homebrew"
|
|
unset EXIT_CODE
|
|
fi
|
|
else
|
|
logg error "brew is unavailable. Cannot use it to perform installation of $1"
|
|
fi
|
|
else
|
|
logg info "$1 is already installed"
|
|
fi
|
|
}
|
|
|
|
# @description Ensure delta is installed via Homebrew
|
|
ensureDeltaInstalled() {
|
|
if ! command -v delta > /dev/null; then
|
|
if command -v brew; then
|
|
logg 'Installing delta via Homebrew'
|
|
brew install --quiet git-delta || DELTA_EXIT_CODE=$?
|
|
if [ -n "$DELTA_EXIT_CODE" ]; then
|
|
logg error 'git-delta was not successfully installed via Homebrew'
|
|
fi
|
|
else
|
|
logg 'brew is unavailable. Cannot use it to perform a system installation of node.'
|
|
fi
|
|
else
|
|
logg 'delta is available'
|
|
fi
|
|
}
|
|
|
|
# @description Ensure Node is installed via Homebrew
|
|
ensureNodeInstalled() {
|
|
### Ensure node is installed
|
|
if ! command -v node > /dev/null; then
|
|
if command -v brew; then
|
|
logg 'Installing node via Homebrew'
|
|
brew install --quiet node || NODE_EXIT_CODE=$?
|
|
if [ -n "$NODE_EXIT_CODE" ]; then
|
|
logg warn 'Calling brew link --overwrite node because the Node.js installation seems to be misconfigured'
|
|
brew link --overwrite node
|
|
fi
|
|
else
|
|
logg 'brew is unavailable. Cannot use it to perform a system installation of node.'
|
|
fi
|
|
else
|
|
logg 'node is available'
|
|
fi
|
|
}
|
|
|
|
# @description
|
|
# This script ensures that there is a group with the same name of the provisioning user available on the system.
|
|
ensureUserGroup() {
|
|
if [ "{{ .host.distro.family }}" = "darwin" ]; then
|
|
if [ -n "$USER" ]; then
|
|
logg info "Adding the $USER user to the $USER group"
|
|
### Ensure user has group of same name (required for Macports)
|
|
logg info "Ensuring user ($USER) has a group with the same name ($USER) and that it is a member. Sudo privileges may be required"
|
|
|
|
GROUP="$USER"
|
|
USERNAME="$USER"
|
|
|
|
### Add group
|
|
sudo dscl . create /Groups/$GROUP
|
|
|
|
### Add GroupID to group
|
|
if [[ "$(sudo dscl . read /Groups/$GROUP gid 2>&1)" == *'No such key'* ]]; then
|
|
MAX_ID_GROUP="$(dscl . -list /Groups gid | awk '{print $2}' | sort -ug | tail -1)"
|
|
GROUP_ID="$((MAX_ID_GROUP+1))"
|
|
sudo dscl . create /Groups/$GROUP gid "$GROUP_ID"
|
|
fi
|
|
|
|
### Add user
|
|
sudo dscl . create /Users/$USERNAME
|
|
|
|
### Add PrimaryGroupID to user
|
|
if [[ "$(sudo dscl . read /Users/$USERNAME PrimaryGroupID 2>&1)" == *'No such key'* ]]; then
|
|
sudo dscl . create /Users/$USERNAME PrimaryGroupID 20
|
|
fi
|
|
|
|
### Add UniqueID to user
|
|
if [[ "$(sudo dscl . read /Users/$USERNAME UniqueID 2>&1)" == *'No such key'* ]]; then
|
|
MAX_ID_USER="$(dscl . -list /Users UniqueID | awk '{print $2}' | sort -ug | tail -1)"
|
|
USER_ID="$((MAX_ID_USER+1))"
|
|
sudo dscl . create /Users/$USERNAME UniqueID "$USERID"
|
|
fi
|
|
|
|
### Add user to group
|
|
sudo dseditgroup -o edit -t user -a $USERNAME $GROUP
|
|
else
|
|
logg warn 'The USER environment variable is unavailable'
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# @description Increases the amount of memory a process can consume on Linux. In the case of `netdata` and other programs, many systems will suggest
|
|
# increasing the `vm.max_map_count`. According to a [RedHat article](https://access.redhat.com/solutions/99913), the default value is `65530`.
|
|
# This function increases that value to `262144` if `sysctl` is available on the system.
|
|
increaseMapCount() {
|
|
if [ ! -d /Applications ] && [ ! -d /System ]; then
|
|
### Linux
|
|
if command -v sysctl > /dev/null; then
|
|
logg info 'Increasing vm.max_map_count size to 262144'
|
|
sudo sysctl -w vm.max_map_count=262144 > /dev/null
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# @description Helper function for installDocker that 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
|
|
}
|
|
|
|
# @description Helper function for installDocker that 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
|
|
}
|
|
|
|
# @description Helper function for installDocker that 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 600 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 (10 minutes)' && exit 6
|
|
fi
|
|
}
|
|
|
|
# @description Helper function for installDocker that Installs systemsecret credential helper for Linux
|
|
function installCredentialSecretService() {
|
|
curl -sSL https://github.com/docker/docker-credential-helpers/releases/download/v0.7.0/docker-credential-secretservice-v0.7.0.linux-amd64 > /tmp/docker-credential-secretservice
|
|
sudo mv /tmp/docker-credential-secretservice /usr/local/bin/docker-credential-secretservice
|
|
}
|
|
|
|
# @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.
|
|
installDocker() {
|
|
### Ensures `~/.config/docker` is symlinked to `~/.docker` which is required for Docker Desktop compatibility since it currently does not honor XDG spec. This will
|
|
### remove the current configuration at `~/.docker` if it is present and not symlinked to `~/.config/docker`.
|
|
if [ "$(readlink -f "$HOME/.docker")" != "${XDG_CONFIG_HOME:-$HOME/.config}/docker" ]; then
|
|
logg info 'Removing ~/.docker if present' && rm -rf "$HOME/.docker"
|
|
logg info 'Ensuring ~/.config/docker exists' && mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/docker"
|
|
logg info 'Symlinking ~/.config/docker to ~/.docker for Docker Desktop compatibility' && ln -s "${XDG_CONFIG_HOME:-$HOME/.config}/docker" "$HOME/.docker"
|
|
else
|
|
logg info 'Symlink from ~/.config/docker to ~/.docker is already present'
|
|
fi
|
|
|
|
### 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 --quiet --no-quarantine 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'
|
|
# TODO - --install-privileged-components may be necessary for `docker extension` command but it causes the command to no longer work
|
|
# open --background -a Docker --args --accept-license --unattended --install-privileged-components
|
|
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
|
|
|
|
### Add gVisor
|
|
if [ ! -d /Applications ] || [ ! -d /System ]; then
|
|
### Linux
|
|
if ! command -v docker-credential-secretservice > /dev/null; then
|
|
installCredentialSecretService
|
|
fi
|
|
|
|
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 "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/home/dot_config/docker/daemon.json.tmpl" ]; then
|
|
logg info 'Creating /etc/docker/daemon.json'
|
|
chezmoi cat "${XDG_CONFIG_HOME:-$HOME/.config}/docker/config.json" | sudo tee /etc/docker/daemon.json
|
|
else
|
|
logg warn "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/home/dot_config/docker/daemon.json.tmpl 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 info 'Docker successfully ran the hello-world container with runsc'
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# @description
|
|
# This script enrolls the device as a JumpCloud managed asset. The `JUMPCLOUD_CONNECT_KEY` secret should
|
|
# be populated using one of the methods described in the [Secrets documentation](https://install.doctor/docs/customization/secrets).
|
|
#
|
|
# *Note: You should check out the supported systems before trying to enroll devices.*
|
|
#
|
|
# ## JumpCloud on macOS
|
|
#
|
|
# macOS offers a native device management feature offered through Apple Business. It is the preferred
|
|
# method since it offers most of the desirable features (like remote wipe). The [JumpCloud MDM documentation](https://support.jumpcloud.com/support/s/article/Getting-Started-MDM)
|
|
# details the steps required to register macOS MDM profiles with JumpCloud.
|
|
#
|
|
# ## Links
|
|
#
|
|
# * [JumpCloud device management requirements](https://support.jumpcloud.com/support/s/article/jumpcloud-agent-compatibility-system-requirements-and-impacts1)
|
|
installJumpCloud() {
|
|
if [ "{{ .host.distro.family }}" = "linux" ]; then
|
|
if [ "{{ if (stat (joinPath .chezmoi.sourceDir ".chezmoitemplates" "secrets" "JUMPCLOUD_CONNECT_KEY")) }}{{- includeTemplate "secrets/JUMPCLOUD_CONNECT_KEY" | decrypt | trim -}}{{ else }}{{- env "JUMPCLOUD_CONNECT_KEY" -}}{{ end }}" != "" ]; then
|
|
logg info 'Enrolling device with JumpCloud by running the kickstart script'
|
|
curl --tlsv1.2 --silent --show-error --header 'x-connect-key: {{ if (stat (joinPath .chezmoi.sourceDir ".chezmoitemplates" "secrets" "JUMPCLOUD_CONNECT_KEY")) }}{{- includeTemplate "secrets/JUMPCLOUD_CONNECT_KEY" | decrypt | trim -}}{{ else }}{{- env "JUMPCLOUD_CONNECT_KEY" -}}{{ end }}' https://kickstart.jumpcloud.com/Kickstart | sudo bash
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# @description Installs commonly depended on Python packages
|
|
installSystemPips() {
|
|
### Upgrade on macOS
|
|
if [ -f /Library/Developer/CommandLineTools/usr/bin/python3 ]; then
|
|
logg info 'Ensuring macOS system python3 has latest version of pip'
|
|
/Library/Developer/CommandLineTools/usr/bin/python3 -m pip install --upgrade pip
|
|
fi
|
|
|
|
### Python 3
|
|
if command -v pip3 > /dev/null; then
|
|
if command -v python3 > /dev/null; then
|
|
if ! python3 -m certifi > /dev/null; then
|
|
pip3 install certifi
|
|
else
|
|
logg info 'certifi is available to python3'
|
|
fi
|
|
else
|
|
logg warn 'python3 is not available on the system'
|
|
fi
|
|
else
|
|
logg warn 'pip3 is not available on the system'
|
|
fi
|
|
}
|
|
|
|
# @description
|
|
# This script removes some of the software deemed to be "bloatware" by cycling through the values defined in
|
|
# `.removeLinuxPackages` of the `home/.chezmoidata.yaml` file.
|
|
removeLinuxBloatware() {
|
|
if [ "{{ .host.distro.family }}" = "linux" ]; then
|
|
{{- $removePackages := join " " .removeLinuxPackages }}
|
|
### Remove bloatware packages defined in .chezmoidata.yaml
|
|
for PKG in {{ $removePackages }}; do
|
|
if command -v apk > /dev/null; then
|
|
if apk list "$PKG" | grep "$PKG" > /dev/null; then
|
|
sudo apk delete "$PKG"
|
|
fi
|
|
elif command -v apt-get > /dev/null; then
|
|
if dpkg -l "$PKG" | grep -E '^ii' > /dev/null; then
|
|
sudo apt-get remove -y "$PKG"
|
|
logg success 'Removed '"$PKG"' via apt-get'
|
|
fi
|
|
elif command -v dnf > /dev/null; then
|
|
if rpm -qa | grep "$PKG" > /dev/null; then
|
|
sudo dnf remove -y "$PKG"
|
|
logg success 'Removed '"$PKG"' via dnf'
|
|
fi
|
|
elif command -v yum > /dev/null; then
|
|
if rpm -qa | grep "$PKG" > /dev/null; then
|
|
sudo yum remove -y "$PKG"
|
|
logg success 'Removed '"$PKG"' via yum'
|
|
fi
|
|
elif command -v pacman > /dev/null; then
|
|
if pacman -Qs "$PKG" > /dev/null; then
|
|
sudo pacman -R "$PKG"
|
|
logg success 'Removed '"$PKG"' via pacman'
|
|
fi
|
|
elif command -v zypper > /dev/null; then
|
|
if rpm -qa | grep "$PKG" > /dev/null; then
|
|
sudo zypper remove -y "$PKG"
|
|
logg success 'Removed '"$PKG"' via zypper'
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
# @description Sets the hostname using `scutil` on macOS and using `hostname` and `hostnamectl` on Linux. On macOS, the HostName, LocalHostName, and ComputerName
|
|
# are set equal to the value stored in `.host.hostname` (in `.chezmoi.yaml.tmpl`) but with the `.host.domain` stripped off. On Linux, the same is done
|
|
# but only the hostname is set. On Linux, the hostname is set with the `hostname` command and then also with the `hostnamectl` command if it is available.
|
|
#
|
|
# ## Sources
|
|
#
|
|
# * [Changing Linux hostname permanently](https://www.tecmint.com/set-hostname-permanently-in-linux/)
|
|
setHostname() {
|
|
if [ -d /Applications ] && [ -d /System ]; then
|
|
# Source: https://apple.stackexchange.com/questions/287760/set-the-hostname-computer-name-for-macos
|
|
logg info 'Setting macOS hostname / local hostname / computer name'
|
|
logg info 'Changing HostName to {{ .host.hostname | replace .host.domain "" | replace "." "" | replace " " "" }}.{{ .host.domain }}' && sudo scutil --set HostName '{{ .host.hostname | replace .host.domain "" | replace "." "" | replace " " "" }}.{{ .host.domain }}' && logg info 'Changed HostName to {{ .host.hostname | replace .host.domain "" | replace "." "" | replace " " "" }}.{{ .host.domain }}'
|
|
logg info 'Changing LocalHostName to {{ .host.hostname | replace .host.domain "" | replace "." "" | replace " " "" }}' && sudo scutil --set LocalHostName '{{ .host.hostname | replace .host.domain "" | replace "." "" | replace " " "" }}' && logg info 'Changed LocalHostName to {{ .host.hostname | replace .host.domain "" | replace "." "" | replace " " "" }}'
|
|
logg info 'Changing ComputerName to {{ .host.hostname | replace .host.domain "" | replace "." "" | replace " " "" }}' && sudo scutil --set ComputerName '{{ .host.hostname | replace .host.domain "" | replace "." "" | replace " " "" }}' && logg info 'Changed ComputerName to {{ .host.hostname | replace .host.domain "" | replace "." "" | replace " " "" }}'
|
|
logg info 'Flushing DNS cache'
|
|
dscacheutil -flushcache
|
|
elif [ -f /etc/passwd ]; then
|
|
logg info 'Setting Linux hostname'
|
|
hostname '{{ .host.hostname | replace .host.domain "" | replace "." "" }}.{{ .host.domain }}' && logg success 'Changed hostname to {{ .host.hostname | replace .host.domain "" | replace "." "" }}.{{ .host.domain }}'
|
|
if command -v hostnamectl > /dev/null; then
|
|
logg info 'Ensuring hostname persists after reboot'
|
|
sudo hostnamectl set-hostname '{{ .host.hostname | replace .host.domain "" | replace "." "" }}.{{ .host.domain }}' && logg success 'Permanently changed hostname to {{ .host.hostname | replace .host.domain "" | replace "." "" }}.{{ .host.domain }}'
|
|
else
|
|
logg warn 'hostnamectl was not available in the PATH - this operating system type might be unsupported'
|
|
fi
|
|
else
|
|
logg warn 'Could not configure hostname because system type was not detectable'
|
|
fi
|
|
}
|
|
|
|
# @description Sets the NTP server using `m` on macOS
|
|
#
|
|
# `2>/dev/null 1>&2` was added after `sudo systemsetup -setusingnetworktime` calls because the command was outputting the following
|
|
# useless error:
|
|
#
|
|
# ```shell
|
|
# ### Error:-99 File:/AppleInternal/Library/BuildRoots/0032d1ee-80fd-11ee-8227-6aecfccc70fe/Library/Caches/com.apple.xbs/Sources/Admin/InternetServices.m Line:379
|
|
# ```
|
|
setNtpServer() {
|
|
if command -v m > /dev/null; then
|
|
### macOS
|
|
logg info 'Copying ~/.local/etc/ntp.conf to /etc/ntp.conf'
|
|
sudo cp -f "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/home/dot_local/etc/ntp.conf" /etc/ntp.conf
|
|
logg info 'Copying ~/.local/etc/ntp.conf to /private/etc/ntp.conf'
|
|
sudo cp -f "${XDG_DATA_HOME:-$HOME/.local/share}/chezmoi/home/dot_local/etc/ntp.conf" /private/etc/ntp.conf
|
|
logg info 'Turning off setusingnetworktime for 2 seconds' && sudo systemsetup -setusingnetworktime off 2>/dev/null 1>&2
|
|
sleep 2
|
|
logg info 'Re-enabling setusingnetworktime' && sudo systemsetup -setusingnetworktime on 2>/dev/null 1>&2
|
|
else
|
|
logg warn 'Skipped setting the NTP server'
|
|
fi
|
|
}
|
|
|
|
# @description Sets the system timezone using `timedatectl` on Linux and `m` on macOS. If neither commands are available
|
|
# then a warning message is printed.
|
|
setTimezone() {
|
|
if command -v timedatectl > /dev/null; then
|
|
### Linux
|
|
logg info 'Setting timezone to {{ .user.timezone }}'
|
|
sudo timedatectl set-timezone {{ .user.timezone }}
|
|
elif command -v systemsetup > /dev/null && [ -d /Applications ] && [ -d /System ]; then
|
|
### macOS
|
|
logg info 'Setting timezone to {{ .user.timezone }}' && sudo systemsetup -settimezone "{{ .user.timezone }}" 2>/dev/null 1>&2
|
|
else
|
|
logg warn 'Neither timedatectl (Linux) or systemsetup (macOS) were found on the system'
|
|
fi
|
|
}
|
|
|
|
# @description Configures macOS to enable the notification center. `&> /dev/null` was added to the command
|
|
# because the following useless error was being reported:
|
|
#
|
|
# ```shell
|
|
# Load failed: 5: Input/output error
|
|
# Try running `launchctl bootstrap` as root for richer errors.
|
|
# Restart your computer for this to take effect
|
|
# ```
|
|
showNotificationCenter() {
|
|
if command -v m > /dev/null; then
|
|
logg info 'Configuring macOS to show notification center' && m notification showcenter YES &> /dev/null
|
|
fi
|
|
}
|
|
|
|
installAnsible() {
|
|
if command -v pipx > /dev/null; then
|
|
if [ ! -f "${XDG_CACHE_HOME:-$HOME/.cache}/install.doctor/ansible-installed" ]; then
|
|
logg info 'Running pipx install ansible-core' && pipx install ansible-core
|
|
if [ -d /Applications ] && [ -d /System ]; then
|
|
logg info 'Injecting ansible-core pipx with ansible-core PyObjC PyObjC-core because system is macOS' && pipx inject ansible-core PyObjC PyObjC-core
|
|
fi
|
|
logg info 'Running pipx inject ansible-core docker lxml netaddr pexpect python-vagrant pywinrm requests-credssp watchdog' && pipx inject ansible-core docker lxml netaddr pexpect python-vagrant pywinrm requests-credssp watchdog
|
|
mkdir -p "${XDG_CACHE_HOME:-$HOME/.cache}/install.doctor"
|
|
touch "${XDG_CACHE_HOME:-$HOME/.cache}/install.doctor/ansible-installed"
|
|
else
|
|
logg info 'Ansible installation routine appears to have already been run'
|
|
fi
|
|
else
|
|
logg warn 'pipx is unavailable to use for installing Ansible'
|
|
fi
|
|
}
|
|
|
|
installBrewPackages() {
|
|
ensureNodeInstalled
|
|
ensureDeltaInstalled
|
|
ensureBrewPackageInstalled "volta"
|
|
volta install node@latest &
|
|
volta install yarn@latest &
|
|
npm install -g npm@latest &
|
|
ensureBrewPackageInstalled "pipx"
|
|
pipx ensurepath &
|
|
ensureBrewPackageInstalled "gh"
|
|
ensureBrewPackageInstalled "go"
|
|
ensureBrewPackageInstalled "ruby"
|
|
ensureBrewPackageInstalled "rustup"
|
|
ensureBrewPackageInstalled "zx"
|
|
ensureBrewPackageInstalled "whalebrew"
|
|
wait
|
|
logg success 'Finished installing auxilary Homebrew packages'
|
|
logg info 'Ensuring Ansible is installed (with plugins)' && installAnsible
|
|
}
|
|
|
|
ensureMacportsInstalled() {
|
|
if [ -d /Applications ] && [ -d /System ]; then
|
|
if ! command -v port > /dev/null; then
|
|
logg info 'Ensuring /opt/mports/macports-base is removed' && sudo rm -rf /opt/mports/macports-base
|
|
logg info 'Cloning source for macports to /opt/mports/macports-base' && sudo git clone --branch v2.8.0 --depth 1 https://github.com/macports/macports-base.git /opt/mports/macports-base
|
|
cd /opt/mports/macports-base
|
|
logg info 'Building macports' && sudo bash --noprofile --norc -c './configure --enable-readline && make && make install && make distclean'
|
|
logg info 'Running sudo port selfupdate' && sudo port selfupdate
|
|
fi
|
|
fi
|
|
}
|
|
|
|
setupSnap() {
|
|
if [ ! -d /Applications ] && [ ! -d /System ] && command -v snap > /dev/null; then
|
|
logg info 'Enabling snapd' && sudo systemctl enable snapd
|
|
logg info 'Starting snapd' && sudo systemctl start snapd
|
|
if [ -d /snap ]; then
|
|
logg info 'Linking /var/lib/snapd/snap to /snap' && sudo ln -s /var/lib/snapd/snap /snap
|
|
fi
|
|
logg info 'Running sudo snap info core' && sudo snap info core
|
|
logg info 'Running sudo snap wait system seed.loaded' && sudo snap wait system seed.loaded
|
|
logg info 'Running sudo snap install core' && sudo snap install core
|
|
fi
|
|
}
|
|
|
|
installYay() {
|
|
if [ -f /etc/arch-release ] && ! command -v yay > /dev/null
|
|
sudo rm -rf /usr/local/src/yay
|
|
sudo git clone https://aur.archlinux.org/yay.git /usr/local/src/yay
|
|
cd /usr/local/src/yay
|
|
sudo makepkg -si
|
|
fi
|
|
}
|
|
|
|
installNix() {
|
|
if ! command -v nix-shell > /dev/null; then
|
|
if [ -d /Applications ] && [ -d /System ]; then
|
|
### macOS
|
|
logg info 'Installing nix for macOS' && sh <(curl -L https://nixos.org/nix/install) --yes
|
|
else
|
|
### Linux
|
|
logg info 'Installing nix' && sh <(curl -L https://nixos.org/nix/install) --daemon --yes
|
|
fi
|
|
fi
|
|
}
|
|
|
|
rustUpInit() {
|
|
if command -v rustup-init > /dev/null && ! command -v rustc > /dev/null; then
|
|
logg info 'Running rustup-init -y' && rustup-init -y
|
|
fi
|
|
}
|
|
|
|
zapInstall() {
|
|
if ! command -v zap > /dev/null; then
|
|
### Architecture
|
|
if [ -z ${ARCH+x} ]; then
|
|
MACHINE_ARCH="$(uname -m)"
|
|
if [ "$MACHINE_ARCH" = "amd64" ]; then
|
|
ARCH="amd64"
|
|
elif [ "$MACHINE_ARCH" = "x86_64" ]; then
|
|
ARCH="amd64"
|
|
elif [ "$MACHINE_ARCH" = "i386" ]; then
|
|
ARCH="386"
|
|
elif [ "$MACHINE_ARCH" = "i686" ]; then
|
|
ARCH="386" # both are 32bit, should be compatible
|
|
elif [ "$MACHINE_ARCH" = "aarch64" ]; then
|
|
ARCH="arm64"
|
|
elif [ "$MACHINE_ARCH" = "arm64" ]; then
|
|
ARCH="arm64"
|
|
elif [ "$MACHINE_ARCH" = "arm" ]; then
|
|
ARCH="arm"
|
|
fi
|
|
export ARCH
|
|
fi
|
|
logg info 'Downloading zap to /usr/local/bin/zap' && sudo curl -sSL --output /usr/local/bin/zap "https://github.com/srevinsaju/zap/releases/download/continuous/zap-${ARCH}"
|
|
logg info 'Making /usr/local/bin/zap executable' && sudo chmod +x /usr/local/bin/zap
|
|
fi
|
|
}
|
|
|
|
addFlathub() {
|
|
if command -v flatpak > /dev/null; then
|
|
logg info 'Adding flatpak flathub repository' && sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
|
fi
|
|
}
|
|
|
|
# TODO - Add install on macOS for macports
|
|
if [ -n "$DEBUG" ] || [ -n "$DEBUG_MODE" ]; then
|
|
logg info 'The DEBUG or DEBUG_MODE environment variable is set so preliminary system tweaks will be run synchronously'
|
|
addFlathub
|
|
allocateSwap
|
|
configureGPG
|
|
disableDStoreFileCreation
|
|
enableDarkTransparentMode
|
|
ensureMacportsInstalled
|
|
ensureUserGroup
|
|
increaseMapCount
|
|
installBrewPackages
|
|
installDocker
|
|
installJumpCloud
|
|
installSystemPips
|
|
installYay
|
|
removeLinuxBloatware
|
|
rustUpInit
|
|
setHostname
|
|
setNtpServer
|
|
setTimezone
|
|
setupSnap
|
|
showNotificationCenter
|
|
zapInstall
|
|
else
|
|
addFlathub &
|
|
allocateSwap &
|
|
configureGPG &
|
|
disableDStoreFileCreation &
|
|
enableDarkTransparentMode &
|
|
ensureMacportsInstalled &
|
|
ensureUserGroup &
|
|
increaseMapCount &
|
|
installBrewPackages &
|
|
installDocker &
|
|
installJumpCloud &
|
|
installSystemPips &
|
|
installYay &
|
|
removeLinuxBloatware &
|
|
rustUpInit &
|
|
setHostname &
|
|
setNtpServer &
|
|
setTimezone &
|
|
setupSnap &
|
|
showNotificationCenter &
|
|
zapInstall &
|
|
wait
|
|
fi
|
|
|
|
logg success 'Successfully applied preliminary system tweaks'
|