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

container users and groups from process root #677

Merged

Conversation

deepskyblue86
Copy link
Contributor

@deepskyblue86 deepskyblue86 commented Oct 20, 2022

What type of PR is this?

/kind feature

Any specific area of the project related to this PR?

/area libsinsp

/area tests

Does this PR require a change in the driver versions?

What this PR does / why we need it:
Introduce add_container_user and add_container_group.
Those are called by sinsp_threadinfo set_user / set_group and load the information from /proc//root/etc.
For that to work, the process running sinsp has to be privileged and in the host pid namespace.
For more information see man 5 proc.

Which issue(s) this PR fixes:

Fixes #

Special notes for your reviewer:

Does this PR introduce a user-facing change?:

NONE

Comment on lines -274 to -279
auto &userlist = m_userlist[container_id];
userlist[uid].uid = uid;
userlist[uid].gid = gid;
strlcpy(userlist[uid].name, name, MAX_CREDENTIALS_STR_LEN);
strlcpy(userlist[uid].homedir, home, SCAP_MAX_PATH_SIZE);
strlcpy(userlist[uid].shell, shell, SCAP_MAX_PATH_SIZE);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved into userinfo_map_insert

Comment on lines -349 to -351
auto &grplist = m_grouplist[container_id];
grplist[gid].gid = gid;
strlcpy(grplist[gid].name, name, MAX_CREDENTIALS_STR_LEN);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved into groupinfo_map_insert

@@ -505,8 +505,15 @@ void sinsp_threadinfo::set_user(uint32_t uid)
scap_userinfo *user = m_inspector->m_usergroup_manager.get_user(m_container_id, uid);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mind that sinsp_threadinfo::set_user gets called by parse_chroot_exit, parse_clone_exit, parse_execve_exit

Comment on lines 261 to 272
if (name == NULL)
{
name = "<NA>";
}
if (home == NULL)
{
home = "<NA>";
}
if (shell == NULL)
{
shell = "<NA>";
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Avoid putting <NA> in cache on containers

@@ -613,45 +691,3 @@ void sinsp_usergroup_manager::notify_group_changed(const scap_groupinfo *group,
m_inspector->m_pending_state_evts.push(cevt);
#endif
}

void sinsp_usergroup_manager::load_from_container(const std::string &container_id, const std::string &overlayfs_root)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This worked well with docker, but wasn't called by containerd.

I tested with a Job on k0s:

apiVersion: batch/v1
kind: Job
metadata:
  name: custom-user
spec:
  template:
    spec:
      securityContext:
        runAsUser: 37
      containers:
      - name: custom-user
        image: busybox:latest
        command: ["sleep",  "3600" ]
      restartPolicy: Never
  backoffLimit: 4

scap_userinfo *retval{nullptr};

#if defined HAVE_PWD_H && defined HAVE_FGET__ENT
std::string proc_root_etc = s_host_root + "/proc/" + std::to_string(pid) + "/root/etc/";
Copy link
Contributor Author

Choose a reason for hiding this comment

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

From https://linux.die.net/man/5/proc:

/proc/[pid]/root
UNIX and Linux support the idea of a per-process root of the file system, set by the chroot system call. This file is a symbolic link that points to the process's root directory

Copy link
Contributor

Choose a reason for hiding this comment

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

This is a freakingly neat idea! Thanks for this!
@terylt @araujof

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Credits to @gnosek for teaching me that. Credits to myself for a few days of experiments 🤣

Copy link
Member

Choose a reason for hiding this comment

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

Really nice!

Copy link
Contributor

@FedeDP FedeDP left a comment

Choose a reason for hiding this comment

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

I love these changes!
I am going to give this a bit of testing asap and come back to you!

@FedeDP
Copy link
Contributor

FedeDP commented Oct 20, 2022

Also, the CI did explode :D
btw @loresuso you might be interested in this since you developed the old solution with me!

Finally, i think we can also drop the container engines part to load the lowerdir .

@deepskyblue86
Copy link
Contributor Author

deepskyblue86 commented Oct 20, 2022

Yeah, the UT (which I actually wrote myself 😅) failed, I was eager to get some feedback and I didn't run them.
I'll update this PR soon!

@loresuso
Copy link
Member

Cool! I am taking a closer look but it SGTM :)

