Update jlmkr.sh
This commit is contained in:
parent
8e5140ec21
commit
65741bec66
623
jlmkr.sh
623
jlmkr.sh
|
@ -7,28 +7,29 @@ ABSOLUTE_SCRIPT_PATH="$(realpath "${BASH_SOURCE[0]}")"
|
||||||
SCRIPT_NAME=$(basename "${ABSOLUTE_SCRIPT_PATH}")
|
SCRIPT_NAME=$(basename "${ABSOLUTE_SCRIPT_PATH}")
|
||||||
SCRIPT_DIR_PATH="$(dirname "${ABSOLUTE_SCRIPT_PATH}")"
|
SCRIPT_DIR_PATH="$(dirname "${ABSOLUTE_SCRIPT_PATH}")"
|
||||||
|
|
||||||
CREATE_DONE=
|
|
||||||
JAILS_DIR_PATH='jails'
|
|
||||||
JAIL_ROOTFS_NAME='rootfs'
|
|
||||||
JAIL_PATH=
|
|
||||||
|
|
||||||
USAGE="WARNING: EXPERIMENTAL AND WORK IN PROGRESS, USE ONLY FOR TESTING!
|
USAGE="WARNING: EXPERIMENTAL AND WORK IN PROGRESS, USE ONLY FOR TESTING!
|
||||||
|
TODO: add version string
|
||||||
Usage: ./${SCRIPT_NAME} COMMAND [ARG...]
|
Usage: ./${SCRIPT_NAME} COMMAND [ARG...]
|
||||||
|
|
||||||
TODO: complete writing usage
|
TODO: complete writing usage
|
||||||
"
|
"
|
||||||
|
|
||||||
|
JAILS_DIR_PATH='jails'
|
||||||
|
JAIL_ROOTFS_NAME='rootfs'
|
||||||
|
JAIL_PATH=
|
||||||
|
JAIL_CONFIG_NAME='config'
|
||||||
|
JAIL_START_SCRIPT_NAME='start.sh'
|
||||||
|
|
||||||
fail() {
|
fail() {
|
||||||
echo -e "$1" >&2 && exit 1
|
echo -e "$1" >&2 && exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
[[ $UID -ne 0 ]] && echo "${USAGE}" && fail "Run this script as root..."
|
[[ $UID -ne 0 ]] && echo "${USAGE}" && fail "Run this script as root..."
|
||||||
|
|
||||||
err() {
|
err() {
|
||||||
# https://unix.stackexchange.com/a/504829/477308
|
# https://unix.stackexchange.com/a/504829/477308
|
||||||
echo 'Error occurred:'
|
echo 'Error occurred:'
|
||||||
awk 'NR>L-4 && NR<L+4 { printf "%-5d%3s%s\n",NR,(NR==L?">>>":""),$0 }' L="${1}" "${ABSOLUTE_SCRIPT_PATH}"
|
awk 'NR>L-4 && NR<L+4 { printf "%-5d%3s%s\n",NR,(NR==L?">>>":""),$0 }' L="${1}" "${ABSOLUTE_SCRIPT_PATH}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Trap errors
|
# Trap errors
|
||||||
|
@ -38,70 +39,75 @@ trap 'err $LINENO' ERR
|
||||||
# START RUN FUNCTIONALITY
|
# START RUN FUNCTIONALITY
|
||||||
#########################
|
#########################
|
||||||
|
|
||||||
run_jail() (
|
start_jail() {
|
||||||
# Create a sub-shell to source the conf file
|
local jail_config_path jail_start_script_path docker_compatible gpu_passthrough key value
|
||||||
|
|
||||||
# Define as global vars: they are also global vars in the sourced start script
|
jail_config_path="${1}/${JAIL_CONFIG_NAME}"
|
||||||
DOCKER_COMPATIBLE=
|
jail_start_script_path="${1}/${JAIL_START_SCRIPT_NAME}"
|
||||||
GPU_PASSTHROUGH=
|
|
||||||
SYSTEMD_RUN_ADDITIONAL_ARGS=()
|
|
||||||
SYSTEMD_NSPAWN_ADDITIONAL_ARGS=()
|
|
||||||
|
|
||||||
echo 'Load the config'
|
! [[ -f "${jail_start_script_path}" ]] && fail "ERROR: Couldn't find: ${jail_start_script_path}"
|
||||||
# shellcheck disable=SC1090
|
! [[ -f "${jail_config_path}" ]] && fail "ERROR: Couldn't find: ${jail_config_path}"
|
||||||
. "${1}"
|
|
||||||
echo 'Config loaded'
|
|
||||||
|
|
||||||
set -eEuo pipefail
|
echo 'Load the config'
|
||||||
if [[ "$(type -t start)" == 'function' ]]; then
|
|
||||||
if [[ "${DOCKER_COMPATIBLE}" -eq 1 ]]; then
|
|
||||||
# Enable ip forwarding on the host (docker needs it)
|
|
||||||
echo 1 >/proc/sys/net/ipv4/ip_forward
|
|
||||||
# To properly run docker inside the jail, we need to lift restrictions
|
|
||||||
# Without DevicePolicy=auto images with device nodes may not be pulled
|
|
||||||
# https://github.com/kinvolk/kube-spawn/pull/328
|
|
||||||
SYSTEMD_RUN_ADDITIONAL_ARGS+=(--setenv=SYSTEMD_SECCOMP=0 --property=DevicePolicy=auto)
|
|
||||||
# Add additional flags required for docker
|
|
||||||
SYSTEMD_NSPAWN_ADDITIONAL_ARGS+=(--capability=all --system-call-filter='add_key keyctl bpf')
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${GPU_PASSTHROUGH}" -eq 1 ]]; then
|
# TODO: also load the additional (user) args for nspawn from this file!
|
||||||
SYSTEMD_NSPAWN_ADDITIONAL_ARGS+=(--property=DeviceAllow='char-drm rw')
|
while IFS="=" read -r key value || [ -n "$key" ]; do
|
||||||
|
case "${key}" in
|
||||||
|
"DOCKER_COMPATIBLE") docker_compatible="$value" ;;
|
||||||
|
"GPU_PASSTHROUGH") gpu_passthrough="$value" ;;
|
||||||
|
esac
|
||||||
|
done <"${jail_config_path}"
|
||||||
|
|
||||||
# Detect intel GPU device and if present add bind flag
|
echo 'Config loaded'
|
||||||
[[ -d /dev/dri ]] && SYSTEMD_NSPAWN_ADDITIONAL_ARGS+=(--bind=/dev/dri)
|
|
||||||
|
|
||||||
# TODO: add bind mount flags in case of nvidia GPU passthrough
|
SYSTEMD_RUN_ADDITIONAL_ARGS=()
|
||||||
fi
|
SYSTEMD_NSPAWN_ADDITIONAL_ARGS=()
|
||||||
|
|
||||||
echo "Starting jail..."
|
if [[ "${docker_compatible}" -eq 1 ]]; then
|
||||||
start
|
# Enable ip forwarding on the host (docker needs it)
|
||||||
else
|
echo 1 >/proc/sys/net/ipv4/ip_forward
|
||||||
echo "Can't call the start function since start.sh didn't contain one..."
|
# To properly run docker inside the jail, we need to lift restrictions
|
||||||
fi
|
# Without DevicePolicy=auto images with device nodes may not be pulled
|
||||||
)
|
# https://github.com/kinvolk/kube-spawn/pull/328
|
||||||
|
SYSTEMD_RUN_ADDITIONAL_ARGS+=(--setenv=SYSTEMD_SECCOMP=0 --property=DevicePolicy=auto)
|
||||||
|
# Add additional flags required for docker
|
||||||
|
SYSTEMD_NSPAWN_ADDITIONAL_ARGS+=(--capability=all --system-call-filter='add_key keyctl bpf')
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${gpu_passthrough}" -eq 1 ]]; then
|
||||||
|
SYSTEMD_NSPAWN_ADDITIONAL_ARGS+=(--property=DeviceAllow='char-drm rw')
|
||||||
|
|
||||||
|
# Detect intel GPU device and if present add bind flag
|
||||||
|
[[ -d /dev/dri ]] && SYSTEMD_NSPAWN_ADDITIONAL_ARGS+=(--bind=/dev/dri)
|
||||||
|
|
||||||
|
# TODO: add bind mount flags in case of nvidia GPU passthrough
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Pass the two arrays to the start script
|
||||||
|
# https://stackoverflow.com/a/43687593
|
||||||
|
echo "Starting jail..."
|
||||||
|
|
||||||
|
"./${jail_start_script_path}" \
|
||||||
|
"${#SYSTEMD_RUN_ADDITIONAL_ARGS[@]}" "${SYSTEMD_RUN_ADDITIONAL_ARGS[@]}" \
|
||||||
|
"${#SYSTEMD_NSPAWN_ADDITIONAL_ARGS[@]}" "${SYSTEMD_NSPAWN_ADDITIONAL_ARGS[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
############################
|
############################
|
||||||
# START CREATE FUNCTIONALITY
|
# START CREATE FUNCTIONALITY
|
||||||
############################
|
############################
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
# If the script didn't complete (not CREATE_DONE) then
|
# Remove the JAIL_PATH (if set, a directory and not the root directory)
|
||||||
# remove the JAIL_PATH (if set and a directory)
|
[[ -d "${JAIL_PATH}" ]] && echo -e "\n\nCleaning up: ${JAIL_PATH}\n" && rm -rf "${JAIL_PATH}"
|
||||||
if [[ "${CREATE_DONE}" -ne 1 && -n "${JAIL_PATH}" && -d "${JAIL_PATH}" &&
|
|
||||||
"${JAIL_PATH}" != "${JAILS_DIR_PATH}" && "${JAIL_PATH}" != "/" ]]; then
|
|
||||||
echo -e "\n\nCleaning up: ${JAIL_PATH}\n"
|
|
||||||
rm -rf "${JAIL_PATH}"
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stat_chmod() {
|
stat_chmod() {
|
||||||
# Only run chmod if mode is different from current mode
|
# Only run chmod if mode is different from current mode
|
||||||
if [[ "$(stat -c%a "${2}")" -ne "${1}" ]]; then chmod "${1}" "${2}"; fi
|
if [[ "$(stat -c%a "${2}")" -ne "${1}" ]]; then chmod "${1}" "${2}"; fi
|
||||||
}
|
}
|
||||||
|
|
||||||
validate_download_script() {
|
validate_download_script() {
|
||||||
echo "6cca2eda73c7358c232fecb4e750b3bf0afa9636efb5de6a9517b7df78be12a4 ${1}" | sha256sum --check >/dev/null
|
echo "6cca2eda73c7358c232fecb4e750b3bf0afa9636efb5de6a9517b7df78be12a4 ${1}" | sha256sum --check >/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
# Use a function as template instead of heredoc
|
# Use a function as template instead of heredoc
|
||||||
|
@ -109,185 +115,149 @@ validate_download_script() {
|
||||||
# Template function must return the line number,
|
# Template function must return the line number,
|
||||||
# this marks the start of the template
|
# this marks the start of the template
|
||||||
template_start_script() {
|
template_start_script() {
|
||||||
echo "$((LINENO + 1))" && return
|
echo "$((LINENO + 1))" && return
|
||||||
# TEMPLATE START
|
# TEMPLATE START
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
# YOU CAN MANUALLY EDIT THE SETTINGS BELOW
|
# If this script is called from PLACEHOLDER_SCRIPT_NAME
|
||||||
|
# and DOCKER_COMPATIBLE or GPU_PASSTHROUGH is enabled
|
||||||
|
# then these arrays will be filled with additional args
|
||||||
|
SYSTEMD_RUN_ADDITIONAL_ARGS=("${@:2:$1}") && shift "$(($1 + 1))"
|
||||||
|
SYSTEMD_NSPAWN_ADDITIONAL_ARGS=("${@:2:$1}") && shift "$(($1 + 1))"
|
||||||
|
|
||||||
DOCKER_COMPATIBLE=PLACEHOLDER_DOCKER_COMPATIBLE
|
# Move into the directory where this script is stored (commands are relative to this directory)
|
||||||
GPU_PASSTHROUGH=PLACEHOLDER_GPU_PASSTHROUGH
|
cd "$(dirname "${BASH_SOURCE[0]}")" || exit
|
||||||
|
# Get the name of the jail from the directory name
|
||||||
|
JAIL_NAME="$(basename "$(pwd)")"
|
||||||
|
|
||||||
# DO NOT EDIT THE SCRIPT FROM HERE ON, UNLESS YOU KNOW WHAT YOU ARE DOING
|
# Use mostly default settings for systemd-nspawn but with systemd-run instead of a service file
|
||||||
# THIS FILE WILL BE SOURCED IN A BASH SUB-SHELL BY PLACEHOLDER_SCRIPT_NAME
|
# https://github.com/systemd/systemd/blob/main/units/systemd-nspawn%40.service.in
|
||||||
# THE START FUNCTION WILL BE CALLED TO START THE JAIL
|
# TODO: also compare settings for docker: https://github.com/docker/engine/blob/master/contrib/init/systemd/docker.service
|
||||||
|
systemd-run --property=KillMode=mixed --property=Type=notify --property=RestartForceExitStatus=133 \
|
||||||
set -euo pipefail
|
--property=SuccessExitStatus=133 --property=Delegate=yes --property=TasksMax=16384 --same-dir \
|
||||||
|
--collect --setenv=SYSTEMD_NSPAWN_LOCK=0 \
|
||||||
# Move into the directory where this script is stored (commands are relative to this directory)
|
--unit="jlmkr-${JAIL_NAME}" --description="jailmaker ${JAIL_NAME}" \
|
||||||
cd "$(dirname "${BASH_SOURCE[0]}")" || exit
|
"${SYSTEMD_RUN_ADDITIONAL_ARGS[@]}" \
|
||||||
JAIL_NAME="$(basename "$(pwd)")"
|
-- \
|
||||||
|
systemd-nspawn --keep-unit --quiet --boot \
|
||||||
# You may add additional args to the two arrays below
|
--machine="${JAIL_NAME}" --directory='./PLACEHOLDER_ROOTFS_NAME' \
|
||||||
# These args will be passed to systemd-run and systemd-nspawn in the start function
|
PLACEHOLDER_SYSTEMD_NSPAWN_USER_ARGS \
|
||||||
SYSTEMD_RUN_ADDITIONAL_ARGS=()
|
"${SYSTEMD_NSPAWN_ADDITIONAL_ARGS[@]}"
|
||||||
SYSTEMD_NSPAWN_ADDITIONAL_ARGS=(PLACEHOLDER_SYSTEMD_NSPAWN_ADDITIONAL_ARGS)
|
# TEMPLATE END
|
||||||
|
|
||||||
start() {
|
|
||||||
# Use mostly default settings for systemd-nspawn but with systemd-run instead of a service file
|
|
||||||
# https://github.com/systemd/systemd/blob/main/units/systemd-nspawn%40.service.in
|
|
||||||
# TODO: also compare settings for docker: https://github.com/docker/engine/blob/master/contrib/init/systemd/docker.service
|
|
||||||
systemd-run --property=KillMode=mixed --property=Type=notify --property=RestartForceExitStatus=133 \
|
|
||||||
--property=SuccessExitStatus=133 --property=Delegate=yes --property=TasksMax=16384 --same-dir \
|
|
||||||
--collect --setenv=SYSTEMD_NSPAWN_LOCK=0 \
|
|
||||||
--unit="jlmkr-${JAIL_NAME}" --description="jailmaker ${JAIL_NAME}" \
|
|
||||||
"${SYSTEMD_RUN_ADDITIONAL_ARGS[@]}" \
|
|
||||||
-- \
|
|
||||||
systemd-nspawn --keep-unit --quiet --boot \
|
|
||||||
--machine="${JAIL_NAME}" --directory='./PLACEHOLDER_ROOTFS_NAME' \
|
|
||||||
"${SYSTEMD_NSPAWN_ADDITIONAL_ARGS[@]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Call the start function if this script is executed directly (not sourced)
|
|
||||||
# https://stackoverflow.com/a/28776166
|
|
||||||
(return 0 2>/dev/null) || {
|
|
||||||
echo 'This script was called directly, not sourced.'
|
|
||||||
echo 'The jail will now start...'
|
|
||||||
echo 'But the DOCKER_COMPATIBLE and GPU_PASSTHROUGH settings are not considered.'
|
|
||||||
echo "For this to work, start the jail from PLACEHOLDER_SCRIPT_NAME."
|
|
||||||
start
|
|
||||||
}
|
|
||||||
# TEMPLATE END
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Helper function to process the body of a function into a bash string
|
# Helper function to process the body of a function into a bash string
|
||||||
# which can be written as a new bash script
|
# which can be written as a new bash script
|
||||||
# Includes find-replace functionality to substitute placeholders in the template
|
# Includes find-replace functionality to substitute placeholders in the template
|
||||||
process_template() {
|
process_template() {
|
||||||
local indent=""
|
local indent=""
|
||||||
# Read the current script file, starting from the passed line number
|
# Read the current script file, starting from the passed line number
|
||||||
while IFS=$'\n' read -r line; do
|
while IFS=$'\n' read -r line; do
|
||||||
# Get the indent level from the first line
|
# Get the indent level from the first line
|
||||||
[[ -z "${indent}" ]] && indent="${line%%#*}" && continue
|
[[ -z "${indent}" ]] && indent="${line%%#*}" && continue
|
||||||
|
|
||||||
# Break when we find the end of the template
|
# Break when we find the end of the template
|
||||||
[[ "$line" = "${indent}# TEMPLATE END" ]] && break
|
[[ "$line" = "${indent}# TEMPLATE END" ]] && break
|
||||||
|
|
||||||
# Remove the indent from the start
|
# Remove the indent from the start
|
||||||
line="${line#"$indent"}"
|
line="${line#"$indent"}"
|
||||||
|
|
||||||
local find replace
|
local find replace
|
||||||
# Loop over the additional argument pairs passed
|
# Loop over the additional argument pairs passed
|
||||||
# and find/replace template key with value
|
# and find/replace template key with value
|
||||||
for ((n = 2; n <= $#; n++)); do
|
for ((n = 2; n <= $#; n++)); do
|
||||||
# https://stackoverflow.com/a/3575950
|
# https://stackoverflow.com/a/3575950
|
||||||
# Indirect expansion: look up the value of the variable whose name is in the variable
|
# Indirect expansion: look up the value of the variable whose name is in the variable
|
||||||
# So this looks up the nth argument passed to this function
|
# So this looks up the nth argument passed to this function
|
||||||
find=${!n}
|
find=${!n}
|
||||||
# Increment counter by one to get next argument
|
# Increment counter by one to get next argument
|
||||||
((n++))
|
((n++))
|
||||||
replace=${!n}
|
replace=${!n}
|
||||||
line=${line//$find/$replace}
|
line=${line//$find/$replace}
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "${line}"
|
echo "${line}"
|
||||||
done < <(tail -n "+${1}" "${ABSOLUTE_SCRIPT_PATH}")
|
done < <(tail -n "+${1}" "${ABSOLUTE_SCRIPT_PATH}")
|
||||||
}
|
|
||||||
|
|
||||||
# Helper function which escapes and concatenates a string of arguments
|
|
||||||
escape() {
|
|
||||||
local result="" spacer=""
|
|
||||||
# Process each argument (newline separated thanks to: xargs -n 1)
|
|
||||||
while IFS=$'\n' read -r arg; do
|
|
||||||
arg="$(declare -p arg)"
|
|
||||||
# Get everything after the first = character
|
|
||||||
arg="${arg#*=}"
|
|
||||||
# Append to string with leading space
|
|
||||||
result+="${spacer}${arg}"
|
|
||||||
# From now on, use a space in between elements
|
|
||||||
spacer=" "
|
|
||||||
done < <(printf "%s" "${1}" | xargs -n 1)
|
|
||||||
|
|
||||||
printf "%s" "${result}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
create_jail() {
|
create_jail() {
|
||||||
|
# TODO: show disclaimer
|
||||||
|
local reply arch distro release lxc_dir_path lxc_cache_path lxc_download_script_path
|
||||||
|
local jail_config_path jail_start_script_path docker_compatible gpu_passthrough
|
||||||
|
local jail_name systemd_nspawn_user_args
|
||||||
|
|
||||||
local reply arch distro release lxc_dir_path lxc_cache_path lxc_download_script_path
|
arch="$(dpkg --print-architecture)"
|
||||||
local jail_config_path jail_start_script_name jail_start_script_path docker_compatible gpu_passthrough
|
lxc_dir_path='.lxc'
|
||||||
local jail_name systemd_nspawn_additional_args
|
lxc_cache_path="${lxc_dir_path}/cache"
|
||||||
|
lxc_download_script_path="${lxc_dir_path}/lxc-download.sh"
|
||||||
|
|
||||||
arch="$(dpkg --print-architecture)"
|
[[ "$(basename "${SCRIPT_DIR_PATH}")" != 'jailmaker' ]] && {
|
||||||
lxc_dir_path='.lxc'
|
echo "${SCRIPT_NAME} needs to create files."
|
||||||
lxc_cache_path="${lxc_dir_path}/cache"
|
echo "Currently it can't decide if it's safe to create files in:"
|
||||||
lxc_download_script_path="${lxc_dir_path}/lxc-download.sh"
|
echo "${SCRIPT_DIR_PATH}"
|
||||||
|
fail "Please create a dedicated directory called 'jailmaker', store ${SCRIPT_NAME} there and try again."
|
||||||
|
}
|
||||||
|
|
||||||
[[ "$(basename "${SCRIPT_DIR_PATH}")" != 'jailmaker' ]] && {
|
if [[ $(findmnt --target . --output TARGET --noheadings --first-only) != /mnt/* ]]; then
|
||||||
echo "${SCRIPT_NAME} needs to create files."
|
echo "${SCRIPT_NAME} should be on a pool mounted under /mnt (it currently isn't)."
|
||||||
echo "Currently it can't decide if it's safe to create files in:"
|
echo "Storing it on the boot-pool means losing all jails when updating TrueNAS."
|
||||||
echo "${SCRIPT_DIR_PATH}"
|
echo "If you continue, jails will be stored under:"
|
||||||
fail "Please create a dedicated directory called 'jailmaker', store ${SCRIPT_NAME} there and try again."
|
echo "${SCRIPT_DIR_PATH}"
|
||||||
}
|
read -p "Do you wish to ignore this warning and continue? [y/N] " -n 1 -r reply && echo
|
||||||
|
# Enter accepts default (no)
|
||||||
|
! [[ "${reply}" =~ ^[Yy]$ ]] && exit
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ $(findmnt --target . --output TARGET --noheadings --first-only) != /mnt/* ]]; then
|
cd "${SCRIPT_DIR_PATH}" || fail "Could not change working directory to ${SCRIPT_DIR_PATH}..."
|
||||||
echo "${SCRIPT_NAME} should be on a pool mounted under /mnt (it currently isn't)."
|
|
||||||
echo "Storing it on the boot-pool means losing all jails when updating TrueNAS."
|
|
||||||
echo "If you continue, jails will be stored under:"
|
|
||||||
echo "${SCRIPT_DIR_PATH}"
|
|
||||||
read -p "Do you wish to ignore this warning and continue? [y/N] " -n 1 -r reply && echo
|
|
||||||
# Enter accepts default (no)
|
|
||||||
! [[ "${reply}" =~ ^[Yy]$ ]] && exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "${SCRIPT_DIR_PATH}" || fail "Could not change working directory to ${SCRIPT_DIR_PATH}..."
|
# Set appropriate permissions (if not already set) for this file, since it's executed as root
|
||||||
|
stat_chmod 700 "${SCRIPT_NAME}"
|
||||||
|
|
||||||
# Set appropriate permissions (if not already set) for this file, since it's executed as root
|
# Create the lxc dirs if nonexistent
|
||||||
stat_chmod 700 "${SCRIPT_NAME}"
|
mkdir -p "${lxc_dir_path}"
|
||||||
|
stat_chmod 700 "${lxc_dir_path}"
|
||||||
|
mkdir -p "${lxc_cache_path}"
|
||||||
|
stat_chmod 700 "${lxc_cache_path}"
|
||||||
|
|
||||||
# Create the lxc dirs if nonexistent
|
# Create the dir where to store the jails
|
||||||
mkdir -p "${lxc_dir_path}"
|
mkdir -p "${JAILS_DIR_PATH}"
|
||||||
stat_chmod 700 "${lxc_dir_path}"
|
stat_chmod 700 "${JAILS_DIR_PATH}"
|
||||||
mkdir -p "${lxc_cache_path}"
|
|
||||||
stat_chmod 700 "${lxc_cache_path}"
|
|
||||||
|
|
||||||
# Create the dir where to store the jails
|
# Fetch the lxc download script if not present locally (or hash doesn't match)
|
||||||
mkdir -p "${JAILS_DIR_PATH}"
|
if ! validate_download_script "${lxc_download_script_path}"; then
|
||||||
stat_chmod 700 "${JAILS_DIR_PATH}"
|
curl -fSL
|
||||||
|
https://raw.githubusercontent.com/Jip-Hop/lxc/58520263041b6864cadad96278848f9b8ce78ee9/templates/lxc-download.in -o "${lxc_download_script_path}"
|
||||||
|
# Validate after download to prevent executing arbitrary code as root
|
||||||
|
validate_download_script "${lxc_download_script_path}" || fail 'Abort! Downloaded script has unexpected contents.'
|
||||||
|
fi
|
||||||
|
|
||||||
# Fetch the lxc download script if not present locally (or hash doesn't match)
|
stat_chmod 700 "${lxc_download_script_path}"
|
||||||
if ! validate_download_script "${lxc_download_script_path}"; then
|
|
||||||
curl -fSL
|
|
||||||
https://raw.githubusercontent.com/Jip-Hop/lxc/58520263041b6864cadad96278848f9b8ce78ee9/templates/lxc-download.in -o "${lxc_download_script_path}"
|
|
||||||
# Validate after download to prevent executing arbitrary code as root
|
|
||||||
validate_download_script "${lxc_download_script_path}" || fail 'Abort! Downloaded script has unexpected contents.'
|
|
||||||
fi
|
|
||||||
|
|
||||||
stat_chmod 700 "${lxc_download_script_path}"
|
read -p "Install the recommended distro (Debian 11)? [Y/n] " -n 1 -r reply && echo
|
||||||
|
if [[ "${reply}" =~ ^([Yy]|)$ ]]; then
|
||||||
|
distro='debian'
|
||||||
|
release='bullseye'
|
||||||
|
else
|
||||||
|
echo
|
||||||
|
echo "ADVANCED USAGE"
|
||||||
|
echo "You may now choose from a list which distro to install."
|
||||||
|
echo "Not all of them will work with ${SCRIPT_NAME} (these images are made for LXC)."
|
||||||
|
echo "Distros based on systemd probably work (e.g. Ubuntu, Arch Linux and Rocky Linux)."
|
||||||
|
echo "Others (Alpine, Devuan, Void Linux) probably won't."
|
||||||
|
echo
|
||||||
|
read -p "Press any key to continue: " -n 1 -r reply && echo
|
||||||
|
lxc_cache_path=${lxc_cache_path} "${lxc_download_script_path}" --list --arch="${arch}" || :
|
||||||
|
echo "Choose from the DIST column."
|
||||||
|
read -e -r -p "Distribution: " distro && echo
|
||||||
|
echo "Choose from the RELEASE column (or ARCH if RELEASE is empty)."
|
||||||
|
read -e -r -p "Release: " release && echo
|
||||||
|
fi
|
||||||
|
|
||||||
read -p "Install the recommended distro (Debian 11)? [Y/n] " -n 1 -r reply && echo
|
while true; do
|
||||||
if [[ "${reply}" =~ ^([Yy]|)$ ]]; then
|
read -e -r -p "Enter jail name: " jail_name && echo
|
||||||
distro='debian'
|
if ! [[ "${jail_name}" =~ ^[.a-zA-Z0-9-]{1,64}$ && "${jail_name}" != '.'* && "${jail_name}" != *'.' && "${jail_name}" != *'..'* ]]; then
|
||||||
release='bullseye'
|
cat <<-'EOF'
|
||||||
else
|
|
||||||
echo
|
|
||||||
echo "ADVANCED USAGE"
|
|
||||||
echo "You may now choose from a list which distro to install."
|
|
||||||
echo "Not all of them will work with ${SCRIPT_NAME} (these images are made for LXC)."
|
|
||||||
echo "Distros based on systemd probably work (e.g. Ubuntu, Arch Linux and Rocky Linux)."
|
|
||||||
echo "Others (Alpine, Devuan, Void Linux) probably won't."
|
|
||||||
echo
|
|
||||||
read -p "Press any key to continue: " -n 1 -r reply && echo
|
|
||||||
lxc_cache_path=${lxc_cache_path} "${lxc_download_script_path}" --list --arch="${arch}" || :
|
|
||||||
echo "Choose from the DIST column."
|
|
||||||
read -e -r -p "Distribution: " distro && echo
|
|
||||||
echo "Choose from the RELEASE column (or ARCH if RELEASE is empty)."
|
|
||||||
read -e -r -p "Release: " release && echo
|
|
||||||
fi
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
read -e -r -p "Enter jail name: " jail_name && echo
|
|
||||||
if ! [[ "${jail_name}" =~ ^[.a-zA-Z0-9-]{1,64}$ && "${jail_name}" != '.'* && "${jail_name}" != *'.' && "${jail_name}" != *'..'* ]]; then
|
|
||||||
cat <<-'EOF'
|
|
||||||
A valid name consists of:
|
A valid name consists of:
|
||||||
- allowed characters (alphanumeric, dash, dot)
|
- allowed characters (alphanumeric, dash, dot)
|
||||||
- no leading or trailing dots
|
- no leading or trailing dots
|
||||||
|
@ -295,139 +265,128 @@ create_jail() {
|
||||||
- max 64 characters
|
- max 64 characters
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
else
|
else
|
||||||
JAIL_PATH="${JAILS_DIR_PATH}/${jail_name}"
|
JAIL_PATH="${JAILS_DIR_PATH}/${jail_name}"
|
||||||
|
|
||||||
if [[ -e "${JAIL_PATH}" ]]; then
|
if [[ -e "${JAIL_PATH}" ]]; then
|
||||||
echo "A jail with this name already exists."
|
echo "A jail with this name already exists."
|
||||||
echo
|
echo
|
||||||
else
|
else
|
||||||
# Accept the name
|
# Accept the name
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Cleanup on exit, but only once the JAIL_PATH name is known
|
# Cleanup on exit, but only once the JAIL_PATH is final
|
||||||
trap cleanup EXIT
|
# Otherwise we may cleanup the wrong directory
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
echo "${SCRIPT_NAME} will not install docker for you."
|
echo "${SCRIPT_NAME} will not install docker for you."
|
||||||
echo "But it can configure the jail with the capabilities required to run docker."
|
echo "But it can configure the jail with the capabilities required to run docker."
|
||||||
echo "You can turn docker_compatible mode on/off post-install."
|
echo "You can turn docker_compatible mode on/off post-install."
|
||||||
echo
|
echo
|
||||||
read -p "Make jail docker compatible right now? [y/N] " -n 1 -r reply && echo
|
read -p "Make jail docker compatible right now? [y/N] " -n 1 -r reply && echo
|
||||||
# Enter accepts default (no)
|
# Enter accepts default (no)
|
||||||
if ! [[ "${reply}" =~ ^[Yy]$ ]]; then docker_compatible=0; else docker_compatible=1; fi
|
if ! [[ "${reply}" =~ ^[Yy]$ ]]; then docker_compatible=0; else docker_compatible=1; fi
|
||||||
|
|
||||||
read -p "Give access to the GPU inside the jail? [y/N] " -n 1 -r reply && echo
|
read -p "Give access to the GPU inside the jail? [y/N] " -n 1 -r reply && echo
|
||||||
# Enter accepts default (no)
|
# Enter accepts default (no)
|
||||||
if ! [[ "${reply}" =~ ^[Yy]$ ]]; then gpu_passthrough=0; else gpu_passthrough=1; fi
|
if ! [[ "${reply}" =~ ^[Yy]$ ]]; then gpu_passthrough=0; else gpu_passthrough=1; fi
|
||||||
|
|
||||||
# TODO: ask for network setup (host, macvlan, bridge, physical nic)
|
# TODO: ask for network setup (host, macvlan, bridge, physical nic)
|
||||||
echo
|
echo
|
||||||
echo "You may pass additional systemd-nspawn flags."
|
echo "You may pass additional systemd-nspawn flags."
|
||||||
echo "For example to mount directories inside the jail you may add:"
|
echo "For example to mount directories inside the jail you may add:"
|
||||||
echo "--bind=/mnt/a/readwrite/directory --bind-ro=/mnt/a/readonly/directory"
|
echo "--bind=/mnt/a/readwrite/directory --bind-ro=/mnt/a/readonly/directory"
|
||||||
echo
|
echo
|
||||||
echo "Double check the syntax:"
|
echo "Double check the syntax:"
|
||||||
echo "https://manpages.debian.org/bullseye/systemd-container/systemd-nspawn.1.en.html"
|
echo "https://manpages.debian.org/bullseye/systemd-container/systemd-nspawn.1.en.html"
|
||||||
echo "With incorrect flags the jail may not start."
|
echo "With incorrect flags the jail may not start."
|
||||||
echo "It's possible to correct/add/remove flags post-install."
|
echo "It's possible to correct/add/remove flags post-install."
|
||||||
echo
|
echo
|
||||||
read -e -r -p "Additional flags: " SYSTEMD_NSPAWN_USER_ARGS_STRING && echo
|
read -e -r -p "Additional flags: " systemd_nspawn_user_args && echo
|
||||||
# Backslashes and colons need to be escaped for systemd-nspawn by the user:
|
# Backslashes and colons need to be escaped for systemd-nspawn by the user:
|
||||||
# e.g. to bind mount a file called:
|
# e.g. to bind mount a file called:
|
||||||
# weird chars :?\"
|
# weird chars :?\"
|
||||||
# the corresponding command would be:
|
# the corresponding command would be:
|
||||||
# --bind-ro='/mnt/data/weird chars \:?\\"'
|
# --bind-ro='/mnt/data/weird chars \:?\\"'
|
||||||
|
|
||||||
# Process the flags (read as a string) as actual command line arguments with xargs and
|
# Create directory for rootfs
|
||||||
# pass these args to bash which then executes the escape function to convert the args into a properly escaped string
|
JAIL_ROOTFS_PATH="${JAIL_PATH}/${JAIL_ROOTFS_NAME}"
|
||||||
# which can be evaluated as an array declaration in the start.sh bash script
|
mkdir -p "${JAIL_ROOTFS_PATH}"
|
||||||
# https://superuser.com/a/1529316/1268213 and https://superuser.com/a/1627765
|
|
||||||
systemd_nspawn_additional_args="$(escape "${SYSTEMD_NSPAWN_USER_ARGS_STRING}")"
|
|
||||||
echo "systemd_nspawn_additional_args=${systemd_nspawn_additional_args}"
|
|
||||||
|
|
||||||
# Create directory for rootfs
|
jail_config_path="${JAIL_PATH}/${JAIL_CONFIG_NAME}"
|
||||||
JAIL_ROOTFS_PATH="${JAIL_PATH}/${JAIL_ROOTFS_NAME}"
|
# LXC download script needs to write to this file during install
|
||||||
mkdir -p "${JAIL_ROOTFS_PATH}"
|
# but we don't need it so we will remove it later
|
||||||
|
touch "${jail_config_path}"
|
||||||
|
|
||||||
jail_config_path="${JAIL_PATH}/config"
|
echo
|
||||||
# LXC download script needs to write to this file during install
|
LXC_CACHE_PATH=${lxc_cache_path} "${lxc_download_script_path}" \
|
||||||
# but we don't need it so we will remove it later
|
--name="${jail_name}" --path="${JAIL_PATH}" --rootfs="${JAIL_ROOTFS_PATH}" \
|
||||||
touch "${jail_config_path}"
|
--arch="${arch}" --dist="${distro}" --release="${release}" ||
|
||||||
|
fail "Aborted creating rootfs..."
|
||||||
|
echo
|
||||||
|
|
||||||
echo
|
if [[ "$(basename "$(readlink -f "${JAIL_ROOTFS_PATH}/sbin/init")")" != systemd ]]; then
|
||||||
LXC_CACHE_PATH=${lxc_cache_path} "${lxc_download_script_path}" \
|
echo "Chosen distro appears not to use systemd..."
|
||||||
--name="${jail_name}" --path="${JAIL_PATH}" --rootfs="${JAIL_ROOTFS_PATH}" \
|
echo
|
||||||
--arch="${arch}" --dist="${distro}" --release="${release}" ||
|
echo "You probably won't get a shell with:"
|
||||||
fail "Aborted creating rootfs..."
|
echo "machinectl shell ${jail_name}"
|
||||||
echo
|
echo
|
||||||
|
echo "You may get a shell with this command:"
|
||||||
|
# About nsenter:
|
||||||
|
# https://github.com/systemd/systemd/issues/12785#issuecomment-503019081
|
||||||
|
# https://github.com/systemd/systemd/issues/3144
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
echo 'nsenter -t $(machinectl show '"${jail_name}"' -p Leader --value) -a /bin/sh -l'
|
||||||
|
echo
|
||||||
|
echo "Using this distro with ${SCRIPT_NAME} is not recommended."
|
||||||
|
echo
|
||||||
|
read -p "Abort creating jail? [Y/n] " -n 1 -r reply && echo
|
||||||
|
# Enter accepts default (yes)
|
||||||
|
[[ "${reply}" =~ ^([Yy]|)$ ]] && exit
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$(basename "$(readlink -f "${JAIL_ROOTFS_PATH}/sbin/init")")" != systemd ]]; then
|
# Config which systemd handles for us
|
||||||
echo "Chosen distro appears not to use systemd..."
|
rm -f "${JAIL_ROOTFS_PATH}/etc/machine-id"
|
||||||
echo
|
rm -f "${JAIL_ROOTFS_PATH}/etc/resolv.conf"
|
||||||
echo "You probably won't get a shell with:"
|
rm -f "${JAIL_ROOTFS_PATH}/etc/resolv.conf"
|
||||||
echo "machinectl shell ${jail_name}"
|
# https://github.com/systemd/systemd/issues/852
|
||||||
echo
|
printf 'pts/%d\n' $(seq 0 10) >"${JAIL_ROOTFS_PATH}/etc/securetty"
|
||||||
echo "You may get a shell with this command:"
|
|
||||||
# About nsenter:
|
|
||||||
# https://github.com/systemd/systemd/issues/12785#issuecomment-503019081
|
|
||||||
# https://github.com/systemd/systemd/issues/3144
|
|
||||||
# shellcheck disable=SC2016
|
|
||||||
echo 'nsenter -t $(machinectl show '"${jail_name}"' -p Leader --value) -a /bin/sh -l'
|
|
||||||
echo
|
|
||||||
echo "Using this distro with ${SCRIPT_NAME} is not recommended."
|
|
||||||
echo
|
|
||||||
read -p "Abort creating jail? [Y/n] " -n 1 -r reply && echo
|
|
||||||
# Enter accepts default (yes)
|
|
||||||
[[ "${reply}" =~ ^([Yy]|)$ ]] && exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Remove file we no longer need
|
jail_start_script_path="${JAIL_PATH}/${JAIL_START_SCRIPT_NAME}"
|
||||||
rm -f "${jail_config_path}"
|
|
||||||
# Config which systemd handles for us
|
|
||||||
rm -f "${JAIL_ROOTFS_PATH}/etc/machine-id"
|
|
||||||
rm -f "${JAIL_ROOTFS_PATH}/etc/resolv.conf"
|
|
||||||
rm -f "${JAIL_ROOTFS_PATH}/etc/resolv.conf"
|
|
||||||
# https://github.com/systemd/systemd/issues/852
|
|
||||||
printf 'pts/%d\n' $(seq 0 10) >"${JAIL_ROOTFS_PATH}/etc/securetty"
|
|
||||||
|
|
||||||
jail_start_script_name='start.sh'
|
cat <<-EOF >"${jail_config_path}"
|
||||||
jail_start_script_path="${JAIL_PATH}/${jail_start_script_name}"
|
DOCKER_COMPATIBLE=${docker_compatible}
|
||||||
|
GPU_PASSTHROUGH=${gpu_passthrough}
|
||||||
|
EOF
|
||||||
|
|
||||||
process_template "$(template_start_script)" PLACEHOLDER_DOCKER_COMPATIBLE "${docker_compatible}" PLACEHOLDER_GPU_PASSTHROUGH "${gpu_passthrough}" \
|
chmod 700 "${jail_config_path}"
|
||||||
PLACEHOLDER_ROOTFS_NAME "${JAIL_ROOTFS_NAME}" PLACEHOLDER_SYSTEMD_NSPAWN_ADDITIONAL_ARGS "${systemd_nspawn_additional_args}" \
|
|
||||||
PLACEHOLDER_SCRIPT_NAME "${SCRIPT_NAME}" >"${jail_start_script_path}"
|
|
||||||
|
|
||||||
chmod 700 "${jail_start_script_path}"
|
process_template "$(template_start_script)" \
|
||||||
|
PLACEHOLDER_ROOTFS_NAME "${JAIL_ROOTFS_NAME}" PLACEHOLDER_SYSTEMD_NSPAWN_USER_ARGS "${systemd_nspawn_user_args}" \
|
||||||
|
PLACEHOLDER_SCRIPT_NAME "${SCRIPT_NAME}" >"${jail_start_script_path}"
|
||||||
|
|
||||||
echo "Done creating the jail."
|
chmod 700 "${jail_start_script_path}"
|
||||||
CREATE_DONE=1
|
|
||||||
echo
|
# Remove the cleanup trap on exit
|
||||||
read -p "Start the jail now? [Y/n] " -n 1 -r reply && echo
|
trap - EXIT
|
||||||
# Enter accepts default (yes)
|
echo "Done creating the jail."
|
||||||
if [[ "${reply}" =~ ^([Yy]|)$ ]]; then
|
echo
|
||||||
run_jail "${jail_start_script_path}"
|
read -p "Start the jail now? [Y/n] " -n 1 -r reply && echo
|
||||||
else
|
# Enter accepts default (yes)
|
||||||
echo 'Skipped starting jail.'
|
if [[ "${reply}" =~ ^([Yy]|)$ ]]; then
|
||||||
fi
|
start_jail "${JAIL_PATH}"
|
||||||
|
else
|
||||||
|
echo 'Skipped starting jail.'
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
##########################
|
##########################
|
||||||
# END CREATE FUNCTIONALITY
|
# END CREATE FUNCTIONALITY
|
||||||
##########################
|
##########################
|
||||||
|
|
||||||
read -p "Create a new jail? [Y/n] " -n 1 -r reply && echo
|
|
||||||
# Enter accepts default (yes)
|
|
||||||
# https://stackoverflow.com/a/1885534
|
|
||||||
if [[ "${reply}" =~ ^([Yy]|)$ ]]; then
|
|
||||||
create_jail
|
|
||||||
else
|
|
||||||
echo "${USAGE}"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# TODO document
|
# TODO document
|
||||||
# machinectl shell
|
# machinectl shell
|
||||||
# If that doesn't work try
|
# If that doesn't work try
|
||||||
|
@ -440,3 +399,29 @@ fi
|
||||||
# machinectl login
|
# machinectl login
|
||||||
# TODO: recommend ssh ;)
|
# TODO: recommend ssh ;)
|
||||||
# TODO: create a jlmkr shell command to try the above in case machinectl shell doesn't work
|
# TODO: create a jlmkr shell command to try the above in case machinectl shell doesn't work
|
||||||
|
|
||||||
|
case "${1-""}" in
|
||||||
|
|
||||||
|
'')
|
||||||
|
read -p "Create a new jail? [Y/n] " -n 1 -r reply && echo
|
||||||
|
# Enter accepts default (yes)
|
||||||
|
# https://stackoverflow.com/a/1885534
|
||||||
|
if [[ "${reply}" =~ ^([Yy]|)$ ]]; then
|
||||||
|
create_jail
|
||||||
|
else
|
||||||
|
echo "${USAGE}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
create)
|
||||||
|
create_jail
|
||||||
|
;;
|
||||||
|
|
||||||
|
start)
|
||||||
|
start_jail "${JAILS_DIR_PATH}/${2}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "${USAGE}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
Loading…
Reference in New Issue