Skip to content

Conversation

@olgeni
Copy link
Contributor

@olgeni olgeni commented Oct 7, 2025

Description

AthenZ's SIA (Service Identity Agent) library currently only supports Linux, Darwin, and Windows platforms. When building projects that vendor this library (like Redpanda Connect) on FreeBSD, the build fails with:

undefined: setupDirOwnership

This occurs because Go's build system selects platform-specific files using filename suffixes (_linux.go, _darwin.go, etc.), and there was no _freebsd.go
implementation.

This PR adds os_util_freebsd.go based on the Linux implementation.

Testing

  • Successfully compiled and tested on FreeBSD 14.2 amd64
  • Verified through building github.com/redpanda-data/[email protected]

Contribution Checklist:

  • The pull request does not introduce any breaking changes
  • I have read the contribution guidelines.
  • Create an issue and link to the pull request.

Add os_util_freebsd.go to provide FreeBSD-specific OS utility
functions for the SIA (Service Identity Agent) library.

This change enables AthenZ's SIA library to build and run on
FreeBSD systems by providing the platform-specific implementations
previously only available for Linux, Darwin, and Windows.

The FreeBSD implementation is based on the Linux version, as both
systems share similar Unix semantics for:

- File ownership management (os.Chown)
- User/group ID lookup (id command)
- Syslog functionality
- Syscall operations (Setuid/Setgid)

This was identified while building recent github.com/redpanda-data/connect,
which is part of FreeBSD ports.

Tested on: FreeBSD 14.2 amd64

Signed-off-by: Giacomo Olgeni <[email protected]>
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @olgeni, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request extends the AthenZ SIA library's platform support to include FreeBSD. By introducing a dedicated _freebsd.go implementation, it addresses a critical build issue that previously prevented projects like Redpanda Connect from compiling on FreeBSD, thereby enhancing the library's portability and usability across different Unix-like operating systems.

Highlights

  • FreeBSD Support: Added a new platform-specific file, os_util_freebsd.go, to provide FreeBSD compatibility for the AthenZ SIA (Service Identity Agent) library. This resolves build failures on FreeBSD for projects that vendor this library.
  • Platform-Specific Utilities: The new file includes FreeBSD-specific implementations for system utilities such as NewSysLogger, UpdateFile, SvcAttrs, UidGidForUserGroup, gidForGroup, idCommand, uidGidForUser, SetupSIADirs, changeDirectoryOwnership, setupDirOwnership, SyscallSetGid, SyscallSetUid, and validateScriptArguments.
  • Ownership and Permissions: Functions for managing file and directory ownership (os.Chown) and permissions (os.MkdirAll) are implemented, ensuring proper security contexts for SIA-related files and directories on FreeBSD.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for FreeBSD by introducing os_util_freebsd.go, which is a good step towards broader platform compatibility. My review focuses on improving the robustness and security of the new file. I've identified a couple of high-severity issues related to shelling out for user and group lookups, which could lead to security vulnerabilities or denial of service. I've provided suggestions to replace these with safer, pure Go implementations.

Additionally, I've noticed that os_util_freebsd.go is identical to os_util_linux.go and very similar to os_util_darwin.go. To improve long-term maintainability and avoid code duplication, I recommend consolidating these files into a single os_util_unix.go using Go build tags (e.g., //go:build linux,darwin,freebsd). The suggested improvements should ideally be applied across all these platform-specific files.

Comment on lines +89 to +112
func gidForGroup(groupname string) int {
//shelling out to id is used here because the os/user package
//requires cgo, which doesn't cross-compile. we can use getent group
//command but instead we opted for a simple grep for /etc/group
cmdStr := fmt.Sprintf("^%s:", groupname)
out, err := exec.Command(GetUtilPath("grep"), cmdStr, "/etc/group").Output()
if err != nil {
log.Printf("Cannot exec '%s %s /etc/group': %v\n", GetUtilPath("grep"), groupname, err)
return -1
}
s := strings.Trim(string(out), "\n\r ")
comps := strings.Split(string(out), ":")
if len(comps) < 3 {
log.Printf("Invalid response from grep group command: %s\n", s)
return -1
}
//the group id should be the third value: 'group_name:password:group_id:group_list'
id, err := strconv.Atoi(comps[2])
if err != nil {
log.Printf("Invalid response from getent group command: %s\n", s)
return -1
}
return id
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation of gidForGroup shells out to grep, which is not robust and can be insecure. The groupname is used in a regex without sanitization, which could lead to unexpected behavior if it contains special regex characters. Also, parsing command output is more fragile than reading the file directly.

A better approach would be to read and parse /etc/group in pure Go. This avoids shelling out, is more secure, and removes the dependency on the grep command being available. This change would also be beneficial for the other OS-specific files (os_util_linux.go, os_util_darwin.go) which have similar implementations.

Please also add "bufio" to the import list.

func gidForGroup(groupname string) int {
	// Reading /etc/group directly is a cross-compile friendly way to get gid without cgo.
	file, err := os.Open("/etc/group")
	if err != nil {
		log.Printf("Cannot open /etc/group: %v\n", err)
		return -1
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		// format is group_name:password:group_id:user_list
		parts := strings.Split(line, ":")
		if len(parts) >= 3 && parts[0] == groupname {
			gid, err := strconv.Atoi(parts[2])
			if err != nil {
				log.Printf("Invalid GID '%s' in /etc/group for group %s: %v\n", parts[2], groupname, err)
				return -1
			}
			return gid
		}
	}

	if err := scanner.Err(); err != nil {
		log.Printf("Error reading /etc/group: %v\n", err)
		return -1
	}

	log.Printf("Group '%s' not found in /etc/group\n", groupname)
	return -1
}

Comment on lines +114 to +133
func idCommand(username, arg string) int {
//shelling out to id is used here because the os/user package
//requires cgo, which doesn't cross-compile
var out []byte
var err error
if username == "" {
out, err = exec.Command(GetUtilPath("id"), arg).Output()
} else {
out, err = exec.Command(GetUtilPath("id"), arg, username).Output()
}
if err != nil {
log.Fatalf("Cannot exec 'id %s %s': %v\n", arg, username, err)
}
s := strings.Trim(string(out), "\n\r ")
id, err := strconv.Atoi(s)
if err != nil {
log.Fatalf("Unexpected UID/GID format in user record: %s\n", string(out))
}
return id
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The function idCommand uses log.Fatalf on error. This will cause the entire application to terminate if the id command fails (e.g., if an invalid username is provided). This can be a potential denial-of-service vulnerability.

It would be more robust to handle the error gracefully by logging it and returning a sentinel value (like -1), similar to how errors are handled in gidForGroup. This would allow the calling functions to decide how to proceed instead of crashing. This change should also be applied to the other OS-specific files (os_util_linux.go, os_util_darwin.go).

func idCommand(username, arg string) int {
	//shelling out to id is used here because the os/user package
	//requires cgo, which doesn't cross-compile
	var out []byte
	var err error
	if username == "" {
		out, err = exec.Command(GetUtilPath("id"), arg).Output()
	} else {
		out, err = exec.Command(GetUtilPath("id"), arg, username).Output()
	}
	if err != nil {
		log.Printf("Cannot exec 'id %s %s': %v\n", arg, username, err)
		return -1
	}
	s := strings.Trim(string(out), "\n\r ")
	id, err := strconv.Atoi(s)
	if err != nil {
		log.Printf("Unexpected UID/GID format in user record: %s\n", string(out))
		return -1
	}
	return id
}

@havetisyan havetisyan merged commit fa063a2 into AthenZ:master Oct 8, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants