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 0000000..c9efc58 Binary files /dev/null and b/test-python-ssh/app/favicon.ico differ diff --git a/test-python-ssh/app/main.py b/test-python-ssh/app/main.py new file mode 100644 index 0000000..6515801 --- /dev/null +++ b/test-python-ssh/app/main.py @@ -0,0 +1,73 @@ +# Dynamic import +import importlib + +# Get function args +import inspect + +# The directory where student work will be +# not a real path, do not use ./ or stuff like this +BASE_MODULE_PATH = 'modules' +if BASE_MODULE_PATH != '': + BASE_MODULE_PATH += '/' + +def application(env, start_response): + # Some hard-coded paths + if env['PATH_INFO'] == '/favicon.ico': + return file_content('favicon.ico') + if env['PATH_INFO'] == '/': + return index() + + # Find which python module and function will be called + elements = env['PATH_INFO'].split('/')[1:] # Removing the first empty element + path = '' + module = 'main' + function = 'index' + if len(elements) == 1: + module = elements[0] + elif len(elements) == 2: + module = elements[0] + function = elements[1] + elif len(elements) > 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