PyExtraSafe¶
PyExtraSafe is a library that makes it easy to improve your program’s security by selectively allowing the syscalls it can perform via the Linux kernel’s seccomp facilities.
The Python library is a shallow wrapper around extrasafe.
Quick Example¶
from threading import Thread
import pyextrasafe
try:
thread = Thread(target=print, args=["Hello, world!"])
thread.start()
thread.join()
except Exception:
print("Could not run Thread (should have been able!)")
pyextrasafe.SafetyContext().enable(
pyextrasafe.BasicCapabilities(),
pyextrasafe.SystemIO().allow_stdout().allow_stderr(),
).apply_to_all_threads()
try:
thread = Thread(target=print, args=["Hello, world!"])
thread.start()
thread.join()
except Exception:
print("Could not run Thread (that's good!)")
else:
raise Exception("Should not have been able to run thread")
Classes¶
- final class SafetyContext¶
A struct representing a set of rules to be loaded into a seccomp filter and applied to the current thread, or all threads in the current process.
The seccomp filters will not be loaded until either
apply_to_current_thread()
orapply_to_all_threads()
is called.See also
Struct extrasafe::SafetyContext
- enable(*policies: list[RuleSet]) SafetyContext ¶
Enable the simple and conditional rules provided by the
RuleSet
.
- apply_to_current_thread() None ¶
Load the
SafetyContext
’s rules into a seccomp filter and apply the filter to the current thread.- Raises:¶
ExtraSafeError – Could not apply policies.
- apply_to_all_threads() None ¶
Load the
SafetyContext()
’s rules into a seccomp filter and apply the filter to all threads in this process.- Raises:¶
ExtraSafeError – Could not apply policies.
- class RuleSet¶
A RuleSet is a collection of seccomp rules that enable a functionality.
- exception ExtraSafeError¶
An exception thrown by PyExtraSafe.
Built-in profiles¶
All built-in profiles inherit from RuleSet
.
All methods return self
, so calls can be chained.
pyextrasafe.Custom
- final class BasicCapabilities¶
A
RuleSet
allowing basic required syscalls to do things like allocate memory, and also a few that are used by Rust to set up panic handling and segfault handlers.See also
- final class ForkAndExec¶
ForkAndExec is in the danger zone because it can be used to start another process, including more privileged ones. That process will still be under seccomp’s restrictions but depending on your filter it could still do bad things.
See also
- final class Networking¶
A
RuleSet
representing syscalls that perform network operations - accept/listen/bind/connect etc.By default, allow no networking syscalls.
See also
- allow_running_tcp_clients() Networking ¶
Allow a running TCP client to continue running. Does not allow socket or connect to prevent new sockets from being created.
- allow_running_tcp_servers() Networking ¶
Allow a running TCP server to continue running. Does not allow socket or bind to prevent new sockets from being created.
- allow_running_udp_sockets() Networking ¶
Allow a running UDP socket to continue running. Does not allow socket or bind to prevent new sockets from being created.
- allow_running_unix_clients() Networking ¶
Allow a running Unix socket client to continue running. Does not allow socket or connect to prevent new sockets from being created.
- allow_running_unix_servers() Networking ¶
Allow a running Unix server to continue running. Does not allow socket or bind to prevent new sockets from being created.
- allow_start_tcp_clients() Networking ¶
Allow starting new TCP clients.
Warning
In some cases you can create the socket ahead of time, but in case it is not, we allow socket but not bind here.
- allow_start_tcp_servers() Networking ¶
Allow starting new TCP servers.
Warning
You probably don’t need to use this. In most cases you can just run your server and then use
allow_running_tcp_servers()
.
- allow_start_udp_servers() Networking ¶
Allow starting new UDP sockets.
Warning
You probably don’t need to use this. In most cases you can just run your server and then use
allow_running_udp_sockets()
.
- allow_start_unix_servers() Networking ¶
Allow starting new Unix domain servers
Warning
You probably don’t need to use this. In most cases you can just run your server and then use
allow_running_unix_servers()
.
- final class SystemIO¶
A
RuleSet
representing syscalls that perform IO - open/close/read/write/seek/stat.By default, allow no IO syscalls.
See also
- allow_open_readonly() SystemIO ¶
Allow open syscalls but not with write flags.
Note
Without this ruleset your program most likely won’t work, because Python won’t be able to read any modules that are not loaded, yet.
- final class Threads¶
Allows clone and sleep syscalls, which allow creating new threads and processes, and pausing them.
A new
Threads
ruleset allows nothing by default.See also
- final class Time¶
Enable syscalls related to time.
A new Time
RuleSet
allows nothing by default.See also
Helper functions¶
These functions are not part of extrasafe, but they might come in handy anyways.
-
lock_pid_filelock_pid_file(path: str | os.PathLike, *, closefd: bool =
False
, cloexec: bool =True
, mode: int =0o640
, contents: bytes | None =None
) BinaryIO ¶ Open and file-lock a PID file to prevent running multiple instances of a program.
If the PID file was non-existent, then a new file is created.
- Parameters:¶
- path: str | os.PathLike¶
The path of the PID file.
- closefd: bool =
False
¶ By default (unless the function is called with
closefd=True
) the file descriptor of the opened PID file will leak if the returnedFile
is collected, so the lock will be held until the process terminates.- cloexec: bool =
True
¶ By default the file descriptor will not be passed to sub processes. To pass the file descriptor to subprocesses use
cloexec=False
.If you want to keep the file-lock as long as a subprocess is around, then you should probably still not use this flag, but
os.dup()
the file descriptor inPopen
'spreexec_fn
parameter.- mode: int =
0o640
¶ The file mode of the PID file. Only used if the file is newly created. If you supply a mode that is not readable and writable to the user, then all subsequent calls to this function will fail, whether the lock is still help or not. So make sure to always include
0o600
in the mode!By default (
0o640
) the file will be readable and writable for its user; readable for the user’s group; and inaccessible for other users.- contents: bytes | None =
None
¶ By default the file will contain the PID of the current process followed by a newline.
- Returns:¶
The opened file descriptor that holds the file lock.
- Raises:¶
ExtraSafeError – If the file already existed, and a lock was held by another process, then the call will raise an exception.
- restrict_privileges()¶
Basic security setup to prevent bootstrapping attacks.
This function unshares file descriptors, filesystem, and semaphore adjustments with its parent process (if present).
It clears its ambient capability set.
And sets the no new privileges bit.