@deepskyblue86
Copy link
Contributor Author

Also, the CI did explode :D btw @loresuso you might be interested in this since you developed the old solution with me!

Finally, i think we can also drop the container engines part to load the lowerdir .

@FedeDP do you want me to drop that in this PR?

@FedeDP
Copy link
Contributor

FedeDP commented Oct 20, 2022

Yep, thanks!

@deepskyblue86
Copy link
Contributor Author

I found something weird: if I checkout 0.9.0 sinsp-example behaves as expected, while on master I have {"error": ""} and no container events (I'm doing -j -f 'evt.type in (execve, execveat) and evt.dir=< and container.id != host').

@Molter73
Copy link
Contributor

I rebased the PR for adding e2e tests to CI on Tuesday, so whatever broke them is a change after that. I'll try to look into them ASAP, but if you guys find what is wrong, you can open a PR.

@FedeDP
Copy link
Contributor

FedeDP commented Oct 21, 2022

Hi Mauro! I think the fix is #678 ! Thanks to Angelo <3

@FedeDP
Copy link
Contributor

FedeDP commented Oct 21, 2022

We can rebase on top of master :)

FedeDP
FedeDP previously approved these changes Oct 21, 2022
Copy link
Contributor

@FedeDP FedeDP left a comment

Choose a reason for hiding this comment

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

/approve
I really like this :)
We can cleanup the overalyfs things later, in a subsequent PR!

@poiana
Copy link
Contributor

poiana commented Oct 21, 2022

LGTM label has been added.

Git tree hash: 7afc1b7e23321ddd423db20bd77bfe3181c566e3

@Andreagit97 Andreagit97 added this to the 0.10.0 milestone Oct 21, 2022
Copy link
Contributor

@FedeDP FedeDP left a comment

Choose a reason for hiding this comment

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

