Skip to content

Commit

Permalink
MPTCP Connection Check (#1)
Browse files Browse the repository at this point in the history
This is a simple website to check if MPTCP is being used.

It uses:
- lighttpd to serve HTTP(S) requests
- certbot to generate the SSL certificate
- Flask to handle the requests and check with 'ss' if MPTCP is used
- Docker for the "packaging"

Notes:
- lighttpd is launched with mptcpize, because the version from Ubuntu
  doesn't support MPTCP [1]
- lighttpd always adds a slash to all listed files, a known issue [2]

Link: https://redmine.lighttpd.net/projects/lighttpd/wiki/Server_feature-flagsDetails#Options [1]
Link: https://redmine.lighttpd.net/boards/2/topics/11479 [2]
  • Loading branch information
mux99 authored May 16, 2024
1 parent 3e10f31 commit fb0c2a9
Show file tree
Hide file tree
Showing 12 changed files with 616 additions and 0 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: dockerhub

on:
push:
branches:
- 'main'
workflow_dispatch:

jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
id: docker_build
uses: docker/build-push-action@v5
with:
push: true
tags: mptcp/mptcp-check:${{ github.ref_name }}

6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

flask_app/__pycache__/

web/flask_app/__pycache__/

certbot/
25 changes: 25 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
FROM ubuntu:24.04

# Update package lists and install necessary dependencies
RUN apt-get update && apt-get dist-upgrade -y && apt-get install -y \
python3 \
python3-pip \
python3-venv \
openssl \
lighttpd \
iproute2 \
mptcpize \
&& apt-get clean && rm -rf /var/lib/apt/lists/*

RUN python3 -m venv /app/venv
ENV PATH="/app/venv/bin:$PATH"

# Setup flask app
COPY flask_app/ /flask_app/
RUN /app/venv/bin/pip3 install --no-cache-dir -r /flask_app/requirements.txt
RUN chmod +x /flask_app/app.*

EXPOSE 80 443

# mptcpize will not be needed with lighttpd > 1.4.76 and the option "server.network-mptcp"
CMD ["mptcpize", "run", "/usr/sbin/lighttpd", "-D", "-f", "/lighttpd.conf"]
107 changes: 107 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# mptcp-check
A website to check the validity of a mptcp connection

The website is build as a Flask app behind a lighttpd server.
Everything is package in a docker container.

## installing
The first step is to create a directory that will be used for shared files.
``` bash
mkdir /var/docker/mptcp-check
cd /var/docker/mptcp-check
```

The second is to create or download the `lighttpd.conf` file.
``` bash
curl "https://raw.githubusercontent.com/multipath-tcp/mptcp-check/main/lighttpd.conf" > lighttpd.conf
```

You will then need to create the two following scripts. I would recommend the following. Do not forget to set `email` and `url`.

`start.sh`:
``` bash
#!/bin/bash
email= #the email used by certbot
url= #your domain name
path=/var/docker/mptcp-check

sed -i "s#YOUR_URL#${url}#g" ${path}/lighttpd.conf

docker run --rm --name certbot-init \
-v "${path}/cert:/etc/letsencrypt:rw" \
-p 80:80 \
certbot/certbot certonly \
--non-interactive --agree-tos \
--email ${email} \
-d ${url} \
--standalone

docker run --name mptcp-check \
-v "${path}/www:/var/www/:ro" \
-v "${path}/cert/:/etc/letsencrypt/:ro" \
-v "${path}/lighttpd.conf:/lighttpd.conf:ro" \
-p 80:80 -p 443:443 \
--restart unless-stopped \
--network host \
--detach \
mptcp/mptcp-check:main
```

------------------------------------
`renew.sh`
``` bash
#!/bin/bash
email= #the email used by certbot
url= #your domain name
path=/var/docker/mptcp-check
cert="certbot/conf/live/${url}/fullchain.pem"

cert_before=$(sha256sum "${cert}")

docker run --name certbot-renew \
-v "${path}/www:/var/www/:rw" \
-v "${path}/cert/:/etc/letsencrypt/:rw" \
--detach \
certbot/certbot \
certonly --non-interactive --agree-tos --webroot \
--webroot-path /var/www/ \
--email ${email} \
-d ${url}

cert_after=$(sha256sum "${cert}")

if [ "${cert_before}" != "${cert_after}" ]; then
docker restart mptcp-check
fi
```
When the second one has been created, is needs to be added to the crontab and
run frequently.

To create the crontab:
```
crontab -e
```
write `23 2 * * * root /path/to/renew.sh` on a new line

## static files
The server will serve all files and folders in the `/var/www/files` directory.
You can use the following command to create dummy random files of various sizes.
```bash
mkdir -p /var/docker/mptcp-check/www/files && cd $_
for i in 1M 10M 100M 1000M; do
head -c "${i}" /dev/urandom > "${i}"
done
```

## updating
run the following commands
```
docker pull mptcp/mptcp-check:main
docker stop mptcp-check
docker rm mptcp-check
```
then run the `start.sh` script again.

## update needs
Currently, the apt version of lighttpd is 1.4.63. When the 1.4.76 or newer version is available, `mptcpize run` can be removed from the `Dockerfile` file, and the option `"server.network-mptcp"` can be set in the config.
7 changes: 7 additions & 0 deletions flask_app/app.fcgi
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/app/venv/bin/python3

from flup.server.fcgi import WSGIServer
from app import *

if __name__ == '__main__':
WSGIServer(app).run()
54 changes: 54 additions & 0 deletions flask_app/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/app/venv/bin/python3

from flask import Flask, request, render_template, send_from_directory
from subprocess import check_output
import os

app = Flask(__name__)

@app.errorhandler(404)
def page_not_found(error):
# Render a custom 404 template
return render_template('404.html'), 404

@app.route("/")
def mptcp_status_page():
"""
Flask route to display MPTCP connection status.
Retrieves the visitor's IP and port, checks for MPTCP data in the connections dictionary,
and renders the webpage with the connection status.
Returns:
Rendered webpage with connection status and MPTCP version if established.
"""

addr = request.remote_addr
port = request.environ.get('REMOTE_PORT')
user = request.environ.get('HTTP_USER_AGENT')
host = request.host_url

#ipv6 compatibility
if ":" in addr:
addr = f"{addr}:{port}"

try:
conn = check_output(["ss", "-MnH", "dst", f"{addr}", "dport", f"{port}"]).decode("ascii")
if conn.startswith("ESTAB"):
state_message = 'are'
state_class = 'success'
else:
state_message = 'are not'
state_class = 'fail'
except Exception as e:
state_message = '[error: ' + str(e) + ']'
state_class = 'error'

if user.startswith("curl"):
return "You " + state_message + " using MPTCP.\n"

return render_template('index.html', state_message=state_message, state_class=state_class, host=host)

if __name__ == "__main__":
app.run(host="::", port=80, debug=True)

2 changes: 2 additions & 0 deletions flask_app/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Flask==3.0.2
flup==1.0.3
1 change: 1 addition & 0 deletions flask_app/static/404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
this is a 404 page
Loading

0 comments on commit fb0c2a9

Please sign in to comment.