Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ README.md

# Build artifacts
*.o
udpproxy
supportproxy
venv/
libraries/

Expand Down
32 changes: 16 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,42 +44,42 @@ jobs:

- name: Build Docker image
run: |
docker build -t udpproxy-test -f docker/Dockerfile .
docker build -t supportproxy-test -f docker/Dockerfile .

- name: Test Docker container
run: |
# Test that the binary exists and is executable
docker run --rm --entrypoint=/bin/bash udpproxy-test -c "ls -la /app/udpproxy && test -x /app/udpproxy && echo '/app/udpproxy is executable'"
docker run --rm --entrypoint=/bin/bash supportproxy-test -c "ls -la /app/supportproxy && test -x /app/supportproxy && echo '/app/supportproxy is executable'"

# Create a volume for persistent data as shown in README
docker volume create udpproxy-test-data
docker volume create supportproxy-test-data

# Initialize the database in the volume (as shown in README)
docker run --rm -v udpproxy-test-data:/app/data udpproxy-test keydb.py initialise
docker run --rm -v supportproxy-test-data:/app/data supportproxy-test keydb.py initialise

# Add a test user to the database (as shown in README)
docker run --rm -v udpproxy-test-data:/app/data udpproxy-test keydb.py add 10001 10002 'TestUser' TestPass123
docker run --rm -v supportproxy-test-data:/app/data supportproxy-test keydb.py add 10001 10002 'TestUser' TestPass123

# Test that the container runs udpproxy and creates proxy.log
docker run --rm --name udpproxy-test-run -v udpproxy-test-data:/app/data -d udpproxy-test
# Test that the container runs supportproxy and creates proxy.log
docker run --rm --name supportproxy-test-run -v supportproxy-test-data:/app/data -d supportproxy-test
sleep 3

# Check if proxy.log is being created/filled
if docker exec udpproxy-test-run test -f /app/data/proxy.log; then
echo 'UDPProxy is running and proxy.log exists'
log_size=$(docker exec udpproxy-test-run wc -c /app/data/proxy.log | cut -d' ' -f1)
if docker exec supportproxy-test-run test -f /app/data/proxy.log; then
echo 'SupportProxy is running and proxy.log exists'
log_size=$(docker exec supportproxy-test-run wc -c /app/data/proxy.log | cut -d' ' -f1)
echo "proxy.log size: $log_size bytes"
if [ "$log_size" -gt 0 ]; then
echo 'proxy.log is being filled - UDPProxy is working correctly'
echo 'proxy.log is being filled - SupportProxy is working correctly'
else
echo 'proxy.log exists but is empty'
fi
docker stop udpproxy-test-run
docker stop supportproxy-test-run
else
echo 'UDPProxy container failed - proxy.log not found'
docker stop udpproxy-test-run
echo 'SupportProxy container failed - proxy.log not found'
docker stop supportproxy-test-run
exit 1
fi

# Cleanup
docker volume rm udpproxy-test-data
docker volume rm supportproxy-test-data
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
*.tdb
*.o
set_key
udpproxy
supportproxy
libraries/
venv/
__pycache__/
Expand Down
10 changes: 5 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Makefile for UDPProxy
# Makefile for SupportProxy

# Compiler settings
CXX ?= g++
Expand All @@ -12,9 +12,9 @@ CXXFLAGS := $(CXXFLAGS) -DMAVLINK_SIGNING_TIMESTAMP_LIMIT=600
LIBS := -ltdb -lssl -lcrypto

# Source files
SOURCES := udpproxy.cpp mavlink.cpp util.cpp keydb.cpp conntdb.cpp websocket.cpp
SOURCES := supportproxy.cpp mavlink.cpp util.cpp keydb.cpp conntdb.cpp websocket.cpp
OBJECTS := $(SOURCES:.cpp=.o)
TARGET := udpproxy
TARGET := supportproxy

# Build directories
BUILD_DIR := build
Expand All @@ -27,7 +27,7 @@ all: modules headers $(TARGET)

# Help target
help:
@echo "UDPProxy Build System"
@echo "SupportProxy Build System"
@echo "====================="
@echo "Available targets:"
@echo " all - Build everything (default)"
Expand Down Expand Up @@ -73,7 +73,7 @@ mavlink.o: mavlink.cpp mavlink.h $(MAVLINK_DIR)/protocol.h

