Skip to content

Commit

Permalink
[Heartbeat] Setuid to regular user / lower capabilities when possible (
Browse files Browse the repository at this point in the history
…#27878)

partial fix for #27648 , this PR:

Detects if the user is running as root then:
Checks to see if an environment variable BEAT_SETUID_AS (set in our Docker.tmpl) is present
Attempts to Setuid , Setgid and Setgroups to that user / groups
Invokes setcap to drop all privileges except NET_RAW+ep
This PR also fixes the broken syscall filtering in heartbeat, some non-syscall strings were breaking that.

With the changes here elastic-agent can still run as root, but the subprocesses can lower their privileges ASAP. This should also make it possible for heartbeat to safely run ICMP pings and synthetics. Synthetics must run as non-root, but ICMP requires NET_RAW. This lets us be consistent in our docs with the recommendation that elastic-agent run as root.

(cherry picked from commit a78a980)

# Conflicts:
#	NOTICE.txt
#	dev-tools/packaging/packages.yml
#	go.mod
  • Loading branch information
andrewvc authored and mergify-bot committed Oct 13, 2021
1 parent 0801ec9 commit 51a7648
Show file tree
Hide file tree
Showing 16 changed files with 1,195 additions and 102 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
- Fixed excessive memory usage introduced in 7.5 due to over-allocating memory for HTTP checks. {pull}15639[15639]
- Fixed scheduler shutdown issues which would in rare situations cause a panic due to semaphore misuse. {pull}16397[16397]
- Fixed TCP TLS checks to properly validate hostnames, this broke in 7.x and only worked for IP SANs. {pull}17549[17549]
- Fix broken seccomp filtering and improve security via `setcap` and `setuid` when running as root on linux in containers. {pull}27878[27878]

*Journalbeat*

Expand Down
812 changes: 812 additions & 0 deletions NOTICE.txt

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions dev-tools/notice/overrides.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@
{"name": "github.com/munnerz/goautoneg", "licenceType": "BSD-3-Clause"}
{"name": "github.com/pelletier/go-buffruneio", "licenceType": "MIT"}
{"name": "github.com/urso/magetools", "licenceType": "Apache-2.0"}
{"name": "kernel.org/pub/linux/libs/security/libcap/cap", "licenceType": "BSD-3-Clause", "note": "dual licensed as GPL-v2 and BSD"}
{"name": "kernel.org/pub/linux/libs/security/libcap/psx", "licenceType": "BSD-3-Clause", "note": "dual licensed as GPL-v2 and BSD"}
5 changes: 5 additions & 0 deletions dev-tools/packaging/packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,12 @@ shared:
dockerfile: 'Dockerfile.elastic-agent.tmpl'
docker_entrypoint: 'docker-entrypoint.elastic-agent.tmpl'
user: '{{ .BeatName }}'
<<<<<<< HEAD
linux_capabilities: ''
=======
linux_capabilities: 'cap_net_raw+eip'
image_name: ''
>>>>>>> a78a980da2 ([Heartbeat] Setuid to regular user / lower capabilities when possible (#27878))
files:
'elastic-agent.yml':
source: 'elastic-agent.docker.yml'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,25 @@ RUN mkdir -p {{ $beatHome }}/data {{ $beatHome }}/data/elastic-agent-{{ commit_s
rm {{ $beatBinary }} && \
ln -s {{ $beatHome }}/data/elastic-agent-{{ commit_short }}/elastic-agent {{ $beatBinary }} && \
chmod 0755 {{ $beatHome }}/data/elastic-agent-*/elastic-agent && \
{{- if .linux_capabilities }}
setcap {{ .linux_capabilities }} {{ $beatBinary }} && \
{{- end }}
{{- range $i, $modulesd := .ModulesDirs }}
chmod 0775 {{ $beatHome}}/{{ $modulesd }} && \
{{- end }}
true

{{- if .linux_capabilities }}
# Since the beat is stored at the other end of a symlink we must follow the symlink first
# For security reasons setcap does not support symlinks. This is smart in the general case
# but in our specific case since we're building a trusted image from trusted binaries this is
# fine. Thus, we use readlink to follow the link and setcap on the actual binary
RUN readlink -f {{ $beatBinary }} | xargs setcap {{ .linux_capabilities }}
{{- end }}

FROM {{ .from }}

# Contains the elastic agent image variant, an empty string for the standard variant
# or "complete" for the bigger one.
ENV ELASTIC_AGENT_IMAGE_VARIANT={{.Variant}}
ENV BEAT_SETUID_AS={{ .user }}

{{- if contains .from "ubi-minimal" }}
RUN for iter in {1..10}; do microdnf update -y && microdnf install -y shadow-utils jq && microdnf clean all && exit_code=0 && break || exit_code=$? && echo "microdnf error: retry $iter in 10s" && sleep 10; done; (exit $exit_code)
Expand Down
12 changes: 9 additions & 3 deletions dev-tools/packaging/templates/docker/Dockerfile.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,19 @@ RUN mkdir -p {{ $beatHome }}/data {{ $beatHome }}/logs && \
find {{ $beatHome }} -type d -exec chmod 0755 {} \; && \
find {{ $beatHome }} -type f -exec chmod 0644 {} \; && \
chmod 0755 {{ $beatBinary }} && \
{{- if .linux_capabilities }}
setcap {{ .linux_capabilities }} {{ $beatBinary }} && \
{{- end }}
{{- range $i, $modulesd := .ModulesDirs }}
chmod 0775 {{ $beatHome}}/{{ $modulesd }} && \
{{- end }}
chmod 0775 {{ $beatHome }}/data {{ $beatHome }}/logs

{{- if .linux_capabilities }}
# Since the beat is stored at the other end of a symlink we must follow the symlink first
# For security reasons setcap does not support symlinks. This is smart in the general case
# but in our specific case since we're building a trusted image from trusted binaries this is
# fine. Thus, we use readlink to follow the link and setcap on the actual binary
RUN readlink -f {{ $beatBinary }} | xargs setcap {{ .linux_capabilities }}
{{- end }}

FROM {{ .from }}

{{- if contains .from "ubi-minimal" }}
Expand Down Expand Up @@ -127,6 +132,7 @@ USER {{ .user }}
{{- if (and (eq .BeatName "heartbeat") (not (contains .from "ubi-minimal"))) }}
# Setup synthetics env vars
ENV ELASTIC_SYNTHETICS_CAPABLE=true
ENV BEAT_SETUID_AS={{ .user }}
ENV SUITES_DIR={{ $beatHome }}/suites
ENV NODE_VERSION=14.17.5
ENV PATH="$NODE_PATH/node/bin:$PATH"
Expand Down
98 changes: 98 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,106 @@ require (
k8s.io/api v0.21.1
k8s.io/apimachinery v0.21.1
k8s.io/client-go v0.21.1
kernel.org/pub/linux/libs/security/libcap/cap v1.2.57
)

<<<<<<< HEAD
=======
require (
code.cloudfoundry.org/gofileutils v0.0.0-20170111115228-4d0c80011a0f // indirect
github.com/Azure/azure-amqp-common-go/v3 v3.0.0 // indirect
github.com/Azure/azure-pipeline-go v0.2.1 // indirect
github.com/Azure/go-amqp v0.12.6 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.3.1 // indirect
github.com/Azure/go-autorest/autorest/to v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.2.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/Microsoft/hcsshim v0.8.7 // indirect
github.com/apache/thrift v0.13.1-0.20200603211036-eac4d0c79a5f // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/containerd/containerd v1.3.3 // indirect
github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41 // indirect
github.com/cyphar/filepath-securejoin v0.2.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgraph-io/ristretto v0.1.0 // indirect
github.com/dimchansky/utfbom v1.1.0 // indirect
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
github.com/eapache/queue v1.1.0 // indirect
github.com/evanphx/json-patch v4.9.0+incompatible // indirect
github.com/go-logr/logr v0.4.0 // indirect
github.com/gobuffalo/here v0.6.0 // indirect
github.com/godbus/dbus/v5 v5.0.3 // indirect
github.com/golang-jwt/jwt/v4 v4.0.0 // indirect
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/google/licenseclassifier v0.0.0-20200402202327-879cb1424de0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
github.com/googleapis/gnostic v0.4.1 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/hashicorp/cronexpr v1.1.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/hashicorp/go-version v1.0.0 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
github.com/jcmturner/gofork v1.0.0 // indirect
github.com/jcmturner/gokrb5/v8 v8.4.2 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
github.com/json-iterator/go v1.1.10 // indirect
github.com/jstemmer/go-junit-report v0.9.1 // indirect
github.com/karrick/godirwalk v1.15.6 // indirect
github.com/klauspost/compress v1.12.3 // indirect
github.com/markbates/pkger v0.17.0 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/iochan v1.0.0 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/opencontainers/runc v1.0.0-rc9 // indirect
github.com/pierrec/lz4 v2.6.0+incompatible // indirect
github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b // indirect
github.com/santhosh-tekuri/jsonschema v1.2.4 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
github.com/sirupsen/logrus v1.4.2 // indirect
github.com/stretchr/objx v0.2.0 // indirect
github.com/urso/diag v0.0.0-20200210123136-21b3cc8eb797 // indirect
github.com/urso/go-bin v0.0.0-20180220135811-781c575c9f0e // indirect
github.com/urso/magetools v0.0.0-20190919040553-290c89e0c230 // indirect
github.com/xdg/stringprep v1.0.3 // indirect
go.elastic.co/fastjson v1.1.0 // indirect
go.opencensus.io v0.23.0 // indirect
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee // indirect
golang.org/x/mod v0.4.2 // indirect
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
honnef.co/go/tools v0.0.1-2020.1.4 // indirect
k8s.io/klog/v2 v2.8.0 // indirect
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 // indirect
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 // indirect
kernel.org/pub/linux/libs/security/libcap/psx v1.2.57 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.1.0 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
)

>>>>>>> a78a980da2 ([Heartbeat] Setuid to regular user / lower capabilities when possible (#27878))
replace (
github.com/Microsoft/go-winio => github.com/bi-zone/go-winio v0.4.15
github.com/Shopify/sarama => github.com/elastic/sarama v1.19.1-0.20210823122811-11c3ef800752
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1264,6 +1264,10 @@ k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 h1:vEx13qjvaZ4yfObSSXW7BrMc/KQBBT/Jyee8XtLf4x0=
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE=
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
kernel.org/pub/linux/libs/security/libcap/cap v1.2.57 h1:2nmqI+aw7EQZuelYktkQHBE4jESD2tOR+lOJEnv/Apo=
kernel.org/pub/linux/libs/security/libcap/cap v1.2.57/go.mod h1:uI99C3r4SXvJeuqoEtx/eWt7UbmfqqZ80H8q+9t/A7I=
kernel.org/pub/linux/libs/security/libcap/psx v1.2.57 h1:NOFATXSf5z/cMR3HIwQ3Xrd3nwnWl5xThmNr5U/F0pI=
kernel.org/pub/linux/libs/security/libcap/psx v1.2.57/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
Expand Down
4 changes: 4 additions & 0 deletions heartbeat/beater/heartbeat.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package beater
import (
"errors"
"fmt"
"syscall"
"time"

"github.com/elastic/beats/v7/heartbeat/config"
Expand Down Expand Up @@ -81,6 +82,9 @@ func New(b *beat.Beat, rawConfig *common.Config) (beat.Beater, error) {
func (bt *Heartbeat) Run(b *beat.Beat) error {
logp.Info("heartbeat is running! Hit CTRL-C to stop it.")

groups, _ := syscall.Getgroups()
logp.Info("Effective user/group ids: %d/%d, with groups: %v", syscall.Geteuid(), syscall.Getegid(), groups)

stopStaticMonitors, err := bt.RunStaticMonitors(b)
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion heartbeat/scripts/mage/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func CustomizePackaging() {
pkgType := args.Types[0]
switch pkgType {
case devtools.Docker:
args.Spec.ExtraVar("linux_capabilities", "cap_net_raw=eip")
args.Spec.ExtraVar("linux_capabilities", "cap_net_raw+eip")
args.Spec.Files[monitorsDTarget] = monitorsD
case devtools.TarGz, devtools.Zip:
args.Spec.Files[monitorsDTarget] = monitorsD
Expand Down
Loading

0 comments on commit 51a7648

Please sign in to comment.