Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] user.present does not leave existing groups alone when groups is unset #64211

Closed
jamesharr opened this issue May 2, 2023 · 17 comments
Closed
Assignees
Labels
Bug broken, incorrect, or confusing behavior Regression The issue is a bug that breaks functionality known to work in previous releases.

Comments

@jamesharr
Copy link

Description
salt.states.user.present documentation states that when the groups parameter is left unset, the groups for a given user will not be changed. The current behavior in saltstack seems to diverge from this behavior and treats groups as an empty list, clearing out all groups except for the user's primary group.

The documented behavior is actually very useful depending on how a saltstack administrator wants to manage group memberships. An administrator can choose to list of each group a user is a member of, or list each user that is in a given group. In our case, we chose the latter as it seems to be easier to manage across multiple systems.

Setup
Salt-Stack 3006.0 installed on a VM (KVM) running on-prem. Salt is installed via onedir packaging and is up-to-date at the creation of this issue. Based on behavior, I don't believe that installation method will affect the apparent bug.

I tested this against the following operating systems and they all have similar behavior:

  • Debian 11, amd64 VM
  • Alma Linux 9, amd64 VM (our primary target platform)
  • Alma Linux 8, amd64 VM
  • Ubuntu 22.04, amd64 VM

SLS File:

Manage account - test-user:
  group.present:
  - name: test-user
  - gid: 61001

  user.present:
  - name: test-user
  - uid: 61001
  - gid: 61001
  - fullname: Test User
  - shell: /bin/bash
  - home: /home/test-user
  - password: $6$REDACTED

Manage group - test-group1:
  group.present:
  - name: test-group1
  - members:
    - test-user

Manage group - test-group2:
  group.present:
  - name: test-group2
  - members:
    - test-user

Steps to Reproduce the behavior

  1. Apply the above state to establish a normal account
  2. Apply the above state with test=true
    • observe that user.present is attempting to modify groups for test-user
    • observe that group.apply are not attempting to modify group membership
  3. Apply the above state; observe
    • observe that user.present is modifying the groups for test-user.
    • observe that group.present is adding the user back to test-group1 and test-group2.

Expected behavior
When the groups parameter of user.present is left unset, it should leave the groups as-is. This is what the documentation states for salt.states.user.present

groups

A list of groups to assign the user to, pass a list object. If a group specified here does not exist on the minion, the state will fail. If set to the empty list, the user will be removed from all groups except the default group. If unset, salt will assume current groups are still wanted, and will make no changes to them.

Screenshots

State run (2nd and 3rd run)
% salt 'test-*' state.apply exp.user_bug2
test-debian11:
----------
          ID: Manage account - test-user
    Function: group.present
        Name: test-user
      Result: True
     Comment: Group test-user is present and up to date
     Started: 16:10:43.019995
    Duration: 3.306 ms
     Changes:   
----------
          ID: Manage account - test-user
    Function: user.present
        Name: test-user
      Result: True
     Comment: Updated user test-user
     Started: 16:10:43.023837
    Duration: 33.082 ms
     Changes:   
              ----------
              groups:
                  - test-user
----------
          ID: Manage group - test-group1
    Function: group.present
        Name: test-group1
      Result: True
     Comment: The following group attributes are set to be changed:
              members: ['test-user']
     Started: 16:10:43.057029
    Duration: 6.138 ms
     Changes:   
              ----------
              Final:
                  All changes applied successfully
----------
          ID: Manage group - test-group2
    Function: group.present
        Name: test-group2
      Result: True
     Comment: The following group attributes are set to be changed:
              members: ['test-user']
     Started: 16:10:43.063261
    Duration: 5.259 ms
     Changes:   
              ----------
              Final:
                  All changes applied successfully

Summary for test-debian11
------------
Succeeded: 4 (changed=3)
Failed:    0
------------
Total states run:     4
Total run time:  47.785 ms
test-ubuntu2204:
----------
          ID: Manage account - test-user
    Function: group.present
        Name: test-user
      Result: True
     Comment: Group test-user is present and up to date
     Started: 20:12:35.922017
    Duration: 3.33 ms
     Changes:   
----------
          ID: Manage account - test-user
    Function: user.present
        Name: test-user
      Result: True
     Comment: Updated user test-user
     Started: 20:12:35.925901
    Duration: 40.327 ms
     Changes:   
              ----------
              groups:
                  - test-user
----------
          ID: Manage group - test-group1
    Function: group.present
        Name: test-group1
      Result: True
     Comment: The following group attributes are set to be changed:
              members: ['test-user']
     Started: 20:12:35.966358
    Duration: 7.435 ms
     Changes:   
              ----------
              Final:
                  All changes applied successfully
----------
          ID: Manage group - test-group2
    Function: group.present
        Name: test-group2
      Result: True
     Comment: The following group attributes are set to be changed:
              members: ['test-user']
     Started: 20:12:35.973914
    Duration: 6.29 ms
     Changes:   
              ----------
              Final:
                  All changes applied successfully

Summary for test-ubuntu2204
------------
Succeeded: 4 (changed=3)
Failed:    0
------------
Total states run:     4
Total run time:  57.382 ms
test-alma9:
----------
          ID: Manage account - test-user
    Function: group.present
        Name: test-user
      Result: True
     Comment: Group test-user is present and up to date
     Started: 16:12:35.967843
    Duration: 3.066 ms
     Changes:   
----------
          ID: Manage account - test-user
    Function: user.present
        Name: test-user
      Result: True
     Comment: Updated user test-user
     Started: 16:12:35.971431
    Duration: 53.881 ms
     Changes:   
              ----------
              groups:
                  - test-user
----------
          ID: Manage group - test-group1
    Function: group.present
        Name: test-group1
      Result: True
     Comment: The following group attributes are set to be changed:
              members: ['test-user']
     Started: 16:12:36.025473
    Duration: 22.06 ms
     Changes:   
              ----------
              Final:
                  All changes applied successfully
----------
          ID: Manage group - test-group2
    Function: group.present
        Name: test-group2
      Result: True
     Comment: The following group attributes are set to be changed:
              members: ['test-user']
     Started: 16:12:36.047647
    Duration: 22.128 ms
     Changes:   
              ----------
              Final:
                  All changes applied successfully

Summary for test-alma9
------------
Succeeded: 4 (changed=3)
Failed:    0
------------
Total states run:     4
Total run time: 101.135 ms
test-alma8:
----------
          ID: Manage account - test-user
    Function: group.present
        Name: test-user
      Result: True
     Comment: Group test-user is present and up to date
     Started: 16:12:36.015804
    Duration: 3.456 ms
     Changes:   
----------
          ID: Manage account - test-user
    Function: user.present
        Name: test-user
      Result: True
     Comment: Updated user test-user
     Started: 16:12:36.019788
    Duration: 257.84 ms
     Changes:   
              ----------
              groups:
                  - test-user
----------
          ID: Manage group - test-group1
    Function: group.present
        Name: test-group1
      Result: True
     Comment: The following group attributes are set to be changed:
              members: ['test-user']
     Started: 16:12:36.277789
    Duration: 210.946 ms
     Changes:   
              ----------
              Final:
                  All changes applied successfully
----------
          ID: Manage group - test-group2
    Function: group.present
        Name: test-group2
      Result: True
     Comment: The following group attributes are set to be changed:
              members: ['test-user']
     Started: 16:12:36.488873
    Duration: 216.239 ms
     Changes:   
              ----------
              Final:
                  All changes applied successfully

Summary for test-alma8
------------
Succeeded: 4 (changed=3)
Failed:    0
------------
Total states run:     4
Total run time: 688.481 ms

Versions Report

salt --versions-report (master)
Salt Version:
          Salt: 3006.0
 
Python Version:
        Python: 3.10.11 (main, Apr 14 2023, 05:57:16) [GCC 11.2.0]
 
