diff --git a/scripts/podman/create-service-users.sh b/scripts/podman/create-service-users.sh new file mode 100644 index 0000000..c6e40fc --- /dev/null +++ b/scripts/podman/create-service-users.sh @@ -0,0 +1,110 @@ +#!/bin/bash + +set -euo pipefail +shopt -s lastpipe + +UID_MAP="/etc/jimlab/uid.map" +SUBID_MAP="/etc/jimlab/subid.map" +DEFAULT_SUBID_SIZE=4096 + +if [[ ! -f "$UID_MAP" ]]; then + echo "❌ Error: $UID_MAP not found." + exit 1 +fi + +touch "$SUBID_MAP" + +# Return true if two ranges [start1, end1) and [start2, end2) overlap +ranges_overlap() { + local start1=$1 + local size1=$2 + local start2=$3 + local size2=$4 + + [[ -z "$start1" || -z "$size1" || -z "$start2" || -z "$size2" ]] && return 1 + [[ ! "$start1" =~ ^[0-9]+$ || ! "$size1" =~ ^[0-9]+$ || ! "$start2" =~ ^[0-9]+$ || ! "$size2" =~ ^[0-9]+$ ]] && return 1 + + local end1=$((start1 + size1)) + local end2=$((start2 + size2)) + + [[ $start1 -lt $end2 && $start2 -lt $end1 ]] +} + +# Get next available subid base +get_next_subid_base() { + local max=100000 + grep -v '^\s*$' "$SUBID_MAP" | while IFS=: read -r _ base size; do + base=$(echo "$base" | xargs) + size=$(echo "$size" | xargs) + [[ "$base" =~ ^[0-9]+$ && "$size" =~ ^[0-9]+$ ]] || continue + local end=$((base + size)) + (( end > max )) && max=$end + done + echo "$max" +} + +# Warn if overlapping any prior block +check_overlap() { + local new_start=$1 + local new_size=$2 + grep -v '^\s*$' "$SUBID_MAP" | while IFS=: read -r user base size; do + if ranges_overlap "$new_start" "$new_size" "$base" "$size"; then + echo "⚠️ Warning: new block $new_start:$new_size overlaps with $user:$base:$size" >&2 + fi + done +} + +grep -v '^\s*$' "$UID_MAP" | while IFS=: read -r service uid; do + service=$(echo "$service" | xargs) + uid=$(echo "$uid" | xargs) + home="/var/lib/$service" + + [[ -z "$service" || -z "$uid" || "$service" == \#* ]] && continue + + echo "▶️ Processing $service (UID=$uid, HOME=$home)" + + # Create group if missing + if ! getent group "$service" > /dev/null; then + echo " ➕ Creating group $service with GID $uid" + groupadd --system --gid "$uid" "$service" + fi + + # Create or fix user + if ! id "$service" &>/dev/null; then + echo " ➕ Creating user $service with UID $uid and home $home" + useradd --system --uid "$uid" --gid "$uid" --home-dir "$home" --create-home --shell /sbin/nologin "$service" + else + current_home=$(getent passwd "$service" | cut -d: -f6) + if [[ "$current_home" != "$home" ]]; then + echo " 🛠 Updating home dir for $service → $home" + usermod -d "$home" "$service" + mkdir -p "$home" + chown "$service:$service" "$home" + fi + fi + + # Enable lingering + if ! loginctl show-user "$uid" &>/dev/null || ! loginctl show-user "$uid" | grep -q "Linger=yes"; then + echo " 🔄 Enabling lingering for $service" + loginctl enable-linger "$service" + fi + + # Subuid/subgid mapping + if ! grep -q "^$service:" "$SUBID_MAP"; then + next_base=$(get_next_subid_base) + check_overlap "$next_base" "$DEFAULT_SUBID_SIZE" + echo " 📦 Assigning subid range: $service:$next_base:$DEFAULT_SUBID_SIZE" + echo "$service:$next_base:$DEFAULT_SUBID_SIZE" >> "$SUBID_MAP" + fi + + # Sync to /etc/subuid and /etc/subgid + map_line=$(grep "^$service:" "$SUBID_MAP" | head -n1) + for file in /etc/subuid /etc/subgid; do + if ! grep -q "^$service:" "$file"; then + echo "$map_line" | sudo tee -a "$file" >/dev/null + elif ! grep -q "^$map_line\$" "$file"; then + echo "⚠️ Mismatch in $file for $service. Check manually!" >&2 + fi + done + +done