-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
enrich container id from process id #15947
Conversation
Since this is a community submitted pull request, a Jenkins build has not been kicked off automatically. Can an Elastic organization member please verify the contents of this patch and then kick off a build manually? |
1 similar comment
Since this is a community submitted pull request, a Jenkins build has not been kicked off automatically. Can an Elastic organization member please verify the contents of this patch and then kick off a build manually? |
// specific language governing permissions and limitations | ||
// under the License. | ||
|
||
package add_process_metadata |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't use an underscore in package name
// specific language governing permissions and limitations | ||
// under the License. | ||
|
||
package add_process_metadata |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't use an underscore in package name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the contribution. I have a few suggestions for config and testing.
@@ -35,6 +35,8 @@ import ( | |||
const ( | |||
processorName = "add_process_metadata" | |||
cacheExpiration = time.Second * 30 | |||
hostPath = "/" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be configurable. Let's do it the same way as add_docker_metadata
.
HostFS string `config:"system.hostfs"` // Specifies the mount point of the host’s filesystem for use in monitoring a host from within a container. |
@@ -35,6 +35,8 @@ import ( | |||
const ( | |||
processorName = "add_process_metadata" | |||
cacheExpiration = time.Second * 30 | |||
hostPath = "/" | |||
cgroupPrefix = "/kubepods" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be configurable too. I'd prefer to have this be search list so that it works out of the box with multiple tools (/kubepods
, /docker
). I don't know how the other tools name their cgroups, but if we can cover them too this would be great.
} | ||
|
||
func (p gosigarCidProvider) GetCid(pid int) (result string, err error) { | ||
cgroups, err := cgroup.ProcessCgroupPaths(p.hostPath, pid) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see there is a mock cidProvider
implementation for testing the processor. But that leaves this part untested. I recommend mocking out the cgroup.ProcessCgroupPaths
function in order to get test coverage of this provider. add_docker_metadata
does this with:
beats/libbeat/processors/add_docker_metadata/add_docker_metadata.go
Lines 49 to 51 in bc71069
// processGroupPaths returns the cgroups associated with a process. This enables | |
// unit testing by allowing us to stub the OS interface. | |
var processCgroupPaths = cgroup.ProcessCgroupPaths |
💚 CLA has been signed |
Also, I signed the CLA, but it still shows not signed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for making those updates. I have just a few minor suggestions on the updates.
gCidProvider = newCidProvider(hostPath, cgroupPrefix) | ||
processCgroupPaths = cgroup.ProcessCgroupPaths | ||
|
||
// wantContainerID = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// wantContainerID = false |
@@ -105,7 +106,7 @@ func newProcessMetadataProcessorWithProvider(cfg *common.Config, provider proces | |||
p := addProcessMetadata{ | |||
config: config, | |||
provider: provider, | |||
cidProvider: cidProvider, | |||
cidProvider: newCidProvider(config.HostPath, config.CgroupPrefixes, processCgroupPaths), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recommend leaving this as nil when container.id
is not in the mappings
. Then adjust the enrich function accordingly. This will save it from doing the work when it's disabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion!
Instead of making the cidProvider
nil when it is disabled, I passed a dummy function to the cidProvider
which also avoids the use of cgroup.ProcessCgroupPaths
to see the cgroup
of the process.
The reason for that is I felt that the use of nil is a code smell, as we could not extend nil in the future. Also, the extra steps introduced by the dummy function will probably be optimized away by the compiler.
container id field is not outputed. If `restricted_fields` is `true`, `cid` | ||
container id can be enriched from cgroup, when running auditbeat on kubernetes. | ||
`host_path`:: (Optional) By default, the `host_path` field is set to the root | ||
directory of the host `/`. HostPath is the path where /proc reside. For different |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
directory of the host `/`. HostPath is the path where /proc reside. For different | |
directory of the host `/`. This is the path where `/proc` is mounted. For different |
container id can be enriched from cgroup, when running auditbeat on kubernetes. | ||
`host_path`:: (Optional) By default, the `host_path` field is set to the root | ||
directory of the host `/`. HostPath is the path where /proc reside. For different | ||
runtime configuration of Kubernetes or docker, the host_path can be set to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
runtime configuration of Kubernetes or docker, the host_path can be set to | |
runtime configuration of Kubernetes or Docker the `host_path` can be set to |
`host_path`:: (Optional) By default, the `host_path` field is set to the root | ||
directory of the host `/`. HostPath is the path where /proc reside. For different | ||
runtime configuration of Kubernetes or docker, the host_path can be set to | ||
overwrite the default `host_path` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overwrite the default `host_path` | |
overwrite the default. |
overwrite the default `host_path` | ||
|
||
`cgroup_prefixes`:: (Optional) By default, the `cgroup_prefixes` field is set to | ||
`/kubepods` and `/docker`. CgroupPrefix is the prefix where the container id is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
`/kubepods` and `/docker`. CgroupPrefix is the prefix where the container id is | |
`/kubepods` and `/docker`. This is the prefix where the container ID is |
|
||
`cgroup_prefixes`:: (Optional) By default, the `cgroup_prefixes` field is set to | ||
`/kubepods` and `/docker`. CgroupPrefix is the prefix where the container id is | ||
inside cgroup. or different runtime configuration of Kubernetes or docker, the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inside cgroup. or different runtime configuration of Kubernetes or docker, the | |
inside cgroup. For different runtime configuration of Kubernetes or Docker the |
`cgroup_prefixes`:: (Optional) By default, the `cgroup_prefixes` field is set to | ||
`/kubepods` and `/docker`. CgroupPrefix is the prefix where the container id is | ||
inside cgroup. or different runtime configuration of Kubernetes or docker, the | ||
cgroup_prefixes can be set to overwrite the default `cgroup_prefixes` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cgroup_prefixes can be set to overwrite the default `cgroup_prefixes` | |
`cgroup_prefixes` can be set to overwrite the defaults. |
Thanks for the suggestion! |
} | ||
if p.mappings, err = config.getMappings(); err != nil { | ||
mappings, err := config.getMappings() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if ok := containsValue(mappings, "container.id"); ok { | ||
cidProvider = newCidProvider(config.HostPath, config.CgroupPrefixes, processCgroupPaths) | ||
} else { | ||
dummyProcessCgroupPaths := func(_ string, pid int) (map[string]string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dummyProcessCgroupPaths := func(_ string, pid int) (map[string]string, error) { | |
dummyProcessCgroupPaths := func(_ string, _ int) (map[string]string, error) { |
dummyProcessCgroupPaths := func(_ string, pid int) (map[string]string, error) { | ||
return map[string]string{}, nil | ||
} | ||
cidProvider = newCidProvider(config.HostPath, []string{}, dummyProcessCgroupPaths) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cidProvider = newCidProvider(config.HostPath, []string{}, dummyProcessCgroupPaths) | |
cidProvider = newCidProvider(config.HostPath, nil, dummyProcessCgroupPaths) |
https://github.com/golang/go/wiki/CodeReviewComments#declaring-empty-slices
return result, nil | ||
} | ||
|
||
// add container.id into meta for mapping to pickup |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// add container.id into meta for mapping to pickup | |
// enrichContainerID adds container.id into meta for mapping to pickup. |
I like to follow godoc style even for unexported types/methods.
@@ -35,6 +35,9 @@ type config struct { | |||
// RestrictedFields make restricted fields available (i.e. env). | |||
RestrictedFields bool `config:"restricted_fields"` | |||
|
|||
// IncludeCid make cid field available | |||
IncludeCid bool `config:"include_cid"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IncludeCid bool `config:"include_cid"` |
|
||
func (p gosigarCidProvider) GetCid(pid int) (result string, err error) { | ||
cgroups, err := p.processCgroupPaths(p.hostPath, pid) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | ||
|
||
func newCidProvider(hostPath string, cgroupPrefixes []string, processCgroupPaths func(string, int) (map[string]string, error)) gosigarCidProvider { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// don't use cgroup.ProcessCgroupPaths to save it from doing the work when container id disabled | ||
if ok := containsValue(mappings, "container.id"); ok { | ||
cidProvider = newCidProvider(config.HostPath, config.CgroupPrefixes, processCgroupPaths) | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason for that is I felt that the use of nil is a code smell, as we could not extend nil in the future. Also, the extra steps introduced by the dummy function will probably be optimized away by the compiler.
I still think that leaving this cidProvider
as nil is simpler when the feature is disabled. It doesn't leave anything to chance with compiler optimizations.
I'm not sure what you meant by "as we could not extend nil in the future".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a small style different, I changed it to use nil.
Trying to explain "as we could not extend nil in the future".
- Put the
cidProvider
to nil, in the case ofcontainer.id
is disabled, Then there has to be a nil check in theenrich
function. If there is another case, there has to be another type of check for whatcidProvider
. - Whereas, by changing the behavior of the
cidProvider
so that whencontainer.id
is disabled it will not add container id. If there is another case, just pass an instance ofcidProvider
which behaved differently, then there is not any change required in theenrich
. - Furthermore, say if there is any change required in the runtime. It would be possible to pass a different instance of
cidProvider
to the processor determined by some conditions.
It is not specifically required in this PR, but just thought it could leave more flexibility
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not just overwrite/expand existing provider instead of creating another one? Some functionalities might still go for regular provider and e.g. try to read /proc/$some_pid/exe
instead $host_path/proc/$some_pid/exe
return result, nil | ||
} | ||
|
||
// add container.id into meta for mapping to pickup | ||
func (p *addProcessMetadata) enrichContainerID(pid int, meta common.MapStr) (common.MapStr, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this needs to pass back the meta
reference. Just have it return the error
.
jenkins, test this please |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Just two minor nits to cleanup.
libbeat/processors/add_process_metadata/add_process_metadata.go
Outdated
Show resolved
Hide resolved
libbeat/processors/add_process_metadata/add_process_metadata.go
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Waiting for CI
jenkins test this please |
Thank you so much for your contribution!! |
* enrich container id from process id (cherry picked from commit 3cb957d)
* enrich container id from process id (cherry picked from commit 3cb957d)
* enrich container id from process id (cherry picked from commit 3aaf027) Co-authored-by: Fang He <fangbrookhe@gmail.com>
Type of change
What does this PR do?
This PR enables add process metadata to enrich container id in Kubernetes environment by cgroup using process id.
Why is it important?
Include important metadata of the container id in the Kubernetes environment, which beats don't have this ability yet.
Checklist
Author's Checklist
How to test this PR locally
Deploy Auditbeat in minikube with the include_cid set to true. Deploy an sshd server in minikube. After the Auditbeat is running, ssh into the sshd server, which will trigger an Auditbeat event. See if the event coming from the sshd container is enriched with container id
cid
Related issues
Closes #14967
Use cases
Screenshots
Logs