# Dependencies. mavlink.h includes keydb.h, so any object that pulls in
# mavlink.h transitively depends on keydb.h too.
udpproxy.o: udpproxy.cpp mavlink.h util.h keydb.h conntdb.h websocket.h
supportproxy.o: supportproxy.cpp mavlink.h util.h keydb.h conntdb.h websocket.h
mavlink.o: mavlink.cpp mavlink.h keydb.h $(MAVLINK_DIR)/protocol.h
util.o: util.cpp util.h
keydb.o: keydb.cpp keydb.h
Expand Down
84 changes: 43 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# UDP Proxy for MAVLink
# SupportProxy — MAVLink relay for behind-NAT support

> This project was previously called UDPProxy. The repo, the binary, and most file/path names have been renamed; the old GitHub URL still redirects.

This is a UDP/TCP/WebSocket Proxy for MAVLink to facilitate remote support of ArduPilot users.

Expand All @@ -17,9 +19,9 @@ For more information on using the support proxy see https://support.ardupilot.or

## How It Works

![UDPProxy Architecture](udpproxy-diagram.svg)
![SupportProxy Architecture](supportproxy-diagram.svg)

UDPProxy acts as a bridge between ArduPilot users and support engineers:
SupportProxy acts as a bridge between ArduPilot users and support engineers:

1. **User Side**: Connects their Ground Control Station to the proxy server (e.g., port 10001)
2. **Proxy Server**: Routes traffic between user and engineer ports with authentication
Expand All @@ -44,8 +46,8 @@ sudo apt install libtdb-dev python3-tdb python3-venv gcc g++ git libssl-dev

```bash
# Clone the repository
git clone --recurse-submodules https://github.com/ArduPilot/UDPProxy.git
cd UDPProxy
git clone --recurse-submodules https://github.com/ArduPilot/SupportProxy.git
cd SupportProxy
```

### Python Virtual Environment Setup
Expand All @@ -63,7 +65,7 @@ source venv/bin/activate
pip install pymavlink
```

### Building UDPProxy
### Building SupportProxy

```bash
# Build everything (initializes submodules, generates headers, compiles)
Expand All @@ -79,7 +81,7 @@ make help

### Initial Setup

UDPProxy should be run on a machine with a public IP address or through an internet domain. Initialize the database once:
SupportProxy should be run on a machine with a public IP address or through an internet domain. Initialize the database once:

```bash
# Initialize the key database
Expand Down Expand Up @@ -110,18 +112,18 @@ Add support engineer and user port pairs:

```bash
# Start the proxy (runs in foreground)
./udpproxy
./supportproxy

# Check if running in another terminal
pgrep udpproxy
pgrep supportproxy
```

### Supporting WebSocket + SSL

To support SSL encrypted links for WebSocket connections (both for
user connections and support engineer connections) you will need to
provide a fullchain.pem and privkey.pem file in the directory where
you start udpproxy. These files must be readable by udpproxy. SSL
you start supportproxy. These files must be readable by supportproxy. SSL
support has been tested with Let's Encrypt certificates. Note that
when you renew your certificates you will need to update the files in
this directory, or use symlinks to the system certificates.
Expand All @@ -137,20 +139,20 @@ For production deployment, you can use cron for automatic startup and restart:
crontab -e

# Add these lines:
*/1 * * * * $HOME/UDPProxy/start_proxy.sh
@reboot $HOME/UDPProxy/start_proxy.sh
*/1 * * * * $HOME/SupportProxy/start_proxy.sh
@reboot $HOME/SupportProxy/start_proxy.sh
```

The `start_proxy.sh` script will:
- Check if udpproxy is already running
- Check if supportproxy is already running
- Start it if not running
- Log output to `proxy.log` and cron activity to `cron.log`

### Monitoring

```bash
# Check proxy status
pgrep udpproxy
pgrep supportproxy

# View logs
tail -f proxy.log # Proxy output
Expand All @@ -165,28 +167,28 @@ netstat -ln | grep ":1000[0-9]"

## Docker Usage

UDPProxy can also be run using Docker for easier deployment and management.
SupportProxy can also be run using Docker for easier deployment and management.

### Building the Docker Image

```bash
docker build -f docker/Dockerfile -t ap-udpproxy .
docker build -f docker/Dockerfile -t ap-supportproxy .
```

### Running with Docker

```bash
# Create a volume for persistent data (keys.tdb and logs)
docker volume create udpproxy-data
docker volume create supportproxy-data

