Skip to content

Commit

Permalink
add e2e tests for spoc
Browse files Browse the repository at this point in the history
  • Loading branch information
mhils committed Mar 1, 2024
1 parent a7481d8 commit 9c40cf8
Show file tree
Hide file tree
Showing 8 changed files with 379 additions and 0 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,21 @@ jobs:
$RUN "echo \"export E2E_TEST_FLAKY_TESTS_ONLY=true\" >> /vagrant/hack/ci/env-flatcar.sh"
$RUN hack/ci/e2e-flatcar-dev-container.sh
e2e-spoc:
runs-on: ubuntu-22.04
timeout-minutes: 90
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v3.5.2
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491
with:
go-version: ${{ env.GO_VERSION }}
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y libseccomp-dev libelf-dev libapparmor-dev
sudo hack/install-libbpf.sh
- run: make test-spoc-e2e

baseprofiles:
needs: image
runs-on: ubuntu-22.04
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ Vagrantfile
build.tar.gz
image.tar
result
/test/spoc/demobinary
/test/spoc/demobinary-child

4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,10 @@ test-flaky-e2e: ## Only run the flaky end-to-end tests
E2E_SKIP_FLAKY_TESTS=false \
$(GO) test -parallel 1 -timeout 20m -count=1 ./test/... -v -testify.m '^(TestSecurityProfilesOperator_Flaky)$$'

.PHONY: test-spoc-e2e
test-spoc-e2e: build/spoc
$(GO) test -v ./test/spoc/...

# Generate CRD manifests
manifests: $(BUILD_DIR)/kubernetes-split-yaml $(BUILD_DIR)/kustomize
./hack/sort-crds.sh "$(CONTROLLER_GEN_CMD) $(CRD_OPTIONS) paths='./api/spod/...' output:crd:stdout" "deploy/base-crds/crds/securityprofilesoperatordaemon.yaml"
Expand Down
31 changes: 31 additions & 0 deletions test/spoc/.snapshots/TestSpoc-record-AppArmor-files
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
kind: AppArmorProfile
metadata:
creationTimestamp: null
name: demobinary
spec:
abstract:
capability:
allowedCapabilities:
- dac_read_search
executable: {}
filesystem:
readOnlyPaths:
- ../../README.md
writeOnlyPaths:
- /dev/null
status: {}

apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
kind: AppArmorProfile
metadata:
creationTimestamp: null
name: demobinary
spec:
abstract:
executable: {}
filesystem:
readWritePaths:
- /dev/null
status: {}

48 changes: 48 additions & 0 deletions test/spoc/.snapshots/TestSpoc-record-AppArmor-sockets
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
kind: AppArmorProfile
metadata:
creationTimestamp: null
name: demobinary
spec:
abstract:
capability:
allowedCapabilities:
- net_admin
executable: {}
filesystem:
readOnlyPaths:
- /proc/@{pid}/sys/net/core/somaxconn
network:
allowedProtocols:
allowTcp: true
status: {}

apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
kind: AppArmorProfile
metadata:
creationTimestamp: null
name: demobinary
spec:
abstract:
executable: {}
network:
allowedProtocols:
allowTcp: true
allowUdp: true
status: {}

apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
kind: AppArmorProfile
metadata:
creationTimestamp: null
name: demobinary
spec:
abstract:
capability:
allowedCapabilities:
- net_raw
executable: {}
network:
allowRaw: true
status: {}

35 changes: 35 additions & 0 deletions test/spoc/.snapshots/TestSpoc-record-AppArmor-subprocess
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
kind: AppArmorProfile
metadata:
creationTimestamp: null
name: demobinary
spec:
abstract:
capability:
allowedCapabilities:
- dac_read_search
- dac_override
executable:
allowedExecutables:
- ./demobinary-child
status: {}

apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
kind: AppArmorProfile
metadata:
creationTimestamp: null
name: demobinary
spec:
abstract:
capability:
allowedCapabilities:
- dac_read_search
- dac_override
executable:
allowedExecutables:
- ./demobinary
filesystem:
readOnlyPaths:
- /dev/null
status: {}

142 changes: 142 additions & 0 deletions test/spoc/demobinary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

// #cgo LDFLAGS: -ldl
// #include <dlfcn.h>
import "C"