Mmh on a better look, it seems like the container start is triggering an useradded event for host users (tested with sinsp-example:

sudo ./libsinsp/examples/sinsp-example --bpf driver/bpf/probe.o -f evt.type=useradded -o "%evt.arg[2] %evt.arg[5]"
  • evt.arg[2] is user name
  • evt.arg[5] is container id

It prints:

{"evt.arg[2]":"root","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"bin","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"daemon","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"mail","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"ftp","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"http","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"nobody","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"dbus","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"systemd-journal-remote","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"systemd-network","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"systemd-oom","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"systemd-resolve","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"systemd-timesync","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"systemd-coredump","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"uuidd","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"avahi","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"polkitd","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"rtkit","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"sddm","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"usbmux","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"federico","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"tss","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"git","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"geoclue","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"nm-openvpn","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"openvpn","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"brltty","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"sophosav","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"redis","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"dnsmasq","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"libvirt-qemu","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"mysql","evt.arg[5]":"2d4a2dd77f70"}
{"evt.arg[2]":"qemu","evt.arg[5]":"2d4a2dd77f70"}

Note the "federico" in there; these are the host users.

@FedeDP
Copy link
Contributor

FedeDP commented Oct 21, 2022

/hold

Have HAVE_FGET__ENT representing _DEFAULT_SOURCE || _SVID_SOURCE, as a
feature test for fgetpwent() / fgetgrent()

Signed-off-by: Angelo Puglisi <[email protected]>
@poiana poiana removed the lgtm label Nov 8, 2022
@poiana poiana requested a review from FedeDP November 8, 2022 19:20
@deepskyblue86
Copy link
Contributor Author

@Molter73 I'd like you to check the e2e part, if you have some time. Thanks in advance.

Copy link
Contributor

@Molter73 Molter73 left a comment

Choose a reason for hiding this comment

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

Sorry for the delay @deepskyblue86! The changes on the e2e tests look solid, great to see someone actively using them! 😄

I left a few comments on the rest of the code, but overall the changes LGTM!

userspace/libsinsp/procfs_utils.cpp Outdated Show resolved Hide resolved
userspace/libsinsp/procfs_utils.cpp Outdated Show resolved Hide resolved
userspace/libsinsp/procfs_utils.cpp Outdated Show resolved Hide resolved
userspace/libsinsp/user.cpp Show resolved Hide resolved
deepskyblue86 and others added 2 commits November 17, 2022 14:50
`readlink` does not append a null terminator to `buf`, so we might want
to ensure at least one byte is left untouched in order to prevent
possible buffer overruns

Signed-off-by: Angelo Puglisi <[email protected]>
Co-authored-by: Mauro Ezequiel Moltrasio <[email protected]>
Signed-off-by: Angelo Puglisi <[email protected]>
Co-authored-by: Mauro Ezequiel Moltrasio <[email protected]>
@deepskyblue86 deepskyblue86 requested review from Molter73 and FedeDP and removed request for FedeDP, hbrueckner and Molter73 November 21, 2022 09:49
Copy link
Contributor

@Molter73 Molter73 left a comment

Choose a reason for hiding this comment

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

/lgtm

@poiana
Copy link
Contributor

poiana commented Nov 21, 2022

LGTM label has been added.

Git tree hash: 60da174f5558ca010ad752432af9a5eb3ecf1ca1

Copy link
Contributor

@gnosek gnosek left a comment

Choose a reason for hiding this comment

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

Just one question, LGTM otherwise :)

Comment on lines +513 to +522
else if(uid != 0)
{
//
// When a container is running with a specific user and this
// get called with 0, it's too early to make an attempt.
// As a downside we won't load users for containers running as
// root, but we will load them if e.g.docker exec -u <specific-user>.
//
user = m_inspector->m_usergroup_manager.add_container_user(m_container_id, m_pid, uid, m_inspector->is_live());
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is it "too early"? Because we caught a thread switching into the container? What if the container is started as root?

What is uid here? If we're calling this from all sorts of parsers, it's not a special uid, like the docker -u user, right? So how does the uid != 0 check work?

Maybe hardcoding uid=0 as "root" would be good enough btw?

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

  1. Why is it "too early"?
    By trial and error, I found that the thread had the container id but wasn't really inside the container (i.e. runc:[1:CHILD] in my tests).
  2. What if the container is started as root?
    We'll have <NA> 🤷 but if / when it switches to a different user we'll load the users.
  3. What is uid here?
    a) sinsp_threadinfo::set_user is being called by parse_chroot_exit, parse_clone_exit, parse_execve_exit, parse_setuid_exit, parse_setresuid_exit.
    b) It's actually the user of docker -u or k8s runAsUser.
    c) you can see that it's being tested (test/e2e/tests/test_process/test_container.py) by starting a hashicorp/http-echo:alpine container with 11:110
  4. Maybe hardcoding uid=0 as "root" would be good enough btw?
    I was a bit uncertain about doing so, but if it's already two of us I'd go with that 😀

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe hardcoding uid=0 as "root" would be good enough btw?

Makes sense.

Copy link
Contributor

@FedeDP FedeDP left a comment

Choose a reason for hiding this comment

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

/approve

@FedeDP
Copy link
Contributor

FedeDP commented Nov 22, 2022

/hold

@FedeDP
Copy link
Contributor

FedeDP commented Nov 22, 2022

Sorry @gnosek i might have left behind your comment and approved :(
Hopefully i was able to hold this.

@FedeDP
Copy link
Contributor

FedeDP commented Nov 22, 2022

Uh i already put this on hold 20d ago ahah @deepskyblue86 care to answer to grzeg question?

@poiana
Copy link
Contributor

poiana commented Nov 23, 2022

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: deepskyblue86, FedeDP, gnosek, Molter73

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:
  • OWNERS [FedeDP,Molter73,gnosek]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@FedeDP
Copy link
Contributor

FedeDP commented Nov 23, 2022

/unhold

@poiana poiana merged commit 6f9bec4 into falcosecurity:master Nov 23, 2022
@deepskyblue86 deepskyblue86 deleted the usergroup_manager-from-pid-root branch November 23, 2022 18:03
@poiana poiana mentioned this pull request Feb 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants