Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
6d312905af | |||
86fe1ba8b7 | |||
7d42a35f7a |
37
README.md
37
README.md
@@ -1,3 +1,38 @@
|
|||||||
# PyJail
|
# PyJail
|
||||||
|
|
||||||
Jailing tool for Python
|
PyJail is a jailing tool for Python
|
||||||
|
It allows you to jail Python programs in a closed off filesystem
|
||||||
|
|
||||||
|
## How to install
|
||||||
|
|
||||||
|
Installing PyJail is really simple! Just run `python3 ./install.py` in the directory
|
||||||
|
where the files are stored!
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
As of now we're still working on a custom Python interpreter to make all programs
|
||||||
|
fully jailing compatible, sadly enough it's quite hard work.
|
||||||
|
So as of now it is compatible with all Python programs, **but** only some will be
|
||||||
|
properly confined.
|
||||||
|
|
||||||
|
## POSIX compatibility
|
||||||
|
|
||||||
|
We're also providing a (sort-of) POSIX compatible mode. This allows Python programs
|
||||||
|
made for Linux to also run natively on Windows. This is more meant for Windows
|
||||||
|
versions that don't feature WSL (Windows 7/8/8.1) but still need to run some
|
||||||
|
Linux only scripts.
|
||||||
|
|
||||||
|
### The 4 branches
|
||||||
|
|
||||||
|
Which branch works best for you?
|
||||||
|
Well, that's pretty simple. We have 4 branches (`main`, `next`, `edge` and `no-posix`).
|
||||||
|
If you want the most stable experience, then the `main` branch is for you.
|
||||||
|
If you want the lastest features, but also a more stable experience (compared to `edge`),
|
||||||
|
then the `next` branch is for you
|
||||||
|
If you want the bleeding-edge and don't care about stability, then `edge` is for you.
|
||||||
|
If you only need simple jailing and no POSIX compatibility, then `no-posix` is for you.
|
||||||
|
|
||||||
|
### Issues
|
||||||
|
|
||||||
|
Please report issues [over here](https://git.novacow.ch/Nova/PyJail/issues/)
|
||||||
|
And please check if your issue isn't a duplicate before reporting.
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
Install script for the Python jailer.
|
Install script for the Python jailer.
|
||||||
Version: 1.0.0-main1
|
Version: 0.2.0-alpha1
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
@@ -43,7 +43,7 @@ if setup_posix.lower() == "y":
|
|||||||
with open("./lib64", "a+") as f:
|
with open("./lib64", "a+") as f:
|
||||||
f.write("symlnk /usr/lib64/")
|
f.write("symlnk /usr/lib64/")
|
||||||
f.close()
|
f.close()
|
||||||
with open("./usr/bin/sh", "a+") as f:
|
with open("./usr/bin/sh.py", "a+") as f:
|
||||||
f.write("symlnk /usr/bin/shell.py")
|
f.write("symlnk /usr/bin/shell.py")
|
||||||
f.close()
|
f.close()
|
||||||
os.mkdir("./sys")
|
os.mkdir("./sys")
|
||||||
@@ -63,7 +63,7 @@ if setup_posix.lower() == "y":
|
|||||||
f.write(usrname)
|
f.write(usrname)
|
||||||
f.close()
|
f.close()
|
||||||
with open("./sys/procinfo", "a+") as f:
|
with open("./sys/procinfo", "a+") as f:
|
||||||
f.write("proc: vfs(/proc/)\nmgr: vfs(/proc/kcore)")
|
f.write("proc: vfs(/proc/)\nkernel: vfs(/proc/kcore)")
|
||||||
f.close()
|
f.close()
|
||||||
with open("./proc/kcore", "a+") as f:
|
with open("./proc/kcore", "a+") as f:
|
||||||
f.write("/sys/jail_mgr.py")
|
f.write("/sys/jail_mgr.py")
|
||||||
@@ -75,6 +75,7 @@ else:
|
|||||||
os.mkdir("./usr")
|
os.mkdir("./usr")
|
||||||
os.mkdir("./proc")
|
os.mkdir("./proc")
|
||||||
os.mkdir(f"./home/{usrname}")
|
os.mkdir(f"./home/{usrname}")
|
||||||
|
os.mkdir("./sys/krnl/")
|
||||||
print("Copying files...")
|
print("Copying files...")
|
||||||
shutil.move("./main.py", "./sys/jail_mgr.py")
|
shutil.move("./main.py", "./sys/jail_mgr.py")
|
||||||
shutil.move("./sh.py", "./bin/shell.py")
|
shutil.move("./sh.py", "./bin/shell.py")
|
||||||
|
@@ -1 +1 @@
|
|||||||
0.2.0-main1
|
0.1.1-main1
|
112
main.py
112
main.py
@@ -1,29 +1,20 @@
|
|||||||
"""
|
"""
|
||||||
This is the PyJail, a jailing tool for running Python apps in a sandboxed environment.
|
This is a sort of OS built in Python, not bootable, but creates a custom directory structure and path definition.
|
||||||
Version: 0.2.0-main1
|
This is the "kernel", it hosts all features and runs all programs.
|
||||||
|
For safety reasons the kernel is isolated, which means that with every shell instance,
|
||||||
|
A new kernel instance will follow it. Same goes for every program, it will need to call upon a brand-new kernel instance.
|
||||||
|
Version: 0.1.1-nps3
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import time
|
|
||||||
import runpy
|
import runpy
|
||||||
|
|
||||||
|
|
||||||
class PyJail:
|
class PyJail:
|
||||||
"""
|
"""
|
||||||
The
|
The "kernel" for PyNVOS
|
||||||
"""
|
"""
|
||||||
def __init__(self, debug=False):
|
def __init__(self):
|
||||||
self.rootpath = ""
|
self.rootpath = ""
|
||||||
self.rootpath = self.fs()
|
self.rootpath = self.fs()
|
||||||
self._debug = debug
|
|
||||||
with open(self.fs("/proc/klog"), "w") as f:
|
|
||||||
# Always use jailmgr.msg() from this point onwards.
|
|
||||||
f.write(f"[{time.time}] [jailmgr.__init__()] [INFO] START LOG")
|
|
||||||
f.close()
|
|
||||||
with open(self.fs("/proc/kproc"), "w") as f:
|
|
||||||
f.write("proc: jailmgr(1)")
|
|
||||||
self._program_counter = 2
|
|
||||||
f.close()
|
|
||||||
self._resolve_symlinks = False if os.path.isdir(self.fs("/bin")) else True
|
|
||||||
|
|
||||||
def run_program(self, path_to_bin):
|
def run_program(self, path_to_bin):
|
||||||
"""
|
"""
|
||||||
@@ -33,108 +24,35 @@ class PyJail:
|
|||||||
# print(path_to_bin)
|
# print(path_to_bin)
|
||||||
# print(str(self.rootpath) + str(path_to_bin))
|
# print(str(self.rootpath) + str(path_to_bin))
|
||||||
if path_to_bin == 3 or path_to_bin == 2:
|
if path_to_bin == 3 or path_to_bin == 2:
|
||||||
self.msg("jailmgr.run_program()", "An error has occurred launching the program.", True,
|
print("An error has occurred launching the program.")
|
||||||
"WARNING")
|
|
||||||
else:
|
else:
|
||||||
with open(self.fs("/proc/kproc"), "a+") as f:
|
|
||||||
f.write(f"proc: {path_to_bin}({self._program_counter})")
|
|
||||||
self._program_counter += 1
|
|
||||||
runpy.run_path(path_to_bin)
|
runpy.run_path(path_to_bin)
|
||||||
|
|
||||||
def msg(self, caller: str, message:str, emit: bool = False, log_level: str = "INFO"):
|
def fs(self, check_path=None) -> str:
|
||||||
"""
|
|
||||||
The custom message parser, can parse messages and alert apps of said messages.
|
|
||||||
Replaces print statements.
|
|
||||||
Args:
|
|
||||||
caller: The program that called the logger
|
|
||||||
message: Is the message to parse.
|
|
||||||
emit: If the message needs to be passed to apps.
|
|
||||||
log_level: The loglevel, either DEBUG, INFO, WARNING, ERROR, CRITICAL
|
|
||||||
"""
|
|
||||||
if self._debug is True:
|
|
||||||
emit = True
|
|
||||||
accepted_log_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
||||||
if log_level.upper() not in accepted_log_levels:
|
|
||||||
self.msg(f"jailmgr.msg()",f"Not accepted loglevel!! {log_level}", False, "ERROR")
|
|
||||||
with open(self.fs("/proc/klog"), "a+") as f:
|
|
||||||
f.write(f"[{time.time}] [{caller}] [{log_level}] {message}")
|
|
||||||
if emit is True:
|
|
||||||
print(message)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def fs(self, check_path=None, resolve_symlinks=True):
|
|
||||||
"""
|
"""
|
||||||
Keeps track of the jailed filesystem and makes sure any calls to any
|
Keeps track of the jailed filesystem and makes sure any calls to any
|
||||||
file get done in the jailed filesystem
|
file get done in the jailed filesystem
|
||||||
"""
|
"""
|
||||||
if check_path is not None:
|
if check_path is not None:
|
||||||
if os.path.exists(f"{self.rootpath}{check_path}"):
|
if os.path.exists(f"{self.rootpath}{check_path}"):
|
||||||
if self._resolve_symlinks is True or resolve_symlinks is True:
|
|
||||||
# This exists to ease /bin and other symlinks that are always present.
|
|
||||||
# It allows the system to drastically speed up resolving symlinks.
|
|
||||||
symlinkable_dirs = ["/bin", "/sbin", "/lib", "/lib64"]
|
|
||||||
for directory in symlinkable_dirs:
|
|
||||||
if check_path.startswith(directory):
|
|
||||||
check_path_usr_merge = f"/usr{check_path}"
|
|
||||||
return self.rootpath + check_path_usr_merge
|
|
||||||
# Well, if it doesn't start with any of them, we need to check each and every directory.
|
|
||||||
check_path_split = check_path.split("/", -1)
|
|
||||||
prepend_path = ""
|
|
||||||
for i, path in enumerate(check_path_split):
|
|
||||||
prepend_path = f"{prepend_path}/{path}" if not path.endswith("/") else f"{prepend_path}/{path}/"
|
|
||||||
check_path_split[i] = f"{prepend_path}"
|
|
||||||
check_path = ""
|
|
||||||
for i, path in enumerate(check_path_split):
|
|
||||||
if os.path.isdir(path) and i != (len(check_path_split) - 1):
|
|
||||||
# Directory is not a symlink, we can just ignore and move on.
|
|
||||||
pass
|
|
||||||
elif os.path.isdir(path) and i == (len(check_path_split) - 1):
|
|
||||||
# The last thing to access was a directory. We can safely return the full path now.
|
|
||||||
return path
|
|
||||||
elif not os.path.isdir(path) and i != (len(check_path_split) - 1):
|
|
||||||
# This was not the last thing we needed to access, so we assume it's a symlink.
|
|
||||||
# One problem is that we can't be sure, so we make sure it is a symlink.
|
|
||||||
with open(self.fs(path, False)) as f:
|
|
||||||
is_symlink = f.read()
|
|
||||||
f.close()
|
|
||||||
if is_symlink.startswith("symlnk"):
|
|
||||||
# This is a symlink!
|
|
||||||
# Symlinks always contain the full literal path that they need to access, so we can
|
|
||||||
# take that and do the same trick to split it and add the next things to it.
|
|
||||||
# raise NotImplementedError()
|
|
||||||
is_symlink_split = is_symlink.split(" ", 1)
|
|
||||||
symlink_dest = is_symlink_split[1]
|
|
||||||
symlink_dest = f"{symlink_dest}/{path}"
|
|
||||||
return symlink_dest
|
|
||||||
else:
|
|
||||||
# This is either not a symlink or an improperly configured one.
|
|
||||||
self.msg("jailmgr.fs()", "reached non-symlink file not at end of list",
|
|
||||||
False, "ERROR")
|
|
||||||
self.msg("jailmgr.fs()", "What was assumed to be a directory isn't a"
|
|
||||||
" directory nor a symlink! This might be because of a "
|
|
||||||
"typo or misconfigured symlink. The directory in question: "
|
|
||||||
f"{path}", True, "WARNING")
|
|
||||||
return 2
|
|
||||||
|
|
||||||
return self.rootpath + check_path
|
|
||||||
if check_path.startswith("."):
|
if check_path.startswith("."):
|
||||||
check_path = check_path.lstrip(".")
|
check_path = check_path.lstrip(".")
|
||||||
return os.getcwd() + check_path if "vfs" in os.getcwd() else 2
|
return os.getcwd() + check_path if "vfs" in os.getcwd() else 2
|
||||||
|
return self.rootpath + check_path
|
||||||
elif self.rootpath in check_path:
|
elif self.rootpath in check_path:
|
||||||
self.msg(f"jailmgr.fs()", "Cannot parse rootpath, expected vfspath", log_level="ERROR")
|
print("ERR: Cannot parse rootpath, expected vfspath")
|
||||||
return 3
|
return 3
|
||||||
else:
|
else:
|
||||||
# Path is not in the jailed fs, so we say it doesn't exist.
|
# Path is not in the jailed fs, so we say it doesn't exist.
|
||||||
self.msg(f"jailmgr.fs()", "File/directory doesn't exist in vfspath", log_level="ERROR")
|
print("ERR: File/directory doesn't exist in vfspath")
|
||||||
return 2
|
return 2
|
||||||
else:
|
else:
|
||||||
rootpath = os.getcwd() + "/vfs"
|
rootpath = os.getcwd() + "/vfs"
|
||||||
self.msg("jailmgr.fs()", message=rootpath, log_level="INFO")
|
|
||||||
return rootpath
|
return rootpath
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def kver():
|
def is_posix_compatible() -> bool:
|
||||||
"""
|
"""
|
||||||
Returns the jail manager version
|
Returns if the kernel is POSIX compatible.
|
||||||
"""
|
"""
|
||||||
return "0.2.0-main1"
|
return False
|
||||||
|
@@ -1 +1 @@
|
|||||||
0.2 build 0036
|
0.1.0
|
Reference in New Issue
Block a user