From a75f99d20d6310039d2bf675e275e8a1b9e66177 Mon Sep 17 00:00:00 2001 From: Manabu Sugimoto Date: Mon, 8 Aug 2022 09:42:41 +0900 Subject: [PATCH] osbuilder: Create guest image for SELinux Create a guest image to support SELinux for containers inside the guest if `SELINUX=yes` is specified. This works only if the guest rootfs is CentOS and the init service is systemd, not the agent init. To enable labeling the guest image on the host, selinuxfs must be mounted on the host. The kata-agent will be labeled as `container_runtime_exec_t` type. Fixes: #4812 Signed-off-by: Manabu Sugimoto --- .../osbuilder/image-builder/image_builder.sh | 45 ++++++++++++++++++- .../osbuilder/rootfs-builder/centos/config.sh | 5 +++ tools/osbuilder/rootfs-builder/rootfs.sh | 16 +++++++ tools/osbuilder/scripts/lib.sh | 18 +++++++- 4 files changed, 82 insertions(+), 2 deletions(-) diff --git a/tools/osbuilder/image-builder/image_builder.sh b/tools/osbuilder/image-builder/image_builder.sh index 8b65ab4e8f..75b23b1765 100755 --- a/tools/osbuilder/image-builder/image_builder.sh +++ b/tools/osbuilder/image-builder/image_builder.sh @@ -64,6 +64,8 @@ readonly -a systemd_files=( # Set a default value AGENT_INIT=${AGENT_INIT:-no} +SELINUX=${SELINUX:-no} +SELINUXFS="/sys/fs/selinux" # Align image to 128M readonly mem_boundary_mb=128 @@ -93,6 +95,10 @@ Extra environment variables: DEFAULT: not set USE_PODMAN: If set and USE_DOCKER not set, will build image in a Podman Container (requries podman) DEFAULT: not set + SELINUX: If set to "yes", the rootfs is labeled for SELinux. + Make sure that selinuxfs is mounted to /sys/fs/selinux on the host + and the rootfs is built with SELINUX=yes. + DEFAULT value: "no" Following diagram shows how the resulting image will look like @@ -134,6 +140,7 @@ build_with_container() { local nsdax_bin="$9" local container_image_name="image-builder-osbuilder" local shared_files="" + local selinuxfs="" image_dir=$(readlink -f "$(dirname "${image}")") image_name=$(basename "${image}") @@ -157,6 +164,14 @@ build_with_container() { shared_files+="-v ${mke2fs_conf}:${mke2fs_conf}:ro " fi + if [ "${SELINUX}" == "yes" ]; then + if mountpoint $SELINUXFS > /dev/null; then + selinuxfs="-v ${SELINUXFS}:${SELINUXFS}" + else + die "Make sure that SELinux is enabled on the host" + fi + fi + #Make sure we use a compatible runtime to build rootfs # In case Clear Containers Runtime is installed we dont want to hit issue: #https://github.com/clearcontainers/runtime/issues/828 @@ -170,12 +185,14 @@ build_with_container() { --env BLOCK_SIZE="${block_size}" \ --env ROOT_FREE_SPACE="${root_free_space}" \ --env NSDAX_BIN="${nsdax_bin}" \ + --env SELINUX="${SELINUX}" \ --env DEBUG="${DEBUG}" \ -v /dev:/dev \ -v "${script_dir}":"/osbuilder" \ -v "${script_dir}/../scripts":"/scripts" \ -v "${rootfs}":"/rootfs" \ -v "${image_dir}":"/image" \ + ${selinuxfs} \ ${shared_files} \ ${container_image_name} \ bash "/osbuilder/${script_name}" -o "/image/${image_name}" /rootfs @@ -384,6 +401,7 @@ create_rootfs_image() { local img_size="$3" local fs_type="$4" local block_size="$5" + local agent_bin="$6" create_disk "${image}" "${img_size}" "${fs_type}" "${rootfs_start}" @@ -402,6 +420,31 @@ create_rootfs_image() { info "Copying content from rootfs to root partition" cp -a "${rootfs}"/* "${mount_dir}" + + if [ "${SELINUX}" == "yes" ]; then + if [ "${AGENT_INIT}" == "yes" ]; then + die "Guest SELinux with the agent init is not supported yet" + fi + + info "Labeling rootfs for SELinux" + selinuxfs_path="${mount_dir}${SELINUXFS}" + mkdir -p $selinuxfs_path + if mountpoint $SELINUXFS > /dev/null && \ + chroot "${mount_dir}" command -v restorecon > /dev/null; then + mount -t selinuxfs selinuxfs $selinuxfs_path + chroot "${mount_dir}" restorecon -RF -e ${SELINUXFS} / + # TODO: This operation will be removed after the updated container-selinux that + # includes the following commit is released. + # https://github.com/containers/container-selinux/commit/39f83cc74d50bd10ab6be4d0bdd98bc04857469f + # We use chcon as an interim solution until then. + chroot "${mount_dir}" chcon -t container_runtime_exec_t "/usr/bin/${agent_bin}" + umount $selinuxfs_path + else + die "Could not label the rootfs. Make sure that SELinux is enabled on the host \ +and the rootfs is built with SELINUX=yes" + fi + fi + sync OK "rootfs copied" @@ -529,7 +572,7 @@ main() { # consider in calculate_img_size rootfs_img_size=$((img_size - dax_header_sz)) create_rootfs_image "${rootfs}" "${image}" "${rootfs_img_size}" \ - "${fs_type}" "${block_size}" + "${fs_type}" "${block_size}" "${agent_bin}" # insert at the beginning of the image the MBR + DAX header set_dax_header "${image}" "${img_size}" "${fs_type}" "${nsdax_bin}" diff --git a/tools/osbuilder/rootfs-builder/centos/config.sh b/tools/osbuilder/rootfs-builder/centos/config.sh index 7226da047c..2123903a06 100644 --- a/tools/osbuilder/rootfs-builder/centos/config.sh +++ b/tools/osbuilder/rootfs-builder/centos/config.sh @@ -8,10 +8,15 @@ OS_VERSION=${OS_VERSION:-stream9} PACKAGES="chrony iptables" [ "$AGENT_INIT" = no ] && PACKAGES+=" systemd" [ "$SECCOMP" = yes ] && PACKAGES+=" libseccomp" +[ "$SELINUX" = yes ] && PACKAGES+=" container-selinux" # Container registry tag is different from metalink repo, e.g. "stream9" => "9-stream" os_repo_version="$(sed -E "s/(stream)(.+)/\2-\1/" <<< "$OS_VERSION")" METALINK="https://mirrors.centos.org/metalink?repo=centos-baseos-$os_repo_version&arch=\$basearch" +if [ "$SELINUX" == yes ]; then + # AppStream repository is required for the container-selinux package + METALINK_APPSTREAM="https://mirrors.centos.org/metalink?repo=centos-appstream-$os_repo_version&arch=\$basearch" +fi GPG_KEY_FILE=RPM-GPG-KEY-CentOS-Official GPG_KEY_URL="https://centos.org/keys/$GPG_KEY_FILE" diff --git a/tools/osbuilder/rootfs-builder/rootfs.sh b/tools/osbuilder/rootfs-builder/rootfs.sh index c69f8dfef0..43c79fd7d5 100755 --- a/tools/osbuilder/rootfs-builder/rootfs.sh +++ b/tools/osbuilder/rootfs-builder/rootfs.sh @@ -25,6 +25,7 @@ LIBC=${LIBC:-musl} # The kata agent enables seccomp feature. # However, it is not enforced by default: you need to enable that in the main configuration file. SECCOMP=${SECCOMP:-"yes"} +SELINUX=${SELINUX:-"no"} lib_file="${script_dir}/../scripts/lib.sh" source "$lib_file" @@ -142,6 +143,11 @@ ROOTFS_DIR Path to the directory that is populated with the rootfs. SECCOMP When set to "no", the kata-agent is built without seccomp capability. Default value: "yes" +SELINUX When set to "yes", build the rootfs with the required packages to + enable SELinux in the VM. + Make sure the guest kernel is compiled with SELinux enabled. + Default value: "no" + USE_DOCKER If set, build the rootfs inside a container (requires Docker). Default value: @@ -346,6 +352,15 @@ build_rootfs_distro() echo "Required rust version: $RUST_VERSION" + if [ "${SELINUX}" == "yes" ]; then + if [ "${AGENT_INIT}" == "yes" ]; then + die "Guest SELinux with the agent init is not supported yet" + fi + if [ "${distro}" != "centos" ]; then + die "The guest rootfs must be CentOS to enable guest SELinux" + fi + fi + if [ -z "${USE_DOCKER}" ] && [ -z "${USE_PODMAN}" ]; then info "build directly" build_rootfs ${ROOTFS_DIR} @@ -426,6 +441,7 @@ build_rootfs_distro() --env OS_VERSION="${OS_VERSION}" \ --env INSIDE_CONTAINER=1 \ --env SECCOMP="${SECCOMP}" \ + --env SELINUX="${SELINUX}" \ --env DEBUG="${DEBUG}" \ --env HOME="/root" \ -v "${repo_dir}":"/kata-containers" \ diff --git a/tools/osbuilder/scripts/lib.sh b/tools/osbuilder/scripts/lib.sh index 5ed0176771..615ff10a95 100644 --- a/tools/osbuilder/scripts/lib.sh +++ b/tools/osbuilder/scripts/lib.sh @@ -79,7 +79,23 @@ gpgcheck=1 gpgkey=file://${CONFIG_DIR}/${GPG_KEY_FILE} EOF fi - + if [ "$SELINUX" == "yes" ]; then + cat > "${DNF_CONF}" << EOF +[appstream] +name=${OS_NAME}-${OS_VERSION} upstream +releasever=${OS_VERSION} +EOF + echo "metalink=$METALINK_APPSTREAM" >> "$DNF_CONF" + if [ -n "$GPG_KEY_URL" ]; then + if [ ! -f "${CONFIG_DIR}/${GPG_KEY_FILE}" ]; then + curl -L "${GPG_KEY_URL}" -o "${CONFIG_DIR}/${GPG_KEY_FILE}" + fi + cat >> "${DNF_CONF}" << EOF +gpgcheck=1 +gpgkey=file://${CONFIG_DIR}/${GPG_KEY_FILE} +EOF + fi + fi } build_rootfs()