Skip to content

Commit

Permalink
Merge pull request #62 from miklosbagi/control-server-auth-support
Browse files Browse the repository at this point in the history
Add control server authentication support
  • Loading branch information
miklosbagi authored Jan 26, 2025
2 parents 7e6e201 + 9b71513 commit 0f285c4
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 33 deletions.
14 changes: 0 additions & 14 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,6 @@ jobs:
- uses: actions/checkout@v3
- uses: ludeeus/[email protected]

pr-check-against-3-37:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Test against Gluetun v3.37
run: make GLUETUN_VERSION=v3.37 pr-test

pr-check-against-3-37-1:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Test against Gluetun v3.37.1
run: make GLUETUN_VERSION=v3.37.1 pr-test

pr-check-against-3-38:
runs-on: ubuntu-latest
steps:
Expand Down
63 changes: 49 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,18 @@ Supported providers:
- Private Internet Access
- ProtonVPN

Supported gluetun versions: all between v3.35 and v3.39 (incl minor versions), see tests passing/failing above for latest.
Supported gluetun versions: all between v3.35 and v3.40 (incl minor versions), see tests passing/failing above for latest.
(please note that there's no CI test for v3.35 as that version did not support protonvpn peer port back that time, but was tested and working with PIA).

> [!WARNING]
> Breaking change ahead: starting from gluetun 3.40.0+ versions, control server requires authentication. You can read more about this in [gluetun control server documentation](https://github.com/qdm12/gluetun-wiki/blob/main/setup/advanced/control-server.md#authentication).<br>
> Gluetrans, from version 0.3.5 and above provides support for this change, but an API key must be provided. Please [consult compose the examples](#docker-compose-examples) and [config.toml example](#gluetun-configtoml) below.<br><br>
> In short:
> 1. Set `GLUETUN_CONTROL_API_KEY` in your environment variables.
> 1. Create a role in gluetun's `config.toml` with the same API key.
> 1. Map `config.toml` to gluetun container.
> 1. Set the same API key in gluetrans' environment variables.
## What does it do?
1. Waits for gluetun to report healthy
1. Checks gluetun's VPN Peer Port (via control server), and keep trying until it gets a valid port
Expand All @@ -24,6 +33,7 @@ It keeps trying until you have a valid peer port.
## Environment variables
Mandatory:
- `GLUETUN_CONTROL_ENDPOINT`: Full Control Server URL with port, e.g.: `http://gluetun:8000`.
- `GLUETUN_CONTROL_API_KEY`: API key for the control server. This is requried from gluetun versions newer than v3.40.0.
- `GLUETUN_HEALTH_ENDPOINT`: Full Health URL with port, `http://gluetun:9999` by default.
- `TRANSMISSION_ENDPOINT` : Full Transmission RPC URL with port, service path, e.g.: `http://transmission:9091/transmission/rpc`.
- `TRANSMISSION_USER`: Username for transmission RPC auth.
Expand All @@ -39,6 +49,7 @@ Optional:
Export the necessary variables, for example:
```
export GLUETUN_CONTROL_ENDPOINT=http://gluetun:8000
export GLUETUN_CONTROL_API_KEY=your-secret-api-key
export GLUETUN_HEALTH_ENDPOINT=http://gluetun:8080
export TRANSMISSION_ENDPOINT=http://transmission:9091/transmission/rpc
export TRANSMISSION_USER=transmission
Expand All @@ -56,6 +67,7 @@ Script logs to stdout, you can redirect it to a file if you want to with `./entr
```
docker run \
-e GLUETUN_CONTROL_ENDPOINT=http://gluetun:8000 \
-e GLUETUN_CONTROL_API_KEY=your-secret-api-key \
-e GLUETUN_HEALTH_ENDPOINT=http://gluetun:8080 \
-e TRANSMISSION_ENDPOINT=http://transmission:9091/transmission/rpc \
-e TRANSMISSION_USER=transmission \
Expand All @@ -72,13 +84,15 @@ miklosbagi/gluetrans:latest
docker run \
-e GLUETUN_CONTROL_ENDPOINT=http://gluetun:8000 \
-e GLUETUN_HEALTH_ENDPOINT=http://gluetun:8080 \
-e GLUETUN_CONTROL_API_KEY=your-secret-api-key \
-e TRANSMISSION_ENDPOINT=http://transmission:9091/transmission/rpc \
-e TRANSMISSION_USER=transmission \
-e TRANSMISSION_PASS=transmission \
gluetrans:local
```

## Docker-compose example with gluetun + transmission + piavpn
## Docker-compose examples
### PiaVPN (gluetun + transmission + piavpn)
Please note that `data` directory will be created if this gets executed as is.
Also, please note that we test against versions, not :latest, as that's like a weather report.

Expand All @@ -100,6 +114,9 @@ services:
SERVER_REGIONS: "FI Helsinki,France,Norway,SE Stockholm,Serbia"
VPN_PORT_FORWARDING: on
VPN_PORT_FORWARDING_PROVIDER: "private internet access"
# from gluetun v3.40.0+ control server auth, mapping config.toml with api key is required
volumes:
- ./gluetun-config/config.toml:/gluetun/auth/config.toml
restart: unless-stopped
# for ubuntu-latest, you may need:
devices:
Expand All @@ -124,6 +141,8 @@ services:
environment:
GLUETUN_CONTROL_ENDPOINT: http://localhost:8000
GLUETUN_HEALTH_ENDPOINT: http://localhost:9999
# from gluetun v3.40.0+ control server auth key must be passed
GLUETUN_CONTROL_API_KEY: "secret-apikey-for-gluetrans" # must match the one in config.toml
TRANSMISSION_ENDPOINT: http://localhost:9091/transmission/rpc
TRANSMISSION_USER: My Transmission Username
TRANSMISSION_PASS: My Transmission Password
Expand All @@ -135,7 +154,7 @@ services:
- gluetun
```

## Gluetun configuration for protonvpn example (for gluetun v3.36 and later please)
### ProtonVPN (gluetun + transmission + protonvpn) (gluetun v3.36 and above only)
Please note that `data` directory will be created if this gets executed as is.
Also, please note that we test against versions, not :latest, as that's like a weather report.

Expand All @@ -157,6 +176,9 @@ services:
SERVER_COUNTRIES: "Romania,Poland,Netherlands,Moldova"
VPN_PORT_FORWARDING: on
VPN_PORT_FORWARDING_PROVIDER: "protonvpn"
# from gluetun v3.40.0+ control server auth, mapping config.toml with api key is required
volumes:
- ./gluetun-config/config.toml:/gluetun/auth/config.toml
restart: unless-stopped
# for ubuntu-latest, you may need:
devices:
Expand All @@ -168,23 +190,36 @@ services:

Please note that the above is example for piavpn. Nightly tests are running against protonvpn provider, feel free to take a look into the compose file in test for a working example.

### Gluetun config.toml
For control server authentication, `config.toml` will be required to allow gluetrans to send authenticated requests to gluetun.
```
[[roles]]
name = "gluetrans"
routes = ["GET /v1/openvpn/portforwarded", "PUT /v1/openvpn/status"]
auth = "apikey"
apikey = "secret-apikey-for-gluetrans"
```

## Debug
`docker logs -f gluetrans` should reveal what's happening.

### Ideal scenario
```
GlueTrans starting...
Oct 10 10:10:11 [gt] waiting for gluetun to become active...
Oct 10 10:10:17 [gt] gluetun is active, country details: "123.123.1.12,UK,Belgrade,CODE Test DataCenterHost Inc."
Oct 10 10:10:17 [gt] monitoring...
Oct 10 10:10:47 [gt] gluetun returned {"port":0}, retrying (1 / 15)...
Oct 10 10:11:17 [gt] tramsmission returned '', retrying (1/15)...
Oct 10 10:11:47 [gt] tramsmission returned '', retrying (2/15)...
Oct 10 10:12:18 [gt] tramsmission returned '', retrying (3/15)...
Oct 10 10:12:48 [gt] port change detected: gluetun is 12345, transmission is 0, updating...
Oct 10 10:12:54 [gt] success: transmission port updated successfully.
Oct 10 10:13:23 [gt] country jump timer: 14 minutes left on this server.
Oct 10 10:13:24 [gt] heartbeat: gluetun & transmission ports match (12345), Port is open: Yes
Jan 26 17:43:11 [gt] waiting for gluetun to establish connection...
Jan 26 17:43:16 [gt] waiting for gluetun to establish connection...
Jan 26 17:43:21 [gt] gluetun is active, country details: Europe/Berlin,
Jan 26 17:43:21 [gt] monitoring...
Jan 26 17:45:43 [gt] country jump timer: 0 minute(s) left on this server.
Jan 26 17:45:43 [gt] port change detected: gluetun is 49198, transmission is 56864, Port is open: Yes updating...
Jan 26 17:45:48 [gt] success: transmission port updated successfully.
Jan 26 17:46:18 [gt] country jump timer: 0 minute(s) left on this server.
Jan 26 17:46:19 [gt] heartbeat: gluetun & transmission ports match (49198), Port is open: Yes
Jan 26 17:46:49 [gt] country jump timer: 0 minute(s) left on this server.
Jan 26 17:46:49 [gt] countryjump: forcing gluetun to pick a new server after 1 minute(s).
Jan 26 17:46:49 [gt] asking gluetun to disconnect from Europe/Berlin,
Jan 26 17:47:04 [gt] gluetun is active, country details: Europe/Paris,
...
```
Please note that this data is sanitized.

Expand Down
12 changes: 7 additions & 5 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# Mandatory env vars, please set these:
# GLUETUN_CONTROL_ENDPOINT
# GLUETUN_HEALTH_ENDPOINT
# GLUETUN_CONTROL_API_KEY
# TRANSMISSION_ENDPOINT
# TRANSMISSION_USER
# TRANSMISSION_PASS
Expand All @@ -36,6 +37,7 @@ transmission_port_fail_count=0

required_vars=(
GLUETUN_CONTROL_ENDPOINT
GLUETUN_CONTROL_API_KEY
TRANSMISSION_ENDPOINT
TRANSMISSION_USER
TRANSMISSION_PASS
Expand Down Expand Up @@ -101,7 +103,7 @@ get_transmission_port() {

# get peer port from vpn via gluetun control server
get_gluetun_port() {
gluetun_response=$(curl -s "$GLUETUN_CONTROL_ENDPOINT/v1/openvpn/portforwarded")
gluetun_response=$(curl -s -H "X-API-Key: $GLUETUN_CONTROL_API_KEY" "$GLUETUN_CONTROL_ENDPOINT/v1/openvpn/portforwarded")
if [ "$gluetun_response" == "" ] || [ "$gluetun_response" == '{"port":0}' ]; then
log "gluetun returned $gluetun_response, retrying ($gluetun_port_fail_count / $GLUETUN_PICK_NEW_SERVER_AFTER)..."
return 1
Expand All @@ -124,11 +126,11 @@ check_transmission_port_open() {
# pick a new gluetun server
pick_new_gluetun_server() {
log "asking gluetun to disconnect from $country_details", "s#$country_details#* OMITTED *#"
gluetun_server_response=$(curl -s -X PUT -d '{"status":"stopped"}' "$GLUETUN_CONTROL_ENDPOINT/v1/openvpn/status") || log "error instructing gluetun to pick new server ($gluetun_server_response)."
if echo "$gluetun_server_response" | grep -qE '{"outcome":"(stopping|stopped)"}'; then
log "bleh, gluetun server response is weird, expected {\"outcome\":\"stopping\"}, got $gluetun_server_response"
gluetun_server_response=$(curl -s -H "X-API-Key: $GLUETUN_CONTROL_API_KEY" -X PUT -d '{"status":"stopped"}' "$GLUETUN_CONTROL_ENDPOINT/v1/openvpn/status") || log "error instructing gluetun to pick new server ($gluetun_server_response)."
if ! echo "$gluetun_server_response" | grep -qE '\{"outcome":"(stopping|stopped)"\}'; then
log "bleh, gluetun server response is weird, expected one of {\"outcome\":\"stopping\"} or {\"outcome\":\"stopped\"}, got $gluetun_server_response"
return 1
fi
fi
# this is fixed as ~ around this time it takes for gluetun to reconnect, this avoids some nag in logs
sleep 15
# just in case this takes longer than expected
Expand Down
3 changes: 3 additions & 0 deletions test/docker-compose-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ services:
SERVER_COUNTRIES: ${TEST_P_SVCS}
VPN_PORT_FORWARDING: "on"
VPN_PORT_FORWARDING_PROVIDER: protonvpn
volumes:
- ./gluetun-config/config.toml:/gluetun/auth/config.toml
restart: unless-stopped
devices:
- /dev/net/tun:/dev/net/tun
Expand All @@ -33,6 +35,7 @@ services:
context: ../
environment:
GLUETUN_CONTROL_ENDPOINT: http://localhost:8000
GLUETUN_CONTROL_API_KEY: "secret-apikey-for-gluetrans" # must match the one in config.toml
GLUETUN_HEALTH_ENDPOINT: http://localhost:9999
GLUETUN_PICK_NEW_SERVER_AFTER: 10
PEERPORT_CHECK_INTERVAL: 30
Expand Down
5 changes: 5 additions & 0 deletions test/gluetun-config/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[[roles]]
name = "gluetrans"
routes = ["GET /v1/openvpn/portforwarded", "GET /v1/openvpn/status", "PUT /v1/openvpn/status"]
auth = "apikey"
apikey = "secret-apikey-for-gluetrans"

0 comments on commit 0f285c4

Please sign in to comment.