diff --git a/.github/workflows/ci-coverage.yaml b/.github/workflows/ci-coverage.yaml new file mode 100644 index 0000000000..2cd14bd192 --- /dev/null +++ b/.github/workflows/ci-coverage.yaml @@ -0,0 +1,474 @@ +name: ci-coverage + +on: + push: + branches: [main] + paths: + - "KubeArmor/**" + - ".github/workflows/ci-coverage.yaml" + - "tests/**" + pull_request: + branches: [main] + paths: + - "KubeArmor/**" + - ".github/workflows/ci-coverage.yaml" + - "tests/**" + +jobs: + calculate-k8s-coverage: + name: Auto-testing Framework / ${{ matrix.os }} / ${{ matrix.runtime }} + runs-on: ${{ matrix.os }} + env: + RUNTIME: ${{ matrix.runtime }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04, bpflsm] + runtime: ["containerd", "crio"] + exclude: + - os: bpflsm + runtime: crio + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - uses: actions/setup-go@v5 + with: + go-version-file: 'KubeArmor/go.mod' + + - name: Check what paths were updated + uses: dorny/paths-filter@v2 + id: filter + with: + filters: | + controller: + - 'pkg/KubeArmorController/**' + + - name: Install the latest LLVM toolchain + run: ./.github/workflows/install-llvm.sh + + - name: Compile libbpf + run: ./.github/workflows/install-libbpf.sh + + - name: Setup a Kubernetes environment + run: ./.github/workflows/install-k3s.sh + + - name: Generate KubeArmor artifacts + run: | + #set the $IS_COVERAGE env var to 'true' to build the kubearmor-test image for coverage calculation + export IS_COVERAGE=true + GITHUB_SHA=$GITHUB_SHA ./KubeArmor/build/build_kubearmor.sh + + - name: Build Kubearmor-Operator + working-directory: pkg/KubeArmorOperator + run: | + make docker-build + + - name: Build KubeArmorController + if: steps.filter.outputs.controller == 'true' + run: make -C pkg/KubeArmorController/ docker-build TAG=latest + + - name: deploy pre existing pod + run: | + kubectl apply -f ./tests/k8s_env/ksp/pre-run-pod.yaml + sleep 60 + kubectl get pods -A + + - name: Run KubeArmor + run: | + if [ ${{ matrix.runtime }} == "containerd" ]; then + docker save kubearmor/kubearmor-test-init:latest | sudo k3s ctr images import - + docker save kubearmor/kubearmor-test:latest | sudo k3s ctr images import - + docker save kubearmor/kubearmor-operator:latest | sudo k3s ctr images import - + docker save kubearmor/kubearmor-snitch:latest | sudo k3s ctr images import - + + if [[ ${{ steps.filter.outputs.controller }} == 'true' ]]; then + docker save kubearmor/kubearmor-controller:latest | sudo k3s ctr images import - + fi + else + if [ ${{ matrix.runtime }} == "crio" ]; then + docker save kubearmor/kubearmor-test-init:latest | sudo podman load + sudo podman tag localhost/latest:latest docker.io/kubearmor/kubearmor-test-init:latest + docker save kubearmor/kubearmor-test:latest | sudo podman load + sudo podman tag localhost/latest:latest docker.io/kubearmor/kubearmor-test:latest + docker save kubearmor/kubearmor-operator:latest | sudo podman load + sudo podman tag localhost/latest:latest docker.io/kubearmor/kubearmor-operator:latest + docker save kubearmor/kubearmor-snitch:latest | sudo podman load + sudo podman tag localhost/latest:latest docker.io/kubearmor/kubearmor-snitch:latest + if [ ${{ steps.filter.outputs.controller }} == 'true' ]; then + docker save kubearmor/kubearmor-controller:latest | sudo podman load + sudo podman tag localhost/latest:latest docker.io/kubearmor/kubearmor-controller:latest + fi + fi + fi + docker system prune -a -f + docker buildx prune -a -f + helm upgrade --install kubearmor-operator ./deployments/helm/KubeArmorOperator -n kubearmor --create-namespace --set kubearmorOperator.image.tag=latest + kubectl wait --for=condition=ready --timeout=5m -n kubearmor pod -l kubearmor-app=kubearmor-operator + kubectl get pods -A + if [[ ${{ steps.filter.outputs.controller }} == 'true' ]]; then + kubectl apply -f pkg/KubeArmorOperator/config/samples/kubearmor-coverage.yaml --dry-run=client -o json | \ + jq '.spec.kubearmorControllerImage.imagePullPolicy = "Never"' | \ + kubectl apply -f - + else + kubectl apply -f pkg/KubeArmorOperator/config/samples/kubearmor-coverage.yaml + fi + # kubectl wait -n kubearmor --timeout=5m --for=jsonpath='{.status.phase}'=Running kubearmorconfigs/kubearmorconfig-test + # kubectl wait --timeout=7m --for=condition=ready pod -l kubearmor-app,kubearmor-app!=kubearmor-snitch,kubearmor-app!=kubearmor-controller -n kubearmor + # kubectl wait --timeout=1m --for=condition=ready pod -l kubearmor-app=kubearmor-controller -n kubearmor + sleep 60 + kubectl get pods -A + DAEMONSET_NAME=$(kubectl get daemonset -n kubearmor -o jsonpath='{.items[0].metadata.name}') + echo "DaemonSet: $DAEMONSET_NAME" + + kubectl patch daemonset $DAEMONSET_NAME -n kubearmor --type='json' -p='[ + { + "op": "add", + "path": "/spec/template/spec/volumes/-", + "value": { + "name": "coverage-storage", + "hostPath": { + "path": "/coverage", + "type": "DirectoryOrCreate" + } + } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/volumeMounts/-", + "value": { + "mountPath": "/coverage", + "name": "coverage-storage" + } + }, + { + "op": "add", + "path": "/spec/template/spec/containers/0/args/-", + "value": "-test.coverprofile=/coverage/coverage_k8s_${{ matrix.os }}_${{ matrix.runtime }}.out" + } + ]' + + sleep 15 + + - name: Add KubeArmor host visibility + run: | + ./.github/workflows/host-visibility.sh + DAEMONSET_NAME=$(kubectl get daemonset -n kubearmor -o jsonpath='{.items[0].metadata.name}') + # normal approach didn't worked for cri-o pod + LABEL_SELECTOR=$(kubectl get daemonset $DAEMONSET_NAME -n kubearmor -o jsonpath='{.spec.selector.matchLabels}' | jq -r 'to_entries[] | "\(.key)=\(.value)"' | paste -sd, -) + POD_NAME=$(kubectl get pods -n kubearmor -l "$LABEL_SELECTOR" -o jsonpath='{.items[*].metadata.name}') + echo "Pod: $POD_NAME" + echo "POD_NAME=$POD_NAME" >> $GITHUB_ENV + + - name: Test KubeArmor using Ginkgo + run: | + kubectl logs $POD_NAME -n kubearmor + go install -mod=mod github.com/onsi/ginkgo/v2/ginkgo + make + working-directory: ./tests/k8s_env + timeout-minutes: 30 + env: + POD_NAME: ${{ env.POD_NAME }} + + - name: Kill KubeArmor prcoess in the pod + run: | + KUBEARMOR_PID=$(kubectl exec ${{ env.POD_NAME }} -n kubearmor -c kubearmor -- sh -c "ps aux | grep '[K]ubeArmor/kubearmor-test' | awk '{print \$1}'") + kubectl exec ${{ env.POD_NAME }} -n kubearmor -c kubearmor -- sh -c "kill -s SIGINT $KUBEARMOR_PID" + sleep 10 + env: + POD_NAME: ${{ env.POD_NAME }} + + - name: Extract coverage file + run: | + for i in {1..24}; do + if [ -f /coverage/coverage_k8s_${{ matrix.os }}_${{ matrix.runtime }}.out ]; then + cp /coverage/coverage_k8s_${{ matrix.os }}_${{ matrix.runtime }}.out coverage_k8s_${{ matrix.os }}_${{ matrix.runtime }}.out + break + fi + sleep 5 + done + ls -l + working-directory: KubeArmor + env: + POD_NAME: ${{ env.POD_NAME }} + + - name: Get karmor sysdump + if: ${{ failure() }} + run: | + kubectl describe pod -n kubearmor -l kubearmor-app=kubearmor + curl -sfL http://get.kubearmor.io/ | sudo sh -s -- -b /usr/local/bin + mkdir -p /tmp/kubearmor/ && cd /tmp/kubearmor && karmor sysdump + + - name: Archive log artifacts + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: kubearmor.logs + path: | + /tmp/kubearmor/ + /tmp/kubearmor.* + + - name: Measure code coverage + if: ${{ always() }} + run: | + ls -l + go tool cover -func coverage_k8s_${{ matrix.os }}_${{ matrix.runtime }}.out + # go install github.com/modocache/gover@latest + # gover + working-directory: KubeArmor + env: + GOPATH: ${{ matrix.os == 'bpflsm' && '/home/vagrant/go' || '/home/runner/go' }} + + - name: Upload coverage file + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: coverage-k8s-${{ matrix.os }}-${{ matrix.runtime }} + path: KubeArmor/coverage_k8s_${{ matrix.os }}_${{ matrix.runtime }}.out + + - name: Run cleanup + if: ${{ always() && matrix.os == 'bpflsm' }} + run: ./.github/workflows/cleanup.sh + + + docker-compose-coverage: + name: Calculate coverage in docker mode / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, bpflsm] + timeout-minutes: 60 + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - uses: actions/setup-go@v5 + with: + go-version-file: 'KubeArmor/go.mod' + + - name: Install the latest LLVM toolchain + run: ./.github/workflows/install-llvm.sh + + - name: Compile libbpf + run: ./.github/workflows/install-libbpf.sh + + - name: Generate KubeArmor artifacts + run: | + # set the $IS_COVERAGE env var to 'true' to build the kubearmor-test image for coverage calculation + export IS_COVERAGE=true + GITHUB_SHA=$GITHUB_SHA ./KubeArmor/build/build_kubearmor.sh + + - name: Run KubeArmor init container + run: | + docker run --name kubearmor-test-init -v /tmp/:/opt/kubearmor/BPF kubearmor/kubearmor-test-init:latest + + - name: Run KubeArmor test container + run: | + sudo mkdir -p /coverage + docker run -d --name kubearmor-test --privileged --pid host -p 32767:32767 \ + -v /tmp/:/opt/kubearmor/BPF \ + -v /sys/fs/bpf:/sys/fs/bpf \ + -v /sys/kernel/security:/sys/kernel/security \ + -v /sys/kernel/debug:/sys/kernel/debug \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v /var/lib/docker:/var/lib/docker \ + -v /etc/apparmor.d:/etc/apparmor.d \ + -v /coverage/:/coverage/ \ + kubearmor/kubearmor-test:latest -k8s=false -enableKubeArmorHostPolicy -coverageTest=false -test.coverprofile=/coverage/coverage_docker.out + + - name: Test KubeArmor using Ginkgo + run: | + go install -mod=mod github.com/onsi/ginkgo/v2/ginkgo + make + working-directory: ./tests/nonk8s_env + timeout-minutes: 30 + + - name: Copy coverage report + run: | + CONTAINER_ID=$(docker ps -qf "name=kubearmor") + sleep 2 + KUBEARMOR_PID=$(docker exec $CONTAINER_ID sh -c "pgrep -o -f 'kubearmor'") + sleep 2 + docker exec $CONTAINER_ID sh -c "kill -s SIGINT $KUBEARMOR_PID" + sleep 15 + for i in {1..24}; do + if [ -f /coverage/coverage_docker.out ]; then + cp /coverage/coverage_docker.out coverage_docker_${{ matrix.os }}.out + break + fi + sleep 5 + done + sleep 2 + working-directory: KubeArmor + + - name: Archive log artifacts + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: kubearmor.logs + path: | + /tmp/kubearmor/ + /tmp/kubearmor.* + + - name: Measure code coverage + if: ${{ always() }} + run: | + ls -l + go tool cover -func coverage_docker_${{ matrix.os }}.out + working-directory: KubeArmor + env: + GOPATH: ${{ matrix.os == 'bpflsm' && '/home/vagrant/go' || '/home/runner/go' }} + + - name: Save coverage file + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: coverage-docker-${{ matrix.os }} + path: KubeArmor/coverage_docker_${{ matrix.os }}.out + + - name: Run cleanup + if: ${{ always() && matrix.os == 'bpflsm' }} + run: ./.github/workflows/cleanup.sh + + + calculate-systemd-coverage: + name: Calculate coverage in systemd mode / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, bpflsm] + timeout-minutes: 60 + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - uses: actions/setup-go@v5 + with: + go-version-file: 'KubeArmor/go.mod' + + - name: Install the latest LLVM toolchain + run: ./.github/workflows/install-llvm.sh + + - name: Compile libbpf + run: ./.github/workflows/install-libbpf.sh + + - name: Install GoReleaser + uses: goreleaser/goreleaser-action@v2 + with: + install-only: true + version: v1.25.0 + + - name: Install protoc + if: ${{ matrix.os == 'ubuntu-latest' }} + run: | + sudo apt-get update + sudo apt-get install -y protobuf-compiler + protoc --version + + - name: Install protoc-gen-go + if: ${{ matrix.os == 'bpflsm' }} + run: | + go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + env: + GOPATH: /home/vagrant/go + + - name: Build Systemd Release + run: make local-release + working-directory: KubeArmor + + - name: Install KubeArmor + run: sudo apt install -y ./dist/kubearmor*amd64.deb + working-directory: KubeArmor + + - name: Compile test binary + run: go test -covermode=atomic -coverpkg=./... -c . -o kubearmor-test + working-directory: KubeArmor + + - name: Replace with test binary + run: | + sudo mkdir -p /coverage + sudo rm /opt/kubearmor/kubearmor + sudo cp kubearmor-test /opt/kubearmor/ + ls -l /opt/kubearmor/ + sudo sed -i 's|ExecStart=/opt/kubearmor/kubearmor|ExecStart=/opt/kubearmor/kubearmor-test -test.coverprofile=/coverage/coverage_systemd.out|' /lib/systemd/system/kubearmor.service + sudo systemctl daemon-reload + sudo systemctl restart kubearmor.service + working-directory: KubeArmor + + - name: Check journalctl + run: sudo journalctl -u kubearmor --no-pager || true + + - name: Test kubearmor using ginkgo + run: | + go install -mod=mod github.com/onsi/ginkgo/v2/ginkgo + make + working-directory: ./tests/nonk8s_env + timeout-minutes: 30 + + - name: Kill kubearmor process and copy coverage file + run: | + sudo systemctl stop kubearmor + sleep 15 + for i in {1..24}; do + if [ -f /coverage/coverage_systemd.out ]; then + sudo cp /coverage/coverage_systemd.out coverage_systemd_${{ matrix.os }}.out + break + fi + sleep 5 + done + working-directory: KubeArmor + + - name: Measure code coverage + if: ${{ always() }} + run: | + ls -l + go tool cover -func coverage_systemd_${{ matrix.os }}.out + working-directory: KubeArmor + env: + GOPATH: ${{ matrix.os == 'bpflsm' && '/home/vagrant/go' || '/home/runner/go' }} + + - name: Save coverage file + if: ${{ always() }} + uses: actions/upload-artifact@v4 + with: + name: coverage-systemd-${{ matrix.os }} + path: KubeArmor/coverage_systemd_${{ matrix.os }}.out + + - name: Run cleanup + if: ${{ always() && matrix.os == 'bpflsm' }} + run: | + sudo systemctl disable kubearmor.service + sudo rm -rf /opt/kubearmor/ + sudo apt-get --purge remove -y kubearmor + sudo systemctl daemon-reload + + merge-and-upload-coverage: + name: Merge and Upload Coverage + runs-on: ubuntu-latest + needs: [calculate-k8s-coverage, docker-compose-coverage, calculate-systemd-coverage] + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - uses: actions/setup-go@v5 + with: + go-version-file: 'KubeArmor/go.mod' + + - name: Download all coverage files + uses: actions/download-artifact@v4 + with: + path: KubeArmor/ + pattern: coverage* + merge-multiple: true + + - uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + diff --git a/.github/workflows/ci-test-docker.yaml b/.github/workflows/ci-test-docker.yaml new file mode 100644 index 0000000000..68b0173b7d --- /dev/null +++ b/.github/workflows/ci-test-docker.yaml @@ -0,0 +1,74 @@ +name: ci-test-docker + +on: + push: + branches: [main] + paths: + - "KubeArmor/**" + - ".github/workflows/ci-test-docker.yaml" + - "!STABLE-RELEASE" + - "tests/nonk8s_env/**" + +jobs: + build-and-run: + name: Build KubeArmor artifacts and run tests / ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + timeout-minutes: 60 + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - uses: actions/setup-go@v5 + with: + go-version-file: 'KubeArmor/go.mod' + + - name: Install the latest LLVM toolchain + run: ./.github/workflows/install-llvm.sh + + - name: Compile libbpf + run: ./.github/workflows/install-libbpf.sh + + - name: Generate KubeArmor artifacts + run: | + GITHUB_SHA=$GITHUB_SHA ./KubeArmor/build/build_kubearmor.sh + + # - name: Run KubeArmor init container + # run: | + # docker run --name kubearmor-init -v /tmp/:/opt/kubearmor/BPF kubearmor/kubearmor-init + + # - name: Run KubeArmor container + # run: | + # docker run -d --name kubearmor --privileged --pid host -p 32767:32767 \ + # -v /tmp/:/opt/kubearmor/BPF \ + # -v /sys/fs/bpf:/sys/fs/bpf \ + # -v /sys/kernel/security:/sys/kernel/security \ + # -v /sys/kernel/debug:/sys/kernel/debug \ + # -v /var/run/docker.sock:/var/run/docker.sock \ + # -v /var/lib/docker:/var/lib/docker \ + # -v /etc/apparmor.d:/etc/apparmor.d \ + # kubearmor/kubearmor -k8s=false -enableKubeArmorHostPolicy + + - name: Run KubeArmor with docker-compose + run: | + docker-compose -f docker-compose.yaml up -d + + - name: Test KubeArmor using Ginkgo + run: | + go install -mod=mod github.com/onsi/ginkgo/v2/ginkgo + make + working-directory: ./tests/nonk8s_env + timeout-minutes: 30 + + - name: Archive log artifacts + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: kubearmor.logs + path: | + /tmp/kubearmor/ + /tmp/kubearmor.* diff --git a/.github/workflows/ci-test-ginkgo.yml b/.github/workflows/ci-test-ginkgo.yml index 553b5e68f7..f67e3b7ce1 100644 --- a/.github/workflows/ci-test-ginkgo.yml +++ b/.github/workflows/ci-test-ginkgo.yml @@ -125,7 +125,7 @@ jobs: kubectl wait --timeout=7m --for=condition=ready pod -l kubearmor-app,kubearmor-app!=kubearmor-snitch,kubearmor-app!=kubearmor-controller -n kubearmor kubectl wait --timeout=1m --for=condition=ready pod -l kubearmor-app=kubearmor-controller -n kubearmor kubectl get pods -A - + sleep 10 DAEMONSET_NAME=$(kubectl get daemonset -n kubearmor -o jsonpath='{.items[0].metadata.name}') echo "DaemonSet: $DAEMONSET_NAME" @@ -158,7 +158,10 @@ jobs: ]' sleep 15 - + + - name: Add KubeArmor host visibility + run: ./.github/workflows/host-visibility.sh + - name: Get KubeArmor POD info run: | DAEMONSET_NAME=$(kubectl get daemonset -n kubearmor -o jsonpath='{.items[0].metadata.name}') diff --git a/.github/workflows/cleanup.sh b/.github/workflows/cleanup.sh index 3c539903a7..b0df5523ea 100755 --- a/.github/workflows/cleanup.sh +++ b/.github/workflows/cleanup.sh @@ -6,9 +6,17 @@ cleanup() { echo "Performing cleanup..." - /usr/local/bin/k3s-killall.sh + if [ -f /usr/local/bin/k3s-killall.sh ]; then + /usr/local/bin/k3s-killall.sh + else + echo "/usr/local/bin/k3s-killall.sh not found. Skipping..." + fi - /usr/local/bin/k3s-uninstall.sh + if [ -f /usr/local/bin/k3s-uninstall.sh ]; then + /usr/local/bin/k3s-uninstall.sh + else + echo "/usr/local/bin/k3s-uninstall.sh not found. Skipping..." + fi docker system prune -a -f @@ -18,5 +26,6 @@ cleanup() { echo "Cleanup complete." } + # Invoke the cleanup function -cleanup \ No newline at end of file +cleanup diff --git a/.github/workflows/host-visibility.sh b/.github/workflows/host-visibility.sh new file mode 100755 index 0000000000..7de6b6d00f --- /dev/null +++ b/.github/workflows/host-visibility.sh @@ -0,0 +1,20 @@ +#!/bin/bash +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Authors of KubeArmor + +DAEMONSET_NAME=$(kubectl get daemonset -n kubearmor -o jsonpath='{.items[0].metadata.name}') + +kubectl patch daemonset $DAEMONSET_NAME -n kubearmor --type='json' -p='[ + { + "op": "add", + "path": "/spec/template/spec/containers/0/args/-", + "value": "-enableKubeArmorHostPolicy" + } + ]' + +sleep 16 + +# Apply annotations to the node +NODE_NAME=$(kubectl get nodes -o=jsonpath='{.items[0].metadata.name}') +kubectl annotate node $NODE_NAME "kubearmorvisibility=process,file,network,capabilities" +kubectl get no -o wide \ No newline at end of file diff --git a/.github/workflows/install-k3s.sh b/.github/workflows/install-k3s.sh index 81bef0811c..870c0e3b2c 100755 --- a/.github/workflows/install-k3s.sh +++ b/.github/workflows/install-k3s.sh @@ -1,6 +1,8 @@ #!/bin/bash # SPDX-License-Identifier: Apache-2.0 # Copyright 2021 Authors of KubeArmor +# Set the hostname +# sudo hostnamectl set-hostname kubearmor-dev echo "RUNTIME="$RUNTIME @@ -15,3 +17,5 @@ if [ "$RUNTIME" == "crio" ]; then fi ./contribution/k3s/install_k3s.sh + +kubectl get no -o wide \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000000..f11f970781 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,24 @@ +services: + kubearmor-init: + image: kubearmor/kubearmor-init:latest + volumes: + - /tmp/:/opt/kubearmor/BPF + + kubearmor: + image: kubearmor/kubearmor:latest + depends_on: + kubearmor-init: + condition: service_completed_successfully + privileged: true + command: ["-k8s=false", "-enableKubeArmorHostPolicy"] + pid: "host" + ports: + - "32767:32767" + volumes: + - /tmp/:/opt/kubearmor/BPF + - /sys/fs/bpf:/sys/fs/bpf + - /sys/kernel/security:/sys/kernel/security + - /sys/kernel/debug:/sys/kernel/debug + - /var/run/docker.sock:/var/run/docker.sock + - /var/lib/docker:/var/lib/docker + - /etc/apparmor.d:/etc/apparmor.d \ No newline at end of file diff --git a/tests/go.sum b/tests/go.sum index f70f29ae8f..d7aa5dd2ab 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -79,8 +79,6 @@ github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3Bum github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -101,8 +99,6 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240319011627-a57c5dfe54fd h1:LjW4RcTwfcqOYGmD7UpFrn1gfBZ9mgu7QN5mSeFkCog= -github.com/google/pprof v0.0.0-20240319011627-a57c5dfe54fd/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= @@ -251,7 +247,6 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= diff --git a/tests/k8s_env/hsp/hsp_suite_test.go b/tests/k8s_env/hsp/hsp_suite_test.go new file mode 100644 index 0000000000..d575392e34 --- /dev/null +++ b/tests/k8s_env/hsp/hsp_suite_test.go @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Authors of KubeArmor + +package hsp_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestHsp(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Hsp Suite") +} diff --git a/tests/k8s_env/hsp/hsp_test.go b/tests/k8s_env/hsp/hsp_test.go new file mode 100644 index 0000000000..c0992c8765 --- /dev/null +++ b/tests/k8s_env/hsp/hsp_test.go @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Authors of KubeArmor + +package hsp + +import ( + "time" + + . "github.com/kubearmor/KubeArmor/tests/util" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Non-k8s HSP tests", func() { + + AfterEach(func() { + KarmorLogStop() + err := DeleteAllHsp() + Expect(err).To(BeNil()) + // wait for policy deletion + // time.Sleep(2 * time.Second) + }) + + Describe("HSP file path block", func() { + + It("can block access to /etc/hostname on the host", func() { + + err := K8sApplyFile("res/hsp-kubearmor-dev-file-path-block.yaml") + Expect(err).To(BeNil()) + + // Start the karmor logs + err = KarmorLogStart("policy", "", "File", "") + Expect(err).To(BeNil()) + + // Access the /etc/hostname file + out, err := ExecCommandHost([]string{"bash", "-c", "cat /etc/hostname"}) + Expect(err).NotTo(BeNil()) + Expect(out).To(MatchRegexp(".*Permission denied")) + + // check policy violation alert + _, alerts, err := KarmorGetLogs(5*time.Second, 1) + Expect(err).To(BeNil()) + Expect(len(alerts)).To(BeNumerically(">=", 1)) + Expect(alerts[0].PolicyName).To(Equal("hsp-kubearmor-dev-file-path-block")) + Expect(alerts[0].Severity).To(Equal("5")) + Expect(alerts[0].Action).To(Equal("Block")) + + }) + }) + + Describe("HSP Process path block", func() { + + It("can block execution of diff command in host", func() { + + err := K8sApplyFile("res/hsp-kubearmor-dev-proc-path-block.yaml") + Expect(err).To(BeNil()) + + // Start the karmor logs + err = KarmorLogStart("policy", "", "Process", "") + Expect(err).To(BeNil()) + + // call the diff command + out, err := ExecCommandHost([]string{"bash", "-c", "diff --help"}) + Expect(err).NotTo(BeNil()) + Expect(out).To(MatchRegexp(".*Permission denied")) + + // check policy violation alert + _, alerts, err := KarmorGetLogs(5*time.Second, 1) + Expect(err).To(BeNil()) + Expect(len(alerts)).To(BeNumerically(">=", 1)) + Expect(alerts[0].PolicyName).To(Equal("hsp-kubearmor-dev-proc-path-block")) + Expect(alerts[0].Severity).To(Equal("5")) + Expect(alerts[0].Action).To(Equal("Block")) + }) + }) + + Describe("HSP dir block from source", func() { + + It("can allow access to everything except /etc/default/* from head", func() { + + err := K8sApplyFile("res/hsp-kubearmor-dev-file-dir-block-fromSource.yaml") + Expect(err).To(BeNil()) + + // Start the karmor logs + err = KarmorLogStart("policy", "", "File", "") + Expect(err).To(BeNil()) + + // call the head command + out, err := ExecCommandHost([]string{"bash", "-c", "head /etc/hostname"}) + Expect(err).To(BeNil()) + Expect(out).NotTo(MatchRegexp(".*Permission denied")) + + // check policy violation alert + _, alerts, err := KarmorGetLogs(5*time.Second, 1) + Expect(err).To(BeNil()) + Expect(len(alerts)).To(BeNumerically("==", 0)) + }) + + It("can block access to /etc/default/* from head", func() { + + err := K8sApplyFile("res/hsp-kubearmor-dev-file-dir-block-fromSource.yaml") + Expect(err).To(BeNil()) + + // Start the karmor logs + err = KarmorLogStart("policy", "", "File", "") + Expect(err).To(BeNil()) + + // call the head command + out, err := ExecCommandHost([]string{"bash", "-c", "head /etc/default/useradd"}) + Expect(err).NotTo(BeNil()) + Expect(out).To(MatchRegexp(".*Permission denied")) + + // check policy violation alert + _, alerts, err := KarmorGetLogs(5*time.Second, 1) + Expect(err).To(BeNil()) + Expect(len(alerts)).To(BeNumerically(">=", 1)) + Expect(alerts[0].PolicyName).To(Equal("hsp-kubearmor-dev-file-dir-block-fromsource")) + Expect(alerts[0].Severity).To(Equal("5")) + Expect(alerts[0].Action).To(Equal("Block")) + }) + }) + + // Describe("HSP file audit", func() { + + // It("can audit access to /etc/passwd", func() { + + // err := K8sApplyFile("res/hsp-kubearmor-dev-file-path-audit.yaml") + // Expect(err).To(BeNil()) + + // // Start the karmor logs + // err = KarmorLogStart("policy", "", "File", "") + // Expect(err).To(BeNil()) + + // // try to access the /etc/passwd file + // out, err := ExecCommandHost([]string{"bash", "-c", "cat /etc/passwd"}) + // Expect(err).To(BeNil()) + // Expect(out).ToNot(MatchRegexp(".*Permission denied")) + + // // check audit alerts + // _, alerts, err := KarmorGetLogs(5*time.Second, 1) + // Expect(err).To(BeNil()) + // Expect(len(alerts)).To(BeNumerically(">=", 1)) + // Expect(alerts[0].PolicyName).To(Equal("hsp-kubearmor-dev-file-path-audit")) + // Expect(alerts[0].Severity).To(Equal("5")) + // Expect(alerts[0].Action).To(Equal("Audit")) + // }) + // }) + + Describe("HSP path block from source", func() { + + It("It can block access to /etc/hostname from head", func() { + + err := K8sApplyFile("res/hsp-kubearmor-dev-file-path-block-fromSource.yaml") + Expect(err).To(BeNil()) + + // Start the karmor logs + err = KarmorLogStart("policy", "", "File", "") + Expect(err).To(BeNil()) + + // try to access the /etc/hostname file from head + out, err := ExecCommandHost([]string{"bash", "-c", "head /etc/hostname"}) + Expect(err).NotTo(BeNil()) + Expect(out).To(MatchRegexp(".*Permission denied")) + + // check policy violation alert + _, alerts, err := KarmorGetLogs(5*time.Second, 1) + Expect(err).To(BeNil()) + Expect(len(alerts)).To(BeNumerically(">=", 1)) + Expect(alerts[0].PolicyName).To(Equal("hsp-kubearmor-dev-file-path-block-fromsource")) + Expect(alerts[0].Severity).To(Equal("5")) + Expect(alerts[0].Action).To(Equal("Block")) + }) + }) + + // Describe("HSP Process path block from source", func() { + + // FIt("can block date command from bash", func() { + + // err := K8sApplyFile("res/hsp-kubearmor-dev-proc-path-block-fromSource.yaml") + // Expect(err).To(BeNil()) + + // // Start the karmor logs + // err = KarmorLogStart("policy", "", "Process", "") + // Expect(err).To(BeNil()) + + // // call the date command from bash + // out, err := ExecCommandHost([]string{"bash", "-c", "date"}) + // Expect(err).To(BeNil()) + // Expect(out).To(MatchRegexp(".*Permission denied")) + + // // execute ls command from bash + // out2, err := ExecCommandHost([]string{"bash", "-c", "ls"}) + // Expect(err).To(BeNil()) + // Expect(out2).NotTo(MatchRegexp(".*Permission denied")) + + // // check policy violation alert + // _, alerts, err := KarmorGetLogs(5*time.Second, 1) + // Expect(err).To(BeNil()) + // Expect(len(alerts)).To(BeNumerically(">=", 1)) + // Expect(alerts[0].PolicyName).To(Equal("hsp-kubearmor-dev-proc-path-block-fromsource")) + // Expect(alerts[0].Severity).To(Equal("5")) + // Expect(alerts[0].Action).To(Equal("Block")) + // }) + // }) + + Describe("HSP Process path block", func() { + + It("can block diff command", func() { + + err := K8sApplyFile("res/hsp-kubearmor-dev-proc-path-block.yaml") + Expect(err).To(BeNil()) + + // Start the karmor logs + err = KarmorLogStart("policy", "", "Process", "") + Expect(err).To(BeNil()) + + // run diff command + out, err := ExecCommandHost([]string{"bash", "-c", "diff"}) + Expect(err).NotTo(BeNil()) + Expect(out).To(MatchRegexp(".*Permission denied")) + + // check policy violation alert + _, alerts, err := KarmorGetLogs(5*time.Second, 1) + Expect(err).To(BeNil()) + Expect(len(alerts)).To(BeNumerically(">=", 1)) + Expect(alerts[0].PolicyName).To(Equal("hsp-kubearmor-dev-proc-path-block")) + Expect(alerts[0].Severity).To(Equal("5")) + Expect(alerts[0].Action).To(Equal("Block")) + }) + }) + + Describe("HSP Network path block", func() { + + It("can block access to UDP protocol from curl", func() { + + err := K8sApplyFile("res/hsp-kubearmor-dev-udp-block.yaml") + Expect(err).To(BeNil()) + + // Start the karmor logs + err = KarmorLogStart("policy", "", "Network", "") + Expect(err).To(BeNil()) + + // run diff command + out, err := ExecCommandHost([]string{"bash", "-c", "curl google.com"}) + Expect(err).NotTo(BeNil()) + Expect(out).To(MatchRegexp(".*Could not resolve host: google.com")) + + // check policy violation alert + _, alerts, err := KarmorGetLogs(5*time.Second, 1) + Expect(err).To(BeNil()) + Expect(len(alerts)).To(BeNumerically(">=", 1)) + Expect(alerts[0].PolicyName).To(Equal("hsp-kubearmor-dev-udp-block-curl")) + Expect(alerts[0].Severity).To(Equal("5")) + Expect(alerts[0].Action).To(Equal("Block")) + }) + }) +}) diff --git a/tests/k8s_env/hsp/manifests/hsp-kubearmor-dev-file-path-block.yaml b/tests/k8s_env/hsp/manifests/hsp-kubearmor-dev-file-path-block.yaml new file mode 100644 index 0000000000..408c8507b9 --- /dev/null +++ b/tests/k8s_env/hsp/manifests/hsp-kubearmor-dev-file-path-block.yaml @@ -0,0 +1,19 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-file-path-block +spec: + nodeSelector: + matchLabels: + kubernetes.io/os: linux + severity: 5 + file: + matchPaths: + - path: /etc/hostname + action: + Block + + +# test +# $ cat /etc/hostname +# cat: /etc/hostname: Permission denied \ No newline at end of file diff --git a/tests/k8s_env/hsp/manifests/hsp-kubearmor-dev-proc-path-block.yaml b/tests/k8s_env/hsp/manifests/hsp-kubearmor-dev-proc-path-block.yaml new file mode 100644 index 0000000000..a5fd26cc16 --- /dev/null +++ b/tests/k8s_env/hsp/manifests/hsp-kubearmor-dev-proc-path-block.yaml @@ -0,0 +1,24 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-proc-path-block +spec: + nodeSelector: + matchLabels: + kubernetes.io/os: linux + severity: 5 + process: + matchPaths: + - path: /bin/date + - path: /usr/bin/date + + action: + Block + +# kubearmor-dev_test_04 + +# test +# $ bash -c date +# bash: 1: date: Permission denied +# $ bash -c ls +# ls ... diff --git a/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-dir-allow-fromSource.yaml b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-dir-allow-fromSource.yaml new file mode 100644 index 0000000000..ae0325f9fb --- /dev/null +++ b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-dir-allow-fromSource.yaml @@ -0,0 +1,29 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-file-dir-allow-fromsource +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + file: + matchDirectories: + - dir: /etc/default/ + recursive: true + fromSource: + - path: /usr/bin/head + action: + Allow + +# kubearmor-dev_test_08 + +# test +# $ head /etc/default/useradd +# Default values for useradd(8) ... +# $ head /etc/hostname +# head: /etc/hostname: Permission denied + +# expectation +# /usr/bin/head can only access /etc/default/* +# /usr/bin/head cannot access any others diff --git a/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-dir-block-fromSource.yaml b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-dir-block-fromSource.yaml new file mode 100644 index 0000000000..8f15d004df --- /dev/null +++ b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-dir-block-fromSource.yaml @@ -0,0 +1,28 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-file-dir-block-fromsource +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + file: + matchDirectories: + - dir: /etc/default/ + fromSource: + - path: /usr/bin/head + action: + Block + +# kubearmor-dev_test_09 + +# test +# $ head /etc/default/useradd +# head: useradd: Permission denied +# $ head /etc/hostname +# kubearmor-dev + +# expectation +# /usr/bin/head cannot access /etc/default/* +# /usr/bin/head can access any others diff --git a/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-path-allow-fromSource.yaml b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-path-allow-fromSource.yaml new file mode 100644 index 0000000000..94ad765683 --- /dev/null +++ b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-path-allow-fromSource.yaml @@ -0,0 +1,28 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-file-path-allow-fromsource +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + file: + matchPaths: + - path: /etc/hostname + fromSource: + - path: /usr/bin/head + action: + Allow + +# kubearmor-dev_test_07 + +# test +# $ head /etc/hostname +# kubearmor-dev +# $ head /etc/hosts +# head: /etc/hosts: Permission denied + +# expectation +# /usr/bin/head can only access /etc/hostname +# /usr/bin/head cannot access any others diff --git a/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-path-audit.yaml b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-path-audit.yaml new file mode 100644 index 0000000000..01e1c84dbf --- /dev/null +++ b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-path-audit.yaml @@ -0,0 +1,25 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-file-path-audit +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + file: + matchPaths: + - path: /etc/passwd + action: + Audit + +# kubearmor-dev_test_02 + +# test +# $ cat /etc/passwd +# ... +# $ head /etc/passwd +# ... + +# expectation +# anyone can access /etc/passwd, but the access would be audited diff --git a/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-path-block-fromSource.yaml b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-path-block-fromSource.yaml new file mode 100644 index 0000000000..beb8256c8b --- /dev/null +++ b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-path-block-fromSource.yaml @@ -0,0 +1,28 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-file-path-block-fromsource +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + file: + matchPaths: + - path: /etc/hostname + fromSource: + - path: /usr/bin/head + action: + Block + +# kubearmor-dev_test_06 + +# test +# $ head /etc/hostname +# head: cannot open '/etc/hostname' for reading: Permission denied +# $ head /etc/hosts +# ... + +# expectation +# /usr/bin/head cannot access /etc/hostname +# /usr/bin/head can access any others diff --git a/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-path-block.yaml b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-path-block.yaml new file mode 100644 index 0000000000..323e014505 --- /dev/null +++ b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-file-path-block.yaml @@ -0,0 +1,23 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-file-path-block +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + file: + matchPaths: + - path: /etc/hostname + action: + Block + +# kubearmor-dev_test_03 + +# test +# $ cat /etc/hostname +# cat: /etc/hostname: Permission denied + +# expectation +# anyone cannot access /etc/hostname \ No newline at end of file diff --git a/tests/k8s_env/hsp/res/hsp-kubearmor-dev-proc-path-allow-fromSource.yaml b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-proc-path-allow-fromSource.yaml new file mode 100644 index 0000000000..7efd962818 --- /dev/null +++ b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-proc-path-allow-fromSource.yaml @@ -0,0 +1,31 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-proc-path-allow-fromsource +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + process: + matchPaths: + - path: /bin/date + fromSource: + - path: /bin/bash # ubuntu # ubuntu also uses /usr/bin/bash + - path: /usr/bin/date + fromSource: + - path: /usr/bin/bash # centos + action: + Allow + +# kubearmor-dev_test_05 + +# test +# $ bash -c date +# ... +# $ bash -c ls +# bash: /usr/bin/ls: Permission denied + +# expectation +# (/usr)/bin/bash can only execute (/usr)/bin/date +# (/usr)/bin/bash cannot execute any others diff --git a/tests/k8s_env/hsp/res/hsp-kubearmor-dev-proc-path-block-fromSource.yaml b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-proc-path-block-fromSource.yaml new file mode 100644 index 0000000000..4d027323c8 --- /dev/null +++ b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-proc-path-block-fromSource.yaml @@ -0,0 +1,31 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-proc-path-block-fromsource +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + process: + matchPaths: + - path: /bin/date + fromSource: + - path: /bin/bash # ubuntu + - path: /usr/bin/date + fromSource: + - path: /usr/bin/bash # centos + action: + Block + +# kubearmor-dev_test_04 + +# test +# (/home/vagrant/selinux-test/) $ bash -c date +# bash: 1: date: Permission denied +# (/home/vagrant/selinux-test/) $ bash -c ls +# ls ... + +# expectation +# (/usr)/bin/bash cannot execute (/usr)/bin/date +# (/usr)/bin/bash can execute any others diff --git a/tests/k8s_env/hsp/res/hsp-kubearmor-dev-proc-path-block.yaml b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-proc-path-block.yaml new file mode 100644 index 0000000000..57fb088fd7 --- /dev/null +++ b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-proc-path-block.yaml @@ -0,0 +1,23 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-proc-path-block +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + process: + matchPaths: + - path: /usr/bin/diff + action: + Block + +# kubearmor-dev_test_01 + +# test +# $ diff --help +# -bash: /usr/bin/diff: Permission denied + +# expectation +# anyone cannot execute /usr/bin/diff diff --git a/tests/k8s_env/hsp/res/hsp-kubearmor-dev-udp-block.yaml b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-udp-block.yaml new file mode 100644 index 0000000000..6076b2adef --- /dev/null +++ b/tests/k8s_env/hsp/res/hsp-kubearmor-dev-udp-block.yaml @@ -0,0 +1,25 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-udp-block-curl +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + network: + matchProtocols: + - protocol: udp + fromSource: + - path: /usr/bin/curl + action: + Block + + +# curl google.com +# curl: (6) Could not resolve host: google.com + +# curl 142.250.194.142 +# ... content + +# resolving google.com requires udp protocol \ No newline at end of file diff --git a/tests/k8s_env/multicontainer/manifests/non-existent-container-block-ls.yaml b/tests/k8s_env/multicontainer/manifests/non-existent-container-block-ls.yaml new file mode 100644 index 0000000000..3b497360d5 --- /dev/null +++ b/tests/k8s_env/multicontainer/manifests/non-existent-container-block-ls.yaml @@ -0,0 +1,17 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorPolicy +metadata: + name: non-existent-container-block-ls + namespace: multicontainer +spec: + severity: 5 + selector: + matchLabels: + container: multicontainer + kubearmor.io/container.name: "[container-1, non-existent-container ]" + process: + matchPaths: + - path: /bin/ls + # ls + action: + Block diff --git a/tests/k8s_env/multicontainer/multicontainer_test.go b/tests/k8s_env/multicontainer/multicontainer_test.go index 6ab4445a4c..bb9a2cac21 100644 --- a/tests/k8s_env/multicontainer/multicontainer_test.go +++ b/tests/k8s_env/multicontainer/multicontainer_test.go @@ -172,6 +172,37 @@ var _ = Describe("Multicontainer", func() { Expect(sout).NotTo(MatchRegexp(".*Permission denied")) }) + + It("Can enforce on container-1 even if non-existent container is present in array", func() { + err := K8sDeploymentCheck("multicontainer-deployment", "multicontainer", 5*time.Minute) + Expect(err).To(BeNil()) + + err = K8sApply([]string{"manifests/non-existent-container-block-ls.yaml"}) + Expect(err).To(BeNil()) + + err = KarmorLogStart("policy", "multicontainer", "Process", multicontainer) + Expect(err).To(BeNil()) + + // container-1 should not run ls + sout, _, err := K8sExecInPodWithContainer(multicontainer, "multicontainer", "container-1", []string{"bash", "-c", "ls"}) + Expect(err).To(BeNil()) + fmt.Printf("---START---\n%s---END---\n", sout) + Expect(sout).To(MatchRegexp(".*Permission denied")) + + // check policy violation alert + _, alerts, err := KarmorGetLogs(10*time.Second, 1) + Expect(err).To(BeNil()) + Expect(len(alerts)).To(BeNumerically(">=", 1)) + Expect(alerts[0].PolicyName).To(Equal("non-existent-container-block-ls")) + Expect(alerts[0].Severity).To(Equal("5")) + Expect(alerts[0].ContainerName).To(Equal("container-1")) + + // container-2 should run ls + sout, _, err = K8sExecInPodWithContainer(multicontainer, "multicontainer", "container-2", []string{"bash", "-c", "ls"}) + Expect(err).To(BeNil()) + fmt.Printf("---START---\n%s---END---\n", sout) + Expect(sout).NotTo(MatchRegexp(".*Permission denied")) + }) }) }) diff --git a/tests/nonk8s_env/hsp/hsp_suite_test.go b/tests/nonk8s_env/hsp/hsp_suite_test.go new file mode 100644 index 0000000000..d575392e34 --- /dev/null +++ b/tests/nonk8s_env/hsp/hsp_suite_test.go @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Authors of KubeArmor + +package hsp_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestHsp(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Hsp Suite") +} diff --git a/tests/nonk8s_env/hsp/hsp_test.go b/tests/nonk8s_env/hsp/hsp_test.go new file mode 100644 index 0000000000..cc01fadb20 --- /dev/null +++ b/tests/nonk8s_env/hsp/hsp_test.go @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2024 Authors of KubeArmor + +package hsp + +import ( + "os" + "time" + + . "github.com/kubearmor/KubeArmor/tests/util" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("Non-k8s HSP tests", func() { + + AfterEach(func() { + KarmorLogStop() + }) + + BeforeEach(func() { + // Set the environment variable + os.Setenv("KUBEARMOR_SERVICE", ":32767") + }) + + Describe("HSP file path block", func() { + + It("can block access to /etc/hostname on the host", func() { + + policyPath := "res/hsp-kubearmor-dev-file-path-block.yaml" + err := SendPolicy("ADDED", policyPath) + Expect(err).To(BeNil()) + + // Start the karmor logs + err = KarmorLogStart("policy", "", "File", "") + Expect(err).To(BeNil()) + + // Access the /etc/hostname file + out, err := ExecCommandHost([]string{"bash", "-c", "cat /etc/hostname"}) + Expect(err).NotTo(BeNil()) + Expect(out).To(MatchRegexp(".*Permission denied")) + + // check policy violation alert + _, alerts, err := KarmorGetLogs(5*time.Second, 1) + Expect(err).To(BeNil()) + Expect(len(alerts)).To(BeNumerically(">=", 1)) + Expect(alerts[0].PolicyName).To(Equal("hsp-kubearmor-dev-file-path-block")) + Expect(alerts[0].Severity).To(Equal("5")) + Expect(alerts[0].Action).To(Equal("Block")) + + // delete the policy + err = SendPolicy("DELETED", policyPath) + Expect(err).To(BeNil()) + + }) + }) + + Describe("HSP Process path block", func() { + + It("can block execution of diff command in host", func() { + + policyPath := "res/hsp-kubearmor-dev-proc-path-block.yaml" + err := SendPolicy("ADDED", policyPath) + Expect(err).To(BeNil()) + + // Start the karmor logs + err = KarmorLogStart("policy", "", "Process", "") + Expect(err).To(BeNil()) + + // call the diff command + out, err := ExecCommandHost([]string{"bash", "-c", "diff --help"}) + Expect(err).NotTo(BeNil()) + Expect(out).To(MatchRegexp(".*Permission denied")) + + // check policy violation alert + _, alerts, err := KarmorGetLogs(5*time.Second, 1) + Expect(err).To(BeNil()) + Expect(len(alerts)).To(BeNumerically(">=", 1)) + Expect(alerts[0].PolicyName).To(Equal("hsp-kubearmor-dev-proc-path-block")) + Expect(alerts[0].Severity).To(Equal("5")) + Expect(alerts[0].Action).To(Equal("Block")) + + // delete the policy + err = SendPolicy("DELETED", policyPath) + Expect(err).To(BeNil()) + }) + }) + + Describe("HSP dir block from source", func() { + + It("can allow access to everything except /etc/default/* from head", func() { + + policyPath := "res/hsp-kubearmor-dev-file-dir-block-fromSource.yaml" + err := SendPolicy("ADDED", policyPath) + Expect(err).To(BeNil()) + + // Start the karmor logs + err = KarmorLogStart("policy", "", "File", "") + Expect(err).To(BeNil()) + + // call the head command + out, err := ExecCommandHost([]string{"bash", "-c", "head /etc/hostname"}) + Expect(err).To(BeNil()) + Expect(out).NotTo(MatchRegexp(".*Permission denied")) + + // check policy violation alert + _, alerts, err := KarmorGetLogs(5*time.Second, 1) + Expect(err).To(BeNil()) + Expect(len(alerts)).To(BeNumerically("==", 0)) + + // delete the policy + err = SendPolicy("DELETED", policyPath) + Expect(err).To(BeNil()) + }) + + It("can block access to /etc/default/* from head", func() { + + policyPath := "res/hsp-kubearmor-dev-file-dir-block-fromSource.yaml" + err := SendPolicy("ADDED", policyPath) + Expect(err).To(BeNil()) + + // Start the karmor logs + err = KarmorLogStart("policy", "", "File", "") + Expect(err).To(BeNil()) + + // call the head command + out, err := ExecCommandHost([]string{"bash", "-c", "head /etc/default/useradd"}) + Expect(err).NotTo(BeNil()) + Expect(out).To(MatchRegexp(".*Permission denied")) + + // check policy violation alert + _, alerts, err := KarmorGetLogs(5*time.Second, 1) + Expect(err).To(BeNil()) + Expect(len(alerts)).To(BeNumerically(">=", 1)) + Expect(alerts[0].PolicyName).To(Equal("hsp-kubearmor-dev-file-dir-block-fromsource")) + Expect(alerts[0].Severity).To(Equal("5")) + Expect(alerts[0].Action).To(Equal("Block")) + + // delete the policy + err = SendPolicy("DELETED", policyPath) + Expect(err).To(BeNil()) + }) + }) + + Describe("HSP file audit", func() { + + It("can audit access to /etc/passwd", func() { + + policyPath := "res/hsp-kubearmor-dev-file-path-audit.yaml" + err := SendPolicy("ADDED", policyPath) + Expect(err).To(BeNil()) + + // Start the karmor logs + err = KarmorLogStart("policy", "", "File", "") + Expect(err).To(BeNil()) + + // try to access the /etc/passwd file + out, err := ExecCommandHost([]string{"bash", "-c", "cat /etc/passwd"}) + Expect(err).To(BeNil()) + Expect(out).ToNot(MatchRegexp(".*Permission denied")) + + // check audit alerts + _, alerts, err := KarmorGetLogs(5*time.Second, 1) + Expect(err).To(BeNil()) + Expect(len(alerts)).To(BeNumerically(">=", 1)) + Expect(alerts[0].PolicyName).To(Equal("hsp-kubearmor-dev-file-path-audit")) + Expect(alerts[0].Severity).To(Equal("5")) + Expect(alerts[0].Action).To(Equal("Audit")) + + // delete the policy + err = SendPolicy("DELETED", policyPath) + Expect(err).To(BeNil()) + }) + }) + + Describe("HSP path block from source", func() { + + It("It can block access to /etc/hostname from head", func() { + + policyPath := "res/hsp-kubearmor-dev-file-path-block-fromSource.yaml" + err := SendPolicy("ADDED", policyPath) + Expect(err).To(BeNil()) + + // Start the karmor logs + err = KarmorLogStart("policy", "", "File", "") + Expect(err).To(BeNil()) + + // try to access the /etc/hostname file from head + out, err := ExecCommandHost([]string{"bash", "-c", "head /etc/hostname"}) + Expect(err).NotTo(BeNil()) + Expect(out).To(MatchRegexp(".*Permission denied")) + + // check policy violation alert + _, alerts, err := KarmorGetLogs(5*time.Second, 1) + Expect(err).To(BeNil()) + Expect(len(alerts)).To(BeNumerically(">=", 1)) + Expect(alerts[0].PolicyName).To(Equal("hsp-kubearmor-dev-file-path-block-fromsource")) + Expect(alerts[0].Severity).To(Equal("5")) + Expect(alerts[0].Action).To(Equal("Block")) + + // delete the policy + err = SendPolicy("DELETED", policyPath) + Expect(err).To(BeNil()) + }) + }) + + // Describe("HSP Process path block from source", func() { + + // It("can block date command from bash", func() { + + // policyPath := "res/hsp-kubearmor-dev-proc-path-block-fromSource.yaml" + // err := SendPolicy("ADDED", policyPath) + // Expect(err).To(BeNil()) + + // // Start the karmor logs + // err = KarmorLogStart("policy", "", "Process", "") + // Expect(err).To(BeNil()) + + // // call the date command from bash + // out, err := ExecCommandHost([]string{"bash", "-c", "date"}) + // Expect(err).To(BeNil()) + // Expect(out).To(MatchRegexp(".*Permission denied")) + + // // // execute ls command from bash + // // out2, err := ExecCommandHost([]string{"bash", "-c", "ls"}) + // // Expect(err).To(BeNil()) + // // Expect(out2).NotTo(MatchRegexp(".*Permission denied")) + + // // check policy violation alert + // _, alerts, err := KarmorGetLogs(5*time.Second, 1) + // Expect(err).To(BeNil()) + // Expect(len(alerts)).To(BeNumerically(">=", 1)) + // Expect(alerts[0].PolicyName).To(Equal("hsp-kubearmor-dev-proc-path-block-fromsource")) + // Expect(alerts[0].Severity).To(Equal("5")) + // Expect(alerts[0].Action).To(Equal("Block")) + + // // delete the policy + // err = SendPolicy("DELETED", policyPath) + // Expect(err).To(BeNil()) + // }) + // }) + + Describe("HSP Process path block", func() { + + It("can block diff command", func() { + + policyPath := "res/hsp-kubearmor-dev-proc-path-block.yaml" + err := SendPolicy("ADDED", policyPath) + Expect(err).To(BeNil()) + + // Start the karmor logs + err = KarmorLogStart("policy", "", "Process", "") + Expect(err).To(BeNil()) + + // run diff command + out, err := ExecCommandHost([]string{"bash", "-c", "diff"}) + Expect(err).NotTo(BeNil()) + Expect(out).To(MatchRegexp(".*Permission denied")) + + // check policy violation alert + _, alerts, err := KarmorGetLogs(5*time.Second, 1) + Expect(err).To(BeNil()) + Expect(len(alerts)).To(BeNumerically(">=", 1)) + Expect(alerts[0].PolicyName).To(Equal("hsp-kubearmor-dev-proc-path-block")) + Expect(alerts[0].Severity).To(Equal("5")) + Expect(alerts[0].Action).To(Equal("Block")) + + // delete the policy + err = SendPolicy("DELETED", policyPath) + Expect(err).To(BeNil()) + }) + }) + + Describe("HSP Network path block", func() { + + It("can block access to UDP protocol from curl", func() { + + policyPath := "res/hsp-kubearmor-dev-udp-block.yaml" + err := SendPolicy("ADDED", policyPath) + Expect(err).To(BeNil()) + + // Start the karmor logs + err = KarmorLogStart("policy", "", "Network", "") + Expect(err).To(BeNil()) + + // run diff command + out, err := ExecCommandHost([]string{"bash", "-c", "curl google.com"}) + Expect(err).NotTo(BeNil()) + Expect(out).To(MatchRegexp(".*Could not resolve host: google.com")) + + // check policy violation alert + _, alerts, err := KarmorGetLogs(5*time.Second, 1) + Expect(err).To(BeNil()) + Expect(len(alerts)).To(BeNumerically(">=", 1)) + Expect(alerts[0].PolicyName).To(Equal("hsp-kubearmor-dev-udp-block-curl")) + Expect(alerts[0].Severity).To(Equal("5")) + Expect(alerts[0].Action).To(Equal("Block")) + + // delete the policy + err = SendPolicy("DELETED", policyPath) + Expect(err).To(BeNil()) + }) + }) +}) diff --git a/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-dir-allow-fromSource.yaml b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-dir-allow-fromSource.yaml new file mode 100644 index 0000000000..ae0325f9fb --- /dev/null +++ b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-dir-allow-fromSource.yaml @@ -0,0 +1,29 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-file-dir-allow-fromsource +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + file: + matchDirectories: + - dir: /etc/default/ + recursive: true + fromSource: + - path: /usr/bin/head + action: + Allow + +# kubearmor-dev_test_08 + +# test +# $ head /etc/default/useradd +# Default values for useradd(8) ... +# $ head /etc/hostname +# head: /etc/hostname: Permission denied + +# expectation +# /usr/bin/head can only access /etc/default/* +# /usr/bin/head cannot access any others diff --git a/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-dir-block-fromSource.yaml b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-dir-block-fromSource.yaml new file mode 100644 index 0000000000..8f15d004df --- /dev/null +++ b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-dir-block-fromSource.yaml @@ -0,0 +1,28 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-file-dir-block-fromsource +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + file: + matchDirectories: + - dir: /etc/default/ + fromSource: + - path: /usr/bin/head + action: + Block + +# kubearmor-dev_test_09 + +# test +# $ head /etc/default/useradd +# head: useradd: Permission denied +# $ head /etc/hostname +# kubearmor-dev + +# expectation +# /usr/bin/head cannot access /etc/default/* +# /usr/bin/head can access any others diff --git a/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-path-allow-fromSource.yaml b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-path-allow-fromSource.yaml new file mode 100644 index 0000000000..94ad765683 --- /dev/null +++ b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-path-allow-fromSource.yaml @@ -0,0 +1,28 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-file-path-allow-fromsource +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + file: + matchPaths: + - path: /etc/hostname + fromSource: + - path: /usr/bin/head + action: + Allow + +# kubearmor-dev_test_07 + +# test +# $ head /etc/hostname +# kubearmor-dev +# $ head /etc/hosts +# head: /etc/hosts: Permission denied + +# expectation +# /usr/bin/head can only access /etc/hostname +# /usr/bin/head cannot access any others diff --git a/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-path-audit.yaml b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-path-audit.yaml new file mode 100644 index 0000000000..01e1c84dbf --- /dev/null +++ b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-path-audit.yaml @@ -0,0 +1,25 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-file-path-audit +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + file: + matchPaths: + - path: /etc/passwd + action: + Audit + +# kubearmor-dev_test_02 + +# test +# $ cat /etc/passwd +# ... +# $ head /etc/passwd +# ... + +# expectation +# anyone can access /etc/passwd, but the access would be audited diff --git a/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-path-block-fromSource.yaml b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-path-block-fromSource.yaml new file mode 100644 index 0000000000..beb8256c8b --- /dev/null +++ b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-path-block-fromSource.yaml @@ -0,0 +1,28 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-file-path-block-fromsource +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + file: + matchPaths: + - path: /etc/hostname + fromSource: + - path: /usr/bin/head + action: + Block + +# kubearmor-dev_test_06 + +# test +# $ head /etc/hostname +# head: cannot open '/etc/hostname' for reading: Permission denied +# $ head /etc/hosts +# ... + +# expectation +# /usr/bin/head cannot access /etc/hostname +# /usr/bin/head can access any others diff --git a/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-path-block.yaml b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-path-block.yaml new file mode 100644 index 0000000000..323e014505 --- /dev/null +++ b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-file-path-block.yaml @@ -0,0 +1,23 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-file-path-block +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + file: + matchPaths: + - path: /etc/hostname + action: + Block + +# kubearmor-dev_test_03 + +# test +# $ cat /etc/hostname +# cat: /etc/hostname: Permission denied + +# expectation +# anyone cannot access /etc/hostname \ No newline at end of file diff --git a/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-proc-path-allow-fromSource.yaml b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-proc-path-allow-fromSource.yaml new file mode 100644 index 0000000000..7efd962818 --- /dev/null +++ b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-proc-path-allow-fromSource.yaml @@ -0,0 +1,31 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-proc-path-allow-fromsource +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + process: + matchPaths: + - path: /bin/date + fromSource: + - path: /bin/bash # ubuntu # ubuntu also uses /usr/bin/bash + - path: /usr/bin/date + fromSource: + - path: /usr/bin/bash # centos + action: + Allow + +# kubearmor-dev_test_05 + +# test +# $ bash -c date +# ... +# $ bash -c ls +# bash: /usr/bin/ls: Permission denied + +# expectation +# (/usr)/bin/bash can only execute (/usr)/bin/date +# (/usr)/bin/bash cannot execute any others diff --git a/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-proc-path-block-fromSource.yaml b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-proc-path-block-fromSource.yaml new file mode 100644 index 0000000000..4d027323c8 --- /dev/null +++ b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-proc-path-block-fromSource.yaml @@ -0,0 +1,31 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-proc-path-block-fromsource +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + process: + matchPaths: + - path: /bin/date + fromSource: + - path: /bin/bash # ubuntu + - path: /usr/bin/date + fromSource: + - path: /usr/bin/bash # centos + action: + Block + +# kubearmor-dev_test_04 + +# test +# (/home/vagrant/selinux-test/) $ bash -c date +# bash: 1: date: Permission denied +# (/home/vagrant/selinux-test/) $ bash -c ls +# ls ... + +# expectation +# (/usr)/bin/bash cannot execute (/usr)/bin/date +# (/usr)/bin/bash can execute any others diff --git a/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-proc-path-block.yaml b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-proc-path-block.yaml new file mode 100644 index 0000000000..57fb088fd7 --- /dev/null +++ b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-proc-path-block.yaml @@ -0,0 +1,23 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-proc-path-block +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + process: + matchPaths: + - path: /usr/bin/diff + action: + Block + +# kubearmor-dev_test_01 + +# test +# $ diff --help +# -bash: /usr/bin/diff: Permission denied + +# expectation +# anyone cannot execute /usr/bin/diff diff --git a/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-udp-block.yaml b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-udp-block.yaml new file mode 100644 index 0000000000..6076b2adef --- /dev/null +++ b/tests/nonk8s_env/hsp/res/hsp-kubearmor-dev-udp-block.yaml @@ -0,0 +1,25 @@ +apiVersion: security.kubearmor.com/v1 +kind: KubeArmorHostPolicy +metadata: + name: hsp-kubearmor-dev-udp-block-curl +spec: + nodeSelector: + matchLabels: + kubearmor.io/hostname: "*" + severity: 5 + network: + matchProtocols: + - protocol: udp + fromSource: + - path: /usr/bin/curl + action: + Block + + +# curl google.com +# curl: (6) Could not resolve host: google.com + +# curl 142.250.194.142 +# ... content + +# resolving google.com requires udp protocol \ No newline at end of file diff --git a/tests/nonk8s_env/smoke/smoke_test.go b/tests/nonk8s_env/smoke/smoke_test.go index 774cb8b6d6..fc255aa2bd 100644 --- a/tests/nonk8s_env/smoke/smoke_test.go +++ b/tests/nonk8s_env/smoke/smoke_test.go @@ -3,6 +3,8 @@ package smoke_test import ( + // "fmt" + "time" . "github.com/kubearmor/KubeArmor/tests/util" @@ -19,8 +21,14 @@ var _ = BeforeSuite(func() { }) var _ = AfterSuite(func() { + + // remove policy + policyPath := "res/ksp-wordpress-block-policy.yaml" + err := SendPolicy("DELETED", policyPath) + Expect(err).To(BeNil()) + // delete wordpress-mysql app - _, err := RunDockerCommand("rm -f wordpress-mysql") + _, err = RunDockerCommand("rm -f wordpress-mysql") Expect(err).To(BeNil()) time.Sleep(5 * time.Second) @@ -106,4 +114,38 @@ var _ = Describe("Systemd", func() { }) }) + // This test works locally but fails in CI, debugging it! + // Describe(" It can block apt and apt-get commands in container ", func() { + + // It(" It can block apt command inside the container ", func() { + + // // Start the karmor logs + // err := KarmorLogStartgRPC("policy", "", "Process", "", ":32767") + // Expect(err).To(BeNil()) + // time.Sleep(2 * time.Second) + + // policyPath := "res/ksp-wordpress-block-policy.yaml" + + // err = SendPolicy("ADDED", policyPath) + // Expect(err).To(BeNil()) + // time.Sleep(5 * time.Second) + + // // out, err := ExecInDockerContainer("wordpress-mysql", []string{"bash", "-c", "apt update"}) + // out, err := RunDockerCommand("exec wordpress-mysql apt update") + // fmt.Println("Docker Command Output:", out) + // fmt.Println("Docker Command Error:", err) + // // Since the apt command won't run, it will return an error + // // Expect(err).NotTo(BeNil()) + // Expect(out).To(MatchRegexp(".*permission denied")) + + // // check policy violation alert + // _, alerts, err := KarmorGetLogs(5*time.Second, 1) + // Expect(err).To(BeNil()) + // Expect(len(alerts)).To(BeNumerically(">=", 1)) + // Expect(alerts[0].PolicyName).To(Equal("ksp-block-policy")) + // Expect(alerts[0].Severity).To(Equal("3")) + // Expect(alerts[0].Action).To(Equal("Block")) + // }) + // }) + }) diff --git a/tests/util/kartutil.go b/tests/util/kartutil.go index 345fef4646..d803e437ef 100644 --- a/tests/util/kartutil.go +++ b/tests/util/kartutil.go @@ -656,7 +656,7 @@ func K8sRuntime() string { func RunDockerCommand(cmdstr string) (string, error) { cmdf := strings.Fields(cmdstr) cmd := exec.Command("docker", cmdf...) - sout, err := cmd.Output() + sout, err := cmd.CombinedOutput() return string(sout), err } @@ -708,3 +708,18 @@ func ContainerInfo() (*pb.ProbeResponse, error) { } return resp, nil } + +// ExecCommandHost function executes command on the host +func ExecCommandHost(command []string) (string, error) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + cmd := exec.CommandContext(ctx, command[0], command[1:]...) + output, err := cmd.CombinedOutput() + + if err != nil { + return string(output), err + } + + return string(output), nil +}