Dependency Versions:
          cffi: 1.14.6
      cherrypy: unknown
      dateutil: 2.8.1
     docker-py: Not Installed
         gitdb: Not Installed
     gitpython: Not Installed
        Jinja2: 3.1.2
       libgit2: Not Installed
  looseversion: 1.0.2
      M2Crypto: Not Installed
          Mako: Not Installed
       msgpack: 1.0.2
  msgpack-pure: Not Installed
  mysql-python: Not Installed
     packaging: 22.0
     pycparser: 2.21
      pycrypto: Not Installed
  pycryptodome: 3.9.8
        pygit2: Not Installed
  python-gnupg: 0.4.8
        PyYAML: 5.4.1
         PyZMQ: 23.2.0
        relenv: 0.11.2
         smmap: Not Installed
       timelib: 0.2.4
       Tornado: 4.5.3
           ZMQ: 4.3.4
 
System Versions:
          dist: almalinux 9.1 Lime Lynx
        locale: utf-8
       machine: x86_64
       release: 5.14.0-162.18.1.el9_1.x86_64
        system: Linux
       version: AlmaLinux 9.1 Lime Lynx
salt --versions-report (minion) ```yaml Salt Version: Salt: 3006.0

Python Version:
Python: 3.10.11 (main, Apr 14 2023, 05:57:16) [GCC 11.2.0]

Dependency Versions:
cffi: 1.14.6
cherrypy: unknown
dateutil: 2.8.1
docker-py: Not Installed
gitdb: Not Installed
gitpython: Not Installed
Jinja2: 3.1.2
libgit2: Not Installed
looseversion: 1.0.2
M2Crypto: Not Installed
Mako: Not Installed
msgpack: 1.0.2
msgpack-pure: Not Installed
mysql-python: Not Installed
packaging: 22.0
pycparser: 2.21
pycrypto: Not Installed
pycryptodome: 3.9.8
pygit2: Not Installed
python-gnupg: 0.4.8
PyYAML: 5.4.1
PyZMQ: 23.2.0
relenv: 0.11.2
smmap: Not Installed
timelib: 0.2.4
Tornado: 4.5.3
ZMQ: 4.3.4

System Versions:
dist: almalinux 9.1 Lime Lynx
locale: utf-8
machine: x86_64
release: 5.14.0-162.18.1.el9_1.x86_64
system: Linux
version: AlmaLinux 9.1 Lime Lynx

</details>

**Additional context**
It's not clear to me when this behavior emerged, but 12-18mo ago, I don't remember this behavior being around.
@jamesharr jamesharr added Bug broken, incorrect, or confusing behavior needs-triage labels May 2, 2023
@OrangeDog
Copy link
Contributor

OrangeDog commented May 3, 2023

You are using gid to put it in the test-user group, which is what it is doing.

You can't mix the methods of managing group membership.

@OrangeDog OrangeDog added the expected-behavior intended functionality label May 3, 2023
@OrangeDog
Copy link
Contributor

Though I see what you mean. Having gid add that group but not remove others might be expected. That's a breaking change though, as everyone relying on that would need to add an explicit empty groups list.

@jamesharr
Copy link
Author

Oh I think I see what's happening now... What's the right way to do this then? Seems like a common pattern.

In my mind, a user's main gid is different than auxiliary groups (which is what I'm really trying to manage). There's no way to create a user account without a primary group.

If I want a specific GID for a user's group, wouldn't it be needed during user creation? Or should I just manage the group and have that as a required item when managing the user account and let user.present implicitly figure out the gid from user group:true?

I feel like no matter what I do, I'm getting into tricky implicit behavior. Either that, or I have to change the approach in which I manage groups and enumerate out all of the groups that any given user is a member of on any given machine (which may not be the same across machines)

@jamesharr
Copy link
Author

I tried managing the user's group prior to creating the user while not specifying a GID, and I couldn't get it to work.

Example state file
Manage group - test-user:
  group.present:
  - name: test-user
  - gid: 61001

Manage user - test-user:
  user.present:
  - name: test-user
  - uid: 61001
  - fullname: Test User
  - shell: /bin/bash
  - home: /home/test-user
  - password: $6$REDACTED
  - require:
    - Manage group - test-user

Manage group - test-group1:
  group.present:
  - name: test-group1
  - members:
    - test-user
  - require:
    - Manage user - test-user

Manage group - test-group2:
  group.present:
  - name: test-group2
  - members:
    - test-user
  - require:
    - Manage user - test-user
state run output
% salt 'test-alma9' state.apply exp.user_bug2 
test-alma9:
----------
          ID: Manage group - test-user
    Function: group.present
        Name: test-user
      Result: True
     Comment: New group test-user created
     Started: 13:23:31.516305
    Duration: 43.458 ms
     Changes:   
              ----------
              gid:
                  61001
              members:
              name:
                  test-user
              passwd:
                  x
----------
          ID: Manage user - test-user
    Function: user.present
        Name: test-user
      Result: False
     Comment: Failed to create new user test-user
     Started: 13:23:31.561391
    Duration: 27.457 ms
     Changes:   
----------
          ID: Manage group - test-group1
    Function: group.present
        Name: test-group1
      Result: False
     Comment: One or more requisite failed: exp.user_bug2.Manage user - test-user
     Started: 13:23:31.589172
    Duration: 0.003 ms
     Changes:   
----------
          ID: Manage group - test-group2
    Function: group.present
        Name: test-group2
      Result: False
     Comment: One or more requisite failed: exp.user_bug2.Manage user - test-user
     Started: 13:23:31.589254
    Duration: 0.001 ms
     Changes:   

Summary for test-alma9
------------
Succeeded: 1 (changed=1)
Failed:    3
------------
Total states run:     4
Total run time:  70.919 ms
ERROR: Minions returned with non-zero exit code
salt-minion log
May 03 13:23:31 test-alma9 groupadd[151352]: new group: name=test-user, GID=61001
May 03 13:23:31 test-alma9 useradd[151358]: failed adding user 'test-user', exit code: 9
May 03 13:23:31 test-alma9 salt-minion[151342]: [ERROR   ] Command '/usr/sbin/useradd' failed with return code: 9
May 03 13:23:31 test-alma9 salt-minion[151342]: [ERROR   ] stderr: useradd: group test-user exists - if you want to add this user to that group, use -g.
May 03 13:23:31 test-alma9 salt-minion[151342]: [ERROR   ] retcode: 9
May 03 13:23:31 test-alma9 salt-minion[151342]: [ERROR   ] Failed to create new user test-user

Is there a better way to approach this?

Going back to what I described in my post above and hoping to clarify -- the data-model (pillar) we use for managing groups is very much oriented towards "on this minion, this group exists, and has exactly these members"; not every group exists on every machine.

We usually don't care if any given user is in additional groups (ie: a locally created group for a shared folder), but we very much care if users there are extra users in a group that shouldn't be. For example, I really don't want a locally created user in the admins group without me knowing that. This seems like a concern other orgs would probably have and I'm curious how they deal with it.

I can write some code to flip that data-model around at state rendering time to figure out which groups a given user belongs in, but I still need to purge out users that shouldn't be in a managed group and this starts to feel really hacky.

@jamesharr
Copy link
Author

... or do I just need to set - remove_groups: False since I'm managing it by-group instead of by-user?

If that's the case, then I think the docs just need to be updated to say that groups being unset will be treated as an empty list or something like that.

@anilsil anilsil added this to the Sulfur v3006.2 milestone May 4, 2023
@FlowBreeze
Copy link

FlowBreeze commented May 10, 2023

same issue here
@Ch3LL @OrangeDog
at v3005.1-2 state user.present when groups param is not set, state will not modify group, it works expected as document said:

groups
A list of groups to assign the user to, pass a list object. If a group specified here does not exist on the minion, the state will fail. If set to the empty list, the user
will be removed from all groups except the default group. If unset, salt will assume current groups are still wanted, and will make no changes to them.

and saltstack already made a breaking change, if it is a bug, it should be fixed.

@Ch3LL
Copy link
Contributor

Ch3LL commented May 12, 2023

Thanks for opening this. I agree this is a breaking change and needs to be fixed, this has been set to be fixed in 3006.2

@OrangeDog
Copy link
Contributor

If it's a breaking change, surely it has to wait until 3008, with a deprecation warning in 3007?

@jamesharr
Copy link
Author

If it's a breaking change, surely it has to wait until 3008, with a deprecation warning in 3007?

I think what @Ch3LL may have meant is that this behavior accidentally changed after v3005.1-2 (as @FlowBreeze noted) and THAT was an accidental breaking change. Fixing this bug would fix that breaking change.

Please correct me if I interpreted that wrong.

@Ch3LL
Copy link
Contributor

Ch3LL commented May 17, 2023

Yes thats what I meant, we should not introduce a breaking change without warning. I have not dove into this issue though so my assessment could change, once I get time to look into this to see if there is a reason behind this change.

@stooj
Copy link
Contributor

stooj commented May 24, 2023

Maybe I can provide a more atomic example:

Sample SLS file:

user-auser-present:
  user.present:
    - name: auser

Assume the user exists already.

If the state is run on 3005.1:

ID: user-auser-present
Function: user.present
Name: auser
Result: True
Comment: User auser is present and up to date
Started: 20:03:23.571999
Duration: 1.905 ms
Changes:

If the same state is run on 3006.1:

ID: user-auser-present
Function: user.present
Name: auser
Result: None
Comment: The following user attributes are set to be changed:
         groups: []
Started: 20:05:28.658696
Duration: 1.859 ms
Changes:

The behaviour in 3006.1 contradicts the documentation as @FlowBreeze quoted, and the old behaviour was definitely preferable for me.

Adding remove_groups: False to the state is a workaround.

@OrangeDog OrangeDog added Regression The issue is a bug that breaks functionality known to work in previous releases. and removed expected-behavior intended functionality labels May 24, 2023
@jamesharr
Copy link
Author

Well put @stooj

I have another question for thought:

Adding remove_groups: False to the state is a workaround.

Breaking change policy aside, is there any semantic behavior difference between 3005.1 with no groups and 3006.x with no groups andremove_groups: False`? They seem to behave the exact same way.

