Skip to content

Instantly share code, notes, and snippets.

@muety
Last active August 17, 2023 10:20
Show Gist options
  • Save muety/eedbc7f6aa5d618e53142cfbd7caf6bc to your computer and use it in GitHub Desktop.
Save muety/eedbc7f6aa5d618e53142cfbd7caf6bc to your computer and use it in GitHub Desktop.
Script to determine the next available subuid on Linux
#!/bin/bash
# Author: Ferdinand Mütsch <[email protected]>
# Script to determine the next subuid / subgid for a user.
# It will consider (a) all already existing subuid / subgid ranges and (b) all "real" user ids.
#
# Note: When using LDAP login (e.g. with SSSD), /etc/passwd won't contain those external users.
# In the case of SSSD, you will have to enable enumeration (https://access.redhat.com/solutions/500433) to make "getent passwd" all local AD / LDAP users.
# However, on the other hand, enabling recommendation is not recommended, because it will cause high load on server and client (https://docs.pagure.org/sssd.sssd/users/faq.html#when-should-i-enable-enumeration-in-sssd-or-why-is-enumeration-disabled-by-default).
#
# Limitations:
# This script is not guaranteed to work with external user authentication like LDAP / AD, because the ID of a future LDAP user might be as high as to conflict with previously assigned subuid ranges.
# To reduce the likelihood of such conflicts, it is recommended to add a large offset to the currently highest ID (as returned by this script) when allocating a new range.
#
# Example 1 (get largest UID):
# ./next_subuid.sh uid
#
# Example 2 (get largest GID):
# ./next_subuid.sh gid
#
# Example 3 (use in combination with usermod):
# export next="$(./next_subuid.sh)"; sudo usermod --add-subuids "$next-$(($next+65536-1))" some-user
uid_file='/etc/subuid'
if [ "$1" = "gid" ]; then
uid_file='/etc/subuid'
fi
pattern='([0-9a-zA-Z_\-]+):([0-9]+):([0-9]+)'
# read file line by line into array
readarray -t subuids < <(cat $uid_file)
# parse file and find highest next available id
max_uid=0
for uid in "${subuids[@]}"; do
[[ $uid =~ $pattern ]]
uid_start="${BASH_REMATCH[2]}"
uid_range="${BASH_REMATCH[3]}"
uid_end=$(($uid_start+$uid_range+1))
((uid_end > max_uid)) && max_uid=$uid_end
done
# read all user ids
if [ "$1" = "gid" ]; then
readarray -t ids < <(getent passwd | cut -d: -f3)
else
readarray -t ids < <(getent passwd | cut -d: -f4)
fi
# find highest user / group id
max_id=0
for id in "${ids[@]}"; do
((id > max_id)) && max_id=$id
done
own_id=$(id -u)
all_ids=($own_id $max_id $max_uid)
max=$(printf '%s\n' "${all_ids[@]}" | sort -n -r | head -n 1)
echo "$max"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment