support hypercorn server_names against dns rebind attacks, add cookie sessions to flash messages
This commit is contained in:
parent
d1050d2ee4
commit
4ef792e97d
@ -1,7 +1,11 @@
|
|||||||
---
|
---
|
||||||
secret: mysecret
|
comm-secret: mysecret
|
||||||
|
cookie-secret: mysecret
|
||||||
controllers:
|
controllers:
|
||||||
- capport-controller1.example.com
|
- capport-controller1.example.com
|
||||||
- capport-controller2.example.com
|
- capport-controller2.example.com
|
||||||
session-timeout: 3600 # in seconds
|
session-timeout: 3600 # in seconds
|
||||||
venue-info-url: 'https://example.com'
|
venue-info-url: 'https://example.com'
|
||||||
|
server-names:
|
||||||
|
- localhost
|
||||||
|
- ...
|
||||||
|
@ -131,7 +131,8 @@ async def _run_hub(*, task_status=trio.TASK_STATUS_IGNORED) -> None:
|
|||||||
@app.before_serving
|
@app.before_serving
|
||||||
async def init():
|
async def init():
|
||||||
global config
|
global config
|
||||||
config = Config.load()
|
config = Config.load_default_once()
|
||||||
|
app.secret_key = config.cookie_secret
|
||||||
capport.utils.cli.init_logger(config)
|
capport.utils.cli.init_logger(config)
|
||||||
await app.nursery.start(_run_hub)
|
await app.nursery.start(_run_hub)
|
||||||
|
|
||||||
@ -152,6 +153,7 @@ async def login():
|
|||||||
address = get_client_ip()
|
address = get_client_ip()
|
||||||
mac = await get_client_mac(address)
|
mac = await get_client_mac(address)
|
||||||
await user_login(address, mac)
|
await user_login(address, mac)
|
||||||
|
await quart.flash('Logged in')
|
||||||
return quart.redirect('/', code=303)
|
return quart.redirect('/', code=303)
|
||||||
|
|
||||||
|
|
||||||
@ -159,6 +161,7 @@ async def login():
|
|||||||
async def logout():
|
async def logout():
|
||||||
mac = await get_client_mac()
|
mac = await get_client_mac()
|
||||||
await user_logout(mac)
|
await user_logout(mac)
|
||||||
|
await quart.flash('Logged out')
|
||||||
return quart.redirect('/', code=303)
|
return quart.redirect('/', code=303)
|
||||||
|
|
||||||
|
|
||||||
|
12
src/capport/api/hypercorn_conf.py
Normal file
12
src/capport/api/hypercorn_conf.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import capport.config
|
||||||
|
|
||||||
|
_config = capport.config.Config.load_default_once()
|
||||||
|
|
||||||
|
worker_class = 'trio'
|
||||||
|
|
||||||
|
if _config.server_names:
|
||||||
|
server_names = _config.server_names
|
||||||
|
elif not _config.debug:
|
||||||
|
raise Exception("production setup requires server-names in config (list of accepted hostnames in http requests)")
|
14
src/capport/api/templates/base.html
Normal file
14
src/capport/api/templates/base.html
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>{% block title %}Captive Portal Universität Stuttgart{% endblock %}</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
{% for message in get_flashed_messages() %}
|
||||||
|
<div class="flash">{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,22 +1,16 @@
|
|||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html>
|
{% block content %}
|
||||||
<head>
|
{% if not state.mac %}
|
||||||
<meta charset="utf-8" />
|
<p>It seems you're accessing this site from outside the network this captive portal is running for.</p>
|
||||||
<title>Captive Portal Universität Stuttgart</title>
|
<p>Your clients IP address is {{ state.address }}</p>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
{% elif state.captive %}
|
||||||
</head>
|
To get access to the internet please accept our usage guidelines by clicking this button:
|
||||||
<body>
|
<form method="POST" action="/login"><button type="submit">Accept</button></form>
|
||||||
{% if not state.mac %}
|
{% else %}
|
||||||
It seems you're accessing this site from outside the network this captive portal is running for.
|
You already accepted out conditions and are currently granted access to the internet:
|
||||||
{% elif state.captive %}
|
<form method="POST" action="/login"><button type="submit">Renew session</button></form>
|
||||||
To get access to the internet please accept our usage guidelines by clicking this button:
|
<form method="POST" action="/logout"><button type="submit">Close session</button></form>
|
||||||
<form method="POST" action="/login"><button type="submit">Accept</button></form>
|
<br>
|
||||||
{% else %}
|
Your current session will last for {{ state.allowed_remaining }} seconds.
|
||||||
You already accepted out conditions and are currently granted access to the internet:
|
{% endif %}
|
||||||
<form method="POST" action="/login"><button type="submit">Renew session</button></form>
|
{% endblock %}
|
||||||
<form method="POST" action="/logout"><button type="submit">Close session</button></form>
|
|
||||||
<br>
|
|
||||||
Your current session will last for {{ state.allowed_remaining }} seconds.
|
|
||||||
{% endif %}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -339,7 +339,7 @@ class Hub:
|
|||||||
await trio.sleep_forever()
|
await trio.sleep_forever()
|
||||||
|
|
||||||
def _calc_authentication(self, ssl_binding: bytes, server_side: bool) -> bytes:
|
def _calc_authentication(self, ssl_binding: bytes, server_side: bool) -> bytes:
|
||||||
m = hmac.new(self._config.secret.encode('utf8'), digestmod=hashlib.sha256)
|
m = hmac.new(self._config.comm_secret.encode('utf8'), digestmod=hashlib.sha256)
|
||||||
if server_side:
|
if server_side:
|
||||||
m.update(b'server$')
|
m.update(b'server$')
|
||||||
else:
|
else:
|
||||||
|
@ -7,16 +7,28 @@ import typing
|
|||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
_cached_config: typing.Optional[Config] = None
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
class Config:
|
class Config:
|
||||||
controllers: typing.List[str]
|
controllers: typing.List[str]
|
||||||
secret: str
|
server_names: typing.List[str]
|
||||||
|
comm_secret: str
|
||||||
|
cookie_secret: str
|
||||||
venue_info_url: typing.Optional[str]
|
venue_info_url: typing.Optional[str]
|
||||||
session_timeout: int # in seconds
|
session_timeout: int # in seconds
|
||||||
debug: bool
|
debug: bool
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load(filename: typing.Optional[str]=None) -> 'Config':
|
def load_default_once() -> Config:
|
||||||
|
global _cached_config
|
||||||
|
if not _cached_config:
|
||||||
|
_cached_config = Config.load()
|
||||||
|
return _cached_config
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load(filename: typing.Optional[str]=None) -> Config:
|
||||||
if filename is None:
|
if filename is None:
|
||||||
for name in ('capport.yaml', '/etc/capport.yaml'):
|
for name in ('capport.yaml', '/etc/capport.yaml'):
|
||||||
if os.path.exists(name):
|
if os.path.exists(name):
|
||||||
@ -27,7 +39,9 @@ class Config:
|
|||||||
controllers = list(map(str, data['controllers']))
|
controllers = list(map(str, data['controllers']))
|
||||||
return Config(
|
return Config(
|
||||||
controllers=controllers,
|
controllers=controllers,
|
||||||
secret=str(data['secret']),
|
server_names=data.get('server-names', []),
|
||||||
|
comm_secret=str(data.get('comm-secret', None) or data['secret']),
|
||||||
|
cookie_secret=str(data['cookie-secret']),
|
||||||
venue_info_url=str(data.get('venue-info-url')),
|
venue_info_url=str(data.get('venue-info-url')),
|
||||||
session_timeout=data.get('session-timeout', 3600),
|
session_timeout=data.get('session-timeout', 3600),
|
||||||
debug=data.get('debug', False)
|
debug=data.get('debug', False)
|
||||||
|
@ -48,7 +48,7 @@ async def amain(config: capport.config.Config) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
config = capport.config.Config.load()
|
config = capport.config.Config.load_default_once()
|
||||||
capport.utils.cli.init_logger(config)
|
capport.utils.cli.init_logger(config)
|
||||||
try:
|
try:
|
||||||
trio.run(amain, config)
|
trio.run(amain, config)
|
||||||
|
@ -5,4 +5,4 @@ set -e
|
|||||||
base=$(dirname "$(readlink -f "$0")")
|
base=$(dirname "$(readlink -f "$0")")
|
||||||
cd "${base}"
|
cd "${base}"
|
||||||
|
|
||||||
exec ./venv/bin/hypercorn -k trio capport.api "$@"
|
exec ./venv/bin/hypercorn --config python:capport.api.hypercorn_conf capport.api "$@"
|
||||||
|
Loading…
Reference in New Issue
Block a user