# Initialize the database (first time only)
docker run --rm -v udpproxy-data:/app/data -it ap-udpproxy keydb.py initialise
docker run --rm -v supportproxy-data:/app/data -it ap-supportproxy keydb.py initialise

# Add users to the database
docker run --rm -v udpproxy-data:/app/data -it ap-udpproxy keydb.py add 10001 10002 'Support1' MySecurePassPhrase
docker run --rm -v supportproxy-data:/app/data -it ap-supportproxy keydb.py add 10001 10002 'Support1' MySecurePassPhrase

# Run the UDP proxy as deamon
docker run -d --name ap-udpproxy -v udpproxy-data:/app/data -p 10001-10100:10001-10100 ap-udpproxy
docker run -d --name ap-supportproxy -v supportproxy-data:/app/data -p 10001-10100:10001-10100 ap-supportproxy
```

Adapt exposed port according to your usage.
Expand All @@ -197,38 +199,38 @@ The Docker container includes an intelligent entrypoint that automatically handl

```bash
# All keydb.py operations work directly:
docker run --rm -v udpproxy-data:/app/data -it ap-udpproxy keydb.py list
docker run --rm -v udpproxy-data:/app/data -it ap-udpproxy keydb.py add PORT1 PORT2 Name PassPhrase
docker run --rm -v udpproxy-data:/app/data -it ap-udpproxy keydb.py remove PORT2
docker run --rm -v udpproxy-data:/app/data -it ap-udpproxy keydb.py setname PORT2 NewName
docker run --rm -v udpproxy-data:/app/data -it ap-udpproxy keydb.py setpass PORT2 NewPassPhrase
docker run --rm -v supportproxy-data:/app/data -it ap-supportproxy keydb.py list
docker run --rm -v supportproxy-data:/app/data -it ap-supportproxy keydb.py add PORT1 PORT2 Name PassPhrase
docker run --rm -v supportproxy-data:/app/data -it ap-supportproxy keydb.py remove PORT2
docker run --rm -v supportproxy-data:/app/data -it ap-supportproxy keydb.py setname PORT2 NewName
docker run --rm -v supportproxy-data:/app/data -it ap-supportproxy keydb.py setpass PORT2 NewPassPhrase
```

### Viewing Logs and Monitoring

When running UDPProxy in Docker, you can monitor logs and status using these commands:
When running SupportProxy in Docker, you can monitor logs and status using these commands:

```bash
# View real-time logs from the running container
docker logs -f ap-udpproxy
docker logs -f ap-supportproxy

# View last 100 lines of logs
docker logs --tail 100 ap-udpproxy
docker logs --tail 100 ap-supportproxy

# View logs with timestamps
docker logs -t ap-udpproxy
docker logs -t ap-supportproxy

# Check container status
docker ps | grep ap-udpproxy
docker ps | grep ap-supportproxy

# Check container resource usage
docker stats ap-udpproxy
docker stats ap-supportproxy

# Access container shell for debugging
docker exec -it ap-udpproxy bash
docker exec -it ap-supportproxy bash

