guest/spec, rego, rego_test: Fix GetUserInfo - #2465
Conversation
568912a to
47d5b76
Compare
|
Added one more test case, made some test changes |
47d5b76 to
4cfa792
Compare
| err = errors.Wrapf(err, "failed to find user by username: %s", parts[0]) | ||
| return |
There was a problem hiding this comment.
can we stick to explicit returns?
the flow is easier to read for than the err = <>; return statements.
| err = errors.Wrapf(err, "failed to find user by username: %s", parts[0]) | |
| return | |
| return res, errors.Wrapf(err, "failed to find user by username: %s", parts[0]) |
There was a problem hiding this comment.
btw, the other place where I've also made use of named returns is in regoEnforcer.GetUserInfo. In that case there are more return variables and return IDName{}, nil, "", err felt a bit awkward to me, but if you want I'm happy to change that to use explicit returns as well.
There was a problem hiding this comment.
I think the general recommendation is to avoid naked returns if the function isn't a couple lines, since its difficult to track down visually what is being returned and where its being set
though no need to belabor the point here
4cfa792 to
a0aedfa
Compare
a0aedfa to
3c4c79a
Compare
|
@helsaawy I rebased it to main to resolve the merge conflict, but didn't realize there is such a huge change on main (otherwise should probably have just done a merge). There is no code changes: Can you approve again and merge this? |
| userInfo, err := getUser(passwdPath, func(user user.User) bool { | ||
| return uint32(user.Uid) == uid | ||
| }) | ||
| if !parsedUserStr { |
There was a problem hiding this comment.
Can this fallback code also be refactored? I think the ParseUserStr method already has same/similar code. Can we do something like :
if process.User.Username != "" {
specGuest.ParseUserStr(rootPath, process.User.Username)
} else {
specGuest.ParseUserStr(rotPath, fmt.Sprintf("%d:%d",process.User.UID, process.User.GID)
}
| return nil | ||
| } | ||
|
|
||
| type ParseUserStrResult struct { |
There was a problem hiding this comment.
nit: I think ParsedUserStr/ParsedUserStrResult is slightly better.
|
Hi @ambarve, this set of changes is already in the C-LCOW release branch (and this PR is in fact more of a backport into main). Can we merge this, then I can implement your suggestions in another PR which would also get ported to that branch separately? |
|
seems like this needs a rebase. we removed 2025 runners. |
3c4c79a to
a3f6d85
Compare
done, pls merge if it passes |
|
|
||
| func (policy *regoEnforcer) GetUserInfo(containerID string, process *oci.Process) (IDName, []IDName, string, error) { | ||
| rootPath := filepath.Join(guestpath.LCOWRootPrefixInUVM, containerID, guestpath.RootfsPath) | ||
| func (policy *regoEnforcer) GetUserInfo(process *oci.Process, rootPath string) ( |
There was a problem hiding this comment.
We should assert that rootPath is not empty IMO. This doesnt make sense and you will accidently end up with a valid path to the root /etc/passwd right?
There was a problem hiding this comment.
The code outside is this:
user, groups, umask, err := h.securityPolicyEnforcer.GetUserInfo(settings.OCISpecification.Process, settings.OCISpecification.Root.Path)and
user, groups, umask, err = h.securityPolicyEnforcer.GetUserInfo(params.OCIProcess, c.spec.Root.Path)so it really should not be empty. While we can add an assert, if it's empty then it would fail much earlier than this.
Given that this PR is already merged in the C-ACI release branch, any changes would require a separate PR, so I'm probably not adding it here.
There was a problem hiding this comment.
btw I will make sure to pick up this suggestion, and the one above, in another set of changes :)
A workload using a custom user for one of its container now fails to start with
policy denial: invalid user.
In the "broken" version, we see this in the input into Rego:
...
"user": {
"id": "100",
"name": ""
},
...
in the working version the input has
...
"user": {
"id": "100",
"name": "curl_user"
},
...
The issue seems to be that the new GetUserInfo, when given a valid userstr, does
not return any username/groupnames in the IDName structs, only IDs. Therefore,
policies written to enforce the name will fail.
To fix this, we extend ParseUserStr to return the user and group names, and
update GetUserInfo to store the names in the IDName structs. Since the function
now contains extra logic to e.g. fetch the group name even when we have known
UID/GID, some slight refactoring was done to avoid code repetition.
Looking at azcri container_create_windows.go (this file handles LCOW as well)
generateContainerSpec and container_create.go:generateUserString, my
understanding is that it would always set Username even if in the container JSON
it's given as run_as_user/run_as_group with a UID (but the username will be set
to a string of the integers). The userstr that GCS sees would only be empty if
neither the container JSON nor the container image itself specify any users, in
which case we would default to uid 0 and gid 0.
Fixes: 7084bd2 ("rego policy enforcer should use the same user parsing logic as GCS"...)
Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
…n GetUserInfo Get rid of some duplicate code in the Rego policy enforcer, by exporting GetUser and GetGroup from guest/spec. Since this code path in the enforcer is now only supposed to happen for rare cases where the user string in the OCI Spec is invalid but we somehow still gets to start the container, removing this duplicate code prevents staleness. Also make both specGuest and the enforcer use moby/sys/user instead of the now deprecated libcontainer/user package, which in the latest version simply redirects to moby/sys/user [1]. [1]: https://github.com/opencontainers/runc/blob/v1.3.0/libcontainer/user/user_deprecated.go Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
The primary purpose of this is to make testing this function easier. This also reduces assumptions. We remove the containerID parameter as it is thus not needed anymore. Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
Test various user strings, with or without /etc/{passwd,group}. Make sure that
empty user strings defaults to 0:0.
Signed-off-by: Tingmao Wang <tingmaowang@microsoft.com>
a3f6d85 to
08db90c
Compare
This PR fixes an issue with the latest GetUserInfo implementation and adds some unit tests for this.
Issue
A workload using a custom user for one of its container now fails to start with policy denial: invalid user.
In the "broken" version, we see this in the input into Rego:
in the working version the input has
The issue seems to be that the new GetUserInfo, when given a valid userstr, does not return any username/groupnames in the IDName structs, only IDs. Therefore, policies written to enforce the name will fail.
Testing
Tested on confidential. Not tested non-conf yet.