Skip to content

Commit

Permalink
CHEF-15083: Update groups resource to use getent utility to fetch g…
Browse files Browse the repository at this point in the history
…roups info (inspec#7284)

* enhance: update groups resource to use getent utility to fetch groups info

Signed-off-by: Sonu Saha <[email protected]>

* spec: add mock call for getent

Signed-off-by: Sonu Saha <[email protected]>

* spec: update mockloader to mock tool exist check for getent

Signed-off-by: Sonu Saha <[email protected]>

* enhance: some refactoring & implementing fallback using inspec.etc_group.entries

Signed-off-by: Sonu Saha <[email protected]>

---------

Signed-off-by: Sonu Saha <[email protected]>
  • Loading branch information
ahasunos authored Feb 3, 2025
1 parent ea94e31 commit d4fdf14
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 0 deletions.
52 changes: 52 additions & 0 deletions lib/inspec/resources/groups.rb
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,60 @@ def groups
# implements generic unix groups via /etc/group
class UnixGroup < GroupInfo
def groups
get_group_info
end

private

def get_group_info
# First, try to fetch group info using getent
group_info = fetch_group_info_using_getent

return group_info unless group_info.empty?

# If getent fails, fallback to reading group info from /etc/group using inspec.etc_group.entries
Inspec::Log.debug("Falling back to reading group info from /etc/group as getent is unavailable or failed.")
inspec.etc_group.entries
end

# Fetches group information using the getent utility
def fetch_group_info_using_getent
# Find getent utility on the system
bin = find_getent_utility

# If getent is available, fetch group info
return [] unless bin

cmd = inspec.command("#{bin} group")
return parse_group_info(cmd) if cmd.exit_status.to_i == 0

# If getent fails, log the error and return an empty array
Inspec::Log.debug("Failed to execute #{bin} group: #{cmd.stderr}.")
[]
end

# Parses group info from the command output
def parse_group_info(cmd)
cmd.stdout.strip.split("\n").map do |line|
name, password, gid, members = line.split(":")
{
"name" => name,
"password" => password,
"gid" => gid.to_i,
"members" => members,
}
end
end

# Checks if getent exists on the system
def find_getent_utility
%w{/usr/bin/getent /bin/getent getent}.each do |cmd|
return cmd if inspec.command(cmd).exist?
end
# Log debug information if getent is not found
Inspec::Log.debug("Could not find `getent` on your system.")
nil # Return nil if getent is not found
end
end

# OSX uses opendirectory for groups, so `/etc/group` may not be fully accurate
Expand Down
4 changes: 4 additions & 0 deletions test/fixtures/cmd/group_info
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
root:x:0:
www-data:x:33:www-data,root
GroupWithCaps:x:999:
sftpusers:x:1000:sftponly
2 changes: 2 additions & 0 deletions test/helpers/mock_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -330,9 +330,11 @@ def md.directory?
# user information for linux
"id root" => cmd.call("id-root"),
"getent passwd root" => cmd.call("getent-passwd-root"),
"getent group" => cmd.call("group_info"),
"chage -l root" => cmd.call("chage-l-root"),
"cat ~/.ssh/authorized_keys" => cmd.call("authorized-keys-mock"),
%{sh -c 'type "getent"'} => empty.call,
%{type "getent"} => empty.call,
"getent shadow root" => cmd.call("getent-shadow-mock"),
# user information for ldap test
"id jfolmer" => cmd.call("id-jfolmer"),
Expand Down

0 comments on commit d4fdf14

Please sign in to comment.