Honestly, for me, the workaround is actually working perfectly fine, I'm comfortable with it as a long term solution, and it makes semantic sense to me too (presuming the docs get updated). A product owner would have to balance the effort of bringing back the old behavior with the impact of keeping a breaking change (which I've lived with for a while, if I'm being honest). It's just food for thought.

@Ch3LL
Copy link
Contributor

Ch3LL commented Jun 8, 2023

@nicholasmhughes any ideas here? It looks like git blame is showing commit 81ff432 (from PR #62503) as the cause to this change.

@jpmckinney
Copy link
Contributor

jpmckinney commented Jun 9, 2023

Edit: I confirm that setting remove_groups: False resolves the issue where using user.present without groups and later group.present was causing the states to run on every state.apply.


I have something like:

{{ user }}_user_exists:
  user.present:
    - name: {{ user }}
    - home: /home/{{ user }}
    - uid: {{ uid }}
    - order: 1
    - shell: /bin/bash

and elsewhere:

add {{ pillar.docker.user }} user to docker group:
  group.present:
    - name: docker
    - addusers:
      - {{ pillar.docker.user }}
    - require:
      - user: {{ pillar.docker.user }}_user_exists

When running state.apply, every time, I see both:

          ID: deployer_user_exists
    Function: user.present
        Name: deployer
      Result: True
     Comment: Updated user deployer
     Started: 17:07:55.922331
    Duration: 7.01 ms
     Changes:   
              ----------
              groups:
                  - deployer

and

          ID: add deployer user to docker group
    Function: group.present
        Name: docker
      Result: True
     Comment: The following group attributes are set to be changed:
              addusers: ['deployer']
     Started: 17:08:05.523025
    Duration: 3.787 ms
     Changes:   
              ----------
              Final:
                  All changes applied successfully

Whereas I would only expect to see these changes once, not every time.

I'm not entirely sure if this has the same cause as the present issue.

@Ch3LL
Copy link
Contributor

Ch3LL commented Jun 22, 2023

Anyone willing to try this fix out? #64530

@Jsollvander
Copy link

@Ch3LL I just tested it and it works for me. I'm not getting any errors when gid is set and groups is unset. I haven't tried setting remove_groups or optional_groups though.

@Ch3LL
Copy link
Contributor

Ch3LL commented Aug 2, 2023

Thanks for confirming. I'll go ahead and close for now, but please feel free to open a new issue if there is an issue with those other arguments.

@Ch3LL Ch3LL closed this as completed Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug broken, incorrect, or confusing behavior Regression The issue is a bug that breaks functionality known to work in previous releases.
Projects
None yet
Development

No branches or pull requests

8 participants