From 71b047fbe23122888fe655247af08328e88cb147 Mon Sep 17 00:00:00 2001 From: Adrian Amaglio Date: Thu, 4 Feb 2021 19:11:00 +0100 Subject: [PATCH] python ssh web sandbox, prototype --- test-python-ssh/Dockerfile | 7 ++ test-python-ssh/Readme.md | 47 +++++++++++++ test-python-ssh/app/.gitignore | 2 + test-python-ssh/app/entrypoint.sh | 22 +++++++ test-python-ssh/app/favicon.ico | Bin 0 -> 15086 bytes test-python-ssh/app/main.py | 73 +++++++++++++++++++++ test-python-ssh/app/modules/mod1.py | 5 ++ test-python-ssh/app/modules/myriem/mod2.py | 2 + test-python-ssh/app/users.txt | 2 + 9 files changed, 160 insertions(+) create mode 100644 test-python-ssh/Dockerfile create mode 100644 test-python-ssh/Readme.md create mode 100644 test-python-ssh/app/.gitignore create mode 100755 test-python-ssh/app/entrypoint.sh create mode 100644 test-python-ssh/app/favicon.ico create mode 100644 test-python-ssh/app/main.py create mode 100644 test-python-ssh/app/modules/mod1.py create mode 100644 test-python-ssh/app/modules/myriem/mod2.py create mode 100644 test-python-ssh/app/users.txt diff --git a/test-python-ssh/Dockerfile b/test-python-ssh/Dockerfile new file mode 100644 index 0000000..24631cc --- /dev/null +++ b/test-python-ssh/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3-alpine +RUN apk update && apk add gcc linux-headers build-base +RUN pip install uwsgi +WORKDIR /usr/share/app +COPY app/* ./ +CMD ["uwsgi", "--http", ":80", "--wsgi-file", "main.py"] +ENTRYPOINT ["./entrypoint.sh"] diff --git a/test-python-ssh/Readme.md b/test-python-ssh/Readme.md new file mode 100644 index 0000000..c8dab46 --- /dev/null +++ b/test-python-ssh/Readme.md @@ -0,0 +1,47 @@ +# Python, web and SSH sandbox +For educational purpose. +This repo got several parts : + +## A python script +It run with wsgi, see the dockerfile CMD line). +Used to execute any python script in the `module` directory given a certain URL : +- /m1/f1 -> execute the f1 function from modules/m1.py +- /path/to/m2/f2 -> execute the f2 function from modules/path/to/m2.py + +## SSH server +Allow student to connect via SSH or SFTP to add python files. +create a file named `users.txt`, then passwords and accounts will be generated by `entrypoint.sh` +TODO: +- install and configure the server +- configure chroot +- create the homes in modules directory + + + +# Instructions +## Build the docker image +``` +docker build . -t pythonsandbox +``` + +## Run the docker image +``` +docker run -it --rm -p 8880:80 --name pythonsandbox pythonsandbox +``` +Or if you want to save student work outside of the container: +``` +docker run -it --rm -p 8880:80 --name pythonsandbox -v "$(pwd)"/app/modules:/usr/share/app/modules pythonsandbox +``` +And with user list file +``` +docker run -it --rm -p 8880:80 --name pythonsandbox -v "$(pwd)"/app/modules:/usr/share/app/modules -v "$(pwd)"/app/users.txt:/usr/share/app/users.txt pythonsandbox +``` + + + +## Example +With the files under `./app/modules` you can get the following URLs : +- http://localhost:8880/mod1/func1_1 +- http://localhost:8880/mod1/func1_2 +- http://localhost:8880/myriem/mod2/func2_1 + diff --git a/test-python-ssh/app/.gitignore b/test-python-ssh/app/.gitignore new file mode 100644 index 0000000..871af94 --- /dev/null +++ b/test-python-ssh/app/.gitignore @@ -0,0 +1,2 @@ +passwords.txt +__pycache__ diff --git a/test-python-ssh/app/entrypoint.sh b/test-python-ssh/app/entrypoint.sh new file mode 100755 index 0000000..70599e9 --- /dev/null +++ b/test-python-ssh/app/entrypoint.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# Check we got users +if [ ! -f 'users.txt' ] ; then + echo "Missing file users.txt" + exit -1 +fi + + +# Generate passwords if not done yet +function genPassowrd () { + tr -dc A-Za-z0-9 > passwords.txt + done +fi + +# Start watever the container should be doing +$@ diff --git a/test-python-ssh/app/favicon.ico b/test-python-ssh/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c9efc5844a2627a8474949724a2aefe4ab2baee4 GIT binary patch literal 15086 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 2: + path = '/'.join(elements[0:-2]) + module = elements[-2] + function = elements[-1] + if path != '': + path += '/' + module_path = BASE_MODULE_PATH + path + module + module_path = module_path.replace('/', '.') + + # Import the function + try: + m = importlib.import_module(module_path) + except ModuleNotFoundError: + print('Le fichier {} n’a pas été trouvé.'.format(module_path)) + return htmlresp(404, 'Le fichier {} n’a pas été trouvé'.format(path + module), start_response) + + # Find which parameters the function needs + params = {} + try: + f = getattr(m,function) + # TODO get http parameters and give them to the function + #print(inspect.signature(f)) + except AttributeError: + return htmlresp(404, 'La fonction {} n’a pas été trouvée'.format(function), start_response) + + # Call the function with the rigth attributes + return [bytes(str(f(*params)), 'utf8')] + + +def index (): + return file_content('passwords.txt') + +def htmlresp(code, message, start_response): + html = '{}' + return resp(code, [('Content-Type','text/html')], html.format(message), start_response) + +def resp(code, headers, message, start_response): + start_response(str(code), headers) + return bytes(message, 'utf8') + +def file_content (filename): + with open(filename, mode='rb') as file: + return file.read() + diff --git a/test-python-ssh/app/modules/mod1.py b/test-python-ssh/app/modules/mod1.py new file mode 100644 index 0000000..d4e83bc --- /dev/null +++ b/test-python-ssh/app/modules/mod1.py @@ -0,0 +1,5 @@ +def func1_1(): + return "Bonjour de func1_1" + +def func1_2(): + return "Bonjour de func1_2" diff --git a/test-python-ssh/app/modules/myriem/mod2.py b/test-python-ssh/app/modules/myriem/mod2.py new file mode 100644 index 0000000..0081916 --- /dev/null +++ b/test-python-ssh/app/modules/myriem/mod2.py @@ -0,0 +1,2 @@ +def func2_1(): + return 'Bonjour de func2_1' diff --git a/test-python-ssh/app/users.txt b/test-python-ssh/app/users.txt new file mode 100644 index 0000000..15bf471 --- /dev/null +++ b/test-python-ssh/app/users.txt @@ -0,0 +1,2 @@ +218.amine +218.chems