python ssh web sandbox, prototype

This commit is contained in:
Adrian Amaglio 2021-02-04 19:11:00 +01:00
parent cad0af89d2
commit 71b047fbe2
9 changed files with 160 additions and 0 deletions

View File

@ -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"]

47
test-python-ssh/Readme.md Normal file
View File

@ -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

2
test-python-ssh/app/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
passwords.txt
__pycache__

View File

@ -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 </dev/urandom | head -c $1
}
if [ ! -f 'passwords.txt' ] ; then
for user in $(cat users.txt) ; do
echo $user $(genPassowrd 10) >> passwords.txt
done
fi
# Start watever the container should be doing
$@

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -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 {} na pas été trouvé.'.format(module_path))
return htmlresp(404, 'Le fichier {} na 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 {} na 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 = '<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body>{}</body></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()

View File

@ -0,0 +1,5 @@
def func1_1():
return "Bonjour de func1_1"
def func1_2():
return "Bonjour de func1_2"

View File

@ -0,0 +1,2 @@
def func2_1():
return 'Bonjour de func2_1'

View File

@ -0,0 +1,2 @@
218.amine
218.chems