From e1494589d54d41a62e17d05de5c16b43708452d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mois=C3=A9s=20Gonz=C3=A1lez?= Date: Thu, 24 Oct 2024 14:35:04 -0400 Subject: [PATCH] feat: sumac upgrade 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. --- .github/workflows/ci.yml | 4 +- setup.py | 2 +- tutorcodejail/__about__.py | 2 +- tutorcodejail/patches/k8s-deployments | 2 +- .../patches/local-docker-compose-services | 2 +- tutorcodejail/plugin.py | 35 +++++++++++++- .../codejail/apps/profiles/docker-edx-sandbox | 22 ++++----- .../codejail/build/codejail/Dockerfile | 46 ++++++++++--------- .../build/codejail_apparmor/Dockerfile | 7 ++- 9 files changed, 78 insertions(+), 44 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index abf9ff6..090344a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,10 +16,10 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.8', '3.12'] + python-version: ['3.9', '3.12'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: diff --git a/setup.py b/setup.py index 83b0d0c..bf14fc7 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ def load_about(): packages=find_packages(exclude=["tests*"]), include_package_data=True, python_requires=">=3.8", - install_requires=["tutor>=18.0.0, <19"], + install_requires=["tutor>=19.0.0, <20"], entry_points={"tutor.plugin.v1": ["codejail = tutorcodejail.plugin"]}, classifiers=[ "Development Status :: 3 - Alpha", diff --git a/tutorcodejail/__about__.py b/tutorcodejail/__about__.py index f80932a..27a3495 100644 --- a/tutorcodejail/__about__.py +++ b/tutorcodejail/__about__.py @@ -1,2 +1,2 @@ """Helps you keep your cool when creating dozens of open edX and eduNEXT environments.""" -__version__ = "18.0.0" +__version__ = "19.0.0" 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 724825a..5865781 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, @@ -30,13 +33,41 @@ "MIN_REPLICAS": 1, "MAX_REPLICAS": 4, "AVG_CPU": 65, - "SERVICE_VERSION": "release/redwood.1", + "SERVICE_VERSION": "release/sumac.1", "SERVICE_REPOSITORY": "https://github.com/edunext/codejailservice.git", }, "overrides": {}, } +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/Dockerfile b/tutorcodejail/templates/codejail/build/codejail/Dockerfile index 654089c..868740a 100644 --- a/tutorcodejail/templates/codejail/build/codejail/Dockerfile +++ b/tutorcodejail/templates/codejail/build/codejail/Dockerfile @@ -1,41 +1,42 @@ -FROM docker.io/ubuntu:22.04 as minimal -MAINTAINER edunext.co +FROM docker.io/ubuntu:22.04 AS minimal +LABEL mantainer="edunext.co " ENV DEBIAN_FRONTEND=noninteractive RUN apt update && \ apt install -y build-essential curl git language-pack-en llvm -ENV LC_ALL en_US.UTF-8 +ENV LC_ALL=en_US.UTF-8 ###### Install python with pyenv in /opt/pyenv and create virtualenv in /openedx/venv -FROM minimal as python +FROM minimal AS python # https://github.com/pyenv/pyenv/wiki/Common-build-problems#prerequisites RUN apt update && \ apt install -y libssl-dev zlib1g-dev libbz2-dev \ libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \ xz-utils tk-dev libffi-dev liblzma-dev python3-openssl git subversion -ENV PYENV_ROOT /opt/pyenv +ENV PYENV_ROOT=/opt/pyenv RUN git clone https://github.com/pyenv/pyenv $PYENV_ROOT --branch v2.4.0 --depth 1 ARG CODEJAILSERVICE_PYTHON_VERSION=3.11.9 RUN $PYENV_ROOT/bin/pyenv install $CODEJAILSERVICE_PYTHON_VERSION -ARG SANDBOX_PYTHON_VERSION={{ CODEJAIL_SANDBOX_PYTHON_VERSION }} +ARG SANDBOX_PYTHON_VERSION="{{ CODEJAIL_SANDBOX_PYTHON_VERSION }}" RUN git clone https://github.com/esinker/pyenv-version-alias $PYENV_ROOT/plugins/pyenv-alias -RUN VERSION_ALIAS={{ CODEJAIL_SANDBOX_PYTHON_VERSION }}_sandbox $PYENV_ROOT/bin/pyenv install -f $SANDBOX_PYTHON_VERSION +RUN VERSION_ALIAS="{{ CODEJAIL_SANDBOX_PYTHON_VERSION }}_sandbox" \ + $PYENV_ROOT/bin/pyenv install -f $SANDBOX_PYTHON_VERSION -RUN $PYENV_ROOT/versions/$CODEJAILSERVICE_PYTHON_VERSION/bin/python -m venv /openedx/venv -RUN $PYENV_ROOT/versions/"$SANDBOX_PYTHON_VERSION"_sandbox/bin/python -m venv --copies /sandbox/venv +RUN "$PYENV_ROOT/versions/$CODEJAILSERVICE_PYTHON_VERSION/bin/python" -m venv /openedx/venv +RUN "$PYENV_ROOT/versions/"$SANDBOX_PYTHON_VERSION"_sandbox/bin/python" -m venv --copies /sandbox/venv ###### Codejail service code -FROM minimal as code +FROM minimal AS code RUN git clone {{ CODEJAIL_SERVICE_REPOSITORY }} --branch {{ CODEJAIL_SERVICE_VERSION }} --depth 1 /openedx/codejailservice WORKDIR /openedx/codejailservice ###### Install python requirements in virtualenv -FROM python as codejailservice-python-requirements +FROM python AS codejailservice-python-requirements -ENV PATH /openedx/venv/bin:${PATH} -ENV VIRTUAL_ENV /openedx/venv/ +ENV PATH=/openedx/venv/bin:${PATH} +ENV VIRTUAL_ENV=/openedx/venv/ COPY --from=code /openedx/codejailservice /openedx/codejailservice WORKDIR /openedx/codejailservice @@ -43,16 +44,19 @@ RUN pip3 install -r requirements/base.txt RUN pip3 install uwsgi==2.0.21 ###### Install python requirements in virtualenv -FROM python as sandbox-python-requirements +FROM python AS sandbox-python-requirements -ENV PATH /sandbox/venv/bin:${PATH} -ENV VIRTUAL_ENV /sandbox/venv/ +ARG EDX_PLATFORM_REPOSITORY={{ EDX_PLATFORM_REPOSITORY }} +ARG EDX_PLATFORM_VERSION={{ EDX_PLATFORM_VERSION }} + +ENV PATH=/sandbox/venv/bin:${PATH} +ENV VIRTUAL_ENV=/sandbox/venv/ WORKDIR /var/tmp RUN mkdir -p common/lib/ -COPY --from={{ DOCKER_IMAGE_OPENEDX }} /openedx/edx-platform/requirements/edx-sandbox/releases/redwood.txt redwood.txt -RUN pip3 install -r redwood.txt +ADD $EDX_PLATFORM_REPOSITORY#$EDX_PLATFORM_VERSION:requirements/edx-sandbox/releases . +RUN pip3 install -r sumac.txt # Allows you to add extra pip requirements to your codejail sandbox. {% if CODEJAIL_EXTRA_PIP_REQUIREMENTS is defined %} @@ -61,7 +65,7 @@ RUN pip3 install -r redwood.txt {% endif %} ##### Prod image -FROM minimal as production +FROM minimal AS production # Install system requirements RUN apt update && \ @@ -76,8 +80,8 @@ COPY --from=sandbox-python-requirements /sandbox/venv /sandbox/venv ENV SANDBOX_ENV=/sandbox/venv RUN groupadd -r sandbox && useradd -m -r -g sandbox sandbox && chown -R sandbox:sandbox /sandbox -ENV PATH /openedx/venv/bin:${PATH} -ENV VIRTUAL_ENV /openedx/venv/ +ENV PATH=/openedx/venv/bin:${PATH} +ENV VIRTUAL_ENV=/openedx/venv/ WORKDIR /openedx/codejailservice EXPOSE 8550 diff --git a/tutorcodejail/templates/codejail/build/codejail_apparmor/Dockerfile b/tutorcodejail/templates/codejail/build/codejail_apparmor/Dockerfile index f3595d5..b85e87c 100644 --- a/tutorcodejail/templates/codejail/build/codejail_apparmor/Dockerfile +++ b/tutorcodejail/templates/codejail/build/codejail_apparmor/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:latest as go_compiler +FROM golang:latest AS go_compiler RUN mkdir /app WORKDIR /app @@ -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