blob: c14cb42995ee7d72a315633d711e4df3ef887010 [file] [log] [blame]
#!/bin/sh
# Copyright 2018 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Perform first-time container setup if necessary, and start the container
# if it isn't already running.
. /usr/share/misc/shflags
DEFINE_boolean dummy "${FLAGS_FALSE}" \
"Dummy run. Exit successfully immediately."
DEFINE_string container_name "" \
"The name of the container"
DEFINE_string container_token "" \
"The garcon access token for the container"
DEFINE_string lxd_image "debian/stretch" \
"The image to create a container from"
DEFINE_string lxd_remote "https://storage.googleapis.com/cros-containers" \
"The LXD remote URL"
DEFINE_boolean shell "${FLAGS_FALSE}" \
"Launch a login shell in the container when the script is complete."
DEFINE_string user "" \
"The main user for the container"
DEFINE_string guest_private_key "" \
"The private SSH key for the guest container"
DEFINE_string host_public_key "" \
"The SSH public key for the CrOS host"
FLAGS "$@" || exit 1
set -e
info() {
if [ -t 2 ]; then
echo "run_container: info: $*" >&2
else
logger -p syslog.info -t "run_container" "$*"
fi
}
warning() {
if [ -t 2 ]; then
echo "run_container: warning: $*" >&2
else
logger -p syslog.warning -t "run_container" "$*"
fi
}
error() {
if [ -t 2 ]; then
echo "run_container: error: $*" >&2
else
logger -p syslog.err -t "run_container" "$*"
fi
}
die() {
error "$*"
exit 1
}
container_exists() {
local container_name="$1"
lxc info "${container_name}" >/dev/null 2>&1
}
container_running() {
local container_name="$1"
lxc info "${container_name}" 2>&1 | grep -q "Status: Running"
}
# Add or remove a bind mount, depending on if the source is available.
container_bind_mount() {
local container_name="$1"
local device_name="$2"
local source="$3"
local dest="$4"
# Remove an existing bind mount if the source doesn't exist.
if lxc config device get "${container_name}" \
"${device_name}" \
source >/dev/null 2>&1; then
if [ ! -e "${source}" ]; then
lxc config device remove "${container_name}" \
"${device_name}" || \
die "Failed to remove ${device_name}"
fi
else
if [ -e "${source}" ]; then
lxc config device add "${container_name}" \
"${device_name}" \
disk \
source="${source}" \
path="${dest}" || \
die "Failed to add ${device_name}"
fi
fi
}
main() {
warning "run_container.sh is deprecated and will be removed"
warning "use vmc container <vm> <container> from crosh instead"
sleep 5
if [ -z "${FLAGS_container_name}" ]; then
die "--container_name is required"
fi
local token_path="/run/tokens/${FLAGS_container_name}_token"
local ssh_key_dir="/run/sshd/${FLAGS_container_name}"
local ssh_host_key="${ssh_key_dir}/ssh_host_key"
local ssh_authorized_keys="${ssh_key_dir}/authorized_keys"
mkdir -p "${ssh_key_dir}"
# If specified, put the keys into the SSH key directory, and garcon container
# token in place.
if [ -n "${FLAGS_guest_private_key}" ]; then
printf "%s" "${FLAGS_guest_private_key}" > "${ssh_host_key}"
fi
if [ -n "${FLAGS_host_public_key}" ]; then
printf "%s" "${FLAGS_host_public_key}" > "${ssh_authorized_keys}"
fi
if [ -n "${FLAGS_container_token}" ]; then
printf "%s" "${FLAGS_container_token}" > "${token_path}"
elif [ ! -f "${token_path}" ]; then
warning "container token not supplied; garcon may not function"
fi
# Wait for tremplin to set the owner of the lxd_conf directory.
# This will only happen on the first boot of a VM.
local attempts=0
while [ "$(stat -c %U "${LXD_CONF}")" != "chronos" ]; do
if [ ${attempts} -ge 300 ]; then
die "Timed out waiting for tremplin to set LXD_CONF owner"
fi
: $(( attempts += 1 ))
sleep 0.1
done
if ! container_exists "${FLAGS_container_name}"; then
if [ -z "${FLAGS_lxd_remote}" ]; then
die "Container does not already exist; you must specify --lxd_remote"
elif [ -z "${FLAGS_lxd_image}" ]; then
die "Container does not already exist; you must specify --lxd_image"
fi
info "Container '${FLAGS_container_name}' does not exist; creating from '${FLAGS_lxd_image}' on ${FLAGS_lxd_remote}"
lxc remote remove "google" >/dev/null 2>&1 || true
lxc remote add "google" "${FLAGS_lxd_remote}" --protocol=simplestreams || \
die "Failed to add lxd remote '${FLAGS_lxd_remote}'"
lxc init "google:${FLAGS_lxd_image}" "${FLAGS_container_name}" || \
die "Unable to create container from image '${FLAGS_lxd_image}'"
# Set up container token for garcon.
lxc config device add "${FLAGS_container_name}" \
container_token \
disk \
source="${token_path}" \
path="/dev/.container_token" || \
die "Failed to add container token"
info "Created container '${FLAGS_container_name}'"
fi
# Start the container if it's not running.
if ! container_running "${FLAGS_container_name}"; then
# Set up or remove bind mounts as necessary.
container_bind_mount "${FLAGS_container_name}" \
"container_token" \
"${token_path}" \
"/dev/.container_token"
container_bind_mount "${FLAGS_container_name}" \
"sshd_config" \
"/usr/share/container_sshd_config" \
"/dev/.ssh/sshd_config"
container_bind_mount "${FLAGS_container_name}" \
"ssh_host_key" \
"${ssh_host_key}" \
"/dev/.ssh/ssh_host_key"
container_bind_mount "${FLAGS_container_name}" \
"ssh_authorized_keys" \
"${ssh_authorized_keys}" \
"/dev/.ssh/ssh_authorized_keys"
lxc start "${FLAGS_container_name}" || \
die "Failed to start container '"${FLAGS_container_name}"'"
info "Started container '${FLAGS_container_name}'"
fi
lxc_exec() { lxc exec "${FLAGS_container_name}" -- "$@"; }
# Check if user exists. If not, create the user.
if [ -n "${FLAGS_user}" ] && \
! lxc_exec id -u "${FLAGS_user}" >/dev/null 2>&1; then
lxc_exec useradd -u 1000 \
-s "/bin/bash" \
-m "${FLAGS_user}" || die "Failed to add user"
local groups="audio cdrom dialout floppy plugdev sudo users video"
local group
for group in ${groups}; do
lxc_exec usermod -aG "${group}" "${FLAGS_user}" >/dev/null 2>&1 || \
warning "Failed to add ${FLAGS_user} to group ${group}"
done
lxc_exec loginctl enable-linger "${FLAGS_user}" || \
die "Failed to enable linger"
fi
if [ "${FLAGS_shell}" -eq "${FLAGS_TRUE}" ]; then
if [ -z "${FLAGS_user}" ]; then
die "--user is required when using --shell"
fi
exec lxc exec "${FLAGS_container_name}" -- /bin/login -f "${FLAGS_user}"
fi
}
main "$@"