From 5a703ddbb8afdae6dabe6cc25401974efeb8ab52 Mon Sep 17 00:00:00 2001 From: Michael Ducy Date: Thu, 29 Aug 2019 23:03:25 -0400 Subject: [PATCH] updates based on code review rnd 1 Signed-off-by: Michael Ducy --- cmd/httploader/main.go | 60 +++++++ docker/README.md | 30 ++++ docker/dev/Dockerfile | 2 +- docker/event-generator/Dockerfile | 1 + docker/kernel/httploader/Dockerfile | 13 +- .../httploader/falcoloader/loader.go | 148 ------------------ docker/kernel/httploader/httploader/main.go | 50 ------ docker/minimal/Dockerfile | 2 + docker/slim-dev/Dockerfile | 2 +- docker/slim-stable/Dockerfile | 2 +- docker/stable/Dockerfile | 2 +- go.mod | 12 ++ go.sum | 20 +++ .../falco-daemonset-configmap-slim.yaml | 6 +- pkg/falcoloader/loader.go | 142 +++++++++++++++++ 15 files changed, 285 insertions(+), 207 deletions(-) create mode 100644 cmd/httploader/main.go create mode 100644 docker/README.md delete mode 100644 docker/kernel/httploader/httploader/falcoloader/loader.go delete mode 100644 docker/kernel/httploader/httploader/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 pkg/falcoloader/loader.go diff --git a/cmd/httploader/main.go b/cmd/httploader/main.go new file mode 100644 index 00000000..79bde9d9 --- /dev/null +++ b/cmd/httploader/main.go @@ -0,0 +1,60 @@ +package main + +import ( + "os" + "log" + "fmt" + "github.com/falcosecurity/falco/pkg/falcoloader" +) + +// Default behavior: calculate kernel module and download from Falco hosted probe library +// ENV FALCO_PROBE_URL = URL to download probe.ko file +// ENV FALCO_PROBE_REPO = URL to download probe.ko, probe name derived from `uname -r` + +func main() { + falcoVersion := getEnv("FALCO_VERSION","0.17.0") + falcoProbePath := getEnv("FALCO_PROBE_PATH","/") + falcoProbeFile := getEnv("FALCO_PROBE_FILE","falco-probe.ko") + falcoProbeFullpath := falcoProbePath + falcoProbeFile + falcoProbeURL := getEnv("FALCO_PROBE_URL","") + falcoProbeRepo := getEnv("FALCO_PROBE_REPO","https://s3.amazonaws.com/download.draios.com/stable/sysdig-probe-binaries/") + falcoConfigHash, err := falcoloader.GetKernelConfigHash() + if err != nil { + log.Fatalf("Error getting Kernel Config Hash: %s", err) + } + falcoKernelRelease, err := falcoloader.GetKernelRelease() + if err != nil { + log.Fatalf("Error getting Kernel Version: %s", err) + } + log.Printf("FALCO_VERSION: %s", falcoVersion) + log.Printf("FALCO_PROBE_URL: %s", falcoProbeURL) + log.Printf("FALCO_PROBE_REPO: %s", falcoProbeRepo) + log.Printf("KERNEL_VERSION: %s", falcoKernelRelease) + log.Printf("KERNEL_CONFIG_HASH: %s", falcoConfigHash) + + // if FALCO_PROBE_URL not set, build it + if falcoProbeURL == "" { + falcoProbeURL = fmt.Sprintf("%sfalco-probe-%s-x86_64-%s-%s.ko", falcoProbeRepo, falcoVersion, falcoKernelRelease, falcoConfigHash) + } + + // fetch module + err = falcoloader.FetchModule(falcoProbeURL, falcoProbeFullpath) + if err != nil { + log.Fatalf("Error fetching module: %s", err) + } + + // load module + // Need to implement removal of module, retry loop, and timeout + err = falcoloader.LoadModule(falcoProbeFullpath) + if err != nil { + log.Fatalf("Error loading module: %s", err) + } +} + +func getEnv(key, def string) string { + value, ok := os.LookupEnv(key) + if ok { + return value + } + return def +} diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 00000000..726b2642 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,30 @@ +# Falco Dockerfiles + +This directory contains the various ways to package Falco as a container. + +## Currently Supported Containers + +### `falcosecurity/falco` Dockerfiles + - `./dev`: Builds a container image from the `dev` apt repo. + - `./stable`: Builds a container image from the `stable` apt repo. + - `./local`: Builds a container image from a locally provided Falco `dpkg` package. + +### Build & Testing Dockerfiles + - `./builder`: `falcosecurity/falco-builder` - The complete build tool chain for compiling Falco from source. See [the documentation](https://falco.org/docs/source/) for more details on building from source. + - `./tester`: `falcosecurity/falco-tester` - Container image for running the Falco test suite. + +## Alpha Release Containers + +These Dockerfiles (and resulting container images) are currently in `alpha`. We'd love for you to test these images and [report any feedback](https://github.com/falcosecurity/falco/issues/new/choose). + +### Slim and Minimal Dockerfiles +The goal of these container images is to reduce the size of the underlying Falco container. + - `./slim-dev`: Like `./dev` above but removes build tools for older kernels. + - `./slim-stable`: Like `./stable` above but removes build tools for older kernels. + - `./minimal`: A minimal container image (~20mb), containing only the files required to run Falco. + +### Init Containers +These container images allow for the delivery of the kernel module or eBPF probe either via HTTP or via a container image. + - `kernel/linuxkit`: Multistage Dockerfile to build a Falco kernel module for Linuxkit (Docker Desktop). Generates an alpine based container image with the kernel module, and `insmod` as the container `CMD`. + - `kernel/httploader`: Multistage Dockerfile to build a Go based application to download (via HTTPS) and load a Falco kernel module. The resulting container image can be ran as an `initContainer` to load the Falco module before Falco starts. + diff --git a/docker/dev/Dockerfile b/docker/dev/Dockerfile index f96a167d..3b73aade 100644 --- a/docker/dev/Dockerfile +++ b/docker/dev/Dockerfile @@ -1,6 +1,6 @@ FROM debian:unstable -LABEL maintainer="Sysdig " +LABEL maintainer="opensource@sysdig.com" ENV FALCO_REPOSITORY dev diff --git a/docker/event-generator/Dockerfile b/docker/event-generator/Dockerfile index 84539a95..d543d846 100644 --- a/docker/event-generator/Dockerfile +++ b/docker/event-generator/Dockerfile @@ -1,4 +1,5 @@ FROM alpine:latest +LABEL maintainer="opensource@sysdig.com" RUN apk add --no-cache bash g++ COPY ./event_generator.cpp /usr/local/bin RUN mkdir -p /var/lib/rpm diff --git a/docker/kernel/httploader/Dockerfile b/docker/kernel/httploader/Dockerfile index 1bad4dcc..9b1cd51e 100644 --- a/docker/kernel/httploader/Dockerfile +++ b/docker/kernel/httploader/Dockerfile @@ -2,10 +2,15 @@ FROM golang:alpine AS build RUN apk --no-cache add build-base git bzr mercurial gcc ca-certificates -ADD ./httploader /src -RUN cd /src && go get golang.org/x/sys/unix && CGO_ENABLED=0 GOOS=linux go build -a -o httploader -ldflags '-extldflags "-static"' . +ADD ./cmd /src/cmd +ADD ./pkg /src/pkg +ADD ./go.mod /src/go.mod +ADD ./go.sum /src/ + +RUN cd /src && CGO_ENABLED=0 GOOS=linux go build -a -o httploader -ldflags '-extldflags "-static"' cmd/httploader/main.go FROM scratch +LABEL maintainer="opensource@sysdig.com" COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt -COPY --from=build /src/httploader / -ENTRYPOINT /httploader \ No newline at end of file +COPY --from=build /src/httploader /httploader +CMD [ "/httploader" ] diff --git a/docker/kernel/httploader/httploader/falcoloader/loader.go b/docker/kernel/httploader/httploader/falcoloader/loader.go deleted file mode 100644 index ece51ee3..00000000 --- a/docker/kernel/httploader/httploader/falcoloader/loader.go +++ /dev/null @@ -1,148 +0,0 @@ -package falcoloader - -import "io/ioutil" -import "io" -import "os" -import "crypto/md5" -import "encoding/hex" -import "strings" -import "net/http" -import "golang.org/x/sys/unix" -import "log" -import "unsafe" -import "compress/gzip" -import "bytes" - -func GetKernelVersion() string { - path := "/proc/version" - - file, err := ioutil.ReadFile(path) - if err != nil { - return "invaildVersion" - } - version_string := string(file) - - version_fields := strings.Split(version_string, " ") - - return version_fields[2] -} - -func GetKernelConfigHash() string { - var hash string - kernelConfigPath := getKernelConfigPath() - hash, _ = genKernelConfigHash(kernelConfigPath) - - return hash -} - -func getKernelConfigPath() string { - kernelConfigPath := "" - - version := GetKernelVersion() - paths := []string{ - "/proc/config.gz", - "/boot/config-" + version, - "/host/boot/config-" + version, - "/usr/lib/ostree-boot/config-" + version, - "/usr/lib/ostree-boot/config-" + version, - "/lib/modules/" + version + "/config" } - - for i := range paths { - _, err := os.Stat(paths[i]) - if err != nil { - continue; - } - log.Print("Found kernel config: " + paths[i]) - return paths[i] - } - log.Fatal("No kernel config found") - return kernelConfigPath -} - -func genKernelConfigHash(path string) (string, error) { - var md5hash string - var err error - var buf bytes.Buffer - - if strings.HasSuffix(path, "gz") { - log.Print("Kernel config " + path + " is gz compressed") - tmpfile, err := os.Open(path) - if err != nil { - return md5hash, err - } - defer tmpfile.Close() - - file, err := gzip.NewReader(tmpfile) - if err != nil { - return md5hash, err - } - defer file.Close() - io.Copy(&buf, file) - } else { - file, err := os.Open(path) - if err != nil { - return md5hash, err - } - defer file.Close() - io.Copy(&buf,file) - } - - hash := md5.New() - if _, err := io.Copy(hash, &buf); err != nil { - return md5hash, err - } - md5hash = hex.EncodeToString(hash.Sum(nil)) - log.Print("Hash calculated: " + md5hash) - - return md5hash, err - -} - -func FetchModule(url string, path string) error { - log.Printf("Downloading kernel module from %s", url) - resp, err := http.Get(url) - if err != nil { - return err - } - defer resp.Body.Close() - log.Printf("Recevied HTTP Status Code: %d", resp.StatusCode) - if resp.StatusCode == 200 { - out, err := os.Create(path) - if err != nil { - log.Fatalf("Error creating file: %s", path) - return err - } - defer out.Close() - - _, err = io.Copy(out, resp.Body) - if err != nil { - log.Fatalf("Unable to write file: %s", path) - return err - } - log.Printf("Wrote kernel module: %s", path) - } else { - log.Fatal("Non-200 Status code received.") - } - return err - -} - -func LoadModule(path string) error { - - file, err := os.Open(path) - if err != nil { - log.Fatalf("Error opening kernel module: %s", path) - return err - } - - log.Print("Opened probe: " + path) - - _p0, err := unix.BytePtrFromString("") - - if _, _, err := unix.Syscall(313, file.Fd(), uintptr(unsafe.Pointer(_p0)), 0); err != 0 { - log.Fatalf("Error loading kernel module: %s. The module may already be loaded.", path) - return err - } - - return err -} diff --git a/docker/kernel/httploader/httploader/main.go b/docker/kernel/httploader/httploader/main.go deleted file mode 100644 index 8094bc34..00000000 --- a/docker/kernel/httploader/httploader/main.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import "os" -import "log" -import "fmt" -import "./falcoloader" - -// Default behavior: calculate kernel module and download from Falco hosted probe library -// ENV FALCO_PROBE_URL = URL to download probe.ko file -// ENV FALCO_PROBE_REPO = URL to download probe.ko, probe name derived from `uname -r` - -func main() { - falco_version := getEnv("FALCO_VERSION","0.17.0") - falco_probe_path := getEnv("FALCO_PROBE_PATH","/") - falco_probe_file := getEnv("FALCO_PROBE_FILE","falco-probe.ko") - falco_probe_fullpath := falco_probe_path + falco_probe_file - falco_probe_url := getEnv("FALCO_PROBE_URL","") - falco_probe_repo := getEnv("FALCO_PROBE_REPO","https://s3.amazonaws.com/download.draios.com/stable/sysdig-probe-binaries/") - falco_config_hash := falcoloader.GetKernelConfigHash() - falco_kernel_version := falcoloader.GetKernelVersion() - - log.Printf("FALCO_VERSION: %s", falco_version) - log.Printf("FALCO_PROBE_URL: %s", falco_probe_url) - log.Printf("FALCO_PROBE_REPO: %s", falco_probe_repo) - log.Printf("KERNEL_VERSION: %s", falco_kernel_version) - log.Printf("KERNEL_CONFIG_HASH: %s", falco_config_hash) - - // if FALCO_PROBE_URL not set, build it - if falco_probe_url == "" { - falco_probe_url = fmt.Sprintf("%sfalco-probe-%s-x86_64-%s-%s.ko", falco_probe_repo, falco_version, falco_kernel_version, falco_config_hash) - } - - // fetch module - if err := falcoloader.FetchModule(falco_probe_url, falco_probe_fullpath); err != nil { - panic(err) - } - - // load module - if err := falcoloader.LoadModule(falco_probe_fullpath); err != nil { - panic(err) - } -} - -func getEnv(key, def string) string { - value, isSet := os.LookupEnv(key) - if (isSet) { - return value - } - return def -} diff --git a/docker/minimal/Dockerfile b/docker/minimal/Dockerfile index 7d9ee578..5d509656 100644 --- a/docker/minimal/Dockerfile +++ b/docker/minimal/Dockerfile @@ -1,5 +1,7 @@ FROM ubuntu:18.04 as ubuntu +LABEL maintainer="opensource@sysdig.com" + ARG FALCO_VERSION=0.17.0 ENV FALCO_VERSION=${FALCO_VERSION} diff --git a/docker/slim-dev/Dockerfile b/docker/slim-dev/Dockerfile index 3ca9928b..90aeb009 100644 --- a/docker/slim-dev/Dockerfile +++ b/docker/slim-dev/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:18.04 -LABEL maintainer="Sysdig " +LABEL maintainer="opensource@sysdig.com" ENV FALCO_REPOSITORY dev diff --git a/docker/slim-stable/Dockerfile b/docker/slim-stable/Dockerfile index 979a060a..e2c0a733 100644 --- a/docker/slim-stable/Dockerfile +++ b/docker/slim-stable/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:18.04 -LABEL maintainer="Sysdig " +LABEL maintainer="opensource@sysdig.com" ENV FALCO_REPOSITORY stable diff --git a/docker/stable/Dockerfile b/docker/stable/Dockerfile index 1cc5fbfc..620ba92d 100644 --- a/docker/stable/Dockerfile +++ b/docker/stable/Dockerfile @@ -1,6 +1,6 @@ FROM debian:unstable -LABEL maintainer="Sysdig " +LABEL maintainer="opensource@sysdig.com" ENV FALCO_REPOSITORY stable diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..f1631973 --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module github.com/falcosecurity/falco + +go 1.12 + +require ( + golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 // indirect + golang.org/x/lint v0.0.0-20190409202823-959b441ac422 // indirect + golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 // indirect + golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 + golang.org/x/text v0.3.2 // indirect + golang.org/x/tools v0.0.0-20190827205025-b29f5f60c37a // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..3e97c5ad --- /dev/null +++ b/go.sum @@ -0,0 +1,20 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422 h1:QzoH/1pFpZguR8NrRHLcO6jKqfv2zpuSqZLgdm7ZmjI= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190827205025-b29f5f60c37a h1:0JEq5ZQ3TgsRlFmz4BcD+E6U6cOk4pOImCQSyIG59ZM= +golang.org/x/tools v0.0.0-20190827205025-b29f5f60c37a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/integrations/k8s-using-daemonset/k8s-with-rbac/falco-daemonset-configmap-slim.yaml b/integrations/k8s-using-daemonset/k8s-with-rbac/falco-daemonset-configmap-slim.yaml index 1671b67e..bc129c5c 100644 --- a/integrations/k8s-using-daemonset/k8s-with-rbac/falco-daemonset-configmap-slim.yaml +++ b/integrations/k8s-using-daemonset/k8s-with-rbac/falco-daemonset-configmap-slim.yaml @@ -25,7 +25,11 @@ spec: # - name: FALCO_PROBE_URL # value: # - name: FALCO_PROBE_REPO - # value: "https://s3.amazonaws.com/download.draios.com/stable/sysdig-probe-binaries/" + # value: "https://s3.amazonaws.com/download.draios.com/stable/sysdig-probe-binaries/" + volumeMounts: + - mountPath: /host/boot + name: boot-fs + readOnly: true containers: - name: falco image: falcosecurity/falco-minimal:latest diff --git a/pkg/falcoloader/loader.go b/pkg/falcoloader/loader.go new file mode 100644 index 00000000..824facd6 --- /dev/null +++ b/pkg/falcoloader/loader.go @@ -0,0 +1,142 @@ +package falcoloader + +import ( + "io" + "os" + "crypto/md5" + "encoding/hex" + "net/http" + "golang.org/x/sys/unix" + "log" + "unsafe" + "compress/gzip" + "bytes" +) + +func GetKernelRelease() (release string, err error) { + name := &unix.Utsname{} + err = unix.Uname(name); + if err != nil { + return release, err + } + release = string(name.Release[:bytes.IndexByte(name.Release[:], 0)]) + return release, err +} + +func GetKernelConfigHash() (string, error){ + var hash string + kernelConfigPath, err := getKernelConfigPath() + if err != nil { + return hash, err + } + hash, err = genKernelConfigHash(kernelConfigPath) + if err != nil { + return hash, err + } + return hash, err +} + +func getKernelConfigPath() (string, error) { + var err error + kernelConfigPath := "" + + version, _ := GetKernelRelease() + paths := []string{ + "/proc/config.gz", + "/boot/config-" + version, + "/host/boot/config-" + version, + "/usr/lib/ostree-boot/config-" + version, + "/usr/lib/ostree-boot/config-" + version, + "/lib/modules/" + version + "/config" } + + for _, path := range paths { + _, err := os.Stat(path) + if err != nil { + continue; + } + return path, err + } + return kernelConfigPath, err +} + +func genKernelConfigHash(path string) (string, error) { + var md5hash string + var err error + + file, err := os.Open(path) + if err != nil { + return md5hash, err + } + defer file.Close() + + fileBuf := bytes.NewBuffer(nil) + io.Copy(fileBuf, file) + + filetype := http.DetectContentType(fileBuf.Bytes()) + + if filetype == "application/x-gzip" { + gzipFile, err := gzip.NewReader(fileBuf) + if err != nil { + return md5hash, err + } + defer gzipFile.Close() + fileBuf = bytes.NewBuffer(nil) + io.Copy(fileBuf, gzipFile) + } + + hash := md5.New() + if _, err := io.Copy(hash, fileBuf); err != nil { + return md5hash, err + } + + return hex.EncodeToString(hash.Sum(nil)), nil +} + +func FetchModule(url string, path string) error { + log.Printf("Downloading kernel module from %s", url) + resp, err := http.Get(url) + if err != nil { + return err + } + defer resp.Body.Close() + log.Printf("Recevied HTTP Status Code: %d", resp.StatusCode) + if resp.StatusCode == http.StatusOK { + out, err := os.Create(path) + if err != nil { + log.Printf("Error creating file: %s", path) + return err + } + defer out.Close() + + _, err = io.Copy(out, resp.Body) + if err != nil { + log.Printf("Unable to write file: %s", path) + return err + } + log.Printf("Wrote kernel module: %s", path) + } else { + log.Printf("Non-200 Status code received %d", resp.StatusCode) + } + return err + +} + +func LoadModule(path string) error { + + file, err := os.Open(path) + if err != nil { + log.Printf("Error opening kernel module: %s", path) + return err + } + + log.Printf("Opened probe: ", path) + + p0, err := unix.BytePtrFromString("") + + if _, _, err := unix.Syscall(313, file.Fd(), uintptr(unsafe.Pointer(p0)), 0); err != 0 { + log.Printf("Error loading kernel module: %s. The module may already be loaded.", path) + return err + } + + return err +}