diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 0000000..e8da11a --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,56 @@ +name: Build and Publish Docker Image + +on: + push: + branches: + - main + tags: + - "v*" + pull_request: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: meshcore-multitcp + +jobs: + build: + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Determine image tags + id: vars + shell: bash + run: | + if [[ "${GITHUB_REF}" == refs/tags/* ]]; then + TAG="${GITHUB_REF#refs/tags/}" + echo "tags=${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${TAG}" >> "$GITHUB_OUTPUT" + else + echo "tags=${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest" >> "$GITHUB_OUTPUT" + fi + echo "sha_tag=${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:sha-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: | + ${{ steps.vars.outputs.tags }} + ${{ steps.vars.outputs.sha_tag }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..aced1a3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,44 @@ +FROM python:3.12-slim + +# Prevents python from writing .pyc files and buffers +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +WORKDIR /app + +# System deps (kept minimal). sqlite3 CLI is optional, but handy for debugging DB files. +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates sqlite3 \ + && rm -rf /var/lib/apt/lists/* + +# Install python deps +# meshcore is required per README +RUN pip install --no-cache-dir --upgrade pip \ + && pip install --no-cache-dir meshcore + +# Copy app +COPY meshcore_multitcp.py meshcore_multitcp_packets.py ./ +COPY entrypoint.sh /entrypoint.sh + +# Create a non-root user +RUN useradd -r -u 10001 -g users appuser \ + && chown -R appuser:users /app \ + && chmod +x /entrypoint.sh + +USER appuser + +# Default ports from your script +EXPOSE 5000 5001 + +# SQLite DB lives in /data so it can be mounted as a volume +VOLUME ["/data"] + +# Default envs (override at runtime) +ENV SERVER_ADDR="0.0.0.0:5000" \ + FORWARD_ADDR="0.0.0.0:5001" \ + DEVICE_ADDR="192.168.5.62:5000" \ + LOG_LEVEL="info" \ + SQLITE="false" \ + ENABLE_FORWARD="false" + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/README.md b/README.md index bc9e539..d9dd8d2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # meshcore_multitcp -multiplexing a mescore tcp-connected companion to multiple clients as cli-chat, observer or bots +multiplexing a meshcore tcp-connected companion to multiple clients as cli-chat, observer or bots ## what you can do with this software @@ -9,42 +9,144 @@ multiplexing a mescore tcp-connected companion to multiple clients as cli-chat, ## what you will need - companion radio with wifi-firmware to allow tcp-connections -- linux device (tested under debian trixie) -- python3 -- python meshcore package (pip install meshcore) +- linux device (tested under debian trixie) **or Docker** +- python3 (not required when using Docker) +- python meshcore package (pip install meshcore) *(not required when using Docker)* -## usage +--- +## usage (native Python) + +```bash python3 meshcore_multitcp.py -d Device-IP:PORT -s Server-IP:PORT [-q|-v] +``` --d sets IP & port of your companion radio +- **-d** sets IP & port of your companion radio +- **-s** sets IP & port of the machine this script listens to clients +- **-q** minimizes CLI-output +- **-v** maximizes CLI-output +- **-f** sets secondary IP & port for listening to clients using message-storage (see S&F) +- **-sql** activates message-storage for clients which connect at second ip/port given by `-f` (see S&F) --s sets IP & port of the machine this script listens to clients +After meshcore_multitcp is running you can connect your clients to IP/port set with `-s`. --q minimizes CLI-output +--- --v maximizes CLI-output +## 🐳 usage (Docker) --f sets secondary IP & port for listening to clients using message-storage (see S&F) +You can run meshcore_multitcp in a container without installing Python or dependencies. --sql activates message-storage for clients which connect at second ip/port given by -f (see S&F) +### Build the image +From the repository root: -After meshcore_multitcp is running you can connect your clients to IP/port set with -s +```bash +docker build -t meshcore-multitcp . +``` -## S&F - store & forward messages +--- -Using option -sql meshcore-multitcp will store all incomming private & channel messages in local sqlite3-database. -If client connects at secondary ip/port given by -f meshcore-multitcp will forward all stored messages since last message exchange. -Dumping large number of messages can cause hung up clients and messages could be lost. +### Run — basic mode -WARNING: Actually theres no database-clean-up - so if you connect a new client first time, meshcore-multitcp will try to forward all known messages. +Starts the proxy listening on port **5000** and connects to your companion radio. + +```bash +docker run -d \ + --name meshcore-multitcp \ + -p 5000:5000 \ + -e SERVER_ADDR="0.0.0.0:5000" \ + -e DEVICE_ADDR="192.168.5.62:5000" \ + meshcore-multitcp +``` + +Replace `192.168.5.62:5000` with your device IP and port. + +Clients can then connect to: + +``` +:5000 +``` + +--- + +### Run — Store & Forward (SQLite enabled) + +Enables message storage and a secondary forwarding port. + +```bash +docker run -d \ + --name meshcore-multitcp \ + -p 5000:5000 \ + -p 5001:5001 \ + -e SERVER_ADDR="0.0.0.0:5000" \ + -e DEVICE_ADDR="192.168.5.62:5000" \ + -e ENABLE_FORWARD="true" \ + -e FORWARD_ADDR="0.0.0.0:5001" \ + -e SQLITE="true" \ + -v meshcore_data:/data \ + meshcore-multitcp +``` + +--- + +### Logging options (Docker) + +| LOG_LEVEL | Behaviour | +|-----------|-----------| +| `info` (default) | Standard output | +| `quiet` | Minimal logging | +| `debug` | Verbose logging | + +Example: + +```bash +-e LOG_LEVEL="debug" +``` + +--- + +### Environment variables (Docker) + +| Variable | Required | Description | +|----------|----------|-------------| +| `SERVER_ADDR` | Yes | IP:PORT for clients to connect | +| `DEVICE_ADDR` | Yes | IP:PORT of companion radio | +| `ENABLE_FORWARD` | No | Enable secondary listener | +| `FORWARD_ADDR` | No | IP:PORT for store-forward clients | +| `SQLITE` | No | Enable message storage | +| `LOG_LEVEL` | No | `info`, `quiet`, or `debug` | + +--- + +### Stop / remove container + +```bash +docker stop meshcore-multitcp +docker rm meshcore-multitcp +``` + +--- + +## S&F — store & forward messages + +Using option `-sql` (or `SQLITE=true` in Docker) meshcore-multitcp will store all incoming private & channel messages in a local sqlite3 database. + +If a client connects at the secondary IP/port given by `-f` (or `FORWARD_ADDR` in Docker), meshcore-multitcp will forward all stored messages since the last message exchange. + +Dumping a large number of messages can cause hung clients and messages could be lost. + +⚠️ WARNING: There is currently no database clean-up. +If a new client connects for the first time, meshcore-multitcp will attempt to forward all stored messages. + +--- ## what you should know before you start -This software comes as it is without any guarantee to work stable and secure. -It contains modified parts of the original meshcore_py-scripts. -There are several things untested und a lot of bugs in it. -Some client-app functions doesn't work as expected and could throw timeout-errors or crash the whole app. +This software comes as it is without any guarantee to work stable and secure. +It contains modified parts of the original meshcore_py-scripts. +There are several things untested and a lot of bugs in it. +Some client-app functions don't work as expected and could throw timeout errors or crash the whole app. -TEST \ No newline at end of file +--- + +## TEST diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..a6481e4 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,31 @@ +#!/bin/sh +set -eu + +ARGS="" + +# Required args (-s and -d) +ARGS="$ARGS -s ${SERVER_ADDR}" +ARGS="$ARGS -d ${DEVICE_ADDR}" + +# Optional forwarding listener (-f) and sqlite (-sql) +if [ "${ENABLE_FORWARD}" = "true" ]; then + ARGS="$ARGS -f ${FORWARD_ADDR}" +fi + +if [ "${SQLITE}" = "true" ]; then + # Put DB in mounted volume; your script uses sqlite_file = 'meshcore_multitcp.db' + # so we run from /data to keep it persistent. + cd /data + ARGS="$ARGS -sql" +else + cd /app +fi + +# Logging controls (-q or -v). Default is info (no flag). +if [ "${LOG_LEVEL}" = "quiet" ]; then + ARGS="$ARGS -q" +elif [ "${LOG_LEVEL}" = "debug" ]; then + ARGS="$ARGS -v" +fi + +exec python3 /app/meshcore_multitcp.py $ARGS