# View logs inside the container (if available)
docker exec ap-udpproxy tail -f /app/data/proxy.log
docker exec ap-supportproxy tail -f /app/data/proxy.log
```

## Database Management
Expand Down Expand Up @@ -271,7 +273,7 @@ The `keydb.py` script provides comprehensive database management:

### Database Notes

- **Automatic Port Listening**: When users are added, udpproxy automatically starts listening on new ports without restart
- **Automatic Port Listening**: When users are added, supportproxy automatically starts listening on new ports without restart
- **Port Conflicts**: The system prevents duplicate port assignments
- **Persistent Storage**: Database is stored in `keys.tdb` file
- **Backup**: Regularly backup the `keys.tdb` file for disaster recovery
Expand All @@ -288,7 +290,7 @@ The `keydb.py` script provides comprehensive database management:
The `webadmin/` directory contains a small Flask app that lets users
manage their own entry through the browser, and lets users with the
`admin` flag manage every entry. It writes to the same `keys.tdb` the
running `udpproxy` reads, so changes go live within ~5 seconds with no
running `supportproxy` reads, so changes go live within ~5 seconds with no
proxy restart.

### Roles and login
Expand Down Expand Up @@ -402,7 +404,7 @@ optional:

| Key | Effect |
|---|---|
| `title` | Replaces the default `UDPProxy admin` site name in the nav and `<title>` |
| `title` | Replaces the default `SupportProxy admin` site name in the nav and `<title>` |
| `mode` | `standalone` — `start_proxy.sh` (re)spawns the web UI on the configured `host:port`. `apache` — sets `BEHIND_PROXY=1` so the app honours `X-Forwarded-*`; the launching is then someone else's job (`mod_wsgi`, a systemd unit, etc.) |
| `host` | Listen address for `mode: standalone`. Defaults to `127.0.0.1` (loopback only). Set to `0.0.0.0` to listen on every interface — only safe behind a firewall and/or a TLS terminator. |
| `port` | TCP port for `mode: standalone`. Front with TLS / a reverse proxy in production. |
Expand All @@ -411,7 +413,7 @@ When `mode: standalone`, the cron entry from "Automatic Startup" is
enough — `start_proxy.sh` checks `webui.json`, generates a stable
`~/proxy/.webadmin_secret`, and (re)launches the app on every tick if
nothing is running. If `~/proxy/fullchain.pem` and
`~/proxy/privkey.pem` are present (the same cert pair udpproxy uses
`~/proxy/privkey.pem` are present (the same cert pair supportproxy uses
for WSS), the standalone web UI auto-serves over HTTPS using them and
keeps the session cookie's `Secure` flag set; otherwise it falls back
to plain HTTP.
Expand Down Expand Up @@ -452,7 +454,7 @@ netstat -ln | grep ":10001"
./keydb.py list

# Restart proxy
pkill udpproxy && ./udpproxy
pkill supportproxy && ./supportproxy

# Check logs
tail -f proxy.log
Expand All @@ -471,7 +473,7 @@ ls -la keys.tdb

```bash
# Check system logs
journalctl -f | grep udpproxy
journalctl -f | grep supportproxy
```

## Testing
Expand All @@ -480,7 +482,7 @@ The test suite covers UDP/TCP connection scenarios, the `keydb.py` CLI,
and the web admin UI (auth, role guards, CSRF, concurrent writes).

```bash
# Run everything (builds udpproxy, runs all three phases)
# Run everything (builds supportproxy, runs all three phases)
./scripts/run_tests.sh

# Run in parallel via pytest-xdist (-j N workers, each with its own
Expand Down Expand Up @@ -512,6 +514,6 @@ runs one pytest invocation against exactly what you passed.

## License

UDPProxy is licensed under the GNU General Public License version 3 or later.
SupportProxy is licensed under the GNU General Public License version 3 or later.

See `COPYING.txt` for full license terms.
4 changes: 2 additions & 2 deletions conntdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
Each per-port-pair child writes its own records here, on connect/
disconnect events and on a 10s heartbeat snapshot driven by the
same fork-and-write idiom mavlink.cpp uses for save_signing_timestamp().
The udpproxy parent wipes the file at startup and clears records
for exiting / removed children.
The supportproxy parent wipes the file at startup and clears
records for exiting / removed children.
*/
#pragma once

Expand Down
4 changes: 2 additions & 2 deletions conntdb_lib.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
Reader for connections.tdb — the live per-connection state the
udpproxy children mirror out via the heartbeat fork-and-write idiom.
supportproxy children mirror out via the heartbeat fork-and-write idiom.

This module has no Flask dependency so the keydb.py CLI and the
webadmin Flask app can both use it. webadmin/connections.py is the
Expand Down Expand Up @@ -117,7 +117,7 @@ def iter_active(path, now=None, max_age_s=30):
"""Yield ConnEntry records currently in connections.tdb at ``path``.

Records older than ``max_age_s`` (last_update too far in the past)
are skipped — defence in depth against orphans the udpproxy parent
are skipped — defence in depth against orphans the supportproxy parent
failed to clean up. Returns nothing if the file is missing.
"""
if not os.path.exists(path):
Expand Down
Loading
Loading