diff --git a/home/scripts/lxc/lab-addkey.sh b/home/scripts/lxc/lab-addkey.sh new file mode 100644 index 0000000..bded493 --- /dev/null +++ b/home/scripts/lxc/lab-addkey.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat < + +Appends an SSH public key to both: + - the host user's ~/.ssh/authorized_keys + - the container's /root/.ssh/authorized_keys + +Examples: + lab-addkey alice /tmp/alice-laptop.pub + lab-addkey alice "ssh-ed25519 AAAA... user@host" +EOF + exit 1 +} + +[[ $# -lt 2 ]] && usage + +USER="$1" +KEY_INPUT="$2" + +# resolve key content +if [[ -f "$KEY_INPUT" ]]; then + KEY=$(cat "$KEY_INPUT") +else + KEY="$KEY_INPUT" +fi + +# basic sanity check +if [[ ! "$KEY" =~ ^ssh- ]] && [[ ! "$KEY" =~ ^ecdsa- ]]; then + echo "ERROR: doesn't look like a valid SSH public key" >&2 + exit 1 +fi + +# resolve container +if [[ -f "/home/${USER}/.lxc-container" ]]; then + CONTAINER=$(cat "/home/${USER}/.lxc-container") +else + echo "ERROR: no container found for user '${USER}'" >&2 + exit 1 +fi + +# --- add to host user --- +HOST_AUTHKEYS="/home/${USER}/.ssh/authorized_keys" +mkdir -p "/home/${USER}/.ssh" +if grep -qF "$KEY" "$HOST_AUTHKEYS" 2>/dev/null; then + echo "Key already present on host for ${USER}" +else + echo "$KEY" >> "$HOST_AUTHKEYS" + chown "${USER}:" "$HOST_AUTHKEYS" + chmod 600 "$HOST_AUTHKEYS" + echo "Added key to host: ${HOST_AUTHKEYS}" +fi + +# --- add to container --- +STATE=$(lxc-info -n "$CONTAINER" -sH 2>/dev/null || true) +if [[ "$STATE" == "RUNNING" ]]; then + echo "$KEY" | lxc-attach --clear-env -n "$CONTAINER" -- /bin/bash -c " + export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + mkdir -p /root/.ssh && chmod 700 /root/.ssh + KEY=\$(cat) + if grep -qF \"\$KEY\" /root/.ssh/authorized_keys 2>/dev/null; then + echo 'Key already present in container' + else + echo \"\$KEY\" >> /root/.ssh/authorized_keys + chmod 600 /root/.ssh/authorized_keys + echo 'Added key to container' + fi + " +else + # container stopped — write directly into rootfs + ROOTFS="/var/lib/lxc/${CONTAINER}/rootfs" + CONT_AUTHKEYS="${ROOTFS}/root/.ssh/authorized_keys" + mkdir -p "${ROOTFS}/root/.ssh" + chmod 700 "${ROOTFS}/root/.ssh" + if grep -qF "$KEY" "$CONT_AUTHKEYS" 2>/dev/null; then + echo "Key already present in container rootfs" + else + echo "$KEY" >> "$CONT_AUTHKEYS" + chmod 600 "$CONT_AUTHKEYS" + echo "Added key to container rootfs: ${CONT_AUTHKEYS}" + fi +fi + +echo "Done." diff --git a/home/scripts/lxc/lab-mount.sh b/home/scripts/lxc/lab-mount.sh new file mode 100644 index 0000000..80d90c3 --- /dev/null +++ b/home/scripts/lxc/lab-mount.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat < [container-path] + +Bind-mounts a host directory into an LXC container, persisting across reboots. +If container-path is omitted, it mirrors host-path inside the container. + +Examples: + lab-mount alice /mnt/nas/shared # -> /mnt/nas/shared inside lxc-alice + lab-mount alice /mnt/nas/shared /data/shared # -> /data/shared inside lxc-alice + lab-mount lxc-alice /mnt/nas/shared # container name directly also works + +To mount for ALL containers: + lab-mount --all /mnt/nas/shared /data/shared +EOF + exit 1 +} + +mount_into() { + local CONTAINER="$1" + local HOST_PATH="$2" + local CONT_PATH="$3" + local ROOTFS="/var/lib/lxc/${CONTAINER}/rootfs" + local CONFIG="/var/lib/lxc/${CONTAINER}/config" + + if [[ ! -d "$ROOTFS" ]]; then + echo "ERROR: container '$CONTAINER' not found" >&2 + return 1 + fi + + if [[ ! -e "$HOST_PATH" ]]; then + echo "ERROR: host path '$HOST_PATH' does not exist" >&2 + return 1 + fi + + # create mountpoint inside rootfs + mkdir -p "${ROOTFS}${CONT_PATH}" + + # check if this bind mount is already in config + local ENTRY="lxc.mount.entry = ${HOST_PATH} ${CONT_PATH#/} none bind,create=dir 0 0" + if grep -qF "$ENTRY" "$CONFIG" 2>/dev/null; then + echo "Already configured: ${CONTAINER} ${HOST_PATH} -> ${CONT_PATH}" + return 0 + fi + + # add persistent bind mount to container config + echo "$ENTRY" >> "$CONFIG" + echo "Added to config: ${CONTAINER} ${HOST_PATH} -> ${CONT_PATH}" + + # if the container is running, also mount it live + local STATE + STATE=$(lxc-info -n "$CONTAINER" -sH 2>/dev/null || true) + if [[ "$STATE" == "RUNNING" ]]; then + # lxc-attach to mount from inside + lxc-attach --clear-env -n "$CONTAINER" -- /bin/bash -c " + export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + mkdir -p '${CONT_PATH}' + " + # note: live bind mounts into running unprivileged containers via config + # may require a container restart to take effect + echo "NOTE: restart the container for the mount to take effect:" + echo " lxc-stop -n ${CONTAINER} && lxc-start -n ${CONTAINER}" + fi +} + +resolve_container() { + local NAME="$1" + # if it already starts with lxc-, use as-is + if [[ "$NAME" == lxc-* ]]; then + echo "$NAME" + elif [[ -f "/home/${NAME}/.lxc-container" ]]; then + cat "/home/${NAME}/.lxc-container" + else + echo "lxc-${NAME}" + fi +} + +# --- parse args --- +[[ $# -lt 2 ]] && usage + +if [[ "$1" == "--all" ]]; then + HOST_PATH="$2" + CONT_PATH="${3:-$HOST_PATH}" + for dir in /var/lib/lxc/lxc-*/; do + CONTAINER=$(basename "$dir") + mount_into "$CONTAINER" "$HOST_PATH" "$CONT_PATH" + done +else + CONTAINER=$(resolve_container "$1") + HOST_PATH="$2" + CONT_PATH="${3:-$HOST_PATH}" + mount_into "$CONTAINER" "$HOST_PATH" "$CONT_PATH" +fi