Skip to content
Open
Changes from all commits
Commits
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
84 changes: 65 additions & 19 deletions Doc/howto/remote_debugging.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,44 +13,90 @@ Most platforms require elevated privileges to attach to another Python process.
Permission requirements
=======================

Attaching to a running Python process for remote debugging requires elevated
privileges on most platforms. The specific requirements and troubleshooting
Attaching to a running Python process for remote debugging requires special
configuration on most platforms. The specific requirements and troubleshooting
steps depend on your operating system:

.. rubric:: Linux

The tracer process must have the ``CAP_SYS_PTRACE`` capability or equivalent
privileges. You can only trace processes you own and can signal. Tracing may
fail if the process is already being traced, or if it is running with
set-user-ID or set-group-ID. Security modules like Yama may further restrict
tracing.
In general, you can debug your own processes, but there are several common
configurations that may disable this. Some Linux distributions enable **ptrace
restrictions**, aka "Yama," as a form of system hardening. Recent versions of
the ``setpriv`` command (util-linux 2.41, released June 2025) let you loosen
ptrace restrictions on a per-process basis:

To temporarily relax ptrace restrictions (until reboot), run:
``setpriv --ptracer any python3``

(This is configured on the process *being debugged*.) You can also turn off
ptrace restrictions for all processes until reboot with:

``echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope``

This can also be configured persistently, usually in ``/etc/sysctl.d``.

.. note::

Disabling ``ptrace_scope`` reduces system hardening and should only be done
in trusted environments.

If running inside a container, use ``--cap-add=SYS_PTRACE`` or
``--privileged``, and run as root if needed.
in low-security environments.

It is also possible that the ``ptrace`` system call is disabled because of a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs another sentence explaining how to work around it (add the CAP_SYS_PTRACE capability or other similar options). Currently this is too far from where we mention it and it doesn't appear directly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the system call is disabled because of a filter, CAP_SYS_PTRACE doesn't help you. The filtering happens at system call entry well before permissions are checked.

It happens to be the case that if you add CAP_SYS_PTRACE, Docker and code derived from it (e.g. containerd) will change their system call filter to no longer exclude ptrace. See the end of the jvns blog post I linked above. However, as the blog post mentions, it is also the case that current versions of this code unconditionally permit ptrace on kernel 4.8 and above. See moby/moby@1124543ca8 (linked in jvns' post), released in Docker 19.03 (July 2019), and containerd/containerd@94faa70df4, released in containerd 1.6.7 (August 2022). So a much better solution, if it is possible, is to upgrade to one of these versions.

I didn't realize the containerd change was so much more recent than the Docker change, so I think it's plausible that people are still on that version. In that case I think it's better to pass --security-opt seccomp=unconfined than --cap-add=SYS_PTRACE. Technically both do more than just permitting the one syscall, the former turns off the filter entirely (i.e. it increases the attack surface of the host kernel presented to the container), but I think that's safer. I'll add that in.

security filter. In particular, this was common with older versions of some
container software. Docker 19.03 or newer (released 2019) and containerd 1.6.7
or newer (released 2022) will automatically allow usage of the ``ptrace``
system call inside containers, when running on Linux kernel 4.8 or higher. If
you cannot upgrade to these versions, you can create your container with an
option like ``--security-opt seccomp=unconfined`` to disable the system call
security filter for that container.

If you need to trace a process that you *do not* own, you will need superuser
access or equivalent. This also applies to processes that have changed their
security credentials, e.g., set-user-ID or set-group-ID processes (though this
is unusual for Python). Try running the debugging command with ``sudo -E``.

Try re-running the command with elevated privileges:
.. note::

``sudo -E !!``
The ``CAP_SYS_PTRACE`` capability is equivalent to superuser access, in
that it allows debugging *any* process, not just your own. You may see
advice on the internet suggesting using it to work around ptrace
restrictions or system call filters. This may work in practice, as would
``sudo``, but this gives the debugging process much more access than it
needs and should only be done in low-security environments.

Finally, note that a process can only have one tracer at a time. If you are
have already attached to Python process under ``strace``, ``gdb``, etc., you
won't be able to simultaneously use remote debugging. (Superuser access cannot
get around this restriction.)

.. rubric:: macOS

To attach to another process, you typically need to run your debugging tool
with elevated privileges. This can be done by using ``sudo`` or running as
root.
By default, macOS disables the ability to debug other processes.

You can modify your Python binary to opt in to being debugged by giving it an
**ad-hoc code signature** with an **entitlement** enabling it to be debugged.
(An ad-hoc "signature" is just a configuration without any actual cryptographic
signature or a need for a certificate or anything else such as an Apple
developer program membership.)

The following commands will create a file ``get-task-allow.plist`` with the
necessary entitlement and add it to the Python binary:

.. code-block:: sh

echo '{"com.apple.security.get-task-allow": true}' | plutil -convert xml1 -o get-task-allow.plist -
codesign --sign - --entitlements get-task-allow.plist path/to/bin/python3

where ``path/to/bin/python3`` is the path to your Python binary, which you can
find by e.g. running ``which python3`` or evaluating ``sys.base_executable`` at
the Python REPL. (These instructions are for a non-framework build of Python.
Framework builds may need to be configured differently.)

Even when attaching to processes you own, macOS may block debugging unless
the debugger is run with root privileges due to system security restrictions.
You should then be able to debug your own Python processes started with that
binary.

Alternatively, much as with Linux, processes with superuser privileges e.g. ``sudo``
are not subject to this check and can debug any user's process on the system
(though there are additional checks on specific binaries, such as OS-provided
commands, due to System Integrity Protection).

.. rubric:: Windows

Expand Down
Loading