From eee8bbdc90422396e14fed5214150c006245a8cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lesimple?= Date: Fri, 5 Jun 2026 16:24:29 +0000 Subject: [PATCH] feat: fix-group-gid.sh: also fix group's corresponding user uid if applicable ... and make the next UID/GID algorithm research faster by using awk directly, instead of repeatedly calling getent, this makes us O(n) => O(1). --- bin/admin/fix-group-gid.sh | 80 +++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/bin/admin/fix-group-gid.sh b/bin/admin/fix-group-gid.sh index fbca242..591b229 100755 --- a/bin/admin/fix-group-gid.sh +++ b/bin/admin/fix-group-gid.sh @@ -1,9 +1,14 @@ #! /usr/bin/env bash # vim: set filetype=sh ts=4 sw=4 sts=4 et: # This script ensures that GIDs of bastion groups (including their group system -# roles) are higher than the configured groupGidMin value. -# It shifts group GIDs from the low range (where they may collide with reserved -# account UIDs) to the high range defined by groupGidMin. +# roles), as well as the UID of the system user matching each group, are higher +# than the configured groupGidMin value. +# It shifts group GIDs and the matching group user's UID from the low range +# (where they may collide with reserved account UIDs/GIDs) to the high range +# defined by groupGidMin. +# Note that a group might already have a correct GID (e.g. because it was fixed +# by a previous run of this script, or created by a recent version of the code) +# while its system user still has a low UID: such groups are handled too. set -e basedir=$(readlink -f "$(dirname "$0")"/../..) @@ -41,9 +46,31 @@ _run() next_available_gid=$((MINGID + 1)) find_next_available_gid() { - while getent group "$next_available_gid" >/dev/null; do - next_available_gid=$((next_available_gid + 1)) - done + next_available_gid=$( + getent group 2>/dev/null | cut -d: -f3 | sort -n | awk -v start="$MINGID" ' + BEGIN { candidate = start } + { + if ($1 + 0 == candidate) { candidate++; } + else if ($1 + 0 > candidate) { exit } + } + END { print candidate } + ' + ) +} + +next_available_uid=$MINGID +find_next_available_uid() +{ + next_available_uid=$( + getent passwd 2>/dev/null | cut -d: -f3 | sort -n | awk -v start="$MINGID" ' + BEGIN { candidate = start } + { + if ($1 + 0 == candidate) { candidate++; } + else if ($1 + 0 > candidate) { exit } + } + END { print candidate } + ' + ) } change_gid() @@ -84,6 +111,46 @@ change_gid() fi } +# only the main group has a matching system user; this shifts that user's UID to +# the high range, mirroring what osh-groupCreate does for newly created groups. +change_uid() +{ + group="$1" + + getent passwd "$group" >/dev/null || fail "user $group doesn't exist" + getent group "$group" >/dev/null || fail "group $group doesn't exist" + + olduid=$(getent passwd "$group" | awk -F: '{print $3}') + + # nothing to do if the UID is already in the high range + [ "$olduid" -ge "$MINGID" ] && return + + # prefer uid == gid: at this point the group's GID is already in the high + # range (either it was already correct, or change_gid() fixed it earlier in + # this batch), so reuse it as the UID when that value is free in the passwd + # namespace; otherwise scan for the next free UID in the high range. + gid=$(getent group "$group" | awk -F: '{print $3}') + if [ "$gid" -ge "$MINGID" ] && ! getent passwd "$gid" >/dev/null; then + newuid=$gid + else + find_next_available_uid + newuid=$next_available_uid + fi + + _run usermod_changeuid_compat "$group" "$newuid" + + # re-own the files that belonged to the old UID: usermod only reassigns the + # home directory on some systems, and not at all on others + tocheck="" + for dir in "/home/$group" "/home/keykeeper/$group"; do + test -d "$dir" && tocheck="$tocheck $dir" + done + if [ -n "$tocheck" ]; then + # shellcheck disable=SC2086 + _run find $tocheck -uid "$olduid" -exec chown "$group" '{}' \; + fi +} + batchrun() { something_to_do=0 @@ -91,6 +158,7 @@ batchrun() change_gid "key$from-gatekeeper" secondary change_gid "key$from-aclkeeper" secondary change_gid "key$from-owner" secondary + change_uid "key$from" } main()