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

Pass process user integration test #243

Merged
merged 10 commits into from
Sep 1, 2021
Merged

Conversation

Furisto
Copy link
Collaborator

@Furisto Furisto commented Aug 28, 2021

Making this test pass is easy enough (just add the supplementary groups using setgroups system call) but there are some subtleties around this for rootless containers due to CVE-2014-8989.

The gist of this priviledge escalation is: Before 3.19 it was possible for an unprivileged user to enter an user namespace, become root and then call setgroups in order to drop membership in supplementary groups. This allowed access to files which blocked access based on being a member of these groups.

This leaves us with three scenarios:

Unprivileged user starting a rootless container: The main process is running as an unprivileged user and therefore cannot write the mapping until "deny" has been written to /proc/{pid}/setgroups. Once written /proc/{pid}/setgroups cannot be reset and the setgroups system call will be disabled for all processes in this user namespace. This also means that we should detect if the user is unprivileged and additional gids have been specified and bail out early as this can never work.

Privileged user starting a rootless container: It is not necessary to write "deny" to /proc/setgroups in order to create the gid mapping and therefore we don't. This means that setgroups could be used to drop groups, but this is fine as the user is privileged and could do so anyway.
We should check if the specified supplemental groups fall into the range that are specified in the gid mapping and bail out early if they do not.

Privileged user starting a normal container: Just add the supplementary groups.

I noticed some problems when testing with rootless containers, which are not directly related to this feature and fixed them:

  • An attempt was made to move the init process into cgroups which should not be done without systemd support
  • When a privileged user starts a rootless container we try to change back into the old directory here, but this fails in this case because the old directory belonged to the privileged user and we are now in a user namespace with a user mapped to an unprivileged user on the host. I removed changing back to the old directory because we pivot_root right after anyway and therefore this doesn't seem needed. Someone correct me if I am wrong here.

@Furisto Furisto requested a review from yihuaf August 28, 2021 20:49
Copy link
Collaborator

@yihuaf yihuaf left a comment

Choose a reason for hiding this comment

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

This leaves us with three scenarios:

Unprivileged user starting a rootless container: The main process is running as an unprivileged user and therefore cannot write the mapping until "deny" has been written to /proc/{pid}/setgroups. Once written /proc/{pid}/setgroups cannot be reset and the setgroups system call will be disabled for all processes in this user namespace. This also means that we should detect if the user is unprivileged and additional gids have been specified and bail out early as this can never work.

Privileged user starting a rootless container: It is not necessary to write "deny" to /proc/setgroups in order to create the gid mapping and therefore we don't. This means that setgroups could be used to drop groups, but this is fine as the user is privileged and could do so anyway.
We should check if the specified supplemental groups fall into the range that are specified in the gid mapping and bail out early if they do not.
Privileged user starting a normal container: Just add the supplementary groups.

Can you put the discussion into comments about the use of in the code. Those discussion are really good and I think they belong in the code, rather than PR.

src/process/init.rs Outdated Show resolved Hide resolved
src/rootfs.rs Outdated Show resolved Hide resolved
@Furisto Furisto force-pushed the setgroups branch 3 times, most recently from b2bf981 to a581129 Compare August 31, 2021 22:12
@codecov-commenter
Copy link

Codecov Report

Merging #243 (aa4d5da) into main (ef4b793) will decrease coverage by 0.58%.
The diff coverage is 0.00%.

@@            Coverage Diff             @@
##             main     #243      +/-   ##
==========================================
- Coverage   38.36%   37.77%   -0.59%     
==========================================
  Files          15       15              
  Lines        1366     1387      +21     
  Branches      372      379       +7     
==========================================
  Hits          524      524              
- Misses        673      694      +21     
  Partials      169      169              

@Furisto
Copy link
Collaborator Author

Furisto commented Aug 31, 2021

@yihuaf PTAL

@yihuaf yihuaf self-requested a review August 31, 2021 23:09
Copy link
Collaborator

@yihuaf yihuaf left a comment

Choose a reason for hiding this comment

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

LGTM, and in my opinion much easier to follow with the added comments. Just two minor questions.

nix::unistd::setgroups(&gids).context("failed to set supplementary gids")?;
}
// this should have been detected during validation
_ => unreachable!(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Under what circumstance where is branch get triggered? Isn't match option can only be either Some or None?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The Some has a match guard, so this is only triggered when the user is not privileged. Ideally I could write
None | Some(r) if r. privileged
but Rust does not allow that (yet).

src/process/init.rs Show resolved Hide resolved
@Furisto Furisto merged commit 03cb29e into youki-dev:main Sep 1, 2021
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.

3 participants