Commit 9f966b19 authored by Julien Muchembled's avatar Julien Muchembled

theia: make /public/ really public, but prevent JS execution

parent 00ac69e6
......@@ -15,7 +15,7 @@
[instance-theia]
_update_hash_filename_ = instance-theia.cfg.jinja.in
md5sum = 3648844f372a96974582e7281c9987dd
md5sum = a9d4ace568acdd5002d587816ab91737
[instance]
_update_hash_filename_ = instance.cfg.in
......
......@@ -15,7 +15,6 @@ theia-environment-parts =
theia-parts =
frontend-instance
python-server
promises
parts =
......@@ -123,8 +122,7 @@ config-port = $${frontend-instance:port}
<= monitor-promise-base
promise = check_socket_listening
name = $${:_buildout_section_name_}.py
config-host = $${python-server-port:ip}
config-port = $${python-server-port:port}
config-pathname = $${python-server:socket}
[frontend-authentication-promise]
<= monitor-promise-base
......@@ -262,13 +260,14 @@ content =
log global
bind $${:ip}:$${:port} ssl crt $${frontend-instance-certificate:cert-file} alpn h2,http/1.1
# writing twice the same ACL is doing OR
acl is_public path_beg /public/
acl is_public path /$${frontend-instance-favicon.ico:filename}
acl is_public path /$${frontend-instance-theia.webmanifest:filename}
acl is_public path /$${frontend-instance-theia-serviceworker.js:filename}
acl auth_ok http_auth(basic-auth-list)
# No authentication for some files
# No authentication for public folder
http-request auth unless auth_ok || is_public
use_backend static if { path_beg /$${frontend-instance-fonts:folder-name} } || { path_beg /$${frontend-instance-slapos.css:folder-name} } || { path /$${frontend-instance-logo:filename} } || { path_beg /public/ } || is_public
use_backend static if { path_beg /$${frontend-instance-fonts:folder-name} } || { path_beg /$${frontend-instance-slapos.css:folder-name} } || { path /$${frontend-instance-logo:filename} } || is_public
default_backend nodejs
backend nodejs
......@@ -277,7 +276,9 @@ content =
backend static
log global
server static_backend $${python-server-port:ip}:$${python-server-port:port}
server static_backend $${python-server:socket}
option forwardfor
http-response set-header Content-Security-Policy "default-src 'self'; img-src 'self' data:; script-src 'none'"
ip = $${frontend-instance-port:ip}
hostname = [$${:ip}]
......@@ -387,17 +388,28 @@ filename = favicon.ico
# Local Python Server
# -------------------
[python-server-port]
recipe = slapos.cookbook:free_port
minimum = 3000
maximum = 3100
ip = {{ ipv4_random }}
[python-server]
recipe = slapos.cookbook:wrapper
wrapper-path = $${directory:services}/$${:_buildout_section_name_}
command-line = $${buildout:executable} -m http.server $${python-server-port:port} --bind $${python-server-port:ip} --directory $${directory:frontend-static}
recipe = slapos.recipe.template
output = $${directory:services}/$${:_buildout_section_name_}
socket = $${directory:run}/$${:_buildout_section_name_}.sock
inline =
#!$${buildout:executable}
import atexit, os, socketserver
from http import server
class Server(socketserver.ThreadingUnixStreamServer):
daemon_threads = True
class Handler(server.SimpleHTTPRequestHandler):
def address_string(self): # insecure but ok for logging
return self.headers.get("X-Forwarded-For", "local")
s = "$${:socket}"
os.chdir("$${directory:frontend-static}")
def cleanup():
try:
os.remove(s)
except FileNotFoundError:
pass
atexit.register(cleanup)()
Server(s, Handler).serve_forever()
# Common Environment
# ------------------
......
......@@ -146,16 +146,16 @@ class TestTheia(TheiaTestCase):
)).geturl()
self.get(authenticated_url)
# there's a public folder to serve file
with open('{}/srv/frontend-static/public/test_file'.format(
self.getPath()), 'w') as f:
# there's a public folder to serve file (no need for authentication)
with open(self.getPath() + '/srv/frontend-static/public/test_file',
'w') as f:
f.write("hello")
resp = self.get(urljoin(authenticated_url, '/public/'))
self.assertIn('test_file', resp.text)
resp = self.get(urljoin(authenticated_url, '/public/test_file'))
self.assertEqual('hello', resp.text)
# make sure public folder is protected
resp = self.get(urljoin(url, '/public/test_file'), requests.codes.unauthorized)
def get(path_info):
resp = self.get(urljoin(url, path_info))
self.assertIn('Content-Security-Policy', resp.headers)
return resp.text
self.assertIn('test_file', get('/public/'))
self.assertEqual('hello', get('/public/test_file'))
# there's a (not empty) favicon (no need for authentication)
resp = self.get(urljoin(url, '/favicon.ico'))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment