From 8367bf95289a4c3bd22981a9be2297d7d425defb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mois=C3=A9s=20Gonz=C3=A1lez?= Date: Thu, 14 Nov 2024 12:02:43 -0400 Subject: [PATCH] fix: adjust the globing and abi rules in the apparmor profile Newer versions of ubuntu (>24.04) do not pin the AppArmor Policy feature ABI which causes certain rules to not be enforced. We include an abi rule to use the relatively common 3.0 policy whenever it's available in the system, if it's not available we rely on the default fallback behaviour. The 3.0 policy should be present on any system using AppArmor>3.x (e.g. Ubuntu 22.04 or newer). The globbing rules in the profile were adjusted to cover a larger range of python versions and avoid creating new profiles for different versions of python used by the sandbox environment. To load the profile we need at least AppArmor 3.0, to avoid confusion in the future we pin the alpine base image and define a proper tag for the apparmorloader image. --- tutorcodejail/patches/k8s-deployments | 2 +- .../patches/local-docker-compose-services | 2 +- tutorcodejail/plugin.py | 33 ++++++++++++++++++- .../codejail/apps/profiles/docker-edx-sandbox | 22 ++++++------- .../build/codejail_apparmor/Dockerfile | 5 ++- 5 files changed, 47 insertions(+), 17 deletions(-) diff --git a/tutorcodejail/patches/k8s-deployments b/tutorcodejail/patches/k8s-deployments index b828d2b..382b9c0 100644 --- a/tutorcodejail/patches/k8s-deployments +++ b/tutorcodejail/patches/k8s-deployments @@ -15,7 +15,7 @@ spec: app.kubernetes.io/name: codejailservice annotations: {% if CODEJAIL_ENFORCE_APPARMOR %} - container.apparmor.security.beta.kubernetes.io/codejailservice: "localhost/docker-edx-sandbox-{{ CODEJAIL_SANDBOX_PYTHON_VERSION }}" + container.apparmor.security.beta.kubernetes.io/codejailservice: "localhost/docker-edx-sandbox" {% endif %} spec: containers: diff --git a/tutorcodejail/patches/local-docker-compose-services b/tutorcodejail/patches/local-docker-compose-services index 28211ad..1ef2446 100644 --- a/tutorcodejail/patches/local-docker-compose-services +++ b/tutorcodejail/patches/local-docker-compose-services @@ -5,7 +5,7 @@ codejailservice: FLASK_APP_SETTINGS: codejailservice.tutor.ProductionConfig {% if CODEJAIL_ENFORCE_APPARMOR %} security_opt: - - apparmor:docker-edx-sandbox-{{ CODEJAIL_SANDBOX_PYTHON_VERSION }} + - apparmor:docker-edx-sandbox {% endif %} volumes: - ../plugins/codejail/apps/config/tutor.py:/openedx/codejailservice/codejailservice/tutor.py:ro diff --git a/tutorcodejail/plugin.py b/tutorcodejail/plugin.py index 0a14c0c..1944381 100644 --- a/tutorcodejail/plugin.py +++ b/tutorcodejail/plugin.py @@ -3,19 +3,22 @@ import os from glob import glob +from pathlib import Path import importlib_resources from tutor import hooks from .__about__ import __version__ +ABI_PATH = "/etc/apparmor.d/abi" + config = { "unique": { "SECRET_KEY": "{{ 24|random_string }}", }, "defaults": { "VERSION": __version__, - "APPARMOR_DOCKER_IMAGE": "docker.io/ednxops/codejail_apparmor_loader:latest", + "APPARMOR_DOCKER_IMAGE": "docker.io/ednxops/codejail_apparmor_loader:apparmor-3", "DOCKER_IMAGE": f"docker.io/ednxops/codejailservice:{__version__}", "ENABLE_K8S_DAEMONSET": False, "ENFORCE_APPARMOR": True, @@ -37,6 +40,34 @@ } +def get_apparmor_abi(): + """ + Return the default abi 3.0 rule if available in the system. + + AppArmor uses the Policy feature ABI to establish which rules it can + enforce based on the kernel capabilities. AppArmor profiles can include an + ABI rule to indicate the ABI they were developed under. If no rule is used + AppArmor will fallback to whichever rule is pinned in the + `/etc/apparmor/parser.conf` file. + + We try to use the 3.0 abi whenever it's available at `/etc/apparmor.d/abi/` + to guarantee that network rules are correctly enforced on newer versions of + the kernel. If the ABI is not present we don't set the abi rule and instead + rely on the default fallback. + + See: https://github.com/netblue30/firejail/issues/3659#issuecomment-711074899 + """ + if Path(f"{ABI_PATH}/3.0").exists(): + return "abi ," + return "" + + +hooks.Filters.ENV_TEMPLATE_VARIABLES.add_items( + [ + ("get_apparmor_abi", get_apparmor_abi()), + ] +) + # To add a custom initialization task, create a bash script template under: # tutorcodejail/templates/codejail/tasks/ # and then add it to the MY_INIT_TASKS list. Each task is in the format: diff --git a/tutorcodejail/templates/codejail/apps/profiles/docker-edx-sandbox b/tutorcodejail/templates/codejail/apps/profiles/docker-edx-sandbox index e25b4a6..73ad020 100644 --- a/tutorcodejail/templates/codejail/apps/profiles/docker-edx-sandbox +++ b/tutorcodejail/templates/codejail/apps/profiles/docker-edx-sandbox @@ -1,6 +1,8 @@ #include -profile docker-edx-sandbox-{{ CODEJAIL_SANDBOX_PYTHON_VERSION }} flags=(attach_disconnected,mediate_deleted) { +{{ get_apparmor_abi }} + +profile docker-edx-sandbox flags=(attach_disconnected,mediate_deleted) { #include network, @@ -9,7 +11,7 @@ profile docker-edx-sandbox-{{ CODEJAIL_SANDBOX_PYTHON_VERSION }} flags=(attach_d umount, signal (receive) peer=unconfined, signal (receive) peer=cri-containerd.apparmor.d, - signal (send,receive) peer=docker-edx-sandbox-{{ CODEJAIL_SANDBOX_PYTHON_VERSION }}, + signal (send,receive) peer=docker-edx-sandbox, deny @{PROC}/* w, # deny write for all files directly in /proc (not in a subdir) # deny write to files not in /proc//** or /proc/sys/** @@ -30,7 +32,7 @@ profile docker-edx-sandbox-{{ CODEJAIL_SANDBOX_PYTHON_VERSION }} flags=(attach_d deny /sys/firmware/** rwklx, deny /sys/kernel/security/** rwklx, - ptrace (trace,read) peer=docker-edx-sandbox-{{ CODEJAIL_SANDBOX_PYTHON_VERSION }}, + ptrace (trace,read) peer=docker-edx-sandbox, /sandbox/venv/bin/python Cx -> child, profile child flags=(attach_disconnected,mediate_deleted){ @@ -39,20 +41,18 @@ profile docker-edx-sandbox-{{ CODEJAIL_SANDBOX_PYTHON_VERSION }} flags=(attach_d # # Python abstractions adapted from https://gitlab.com/apparmor/apparmor/-/raw/master/profiles/apparmor.d/abstractions/python # - /opt/pyenv/versions/3.[0-9].*/lib/python3.[0-9]/**.{pyc,so} mr, - /opt/pyenv/versions/3.[0-9].*/lib/python3.[0-9]/**.{egg,py,pth} r, - /opt/pyenv/versions/3.[0-9].*/lib/python3.[0-9]/site-packages/ r, - /opt/pyenv/versions/3.[0-9].*/lib/python3.[0-9]/lib-dynload/*.so mr, - - /opt/pyenv/versions/3.[0-9].*/include/python3.[0-9]*/pyconfig.h r, - + /opt/pyenv/versions/{3.[0-9],3.[1-9][0-9]}.*/lib/python{3.[0-9],3.[1-9][0-9]}/**.{pyc,so} mr, + /opt/pyenv/versions/{3.[0-9],3.[1-9][0-9]}.*/lib/python{3.[0-9],3.[1-9][0-9]}/**.{egg,py,pth} r, + /opt/pyenv/versions/{3.[0-9],3.[1-9][0-9]}.*/lib/python{3.[0-9],3.[1-9][0-9]}/site-packages/ r, + /opt/pyenv/versions/{3.[0-9],3.[1-9][0-9]}.*/lib/python{3.[0-9],3.[1-9][0-9]}/lib-dynload/*.so mr, + /opt/pyenv/versions/{3.[0-9],3.[1-9][0-9]}.*/include/python{3.[0-9],3.[1-9][0-9]}*/pyconfig.h r, # # Whitelist particiclar shared objects from the system # python installation # /sandbox/venv/** mr, - /opt/pyenv/versions/{{ CODEJAIL_SANDBOX_PYTHON_VERSION }}_sandbox/** mr, + /opt/pyenv/versions/{3.[0-9],3.[1-9][0-9]}.*_sandbox/** mr, /tmp/codejail-*/ rix, /tmp/codejail-*/** wrix, /tmp/* wrix, diff --git a/tutorcodejail/templates/codejail/build/codejail_apparmor/Dockerfile b/tutorcodejail/templates/codejail/build/codejail_apparmor/Dockerfile index f3595d5..76b9fe5 100644 --- a/tutorcodejail/templates/codejail/build/codejail_apparmor/Dockerfile +++ b/tutorcodejail/templates/codejail/build/codejail_apparmor/Dockerfile @@ -7,9 +7,8 @@ RUN go mod init loader RUN go get k8s.io/klog/v2 RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -a -installsuffix cgo --ldflags '-w' -o loader . -FROM alpine:latest +FROM alpine:3.20 -RUN apk add apparmor libapparmor --update-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/testing/ --allow-untrusted && \ - apk add --no-cache musl\>1.1.20 --repository http://dl-cdn.alpinelinux.org/alpine/edge/main/ +RUN apk add apparmor libapparmor --update-cache COPY --from=go_compiler /app/loader /usr/bin/loader