This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
FROM golang:1.23-alpine AS builder
|
||||
|
||||
RUN apk add --no-cache git ca-certificates \
|
||||
&& go install github.com/kylegrantlucas/pia-wg-config@v1.1.1
|
||||
|
||||
FROM docker:cli
|
||||
|
||||
RUN apk add --no-cache bash ca-certificates tzdata jq
|
||||
|
||||
COPY --from=builder /go/bin/pia-wg-config /usr/local/bin/pia-wg-config
|
||||
COPY rotate.sh entrypoint.sh /usr/local/bin/
|
||||
|
||||
RUN chmod +x /usr/local/bin/rotate.sh /usr/local/bin/entrypoint.sh
|
||||
|
||||
ENV TZ=Europe/Brussels
|
||||
ENV WG_CONFIG_PATH=/config/wireguard/wg0.conf
|
||||
ENV ROTATOR_STATE_PATH=/config/rotator-state.json
|
||||
ENV GLUETUN_CONTAINER=m3u-filter-vpn
|
||||
ENV ROTATE_AT=03:00
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||
@@ -0,0 +1,109 @@
|
||||
# gluetun-pia-wireguard-rotator
|
||||
|
||||
Sidecar die dagelijks (en bij container-start) een willekeurige PIA WireGuard-region kiest via [pia-wg-config](https://github.com/kylegrantlucas/pia-wg-config), `wg0.conf` op het gedeelde gluetun-volume schrijft, en de gluetun-container herstart.
|
||||
|
||||
## Vereisten
|
||||
|
||||
- Gluetun met `VPN_SERVICE_PROVIDER=custom` en `VPN_TYPE=wireguard`
|
||||
- Gedeeld volume met gluetun (bijv. `/var/dockers/m3u-filter-pia:/gluetun` op gluetun, `/config` op de rotator)
|
||||
- Docker socket (alleen voor `docker restart` van de gluetun-container)
|
||||
- Actief PIA-abonnement
|
||||
|
||||
## Environment variables
|
||||
|
||||
### Required
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `PIA_USER` | PIA-gebruikersnaam |
|
||||
| `PIA_PASS` | PIA-wachtwoord |
|
||||
| `PIA_REGIONS` | CSV (`netherlands,france,belgium`) of JSON-array (`["netherlands","france"]`) |
|
||||
|
||||
Region-codes moeten overeenkomen met `pia-wg-config` (niet de OpenVPN-namen uit Gluetun's ingebouwde PIA-provider).
|
||||
|
||||
Lijst opvragen:
|
||||
|
||||
```bash
|
||||
docker run --rm --entrypoint pia-wg-config bramkel/gluetun-pia-wireguard-rotator:latest regions
|
||||
```
|
||||
|
||||
### Optional
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `GLUETUN_CONTAINER` | `m3u-filter-vpn` | Container name/id om te herstarten |
|
||||
| `WG_CONFIG_PATH` | `/config/wireguard/wg0.conf` | Pad waar `wg0.conf` wordt geschreven |
|
||||
| `ROTATOR_STATE_PATH` | `/config/rotator-state.json` | Laatste rotatie-metadata |
|
||||
| `ROTATE_AT` | `03:00` | Dagelijks rotatietijdstip (`HH:MM`, in `TZ`) |
|
||||
| `TZ` | `Europe/Brussels` | Tijdzone voor scheduling |
|
||||
|
||||
## Output
|
||||
|
||||
- `wireguard/wg0.conf` op het gedeelde volume — Gluetun leest dit als `/gluetun/wireguard/wg0.conf` en dit **overschrijft** `WIREGUARD_*` environment variables
|
||||
- `rotator-state.json` — laatste gekozen region en timestamp
|
||||
|
||||
## Compose-integratie
|
||||
|
||||
Zie [`docker-compose.example.yml`](docker-compose.example.yml) voor een volledig voorbeeld met `m3u-filter-vpn`, m3u-editor en xtream-proxy.
|
||||
|
||||
### Nieuwe service toevoegen
|
||||
|
||||
```yaml
|
||||
gluetun-pia-wireguard-rotator:
|
||||
image: bramkel/gluetun-pia-wireguard-rotator:latest
|
||||
container_name: gluetun-pia-wireguard-rotator
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=Europe/Brussels
|
||||
- PIA_USER=${PIA_USER}
|
||||
- PIA_PASS=${PIA_PASSWORD}
|
||||
- PIA_REGIONS=netherlands,france,belgium
|
||||
- GLUETUN_CONTAINER=m3u-filter-vpn
|
||||
- ROTATE_AT=03:00
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- /var/dockers/m3u-filter-pia:/config
|
||||
depends_on:
|
||||
- m3u-filter-vpn
|
||||
```
|
||||
|
||||
Zet `PIA_USER` en `PIA_PASSWORD` in een host-`.env` (niet inline in compose).
|
||||
|
||||
### Gluetun opschonen (aanbevolen na eerste succesvolle rotatie)
|
||||
|
||||
Zodra `wg0.conf` bestaat, heeft het bestand voorrang op env-vars. Verwijder uit `m3u-filter-vpn` om verwarring te voorkomen:
|
||||
|
||||
- `WIREGUARD_ENDPOINT_IP`
|
||||
- `WIREGUARD_PUBLIC_KEY`
|
||||
- `WIREGUARD_PRIVATE_KEY`
|
||||
- `WIREGUARD_ADDRESSES`
|
||||
|
||||
Behoud minimaal:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
- VPN_SERVICE_PROVIDER=custom
|
||||
- VPN_TYPE=wireguard
|
||||
```
|
||||
|
||||
Optioneel host-`.env`-keys (`WIREGUARD_*`) opruimen als die niet meer gebruikt worden.
|
||||
|
||||
## Deploy
|
||||
|
||||
1. Push/build image (`Dockers/gluetun-pia-wireguard-rotator/**` triggert Gitea CI → `bramkel/gluetun-pia-wireguard-rotator:latest`)
|
||||
2. `docker compose up -d gluetun-pia-wireguard-rotator`
|
||||
3. Controleer logs: `docker logs gluetun-pia-wireguard-rotator` en `docker logs m3u-filter-vpn`
|
||||
|
||||
## Gedrag
|
||||
|
||||
1. Bij start: direct roteren (nieuwe config + gluetun restart)
|
||||
2. Daarna: elke dag om `ROTATE_AT` opnieuw
|
||||
3. Willekeurige region uit `PIA_REGIONS`
|
||||
|
||||
**Let op:** `docker restart` op gluetun veroorzaakt kort VPN-verlies voor alle containers met `network_mode: service:m3u-filter-vpn`.
|
||||
|
||||
## Security
|
||||
|
||||
- De Docker socket geeft de rotator rechten om containers te herstarten; mount read-only waar mogelijk
|
||||
- `wg0.conf` bevat private keys (`chmod 600`)
|
||||
- Bewaar PIA-credentials in `.env`, niet in version control
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
log() { echo "[$(date -Is)] $*"; }
|
||||
|
||||
sleep_until_next_rotate() {
|
||||
local rotate_at="${ROTATE_AT:-03:00}"
|
||||
local target_h target_m
|
||||
IFS=: read -r target_h target_m <<<"$rotate_at"
|
||||
target_h=$((10#$target_h))
|
||||
target_m=$((10#$target_m))
|
||||
|
||||
local now_h now_m now_s target_s wait_s
|
||||
now_h=$(date +%H)
|
||||
now_m=$(date +%M)
|
||||
now_h=$((10#$now_h))
|
||||
now_m=$((10#$now_m))
|
||||
now_s=$((now_h * 3600 + now_m * 60))
|
||||
target_s=$((target_h * 3600 + target_m * 60))
|
||||
|
||||
if ((now_s < target_s)); then
|
||||
wait_s=$((target_s - now_s))
|
||||
else
|
||||
wait_s=$((86400 - now_s + target_s))
|
||||
fi
|
||||
|
||||
log "Next rotation at ${rotate_at} (${TZ:-UTC}) in ${wait_s}s"
|
||||
sleep "$wait_s"
|
||||
}
|
||||
|
||||
log "Starting gluetun PIA WireGuard rotator (TZ=${TZ:-UTC}, ROTATE_AT=${ROTATE_AT:-03:00})"
|
||||
|
||||
log "Running rotation on startup"
|
||||
/usr/local/bin/rotate.sh
|
||||
|
||||
while true; do
|
||||
sleep_until_next_rotate
|
||||
log "Running scheduled daily rotation"
|
||||
/usr/local/bin/rotate.sh
|
||||
done
|
||||
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
log() { echo "[$(date -Is)] $*"; }
|
||||
|
||||
require_env() {
|
||||
local name="$1"
|
||||
if [[ -z "${!name:-}" ]]; then
|
||||
>&2 echo "Missing required env var: $name"
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
parse_regions() {
|
||||
# CSV: "netherlands,france,belgium" or JSON: '["netherlands","france"]'
|
||||
local raw="${PIA_REGIONS:-}"
|
||||
if [[ -z "$raw" ]]; then
|
||||
>&2 echo "Missing required env var: PIA_REGIONS"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ "$raw" =~ ^\[.*\]$ ]]; then
|
||||
jq -r '.[]' <<<"$raw"
|
||||
return
|
||||
fi
|
||||
|
||||
tr ',' '\n' <<<"$raw" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | awk 'NF'
|
||||
}
|
||||
|
||||
pick_random_region() {
|
||||
local regions=()
|
||||
while IFS= read -r region; do
|
||||
[[ -n "$region" ]] && regions+=("$region")
|
||||
done < <(parse_regions)
|
||||
|
||||
if [[ "${#regions[@]}" -eq 0 ]]; then
|
||||
>&2 echo "PIA_REGIONS is empty after parsing"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
printf '%s\n' "${regions[@]}" | shuf -n 1
|
||||
}
|
||||
|
||||
write_state() {
|
||||
local region="$1"
|
||||
local state_path="${ROTATOR_STATE_PATH:-/config/rotator-state.json}"
|
||||
local rotated_at
|
||||
rotated_at="$(date -Is)"
|
||||
mkdir -p "$(dirname "$state_path")"
|
||||
jq -n \
|
||||
--arg region "$region" \
|
||||
--arg rotated_at "$rotated_at" \
|
||||
--arg wg_config "${WG_CONFIG_PATH:-/config/wireguard/wg0.conf}" \
|
||||
'{region: $region, rotated_at: $rotated_at, wg_config: $wg_config}' >"$state_path"
|
||||
chmod 644 "$state_path"
|
||||
}
|
||||
|
||||
rotate_once() {
|
||||
require_env PIA_USER
|
||||
require_env PIA_PASS
|
||||
require_env PIA_REGIONS
|
||||
|
||||
local region wg_path tmp_dir gluetun_container
|
||||
region="$(pick_random_region)"
|
||||
wg_path="${WG_CONFIG_PATH:-/config/wireguard/wg0.conf}"
|
||||
gluetun_container="${GLUETUN_CONTAINER:-m3u-filter-vpn}"
|
||||
|
||||
log "Selected region: $region"
|
||||
|
||||
tmp_dir="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmp_dir"' RETURN
|
||||
|
||||
local tmp_conf="${tmp_dir}/wg0.conf"
|
||||
log "Generating WireGuard config with pia-wg-config"
|
||||
if ! pia-wg-config -v -r "$region" -o "$tmp_conf" "$PIA_USER" "$PIA_PASS"; then
|
||||
>&2 echo "pia-wg-config failed for region=$region"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q '^\[Interface\]' "$tmp_conf"; then
|
||||
>&2 echo "Generated config missing [Interface] section"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$wg_path")"
|
||||
mv "$tmp_conf" "$wg_path"
|
||||
chmod 600 "$wg_path"
|
||||
log "Wrote $wg_path"
|
||||
|
||||
write_state "$region"
|
||||
|
||||
log "Restarting gluetun container: $gluetun_container"
|
||||
if ! docker restart "$gluetun_container"; then
|
||||
>&2 echo "docker restart failed for container=$gluetun_container"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Rotation complete for region=$region"
|
||||
}
|
||||
|
||||
rotate_once
|
||||
@@ -0,0 +1 @@
|
||||
latest
|
||||
Reference in New Issue
Block a user