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

[Heartbeat] ARM seccomp profile #31422

Merged
merged 9 commits into from
May 20, 2022
Merged

Conversation

emilioalvap
Copy link
Collaborator

@emilioalvap emilioalvap commented Apr 26, 2022

What does this PR do?

Closes #31285.

Added a custom arm64 seccomp deny-by-default profile to override libbeat's allow-by-default. Also refactored how hb loads platform-specific seccomp profiles, following the example set on seccomp module.

This profile has been compiled by stracing heartbeat execution to list system calls and manually comparing the discrepancies with amd64 profile. These are the system calls that are most likely re-mapped to arm64 supported ones:

chmod           --> fchmodat from strace profile
chown           --> fchownat from strace profile
dup2            --> dup/dup3 from strace profile
epoll_create    --> epoll_create1 from strace profile
epoll_wait      --> epoll_pwait from strace profile
getdents        --> getdents64 from strace profile
getpgrp         --> getpgid from strace profile
lstat           --> statfs/statx from strace profile
newfstatat      --> fstatat by comparing kernel # id
open            --> openat from strace profile
pipe            --> pipe2 from strace profile
poll            --> ppoll from strace profile
readlink        --> readlinkat from strace profile
rename          --> renameat from strace profile
stat            --> statfs/statx from strace profile
unlink          --> unlinkat from strace profile
access          --> faccessat from strace profile
creat           --> Probably openat from strace profile
inotify_init    --> inotify_init1 from strace profile
lchown          --> fchownat from strace profile
link            --> linkat from strace profile
mkdir           --> mkdirat from strace profile
arch_prctl      --> prctl from strace profile
select          --> Probably pselect6 from amd64 profile
symlink         --> symlinkat from strace profile

Supported system calls can be checked with this tool.

Strace profile has been obtained with the following heartbeat.yml config:

heartbeat.monitors:
- type: http
  schedule: '@every 5s'
  urls:
  - https://google.es
  - http://google.es

- type: icmp
  schedule: '@every 5s'
  hosts:
  - 8.8.8.8
- type: tcp
  id: my-host-services
  name: My Host Services
  hosts: ["google.es"]
  ports: [80]
  schedule: '@every 5s'
- type: browser
  id: elastic-website
  name: Elastic website
  schedule: "@every 1m"
  source:
    inline:
      script: |-
        step("load homepage", async () => {
          await page.goto('https://www.elastic.co');
        });

Why is it important?

Actual profile provided by libbeat is configured as allow-by-default, with one system call disabled. With a custom profile, we can change the default logic and pass only the required calls.

Checklist

  • My code follows the style guidelines of this project
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have made corresponding change to the default configuration files
  • I have added tests that prove my fix is effective or that my feature works
  • I have added an entry in CHANGELOG.next.asciidoc or CHANGELOG-developer.next.asciidoc.

Author's Checklist

  • [ ]

Screenshots

image
*ICMP check down due to hb not running as root.

How to test this PR locally

There's no easy way to dump the seccomp profile of a running process in a readable format, so we will make do with comparing the raw system call:

  1. Build heartbeat in the target system (arm64).
x-pack/heartbeat $  uname -a
Linux ip-172-31-3-177 5.15.0-1004-aws #6-Ubuntu SMP Thu Mar 31 09:49:20 UTC 2022 aarch64 aarch64 aarch64 GNU/Linux
x-pack/heartbeat $ mage build
>> build: Building heartbeat
  1. Dump strace execution profile filtering for seccomp():
ELASTIC_SYNTHETICS_CAPABLE=true strace -ff -o /tmp/test -v -s 10000 --trace=seccomp ./heartbeat --strict.perms=false run -e
  1. Check that HB logs seccomp profile successfully registered:
{"log.level":"info","@timestamp":"2022-05-10T13:00:43.912Z","log.logger":"seccomp","log.origin":{"file.name":"seccomp/seccomp.go","file.line":124},"message":"Syscall filter successfully installed","service.name":"heartbeat","ecs.version":"1.6.0"}
  1. Compare it to the filter previously applied:
$ grep seccomp /tmp/test.569* // New filter
/tmp/test.56904:seccomp(SECCOMP_SET_MODE_STRICT, 0x1, NULL) = -1 EINVAL (Invalid argument)
/tmp/test.56904:seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, {len=141, filter=[BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 0x4), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0xc00000b7, 0, 0x8a), 
BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 0), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0xca, 0x87, 0), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 
0xf2, 0x86, 0), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0xc8, 0x85, 0), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0xd6, 0x84, 0), 
BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x5a, 0x83, 0), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x5b, 0x82, 0), 
BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x31, 0x81, 0), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x71, 0x80, 0), 
BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0xdc, 0x7f, 0), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x1b3, 0x7e, 0), 
BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x39, 0x7d, 0), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0xcb, 0x7c, 0), 
BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x17, 0x7b, 0), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x18, 0x7a, 0), 
BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x14, 0x79, 0), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x15, 0x78, 0), 
...

$ grep seccomp /tmp/test.609* // Previous filter from (main)
/tmp/test.60971:seccomp(SECCOMP_SET_MODE_STRICT, 0x1, NULL) = -1 EINVAL (Invalid argument)
/tmp/test.60971:seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, {len=6, filter=
[BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 0x4), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0xc00000b7, 0, 0x3), 
BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 0), BPF_JUMP(BPF_JMP|BPF_K|BPF_JEQ, 0x119, 0, 0x1), BPF_STMT(BPF_RET|BPF_K, 
SECCOMP_RET_ERRNO|0x1), BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW)]}) = 0

@botelastic botelastic bot added the needs_team Indicates that the issue/PR needs a Team:* label label Apr 26, 2022
@emilioalvap emilioalvap changed the title Add base seccomp policy [Heartbeat] ARM seccomp profile Apr 26, 2022
@elasticmachine
Copy link
Collaborator

elasticmachine commented Apr 26, 2022

💚 Build Succeeded

the below badges are clickable and redirect to their specific view in the CI or DOCS
Pipeline View Test View Changes Artifacts preview preview

Expand to view the summary

Build stats

  • Start Time: 2022-05-16T16:41:48.288+0000

  • Duration: 48 min 56 sec

Test stats 🧪

Test Results
Failed 0
Passed 1565
Skipped 22
Total 1587

💚 Flaky test report

Tests succeeded.

🤖 GitHub comments

To re-run your PR in the CI, just comment with:

  • /test : Re-trigger the build.

  • /package : Generate the packages and run the E2E tests.

  • /beats-tester : Run the installation tests with beats-tester.

  • run elasticsearch-ci/docs : Re-trigger the docs validation. (use unformatted text in the comment!)

@emilioalvap emilioalvap added enhancement Team:obs-ds-hosted-services Label for the Observability Hosted Services team labels May 9, 2022
@botelastic botelastic bot removed the needs_team Indicates that the issue/PR needs a Team:* label label May 9, 2022
@emilioalvap emilioalvap added backport-skip Skip notification from the automated backport with mergify release-note:skip The PR should be ignored when processing the changelog labels May 10, 2022
@emilioalvap emilioalvap marked this pull request as ready for review May 10, 2022 14:16
@emilioalvap emilioalvap requested a review from a team as a code owner May 10, 2022 14:16
@elasticmachine
Copy link
Collaborator

Pinging @elastic/uptime (Team:Uptime)

@mergify
Copy link
Contributor

mergify bot commented May 16, 2022

This pull request is now in conflicts. Could you fix it? 🙏
To fixup this pull request, you can check out it locally. See documentation: https://help.github.com/articles/checking-out-pull-requests-locally/

git fetch upstream
git checkout -b hb-31285 upstream/hb-31285
git merge upstream/main
git push upstream hb-31285

Copy link
Contributor

@lucasfcosta lucasfcosta left a comment

Choose a reason for hiding this comment

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

LGTM! I've tested this on the Graviton instance to which you've given me access by:

  • Cloning the beats repo
  • Copying the HB file in this PR and pointing to an ES instance
  • Runing the provided strace command
  • Making sure all monitors run correctly
  • Checking the len of the seccomp filters applied (6 from main, 141 from this branch)
  • Ensuring the last instruction not BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW) but actually BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO|0x1)

@emilioalvap emilioalvap merged commit 39f084d into elastic:main May 20, 2022
@emilioalvap emilioalvap added backport-v8.3.0 Automated backport with mergify v8.3.0 and removed backport-v8.3.0 Automated backport with mergify labels May 24, 2022
chrisberkhout pushed a commit that referenced this pull request Jun 1, 2023
* Refactor seccomp policy logic to mimic seccomp module's

* Remove custom profile for arm32, add syscalls for arm64

* Add manually-mapped system calls inherited from amd64 profile
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport-skip Skip notification from the automated backport with mergify enhancement release-note:skip The PR should be ignored when processing the changelog Team:obs-ds-hosted-services Label for the Observability Hosted Services team v8.3.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Heartbeat] Adjust ARM Seccomp profile
3 participants