Drop the Buildah dependency and the user-specific customized image

This works by configuring the toolbox container after it has been
created, instead of before. The toolbox script itself is mentioned as
the entry point of the container, which does 'exec sleep +Inf' once the
initialization is done.

A new command 'init-container' was added to perform the initialization.
It is primarily meant to be used as the entry point for all toolbox
containers, and must be run inside the container that's to be
initialized. It is not expected to be directly invoked by humans, and
cannot be used on the host.

As a result, the default name for the toolbox containers is now
fedora-toolbox-<version-id>, not fedora-toolbox-<user>-<version-id>.
For backwards compatibility, 'toolbox enter' and 'toolbox run' will
continue to work with containers using the old naming scheme.

https://github.com/debarshiray/toolbox/pull/160
This commit is contained in:
Debarshi Ray 2019-05-10 20:38:46 +02:00
parent dadb21dade
commit 8b84b5e460
6 changed files with 263 additions and 249 deletions

View file

@ -19,20 +19,20 @@ works equally well if you're running e.g. existing Fedora Workstation or
Server, and that's a useful way to incrementally adopt containerization.
The toolbox environment is based on an [OCI](https://www.opencontainers.org/)
image. On Fedora this is the `fedora-toolbox` image. This image is then
customized for the current user to create a toolbox container that seamlessly
integrates with the rest of the operating system.
image. On Fedora this is the `fedora-toolbox` image. This image is used to
create a toolbox container that seamlessly integrates with the rest of the
operating system.
## Usage
### Create your toolbox container:
```
[user@hostname ~]$ toolbox create
Created container: fedora-toolbox-30
Enter with: toolbox enter
[user@hostname ~]$
```
This will create a container, and an image, called
`fedora-toolbox-<your-username>:<version-id>` that's specifically customised
for your host user.
This will create a container called `fedora-toolbox-<version-id>`.
### Enter the toolbox:
```

View file

@ -13,12 +13,13 @@ __toolbox() {
local MIN_VERSION=29
local RAWHIDE_VERSION=31
local verbose_commands="create enter list run"
local verbose_commands="create enter init-container list run"
local commands="$verbose_commands rm rmi"
declare -A options
local options=([create]="--candidate-registry --container --image --release" \
[enter]="--container --release" \
[init-container]="--home --monitor-host --shell --uid --user" \
[list]="--containers --images" \
[rm]="--all --force" \
[rmi]="--all --force" \

View file

@ -8,6 +8,7 @@ manuals = [
'toolbox.1',
'toolbox-create.1',
'toolbox-enter.1',
'toolbox-init-container.1',
'toolbox-list.1',
'toolbox-rm.1',
'toolbox-rmi.1',

View file

@ -0,0 +1,48 @@
% toolbox-init-container(1)
## NAME
toolbox\-init\-container - Initialize a running container
## SYNOPSIS
**toolbox init-container** *--home HOME*
*--monitor-host*
*--shell SHELL*
*--uid UID*
*--user USER*
## DESCRIPTION
Initializes a newly created container that's running. It is primarily meant to
be used as the entry point for all toolbox containers, and must be run inside
the container that's to be initialized. It is not expected to be directly
invoked by humans, and cannot be used on the host.
## OPTIONS ##
The following options are understood:
**--home** HOME
Create a user inside the toolbox container whose login directory is HOME.
**--monitor-host**
Ensure that certain configuration files inside the toolbox container are kept
synchronized with their counterparts on the host. Currently, these files are
`/etc/hosts` and `/etc/resolv.conf`.
**--shell** SHELL
Create a user inside the toolbox container whose login shell is SHELL.
**--uid** UID
Create a user inside the toolbox container whose numerical user ID is UID.
**--user** USER
Create a user inside the toolbox container whose login name is LOGIN.
## SEE ALSO
`podman(1)`, `podman-create(1)`, `podman-start(1)`

View file

@ -57,6 +57,10 @@ Create a new toolbox container.
Enter a toolbox container for interactive use.
**toolbox-init-container(1)**
Initialize a running container.
**toolbox-list(1)**
List existing toolbox containers and images.

414
toolbox
View file

@ -62,7 +62,8 @@ tab="$(printf '\t')"
toolbox_command_path=""
toolbox_container=""
toolbox_container_default=""
toolbox_container_old=""
toolbox_container_old_v1=""
toolbox_container_old_v2=""
toolbox_container_prefix_default=""
toolbox_image=""
user_id_real=$(id -ru 2>&3)
@ -233,123 +234,6 @@ ask_for_confirmation()
)
check_image_for_volumes()
(
image="$1"
echo "$base_toolbox_command: checking if image $image has volumes for host bind mounts" >&3
if volumes=$($prefix_sudo podman inspect \
--format "{{.Config.Volumes}}" \
--type image \
"$image" 2>&3); then
if echo "$volumes" | grep "/dev/dri\|/dev/fuse" >/dev/null 2>&3; then
echo "$base_toolbox_command: image $image has volumes for host bind mounts:" >&3
echo "$base_toolbox_command: $volumes" >&3
echo "$base_toolbox_command: consider re-creating image $image" >&3
fi
fi
)
configure_working_container()
(
working_container_name="$1"
podman_create_supports_dns_none_no_hosts="$2"
buildah_unshare_supports_sh_c=false
echo "$base_toolbox_command: checking if 'buildah unshare' supports sh -c" >&3
if $prefix_sudo buildah unshare -- sh -c 'echo "hello world"' >/dev/null 2>&3; then
echo "$base_toolbox_command: 'buildah unshare' supports sh -c" >&3
buildah_unshare_supports_sh_c=true
fi
if [ "$(readlink /home)" = var/home ] ; then
need_home_link=true
else
need_home_link=false
fi
if $need_home_link ; then
if ! $prefix_sudo buildah run "$working_container_name" -- \
sh -c 'rmdir /home && mkdir -m 0755 -p /var/home && ln -s var/home /home' 2>&3; then
$prefix_sudo buildah rm "$working_container_name" >/dev/null 2>&3
echo "$base_toolbox_command: failed to make /home a symlink" >&2
return 1
fi
fi
if ! $prefix_sudo buildah copy "$working_container_name" \
/etc/krb5.conf.d/kcm_default_ccache \
/etc/krb5.conf.d >/dev/null 2>&3; then
$prefix_sudo buildah rm "$working_container_name" >/dev/null 2>&3
echo "$base_toolbox_command: failed to set KCM as the default Kerberos credential cache" >&2
return 1
fi
if ! $prefix_sudo buildah run "$working_container_name" -- useradd \
--home-dir "$HOME" \
--no-create-home \
--shell "$SHELL" \
--uid "$user_id_real" \
--groups wheel \
"$USER" \
>/dev/null 2>&3; then
echo "$base_toolbox_command: failed to create user $USER with UID $user_id_real" >&2
return 1
fi
if ! $prefix_sudo buildah run "$working_container_name" -- passwd -d "$USER" >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to remove password for user $USER" >&2
return 1
fi
if ! $prefix_sudo buildah run "$working_container_name" -- passwd -d root >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to remove password for user root" >&2
return 1
fi
if ! $prefix_sudo buildah config \
--label "com.github.debarshiray.toolbox=true" \
"$working_container_name" >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to add com.github.debarshiray.toolbox=true" >&2
return 1
fi
if $buildah_unshare_supports_sh_c && $podman_create_supports_dns_none_no_hosts; then
# shellcheck disable=SC2016
if ! $prefix_sudo buildah unshare --\
sh -c 'working_container_root=$(buildah mount "$1") '\
' && cd "$working_container_root/etc" '\
' && unlink hosts '\
' && ln --symbolic /run/host/etc/hosts hosts '\
' && buildah umount "$1"' \
"/bin/sh" \
"$working_container_name" >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to redirect /etc/hosts to /run/host/etc/hosts" >&2
return 1
fi
# shellcheck disable=SC2016
if ! $prefix_sudo buildah unshare -- \
sh -c 'working_container_root=$(buildah mount "$1") '\
' && cd "$working_container_root/etc" '\
' && unlink resolv.conf '\
' && ln --symbolic /run/host/etc/resolv.conf resolv.conf '\
' && buildah umount "$1"' \
"/bin/sh" \
"$working_container_name" >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to redirect /etc/resolv.conf to /run/host/etc/resolv.conf" >&2
return 1
fi
fi
return 0
)
container_name_is_valid()
(
name="$1"
@ -730,11 +614,10 @@ create()
dns_none=""
kcm_socket=""
kcm_socket_bind=""
monitor_host=""
no_hosts=""
podman_create_supports_dns_none_no_hosts=false
tmpfs_size=$((64 * 1024 * 1024)) # 64 MiB
toolbox_profile_bind=""
working_container_name="toolbox-working-container-$(uuidgen --time)"
# shellcheck disable=SC2153
if [ "$DBUS_SYSTEM_BUS_ADDRESS" != "" ]; then
@ -778,14 +661,13 @@ create()
if $prefix_sudo podman create --help 2>&3 | grep "hosts" >/dev/null 2>&3; then
echo "$base_toolbox_command: 'podman create' supports --dns=none and --no-hosts" >&3
podman_create_supports_dns_none_no_hosts=true
dns_none="--dns none"
no_hosts="--no-hosts"
monitor_host="--monitor-host"
fi
echo "$base_toolbox_command: checking if image $toolbox_image already exists" >&3
if ! $prefix_sudo podman image exists $toolbox_image >/dev/null 2>&3; then
if ! pull_base_toolbox_image; then
return 1
fi
@ -804,86 +686,6 @@ create()
echo "$base_toolbox_command: base image $base_toolbox_image resolved to $base_toolbox_image_full" >&3
fi
echo "$base_toolbox_command: trying to create working container $working_container_name" >&3
if spinner_directory=$(mktemp --directory --tmpdir $spinner_template 2>&3); then
spinner_message="$base_toolbox_command: creating working container: "
if ! spinner_start "$spinner_directory" "$spinner_message"; then
spinner_directory=""
fi
else
echo "$base_toolbox_command: unable to start spinner: spinner directory not created" >&2
spinner_directory=""
fi
$prefix_sudo buildah from --name "$working_container_name" "$base_toolbox_image_full" >/dev/null 2>&3
ret_val=$?
if [ "$spinner_directory" != "" ]; then
spinner_stop "$spinner_directory"
fi
if [ $ret_val -ne 0 ]; then
echo "$base_toolbox_command: failed to create working container" >&2
return 1
fi
echo "$base_toolbox_command: trying to configure working container $working_container_name" >&3
if spinner_directory=$(mktemp --directory --tmpdir $spinner_template 2>&3); then
spinner_message="$base_toolbox_command: configuring working container: "
if ! spinner_start "$spinner_directory" "$spinner_message"; then
spinner_directory=""
fi
else
echo "$base_toolbox_command: unable to start spinner: spinner directory not created" >&2
spinner_directory=""
fi
configure_working_container \
"$working_container_name" \
"$podman_create_supports_dns_none_no_hosts"
ret_val=$?
if [ "$spinner_directory" != "" ]; then
spinner_stop "$spinner_directory"
fi
if [ $ret_val -ne 0 ]; then
$prefix_sudo buildah rm "$working_container_name" >/dev/null 2>&3
return 1
fi
echo "$base_toolbox_command: trying to create image $toolbox_image" >&3
if spinner_directory=$(mktemp --directory --tmpdir $spinner_template 2>&3); then
spinner_message="$base_toolbox_command: creating image $toolbox_image: "
if ! spinner_start "$spinner_directory" "$spinner_message"; then
spinner_directory=""
fi
else
echo "$base_toolbox_command: unable to start spinner: spinner directory not created" >&2
spinner_directory=""
fi
$prefix_sudo buildah commit --rm "$working_container_name" "$toolbox_image" >/dev/null 2>&3
ret_val=$?
if [ "$spinner_directory" != "" ]; then
spinner_stop "$spinner_directory"
fi
if [ $ret_val -ne 0 ]; then
$prefix_sudo buildah rm "$working_container_name" >/dev/null 2>&3
echo "$base_toolbox_command: failed to create image $toolbox_image" >&2
return 1
fi
echo "$base_toolbox_command: created image $toolbox_image" >&3
fi
check_image_for_volumes "$toolbox_image"
echo "$base_toolbox_command: checking if container $toolbox_container already exists" >&3
enter_command=$(create_enter_command "$toolbox_container")
@ -954,8 +756,13 @@ create()
--volume /mnt:/mnt:rslave \
--volume /run/media:/run/media:rslave \
--workdir "$HOME" \
"$toolbox_image" \
sleep +Inf >/dev/null 2>&3
"$base_toolbox_image_full" \
toolbox init-container \
--home "$HOME" \
$monitor_host \
--shell "$SHELL" \
--uid "$user_id_real" \
--user "$USER" >/dev/null 2>&3
ret_val=$?
if [ "$spinner_directory" != "" ]; then
@ -982,6 +789,94 @@ enter()
}
init_container()
{
init_container_home="$1"
init_container_monitor_host="$2"
init_container_shell="$3"
init_container_uid="$4"
init_container_user="$5"
if $init_container_monitor_host; then
working_directory="$PWD"
if ! readlink /etc/hosts >/dev/null 2>&3; then
if ! cd /etc 2>&3 && unlink hosts 2>&3 && ln --symbolic /run/host/etc/hosts hosts 2>&3; then
echo "$base_toolbox_command: failed to redirect /etc/hosts to /run/host/etc/hosts" >&2
return 1
fi
fi
if ! readlink /etc/resolv.conf >/dev/null 2>&3; then
if ! cd /etc 2>&3 \
&& unlink resolv.conf 2>&3 \
&& ln --symbolic /run/host/etc/resolv.conf resolv.conf 2>&3; then
echo "$base_toolbox_command: failed to redirect /etc/resolv.conf to /run/host/etc/resolv.conf" >&2
return 1
fi
fi
if ! cd "$working_directory" 2>&3; then
echo "$base_toolbox_command: failed to restore working directory" >&2
fi
fi
if ! id -u "$init_container_user" >/dev/null 2>&3; then
if [ "$(readlink /home)" = var/home ] 2>&3; then
# shellcheck disable=SC2174
if ! rmdir /home 2>&3 \
&& mkdir --mode 0755 --parents /var/home 2>&3 \
&& ln --symbolic var/home /home 2>&3; then
echo "$base_toolbox_command: failed to make /home a symlink" >&2
return 1
fi
fi
if ! useradd \
--home-dir "$init_container_home" \
--no-create-home \
--shell "$init_container_shell" \
--uid "$init_container_uid" \
--groups wheel \
"$init_container_user" >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to add user $init_container_user with UID $init_container_uid" >&2
return 1
fi
if ! passwd --delete "$init_container_user" >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to remove password for user $init_container_user" >&2
return 1
fi
if ! passwd --delete root >/dev/null 2>&3; then
echo "$base_toolbox_command: failed to remove password for user root" >&2
return 1
fi
fi
if ! [ -f /etc/krb5.conf.d/kcm_default_ccache ] 2>&3; then
cat <<EOF >/etc/krb5.conf.d/kcm_default_ccache 2>&3
# Written by Toolbox
# https://github.com/debarshiray/toolbox
#
# # To disable the KCM credential cache, comment out the following lines.
[libdefaults]
default_ccache_name = KCM:
EOF
ret_val=$?
if [ "$ret_val" -ne 0 ] 2>&3; then
echo "$base_toolbox_command: failed to set KCM as the default Kerberos credential cache" >&2
return 1
fi
fi
exec sleep +Inf
}
run()
(
fallback_to_bash="$1"
@ -994,20 +889,19 @@ run()
echo "$base_toolbox_command: checking if container $toolbox_container exists" >&3
if ! toolbox_image=$($prefix_sudo podman inspect \
--format "{{.ImageName}}" \
--type container \
$toolbox_container 2>&3); then
if ! $prefix_sudo podman container exists "$toolbox_container" 2>&3; then
echo "$base_toolbox_command: container $toolbox_container not found" >&3
if toolbox_image=$($prefix_sudo podman inspect \
--format "{{.ImageName}}" \
--type container \
"$toolbox_container_old" 2>&3); then
echo "$base_toolbox_command: container $toolbox_container_old found" >&3
if $prefix_sudo podman container exists "$toolbox_container_old_v1" 2>&3; then
echo "$base_toolbox_command: container $toolbox_container_old_v1 found" >&3
# shellcheck disable=SC2030
toolbox_container="$toolbox_container_old"
toolbox_container="$toolbox_container_old_v1"
elif $prefix_sudo podman container exists "$toolbox_container_old_v2" 2>&3; then
echo "$base_toolbox_command: container $toolbox_container_old_v2 found" >&3
# shellcheck disable=SC2030
toolbox_container="$toolbox_container_old_v2"
else
if $pedantic; then
enter_print_container_not_found "$toolbox_container"
@ -1072,10 +966,6 @@ run()
fi
fi
echo "$base_toolbox_command: container $toolbox_container was created from image $toolbox_image" >&3
[ "$toolbox_image" != "" ] 2>&3 && check_image_for_volumes "$toolbox_image"
echo "$base_toolbox_command: trying to start container $toolbox_container" >&3
if ! $prefix_sudo podman start "$toolbox_container" >/dev/null 2>&3; then
@ -1490,18 +1380,16 @@ update_container_and_image_names()
return 1
fi
echo "$base_toolbox_command: customized user-specific image is $toolbox_image" >&3
# shellcheck disable=SC2031
if [ "$toolbox_container" = "" ]; then
toolbox_container=$(create_toolbox_container_name "$toolbox_image")
toolbox_container=$(create_toolbox_container_name "$base_toolbox_image")
if ! (
ret_val=$?
if [ "$ret_val" -ne 0 ] 2>&3; then
if [ "$ret_val" -eq 100 ] 2>&3; then
echo "$base_toolbox_command: failed to get the basename of image $toolbox_image" >&2
echo "$base_toolbox_command: failed to get the basename of image $base_toolbox_image" >&2
elif [ "$ret_val" -eq 101 ] 2>&3; then
echo "$base_toolbox_command: failed to get the tag of image $toolbox_image" >&2
echo "$base_toolbox_command: failed to get the tag of image $base_toolbox_image" >&2
else
echo "$base_toolbox_command: failed to create a name for the toolbox container" >&2
fi
@ -1521,7 +1409,27 @@ update_container_and_image_names()
return 1
fi
toolbox_container_old="$toolbox_image"
toolbox_container_old_v1=$(create_toolbox_container_name "$toolbox_image")
if ! (
ret_val=$?
if [ "$ret_val" -ne 0 ] 2>&3; then
if [ "$ret_val" -eq 100 ] 2>&3; then
echo "$base_toolbox_command: failed to get the basename of image $toolbox_image" >&2
elif [ "$ret_val" -eq 101 ] 2>&3; then
echo "$base_toolbox_command: failed to get the tag of image $toolbox_image" >&2
else
echo "$base_toolbox_command: failed to create a name for the toolbox container" >&2
fi
exit 1
fi
exit 0
); then
return 1
fi
toolbox_container_old_v2="$toolbox_image"
fi
echo "$base_toolbox_command: container is $toolbox_container" >&3
@ -1543,6 +1451,13 @@ usage()
echo " [-r | --release <release>]"
echo " or: toolbox [-v | --verbose]"
echo " [-y | --assumeyes]"
echo " init-container --home"
echo " --monitor-host"
echo " --shell"
echo " --uid"
echo " --user"
echo " or: toolbox [-v | --verbose]"
echo " [-y | --assumeyes]"
echo " list [-c | --containers]"
echo " [-i | --images]"
echo " or: toolbox [-y | --assumeyes]"
@ -1567,7 +1482,7 @@ if [ "$host_id" = "fedora" ] 2>&3; then
else
release_default="29"
fi
toolbox_container_prefix_default="fedora-toolbox-$USER"
toolbox_container_prefix_default="fedora-toolbox"
toolbox_container_default="$toolbox_container_prefix_default-$release_default"
while has_prefix "$1" -; do
@ -1639,6 +1554,46 @@ if [ -f /run/.containerenv ] 2>&3; then
forward_to_host
exit "$?"
;;
init-container )
init_container_monitor_host=false
while has_prefix "$1" -; do
case $1 in
--home )
shift
exit_if_missing_argument --home "$1"
init_container_home="$1"
;;
--monitor-host )
init_container_monitor_host=true
;;
--shell )
shift
exit_if_missing_argument --shell "$1"
init_container_shell="$1"
;;
--uid )
shift
exit_if_missing_argument --uid "$1"
init_container_uid="$1"
;;
--user )
shift
exit_if_missing_argument --user "$1"
init_container_user="$1"
;;
* )
exit_if_unrecognized_option "$1"
esac
shift
done
init_container \
"$init_container_home" \
"$init_container_monitor_host" \
"$init_container_shell" \
"$init_container_uid" \
"$init_container_user"
exit "$?"
;;
* )
echo "$base_toolbox_command: unrecognized command '$op'" >&2
echo "Try '$base_toolbox_command --help' for more information." >&2
@ -1719,6 +1674,11 @@ case $op in
enter
exit
;;
init-container )
echo "$base_toolbox_command: The 'init-container' command can only be used inside containers" >&2
echo "Try '$base_toolbox_command --help' for more information." >&2
exit 1
;;
list )
ls_images=false
ls_containers=false