import (
"flag"
"fmt"
"log"
"net"
"os"
"os/exec"
"strings"
"syscall"
"time"
)

const LogPrefixEnvVar = "LOGPREFIX"

// Demo binary to exercise various capabilities that may be restricted by seccomp/apparmor.
func main() {
log.SetPrefix(fmt.Sprintf("%s[pid:%d] ", os.Getenv(LogPrefixEnvVar), os.Getpid()))
log.SetFlags(log.Lshortfile)
log.Println("⏩", os.Args)

fileWrite := flag.String("file-write", "", "write file (e.g. /dev/null)")
fileRead := flag.String("file-read", "", "read file (e.g. /dev/null)")
fileSymlink := flag.String("file-symlink", "", "Create symlink using the following syntax: OLD:NEW")
netTCP := flag.Bool("net-tcp", false, "spawn a tcp server")
netUDP := flag.Bool("net-udp", false, "spawn a udp server")
netIcmp := flag.Bool("net-icmp", false, "open an icmp socket, exercise NET_RAW capability.")
library := flag.String("load-library", "", "load a shared library")
sleep := flag.Int("sleep", 0, "sleep N seconds before exiting.")
crash := flag.Bool("crash", false, "crash instead of exiting.")

flag.Parse()

subprocess := flag.Args()

if *fileWrite != "" {
const fileMode = 0o666
err := os.WriteFile(*fileWrite, []byte{}, fileMode)
if err != nil {
log.Fatal("❌ Error creating file:", err)
}
log.Println("✅ File write successful:", *fileWrite)
// make file writable for other users so that sudo/non-sudo testing works.
err = os.Chmod(*fileWrite, fileMode)
if err != nil {
log.Fatal("❌ Error setting file permissions:", err)
}
}
if *fileSymlink != "" {
oldname, newname, found := strings.Cut(*fileSymlink, ":")
if !found {
log.Fatal("❌ Symlink syntax: OLD:NEW")
}
err := os.Symlink(oldname, newname)
if err != nil {
log.Fatal("❌ Error creating symlink:", err)
}
log.Println("✅ Symlink created:", newname, "->", oldname)
}
if *fileRead != "" {
_, err := os.ReadFile(*fileRead)
if err != nil {
log.Fatal("❌ Error reading file:", err)
}
log.Println("✅ File read successful:", *fileRead)
}
if *netTCP {
listener, err := net.Listen("tcp", ":0")
if err != nil {
log.Fatal("❌ Error starting TCP server:", err)
}
log.Println("✅ TCP server spawned:", listener.Addr())
defer listener.Close()
}
if *netUDP {
server, err := net.ListenPacket("udp", ":0")
if err != nil {
//nolint:gocritic // gocritic is terminally confused here.
log.Fatal("❌ Error starting UDP server:", err)
}
log.Println("✅ UDP server spawned:", server.LocalAddr())
defer server.Close()
}
if *netIcmp {
fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_ICMP)
if err != nil {
log.Fatal("❌ Error opening ICMP socket:", err)
}
log.Println("✅ ICMP socket opened: fd", fd)
defer syscall.Close(fd)
}
if len(subprocess) > 0 {
cmd := exec.Command(subprocess[0], subprocess[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = append(os.Environ(), "LOGPREFIX=\t"+os.Getenv(LogPrefixEnvVar))
err := cmd.Run()
if err != nil {
log.Fatal("❌ Error running subprocess:", err)
}
log.Println("✅ Subprocess ran successfully:", subprocess)
}
if *library != "" {
if handle := C.dlopen(C.CString(*library), C.RTLD_NOW); handle == nil {
log.Fatal("❌ Error loading library: ", C.GoString(C.dlerror()))
}
log.Println("✅ Library loaded successfully:", *library)
}
if *sleep > 0 {
log.Println("⏳ Sleeping for", *sleep, "seconds...")
time.Sleep(time.Duration(*sleep) * time.Second)
}
if *crash {
log.Println("🫡 Terminating with SIGKILL...")
err := syscall.Kill(syscall.Getpid(), syscall.SIGKILL)
if err != nil {
log.Fatal("❌ Error sending SIGKILL:", err)
}
}
log.Println("⭐️ Success.")
}
Loading

0 comments on commit 9c40cf8

Please sign in to comment.