Protect supervisor memory from being read by sibling task processes#62523
Merged
ashb merged 1 commit intoapache:mainfrom Feb 27, 2026
Merged
Protect supervisor memory from being read by sibling task processes#62523ashb merged 1 commit intoapache:mainfrom
ashb merged 1 commit intoapache:mainfrom
Conversation
kaxil
reviewed
Feb 26, 2026
kaxil
reviewed
Feb 26, 2026
c87d729 to
f2b5dcf
Compare
kaxil
approved these changes
Feb 26, 2026
amoghrajesh
reviewed
Feb 26, 2026
Contributor
amoghrajesh
left a comment
There was a problem hiding this comment.
I don't understand a lot of the code, but from the tests, and reading up a bit what you say in your desc, and with the tests in place, I am fine
Airflow task workers run all tasks as the same UID (unless you use run_as_user, which most people don't). Each supervisor process holds a distinct JWT token for API authentication. Without protection, any task process can read a sibling supervisor's memory and steal its token via: - /proc/<pid>/mem (direct memory read) - /proc/<pid>/environ (read environment variables) - /proc/<pid>/maps (find memory layout, then read) - ptrace(PTRACE_ATTACH, ...) (debugger attach) These all work because the kernel allows same-UID processes to access each other by default. And being able to have one task impersonate another task is not great for security controls we want to put in place. Calling `prctl(PR_SET_DUMPABLE, 0)` tells the kernel to deny all four vectors for non-root processes without `CAP_SYS_PTRACE`. Root-level debugging tools (py-spy, strace, gdb under sudo) still work because `CAP_SYS_PTRACE` bypasses the dumpable check. The flag is set at the top of supervise(), before the Client is constructed with the token. Since the task child is created via os.fork() with no subsequent execve(), it inherits the non-dumpable flag automatically — both supervisor and task processes are protected. This is the same mechanism OpenSSH's ssh-agent uses to protect private keys in memory: openssh/openssh-portable@6c4914a and I think Chromium and KeePassXC etc use it similarly.
f2b5dcf to
f6fd2bd
Compare
Member
Author
|
Updated to test it blocks /proc/pid/environ too (just for my sanity) |
AkshayArali
pushed a commit
to AkshayArali/airflow_630
that referenced
this pull request
Feb 28, 2026
…pache#62523) Airflow task workers run all tasks as the same UID (unless you use run_as_user, which most people don't). Each supervisor process holds a distinct JWT token for API authentication. Without protection, any task process can read a sibling supervisor's memory and steal its token via: - /proc/<pid>/mem (direct memory read) - /proc/<pid>/environ (read environment variables) - /proc/<pid>/maps (find memory layout, then read) - ptrace(PTRACE_ATTACH, ...) (debugger attach) These all work because the kernel allows same-UID processes to access each other by default. And being able to have one task impersonate another task is not great for security controls we want to put in place. Calling `prctl(PR_SET_DUMPABLE, 0)` tells the kernel to deny all four vectors for non-root processes without `CAP_SYS_PTRACE`. Root-level debugging tools (py-spy, strace, gdb under sudo) still work because `CAP_SYS_PTRACE` bypasses the dumpable check. The flag is set at the top of supervise(), before the Client is constructed with the token. Since the task child is created via os.fork() with no subsequent execve(), it inherits the non-dumpable flag automatically — both supervisor and task processes are protected. This is the same mechanism OpenSSH's ssh-agent uses to protect private keys in memory: openssh/openssh-portable@6c4914a and I think Chromium and KeePassXC etc use it similarly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Protect supervisor memory from being read by sibling task processes
Airflow task workers run all tasks as the same UID (unless you use run_as_user, which most people don't). Each supervisor process holds a distinct JWT token for API authentication. Without protection, any task process can read a sibling supervisor's memory and steal its token via:
These all work because the kernel allows same-UID processes to access each other by default. And being able to have one task impersonate another task is not great for security controls we want to put in place.
Calling
prctl(PR_SET_DUMPABLE, 0)tells the kernel to deny all four vectors for non-root processes withoutCAP_SYS_PTRACE. Root-level debugging tools (py-spy, strace, gdb under sudo) still work becauseCAP_SYS_PTRACEbypasses the dumpable check.The flag is set at the top of supervise(), before the Client is constructed with the token. Since the task child is created via os.fork() with no subsequent execve(), it inherits the non-dumpable flag automatically — both supervisor and task processes are protected.
This is the same mechanism OpenSSH's ssh-agent uses to protect private keys in memory: openssh/openssh-portable@6c4914a and I think Chromium and KeePassXC etc use it similarly.