Skip to content

internal/exec/util: handle existing group in EnsureGroup#2158

Merged
prestist merged 5 commits intocoreos:mainfrom
onurhanak:ensure-groupadd-idempotency
Dec 11, 2025
Merged

internal/exec/util: handle existing group in EnsureGroup#2158
prestist merged 5 commits intocoreos:mainfrom
onurhanak:ensure-groupadd-idempotency

Conversation

@onurhanak
Copy link
Copy Markdown
Contributor

When groupadd is called with a group name that already exists, it returns exit code 9. This causes ignition to fail. This change catches exit code 9 and treats it as success to make EnsureGroup idempotent.

Copy link
Copy Markdown

@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 makes EnsureGroup idempotent by handling the exit code from groupadd when a group already exists. While the change prevents a crash, it introduces a more subtle issue. If a group exists, any configuration changes for that group (like GID or password) will be silently ignored, leading to potential configuration drift. My review comment details this problem and suggests a more robust implementation that uses groupmod for existing groups, similar to how EnsureUser is implemented. This would ensure that group configurations are correctly applied whether the group is being created or modified.

Comment on lines +353 to +355
if err != nil && exitCode == 9 {
return nil
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

While this change correctly makes EnsureGroup idempotent for group creation and prevents a crash, it introduces a potential issue of silent configuration drift. If a group already exists, this change will cause the function to return success without applying any specified modifications like Gid or PasswordHash. This can lead to the system being in an incorrect state without any warning.

To address this properly, EnsureGroup should be updated to handle existing groups by modifying them, similar to how EnsureUser uses usermod. I recommend the following approach:

  1. Use the exists variable (which is already available) to differentiate between creating and modifying a group.
  2. If the group exists, use groupmod to apply any changes to GID or password hash. This will require adding a GroupmodCmd to internal/distro/distro.go.
  3. If the group does not exist, use groupadd as is currently done.

This will make EnsureGroup robust and ensure the system state matches the desired configuration.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is actually the SAME design limitation that EnsureUser has, and is documented in config/doc/ignition.yaml

- name: system
    desc: "whether or not the group should be a system group. This only has an effect if the group doesn't exist yet."

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You are right. That is a limitation of groupmod and usermod. I opted to maintain consistency. Do you think that should be handled differently?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nah, I am pretty sure keeping it as it has been is correct. Its been documented as a limitation for a while. I wanted to just comment on it so there is a trail in-case we ever need to ask why it was done this way.

@onurhanak onurhanak marked this pull request as draft November 17, 2025 20:22
@onurhanak onurhanak force-pushed the ensure-groupadd-idempotency branch from ecdf347 to 1b3a2d2 Compare November 17, 2025 20:36
@onurhanak onurhanak marked this pull request as ready for review November 17, 2025 20:37
@onurhanak
Copy link
Copy Markdown
Contributor Author

onurhanak commented Nov 19, 2025

Looks like I forgot to update release notes, should I revise the PR?

Edit: updated.

prestist
prestist previously approved these changes Dec 3, 2025
Copy link
Copy Markdown
Collaborator

@prestist prestist left a comment

Choose a reason for hiding this comment

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

Great find, thank you for the fix, the changes make sense and LGTM

edit: sorry

One request, would you mind adding blackbox tests that follow the ensureUser example but for ensureGroup. as there are no create/addition tests that I can see.

You can find the user tests and group tests mixed in => ignition/tests/positive/passwd/users.go

It looks like we can expand the group coverage for creation and modification now, you might need to expand the stubs in => ignition/tests/stubs/groupdel-stub/main.go

@prestist prestist self-requested a review December 3, 2025 02:48
@prestist prestist dismissed their stale review December 3, 2025 02:56

Noticed the need for test coverage.

Add groupadd-stub and groupmod-stub test helpers to test the groupadd
and groupmod commands.
Add test cases for group creation and group idempotency to verify
EnsureGroup uses groupmod when a group already exists.
@onurhanak onurhanak marked this pull request as draft December 3, 2025 13:00
@onurhanak onurhanak marked this pull request as ready for review December 3, 2025 13:03
@prestist
Copy link
Copy Markdown
Collaborator

prestist commented Dec 3, 2025

I think the only thing we should do is re-base rather then the merges from main, its not terrible but it does add noise to the git history. Otherwise lgtm.

When a group already exists, modify it using groupmod instead of
attempting to create it with groupadd. This makes EnsureGroup
idempotent and prevents failure when the group already exists (exit code
9).
@onurhanak onurhanak force-pushed the ensure-groupadd-idempotency branch from c3a467c to 7572fa8 Compare December 3, 2025 15:55
@onurhanak
Copy link
Copy Markdown
Contributor Author

Does this look better?

@prestist
Copy link
Copy Markdown
Collaborator

prestist commented Dec 3, 2025

@onurhanak perfect! thank you.

Copy link
Copy Markdown
Collaborator

@prestist prestist left a comment

Choose a reason for hiding this comment

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

LGTM

@prestist
Copy link
Copy Markdown
Collaborator

prestist commented Dec 4, 2025

@onurhanak unfortunately looks like the blackbox tests are failing

You can test them locally by

./build_blackbox_tests
sudo sh -c 'PATH=$PWD/bin/amd64:$PATH ./tests.test -test.run <testname>'

so if you want to test groups.add_3.6.0-experimental it would look like:

sudo sh -c 'PATH=$PWD/bin/amd64:$PATH ./tests.test -test.run TestIgnitionBlackBox/groups.add_3.6.0-experimental

@onurhanak
Copy link
Copy Markdown
Contributor Author

@prestist Theblackbox tests pass locally on my end, any idea why they are failing here? This is from the Jenkins log:

passwd.go:173: "group1" wasn't correctly created: got "group1:!::", expected "group1:*::"

I cannot see any reference to group1 in the codebase when I look through the tests. Also, when I check /etc/gpasswd I see it uses !, not * for empty passwords. Any suggestions on how to proceed?

@prestist
Copy link
Copy Markdown
Collaborator

prestist commented Dec 4, 2025

@prestist Theblackbox tests pass locally on my end, any idea why they are failing here? This is from the Jenkins log:

passwd.go:173: "group1" wasn't correctly created: got "group1:!::", expected "group1:*::"

I cannot see any reference to group1 in the codebase when I look through the tests. Also, when I check /etc/gpasswd I see it uses !, not * for empty passwords. Any suggestions on how to proceed?

Thats soo weird, at first glance no, but looking into it currently.

@prestist
Copy link
Copy Markdown
Collaborator

prestist commented Dec 4, 2025

@onurhanak Ah, its a kola test this time. No longer blackbox tests

Our kola tests are mostly in fedora-coreos-config here is an example of one for passwd on users https://github.com/coreos/fedora-coreos-config/blob/c20db91bd5780a3da22204677b251048cf91b8d7/tests/kola/security/passwd/test.sh

I cant seem to find the one that is failing but its likely in this repo, still investigating.

Ah this kola test is in coreos-asembler's repo https://github.com/coreos/coreos-assembler/blob/bf8b944d6fd211cb3f9071820934db65138b80b6/mantle/kola/tests/ignition/passwd.go#L144

Looks like the testgroup section is not passing due to the changes here. The * makes me think about the else you removed from passwd.. but not sure.

@onurhanak
Copy link
Copy Markdown
Contributor Author

@prestist Thanks. I think the error is caused by the EnsureGroup function in internal/exec/util/passwd.go . Specifically these lines:

	if util.NotEmpty(g.PasswordHash) {
		args = append(args, "--password", *g.PasswordHash)
	}

I see that other functions in the file default to "*" when password is nil. The tests seem to expect this behavior.

I can implement that, but groupadd man page specifies that it defaults to "!" when password is not supplied:

-p, --password PASSWORD
           defines an initial password for the group account. PASSWORD is expected to be encrypted, as returned by crypt (3).

           Without this option, the group account will be locked and with no password defined, i.e. a single exclamation mark in
           the respective field of ths system account file /etc/group or /etc/gshadow.

           Note: This option is not recommended because the password (or encrypted password) will be visible by users listing the
           processes.

           You should make sure the password respects the system's password policy.

How do you think we should proceed?

@prestist
Copy link
Copy Markdown
Collaborator

prestist commented Dec 4, 2025

Hmm, it makes my hands itch to give the final verdict. Let me reach out to a more knowledgeable party => @travier for their view.

So from my perspective, we should probably maintain what has been true in the past and what has been enforced by the kola test. Mostly to avoid unintentional configuration drift. I.e bring back the default of * when not specified.

@onurhanak
Copy link
Copy Markdown
Contributor Author

@travier what do you think?

@travier
Copy link
Copy Markdown
Member

travier commented Dec 10, 2025

This PR adds groupadd-stub & groupmod-stub binaries that I don't think should be included.

How do you think we should proceed?

For the * vs !, gshadow says:

If the password field contains some string that is not a valid
result of crypt(3), for instance ! or *, users will not be
able to use a unix password to access the group (but group
members do not need the password).

So I think we should update our kola test to accept both.

@travier
Copy link
Copy Markdown
Member

travier commented Dec 10, 2025

I have not looked too closely to the tests but the code change looks OK from a quick look. Thanks

@onurhanak onurhanak force-pushed the ensure-groupadd-idempotency branch from ddbc66e to 87da603 Compare December 10, 2025 13:20
@onurhanak
Copy link
Copy Markdown
Contributor Author

This PR adds groupadd-stub & groupmod-stub binaries that I don't think should be included.

You are right. My mistake, should be fixed now.

@prestist What are the next steps? I think the kola tests are in another repository right? Should I make a PR there too?

@prestist
Copy link
Copy Markdown
Collaborator

@onurhanak yeah the kola tests that are being effected are here. And yeah we would want to create a PR which accepts both * and !. If you would like @onurhanak I can make those changes?

@onurhanak
Copy link
Copy Markdown
Contributor Author

@prestist Sure, if you do not mind, please go ahead and make the changes.

@prestist prestist merged commit 966af01 into coreos:main Dec 11, 2025
10 checks passed
@prestist
Copy link
Copy Markdown
Collaborator

@onurhanak Thank you for working on this, just landed it!

@onurhanak
Copy link
Copy Markdown
Contributor Author

@prestist That's great news, thanks for the help along the way.

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.

4 participants