git: 205797b9ef9d - stable/14 - nuageinit: Ignore non-existent groups

From: Baptiste Daroussin <bapt_at_FreeBSD.org>
Date: Tue, 07 Oct 2025 06:52:12 UTC
The branch stable/14 has been updated by bapt:

URL: https://cgit.FreeBSD.org/src/commit/?id=205797b9ef9df979da489aba5466e6d4d9bc6ad9

commit 205797b9ef9df979da489aba5466e6d4d9bc6ad9
Author:     Jesús Daniel Colmenares Oviedo <dtxdf@FreeBSD.org>
AuthorDate: 2025-09-27 22:03:09 +0000
Commit:     Baptiste Daroussin <bapt@FreeBSD.org>
CommitDate: 2025-10-07 06:52:06 +0000

    nuageinit: Ignore non-existent groups
    
    In cloud-init, when a group specified in the 'users.{index}.groups' parameter
    does not exist, it is ignored, but the user is created anyway. In the case of
    nuageinit, it exits with an exception, since pw(8) expects each group to exist.
    
    Reviewed by:            bapt@
    Approved by:            bapt@
    Differential Revision:  https://reviews.freebsd.org/D52718
    
    (cherry picked from commit 68691160f41bf6ce9ab70ddeeb7eeec2a7bff245)
---
 libexec/nuageinit/nuage.lua | 61 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 60 insertions(+), 1 deletion(-)

diff --git a/libexec/nuageinit/nuage.lua b/libexec/nuageinit/nuage.lua
index ef3cfd994fe1..3eeb2ea0b44c 100644
--- a/libexec/nuageinit/nuage.lua
+++ b/libexec/nuageinit/nuage.lua
@@ -139,6 +139,58 @@ local function splitlist(list)
 	return ret
 end
 
+local function splitlines(s)
+	local ret = {}
+
+	for line in string.gmatch(s, "[^\n]+") do
+		ret[#ret + 1] = line
+	end
+
+	return ret
+end
+
+local function getgroups()
+	local ret = {}
+
+	local root = os.getenv("NUAGE_FAKE_ROOTDIR")
+	local cmd = "pw "
+	if root then
+		cmd = cmd .. "-R " .. root .. " "
+	end
+
+	local f = io.popen(cmd .. "groupshow -a 2> /dev/null | cut -d: -f1")
+	local groups = f:read("*a")
+	f:close()
+
+	return splitlines(groups)
+end
+
+local function checkgroup(group)
+	local groups = getgroups()
+
+	for _, group2chk in ipairs(groups) do
+		if group == group2chk then
+			return true
+		end
+	end
+
+	return false
+end
+
+local function purge_group(groups)
+	local ret = {}
+
+	for _, group in ipairs(groups) do
+		if checkgroup(group) then
+			ret[#ret + 1] = group
+		else
+			warnmsg("ignoring non-existent group '" .. group .. "'")
+		end
+	end
+
+	return ret
+end
+
 local function adduser(pwd)
 	if (type(pwd) ~= "table") then
 		warnmsg("Argument should be a table")
@@ -164,7 +216,14 @@ local function adduser(pwd)
 	local extraargs = ""
 	if pwd.groups then
 		local list = splitlist(pwd.groups)
-		extraargs = " -G " .. table.concat(list, ",")
+		-- pw complains if the group does not exist, so if the user
+		-- specifies one that cannot be found, nuageinit will generate
+		-- an exception and exit, unlike cloud-init, which only issues
+		-- a warning but creates the user anyway.
+		list = purge_group(list)
+		if #list > 0 then
+			extraargs = " -G " .. table.concat(list, ",")
+		end
 	end
 	-- pw will automatically create a group named after the username
 	-- do not add a -g option in this case