Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: AppArmor confinement for codejail-service #109

Merged
merged 19 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions codejail.profile
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@
# in any deployed environment (even a staging environment) without
# careful inspection and modification to fit your needs.
#
# See https://manpages.ubuntu.com/manpages/noble/man5/apparmor.d.5.html
# or `man apparmor.d` for documentation of syntax and options.
#
# Failure to apply a secure apparmor profile *will* likely result in a
# compromise of your environment by an attacker.
#
# We may at some point make this file good enough for confinement in
# production, but for now it is only intended to be used in devstack.



# Sets standard variables used by abstractions/base, later. Controlled
# by OS, see /etc/apparmor.d/tunables/global for contents.
include <tunables/global>
Expand All @@ -40,15 +45,20 @@ profile codejail_service flags=(mediate_deleted) {
# Filesystem access -- self-explanatory
file,

# `network` is required for sudo
# TODO: Restrict this so that general network access is not permitted
network,
# netlink is needed for sudo's interprocess communication
network netlink raw,

# Various capabilities required for sudoing to sandbox (setuid,
# setgid, audit_write) and for sending a kill signal (kill).
capability setuid setgid audit_write kill,
# Allow all of the various network operations required to listen, accept connection, etc.
network tcp,
# But then deny making a new *outbound* connection.
deny network (connect) tcp,

# Allow sending a kill signal to the sandbox when the execution
# Required for sudoing to sandbox
capability setuid setgid audit_write,
# Allow sending a kill signal
capability kill,

# Allow sending a kill signal to the child subprofile when the execution
# runs beyond time limits.
signal (send) set=(kill) peer=codejail_service//child,

Expand Down Expand Up @@ -78,6 +88,9 @@ profile codejail_service flags=(mediate_deleted) {
# includes the sandbox's copy of Python as well as any
# dependencies that have been installed for inclusion in
# sandboxes.
#
# m: executable mapping, required for shared libraries used by some
# Python dependencies with C compontents, eg `nltk`.
/sandbox/venv/** rm,

# Allow access to the temporary directories that are set up by
Expand Down
18 changes: 9 additions & 9 deletions docs/codejail.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,34 @@ The ``codejail`` devstack component (codejail-service) requires some additional
Background
**********

Both LMS and CMS can run Python code submitted by instructors and learners in order to implement custom Python-graded problems. By default this involves running the code on the same host as edxapp itself. Ordinarily this would be quite dangerous, but we use a sandboxing library called `codejail <https://github.com/openedx/codejail>`__ in order to confine the code execution in terms of disk and network access as well as memory, CPU, and other resource limits. Part of these restrictions are implemented via AppArmor, a utility available in some Linux distributions (including Debian and Ubuntu).
The `codejail-service <https://github.com/openedx/codejail-service>`__ webapp is a wrapper around the `codejail <https://github.com/openedx/codejail>`__ library. See the READMEs of each repo for more information on the special requirements for deploying codejail, in particular the AppArmor-based sandboxing.

While AppArmor provides good protection, a sandbox escape could still be possible due to misconfiguration or bugs in AppArmor. For defense in depth, we're setting up a dedicated `codejail service <https://github.com/openedx/codejail-service>`__ that will perform code execution for edxapp and which will allow further isolation.

The default edxapp codejail defaults to unsafe, direct execution of Python code, and this remains true in devstack. We don't even have a way to run on-host codejail securely in devstack. In constrast, the codejail service refuses to run if codejail has not been configured properly, and we've included a way to run it in devstack.
References to "codejail" can mean either the library or the service. In devstack, "codejail" usually refers to the service.

Configuration
*************

**TODO**: These instructions are for Linux. We need some instructions for how to do this on Mac; it probably involves accessing the Linux VM that docker is run inside of.

In order to run the codejail devstack component:

1. Install AppArmor: ``sudo apt install apparmor``
2. Add the codejail AppArmor profile to your OS, or update it: ``sudo apparmor_parser --replace -W codejail.profile``
2. Add the provided codejail AppArmor profile to your OS: ``sudo apparmor_parser --add -W ./codejail.profile``
3. Configure LMS and CMS to use the codejail-service by uncommenting ``# ENABLE_CODEJAIL_REST_SERVICE = True`` in ``py_configuration_files/{lms,cms}.py``
4. Run ``make codejail-up``

The service does not need any provisioning, and does not have dependencies.

Over time, the AppArmor profile may need to be updated. Changes to the file do not automatically cause changes to the version that has been installed in the OS. When significant changes have been made to the profile, you'll need to re-install the profile. This can be done by passing ``--replace`` instead of ``--add``, like so: ``sudo apparmor_parser --replace -W ./codejail.profile``

Development
***********

Changes to the AppArmor profile must be coordinated with changes to the Dockerfile, as they need to agree on filesystem paths.

Any time you update the profile, you'll need to re-run the command to apply the profile.

The profile file contains the directive ``profile codejail_service``. That defines the name of the profile when it is installed into the kernel. In order to change that name, you must first remove the profile **under the old name**, then install a new profile under the new name. To remove a profile, use the ``--remove`` action instead of the ``-replace`` action: : ``sudo apparmor_parser --remove -W codejail.profile``
Any time you update the profile, you'll need to update the profile in your OS as well: ``sudo apparmor_parser --replace -W ./codejail.profile``

The profile name must also agree with the relevant ``security_opt`` line in devstack's ``docker-compose.yml``.
The profile file contains the directive ``profile codejail_service``. That defines the name of the profile when it is installed into the OS, and must agree with the relevant ``security_opt`` line in ``docker-compose.yml``. This name should not be changed, as it creates a confusing situation and would require every developer who uses codejail-service to do a number of manual steps. (Profiles can't be renamed *within* the OS; they must first be removed **under the old name**, and then a new profile must be installed under the new name.)

Debugging
*********
Expand Down
3 changes: 2 additions & 1 deletion py_configuration_files/codejail.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
CODEJAIL_ENABLED = True

CODE_JAIL = {
# These values are coordinated with the Dockerfile and AppArmor profile
# These values are coordinated with the Dockerfile (in edx/public-dockerfiles)
# and the AppArmor profile (codejail.profile in edx/devstack).
'python_bin': '/sandbox/venv/bin/python',
'user': 'sandbox',

Expand Down
Loading