mirror of
https://github.com/wfg/docker-openvpn-client.git
synced 2025-04-18 17:44:40 -04:00
Image rewrite
This commit is contained in:
parent
ac8a5ba3d4
commit
b5489ea7ce
18 changed files with 122 additions and 458 deletions
|
@ -1,2 +0,0 @@
|
|||
*
|
||||
!data/
|
|
@ -1,24 +1,15 @@
|
|||
name: Publish
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
|
||||
env:
|
||||
IMAGE_NAME: openvpn-client
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- run: cat build-variables >> $GITHUB_ENV
|
||||
|
||||
- uses: docker/setup-qemu-action@v2
|
||||
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
|
||||
- uses: docker/login-action@v2
|
||||
|
@ -30,7 +21,7 @@ jobs:
|
|||
- id: tags
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ${{ env.IMAGE_NAME }}
|
||||
images: ghcr.io/wfg/openvpn-client
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
|
@ -46,9 +37,10 @@ jobs:
|
|||
|
||||
- uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: "{{defaultContext}}:build"
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6
|
||||
tags: ${{ steps.tags.outputs.tags }}
|
||||
build-args: |
|
||||
BUILD_DATE=${{ steps.build-args.outputs.date }}
|
||||
IMAGE_VERSION=${{ steps.build-args.outputs.version }}
|
||||
tags: ${{ steps.tags.outputs.tags }}
|
||||
push: true
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
.local/
|
||||
# Anything used during development should be put in local/ to prevent accidental committing.
|
||||
local/
|
||||
|
|
28
CHANGELOG.md
28
CHANGELOG.md
|
@ -1,28 +0,0 @@
|
|||
# Changelog
|
||||
|
||||
## Version 3.1.0 - 2022-06-30
|
||||
### Changed
|
||||
- `KILL_SWITCH` now requires `iptables` or `nftables` to be enabled. It defaults to `iptables`. See documentation for more information.
|
||||
|
||||
### Added
|
||||
- Modified OpenVPN configuration file cleanup function.
|
||||
|
||||
## Version 3.0.0 - 2022-06-14
|
||||
### Changed
|
||||
- Refactored scripts
|
||||
- Renamed a lot of variables ([PLEASE see docs](README.md#environment-variables))
|
||||
- Updated logic used to select the OpenVPN configuration file
|
||||
- Switched to `nftables`
|
||||
- Updated to Alpine 3.16
|
||||
- Fixed outdated proxy configuration files
|
||||
|
||||
## Version 2.1.0 - 2022-03-06
|
||||
### Added
|
||||
- `VPN_CONFIG_PATTERN` environment variable.
|
||||
|
||||
## Version 2.0.0 - 2022-01-02
|
||||
### Changed
|
||||
- `OPENVPN_AUTH_SECRET` changed to `VPN_AUTH_SECRET` for consistency.
|
||||
|
||||
### Fixed
|
||||
- Commented remotes are no longer processed.
|
29
Dockerfile
29
Dockerfile
|
@ -1,29 +0,0 @@
|
|||
FROM alpine:3.16
|
||||
|
||||
RUN apk add --no-cache \
|
||||
bash \
|
||||
bind-tools \
|
||||
dante-server \
|
||||
iptables \
|
||||
openvpn \
|
||||
nftables \
|
||||
shadow \
|
||||
tinyproxy
|
||||
|
||||
COPY data/ /data/
|
||||
|
||||
ENV KILL_SWITCH=iptables
|
||||
ENV USE_VPN_DNS=on
|
||||
ENV VPN_LOG_LEVEL=3
|
||||
|
||||
ARG BUILD_DATE
|
||||
ARG IMAGE_VERSION
|
||||
|
||||
LABEL build-date=$BUILD_DATE
|
||||
LABEL image-version=$IMAGE_VERSION
|
||||
|
||||
HEALTHCHECK CMD ping -c 3 1.1.1.1 || exit 1
|
||||
|
||||
WORKDIR /data
|
||||
|
||||
ENTRYPOINT [ "scripts/entry.sh" ]
|
21
README.md
21
README.md
|
@ -61,24 +61,9 @@ services:
|
|||
#### Environment variables
|
||||
| Variable | Default (blank is unset) | Description |
|
||||
| --- | --- | --- |
|
||||
| `USE_VPN_DNS` | `on` | Whether or not to use the DNS servers pushed from the VPN server. It's best to leave this enabled unless you have a good reason to disable it. |
|
||||
| `VPN_CONFIG_FILE` | | The OpenVPN configuration file to use. If unset, the `VPN_CONFIG_PATTERN` is used. |
|
||||
| `VPN_CONFIG_PATTERN` | | The search pattern to use when looking for an OpenVPN configuration file. If unset, the search will include `*.conf` and `*.ovpn`. |
|
||||
| `VPN_AUTH_SECRET` | | Docker secret that contain the credentials for accessing the VPN. |
|
||||
| `VPN_LOG_LEVEL` | `3` | OpenVPN logging verbosity (`1`-`11`) |
|
||||
| `SUBNETS` | | A list of one or more comma-separated subnets (e.g. `192.168.0.0/24,192.168.1.0/24`) to allow outside of the VPN tunnel. |
|
||||
| `KILL_SWITCH` | `iptables` | Which packet filterer to use for the kill switch. This value likely depends on your underlying host. Recommended to leave default unless you have problems. Acceptable values are `iptables` and `nftables`. To disable the kill switch, set to any other value. |
|
||||
| `HTTP_PROXY` | | Whether or not to enable the built-in HTTP proxy server. To enable, set to any "truthy" value (see below the table). Any other value (including unset) will cause the proxy server to not run. It listens on port 8080. |
|
||||
| `HTTP_PROXY_USERNAME` | | Credentials for accessing the HTTP proxy. If `HTTP_PROXY_USERNAME` is specified, you should also specify `HTTP_PROXY_PASSWORD`. |
|
||||
| `HTTP_PROXY_PASSWORD` | | Credentials for accessing the HTTP proxy. If `HTTP_PROXY_PASSWORD` is specified, you should also specify `HTTP_PROXY_USERNAME`. |
|
||||
| `HTTP_PROXY_USERNAME_SECRET` | | Docker secrets that contain the credentials for accessing the HTTP proxy. If `HTTP_PROXY_USERNAME_SECRET` is specified, you should also specify `HTTP_PROXY_PASSWORD_SECRET`. |
|
||||
| `HTTP_PROXY_PASSWORD_SECRET` | | Docker secrets that contain the credentials for accessing the HTTP proxy. If `HTTP_PROXY_PASSWORD_SECRET` is specified, you should also specify `HTTP_PROXY_USERNAME_SECRET`. |
|
||||
| `SOCKS_PROXY` | | Whether or not to enable the built-in SOCKS proxy server. To enable, set to any "truthy" value (see below the table). Any other value (including unset) will cause the proxy server to not run. It listens on port 1080. |
|
||||
| `SOCKS_LISTEN_ON` | | Address the proxies will be listening on. Set to `0.0.0.0` to listen on all IP addresses. |
|
||||
| `SOCKS_PROXY_USERNAME` | | Credentials for accessing the proxies. If `SOCKS_PROXY_USERNAME` is specified, you should also specify `SOCKS_PROXY_PASSWORD`. |
|
||||
| `SOCKS_PROXY_PASSWORD` | | Credentials for accessing the proxies. If `SOCKS_PROXY_PASSWORD` is specified, you should also specify `SOCKS_PROXY_USERNAME`. |
|
||||
| `SOCKS_PROXY_USERNAME_SECRET` | | Docker secrets that contain the credentials for accessing the proxies. If `SOCKS_PROXY_USERNAME_SECRET` is specified, you should also specify `SOCKS_PROXY_PASSWORD_SECRET`. |
|
||||
| `SOCKS_PROXY_PASSWORD_SECRET` | | Docker secrets that contain the credentials for accessing the proxies. If `SOCKS_PROXY_PASSWORD_SECRET` is specified, you should also specify `SOCKS_PROXY_USERNAME_SECRET`. |
|
||||
| `ALLOWED_SUBNETS` | | A list of one or more comma-separated subnets (e.g. `192.168.0.0/24,192.168.1.0/24`) to allow outside of the VPN tunnel. |
|
||||
| `AUTH_SECRET` | | Docker secret that contains the credentials for accessing the VPN. |
|
||||
| `CONFIG_FILE` | | The OpenVPN configuration file or search pattern. If unset, a random `.conf` or `.ovpn` file will be selected. |
|
||||
"Truthy" values are the following: `true`, `t`, `yes`, `y`, `1`, `on`, `enable`, or `enabled`.
|
||||
|
||||
##### Environment variable considerations
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
IMAGE_NAME=ghcr.io/wfg/openvpn-client
|
6
build.py
6
build.py
|
@ -6,15 +6,15 @@ import subprocess
|
|||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('image_version', type=str)
|
||||
parser.add_argument('image_version')
|
||||
args = parser.parse_args()
|
||||
|
||||
docker_build_cmd = [
|
||||
'docker', 'build',
|
||||
'--build-arg', f'BUILD_DATE={str(datetime.datetime.now())}',
|
||||
'--build-arg', f'BUILD_DATE={str(datetime.datetime.utcnow())}',
|
||||
'--build-arg', f'IMAGE_VERSION={args.image_version}',
|
||||
'--tag', f'ghcr.io/wfg/openvpn-client:{args.image_version}',
|
||||
'--tag', 'ghcr.io/wfg/openvpn-client:latest',
|
||||
'.',
|
||||
'./build',
|
||||
]
|
||||
subprocess.run(docker_build_cmd)
|
||||
|
|
1
build/.dockerignore
Normal file
1
build/.dockerignore
Normal file
|
@ -0,0 +1 @@
|
|||
Dockerfile
|
12
build/Dockerfile
Normal file
12
build/Dockerfile
Normal file
|
@ -0,0 +1,12 @@
|
|||
FROM alpine:3.16
|
||||
|
||||
RUN apk add --no-cache \
|
||||
bash \
|
||||
bind-tools \
|
||||
iptables \
|
||||
ip6tables \
|
||||
openvpn
|
||||
|
||||
COPY . /usr/local/bin
|
||||
|
||||
ENTRYPOINT [ "entry.sh" ]
|
48
build/entry.sh
Executable file
48
build/entry.sh
Executable file
|
@ -0,0 +1,48 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
cleanup() {
|
||||
kill TERM "$openvpn_pid"
|
||||
exit 0
|
||||
}
|
||||
|
||||
is_enabled() {
|
||||
[[ ${1,,} =~ ^(true|t|yes|y|1|on|enable|enabled)$ ]]
|
||||
}
|
||||
|
||||
# Either a specific file name or a pattern.
|
||||
if [[ $CONFIG_FILE ]]; then
|
||||
config_file=$(find /config -name "$CONFIG_FILE" 2> /dev/null | sort | shuf -n 1)
|
||||
else
|
||||
config_file=$(find /config -name '*.conf' -o -name '*.ovpn' 2> /dev/null | sort | shuf -n 1)
|
||||
fi
|
||||
|
||||
if [[ -z $config_file ]]; then
|
||||
>&2 echo 'no openvpn configuration file found'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "using openvpn configuration file: $config_file"
|
||||
|
||||
|
||||
openvpn_args=(
|
||||
"--config" "$config_file"
|
||||
"--cd" "/config"
|
||||
)
|
||||
|
||||
if is_enabled "$KILL_SWITCH"; then
|
||||
openvpn_args+=("--route-up" "/usr/local/bin/killswitch.sh $ALLOWED_SUBNETS")
|
||||
fi
|
||||
|
||||
# Docker secret that contains the credentials for accessing the VPN.
|
||||
if [[ $AUTH_SECRET ]]; then
|
||||
openvpn_args+=("--auth-user-pass" "/run/secrets/$AUTH_SECRET")
|
||||
fi
|
||||
|
||||
openvpn "${openvpn_args[@]}" &
|
||||
openvpn_pid=$!
|
||||
|
||||
trap cleanup TERM
|
||||
|
||||
wait $openvpn_pid
|
42
build/killswitch.sh
Executable file
42
build/killswitch.sh
Executable file
|
@ -0,0 +1,42 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
iptables --insert OUTPUT \
|
||||
! --out-interface tun0 \
|
||||
--match addrtype ! --dst-type LOCAL \
|
||||
! --destination "$(ip -4 -oneline addr show dev eth0 | awk 'NR == 1 { print $4 }')" \
|
||||
--jump REJECT
|
||||
|
||||
# Create static routes for any ALLOWED_SUBNETS and punch holes in the firewall
|
||||
# (ALLOWED_SUBNETS is passed as $1 from entry.sh)
|
||||
default_gateway=$(ip -4 route | awk '$1 == "default" { print $3 }')
|
||||
for subnet in ${1//,/ }; do
|
||||
ip route add "$subnet" via "$default_gateway"
|
||||
iptables --insert OUTPUT --destination "$subnet" --jump ACCEPT
|
||||
done
|
||||
|
||||
# Punch holes in the firewall for the OpenVPN server addresses
|
||||
# $config is set by OpenVPN:
|
||||
# "Name of first --config file. Set on program initiation and reset on SIGHUP."
|
||||
global_port=$(awk '$1 == "port" { print $2 }' "${config:?}")
|
||||
global_protocol=$(awk '$1 == "proto" { print $2 }' "${config:?}")
|
||||
remotes=$(awk '$1 == "remote" { print $2, $3, $4 }' "${config:?}")
|
||||
ip_regex='^(([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))\.){3}([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))$'
|
||||
while IFS= read -r line; do
|
||||
# Read a comment-stripped version of the line
|
||||
# Fixes #84
|
||||
IFS=' ' read -ra remote <<< "${line%%\#*}"
|
||||
address=${remote[0]}
|
||||
port=${remote[1]:-${global_port:-1194}}
|
||||
protocol=${remote[2]:-${global_protocol:-udp}}
|
||||
|
||||
if [[ $address =~ $ip_regex ]]; then
|
||||
iptables --insert OUTPUT --destination "$address" --protocol "$protocol" --destination-port "$port" --jump ACCEPT
|
||||
else
|
||||
for ip in $(dig -4 +short "$address"); do
|
||||
iptables --insert OUTPUT --destination "$ip" --protocol "$protocol" --destination-port "$port" --jump ACCEPT
|
||||
echo "$ip $address" >> /etc/hosts
|
||||
done
|
||||
fi
|
||||
done <<< "$remotes"
|
|
@ -1,14 +0,0 @@
|
|||
User tinyproxy
|
||||
Group tinyproxy
|
||||
|
||||
Port 8080
|
||||
Listen
|
||||
Bind
|
||||
Timeout 600
|
||||
|
||||
LogLevel Info
|
||||
LogFile "/var/log/tinyproxy/tinyproxy.log"
|
||||
DefaultErrorFile "/usr/share/tinyproxy/default.html"
|
||||
StatFile "/usr/share/tinyproxy/stats.html"
|
||||
|
||||
DisableViaHeader yes
|
|
@ -1,26 +0,0 @@
|
|||
logoutput: /var/log/dante.log
|
||||
errorlog: stderr
|
||||
|
||||
internal: eth0 port = 1080
|
||||
external: tun0
|
||||
|
||||
socksmethod: none
|
||||
|
||||
user.unprivileged: sockd
|
||||
|
||||
client pass {
|
||||
from: 0.0.0.0/0 to: 0.0.0.0/0
|
||||
log: error connect disconnect
|
||||
}
|
||||
|
||||
socks pass {
|
||||
from: 0.0.0.0/0 to: 0.0.0.0/0
|
||||
command: bind connect udpassociate
|
||||
log: error connect disconnect
|
||||
}
|
||||
|
||||
socks pass {
|
||||
from: 0.0.0.0/0 to: 0.0.0.0/0
|
||||
command: bindreply udpreply
|
||||
log: error connect disconnect
|
||||
}
|
|
@ -1,242 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
|
||||
cleanup() {
|
||||
if [[ $openvpn_child ]]; then
|
||||
kill SIGTERM "$openvpn_child"
|
||||
fi
|
||||
|
||||
sleep 0.5
|
||||
rm -f "$modified_config_file"
|
||||
echo "info: exiting"
|
||||
exit 0
|
||||
}
|
||||
|
||||
is_enabled() {
|
||||
[[ ${1,,} =~ ^(true|t|yes|y|1|on|enable|enabled)$ ]]
|
||||
}
|
||||
|
||||
mkdir -p /data/{config,scripts,vpn}
|
||||
|
||||
echo "
|
||||
--- Running with the following variables ---"
|
||||
|
||||
if [[ $VPN_CONFIG_FILE ]]; then
|
||||
echo "VPN configuration file: $VPN_CONFIG_FILE"
|
||||
fi
|
||||
if [[ $VPN_CONFIG_PATTERN ]]; then
|
||||
echo "VPN configuration file name pattern: $VPN_CONFIG_PATTERN"
|
||||
fi
|
||||
|
||||
echo "Use default resolv.conf: ${USE_VPN_DNS:-off}
|
||||
Allowing subnets: ${SUBNETS:-none}
|
||||
Kill switch: $KILL_SWITCH
|
||||
Using OpenVPN log level: $VPN_LOG_LEVEL"
|
||||
|
||||
if is_enabled "$HTTP_PROXY"; then
|
||||
echo "HTTP proxy: $HTTP_PROXY"
|
||||
if is_enabled "$HTTP_PROXY_USERNAME"; then
|
||||
echo "HTTP proxy username: $HTTP_PROXY_USERNAME"
|
||||
elif is_enabled "$HTTP_PROXY_USERNAME_SECRET"; then
|
||||
echo "HTTP proxy username secret: $HTTP_PROXY_USERNAME_SECRET"
|
||||
fi
|
||||
fi
|
||||
if is_enabled "$SOCKS_PROXY"; then
|
||||
echo "SOCKS proxy: $SOCKS_PROXY"
|
||||
if [[ $SOCKS_LISTEN_ON ]]; then
|
||||
echo "Listening on: $SOCKS_LISTEN_ON"
|
||||
fi
|
||||
if is_enabled "$SOCKS_PROXY_USERNAME"; then
|
||||
echo "SOCKS proxy username: $SOCKS_PROXY_USERNAME"
|
||||
elif is_enabled "$SOCKS_PROXY_USERNAME_SECRET"; then
|
||||
echo "SOCKS proxy username secret: $SOCKS_PROXY_USERNAME_SECRET"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "---
|
||||
"
|
||||
|
||||
if [[ $VPN_CONFIG_FILE ]]; then
|
||||
original_config_file=vpn/$VPN_CONFIG_FILE
|
||||
elif [[ $VPN_CONFIG_PATTERN ]]; then
|
||||
original_config_file=$(find vpn -name "$VPN_CONFIG_PATTERN" 2> /dev/null | sort | shuf -n 1)
|
||||
else
|
||||
original_config_file=$(find vpn -name '*.conf' -o -name '*.ovpn' 2> /dev/null | sort | shuf -n 1)
|
||||
fi
|
||||
|
||||
if [[ -z $original_config_file ]]; then
|
||||
>&2 echo 'erro: no vpn configuration file found'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "info: original configuration file: $original_config_file"
|
||||
|
||||
# Create a new configuration file to modify so the original is left untouched.
|
||||
modified_config_file=vpn/openvpn.$(tr -dc A-Za-z0-9 </dev/urandom | head -c8).conf
|
||||
trap cleanup SIGTERM
|
||||
|
||||
echo "info: modified configuration file: $modified_config_file"
|
||||
grep -Ev '(^up\s|^down\s)' "$original_config_file" > "$modified_config_file"
|
||||
|
||||
# Remove carriage returns (\r) from the config file
|
||||
sed -i 's/\r$//g' "$modified_config_file"
|
||||
|
||||
|
||||
default_gateway=$(ip -4 route | grep 'default via' | awk '{print $3}')
|
||||
|
||||
case "$KILL_SWITCH" in
|
||||
'iptables')
|
||||
echo "info: kill switch is using iptables"
|
||||
|
||||
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
|
||||
iptables -A INPUT -i lo -j ACCEPT
|
||||
iptables -A OUTPUT -o lo -j ACCEPT
|
||||
|
||||
local_subnet=$(ip -4 route | grep 'scope link' | awk '{print $1}')
|
||||
iptables -A INPUT -s "$local_subnet" -j ACCEPT
|
||||
iptables -A OUTPUT -d "$local_subnet" -j ACCEPT
|
||||
|
||||
if [[ $SUBNETS ]]; then
|
||||
for subnet in ${SUBNETS//,/ }; do
|
||||
ip route add "$subnet" via "$default_gateway" dev eth0
|
||||
iptables -A INPUT -s "$subnet" -j ACCEPT
|
||||
iptables -A OUTPUT -d "$subnet" -j ACCEPT
|
||||
done
|
||||
fi
|
||||
|
||||
global_port=$(grep "^port " "$modified_config_file" | awk '{print $2}')
|
||||
global_protocol=$(grep "^proto " "$modified_config_file" | awk '{print $2}') # {$2 = substr($2, 1, 3)} 2
|
||||
remotes=$(grep "^remote " "$modified_config_file" | awk '{print $2, $3, $4}')
|
||||
ip_regex='^(([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))\.){3}([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))$'
|
||||
while IFS= read -r line; do
|
||||
IFS=' ' read -ra remote <<< "$line"
|
||||
address=${remote[0]}
|
||||
port=${remote[1]:-${global_port:-1194}}
|
||||
protocol=${remote[2]:-${global_protocol:-udp}}
|
||||
|
||||
if [[ $address =~ $ip_regex ]]; then
|
||||
iptables -A OUTPUT -o eth0 -d "$address" -p "$protocol" --dport "$port" -j ACCEPT
|
||||
else
|
||||
for ip in $(dig -4 +short "$address"); do
|
||||
iptables -A OUTPUT -o eth0 -d "$ip" -p "$protocol" --dport "$port" -j ACCEPT
|
||||
printf "%s %s\n" "$ip" "$address" >> /etc/hosts
|
||||
done
|
||||
fi
|
||||
done <<< "$remotes"
|
||||
iptables -A INPUT -i tun0 -j ACCEPT
|
||||
iptables -A OUTPUT -o tun0 -j ACCEPT
|
||||
iptables -P INPUT DROP
|
||||
iptables -P OUTPUT DROP
|
||||
iptables -P FORWARD DROP
|
||||
iptables-save > config/iptables.conf
|
||||
;;
|
||||
|
||||
'nftables')
|
||||
echo "info: kill switch is using nftables"
|
||||
nftables_config_file=config/nftables.conf
|
||||
|
||||
printf '%s\n' \
|
||||
'#!/usr/bin/nft' '' \
|
||||
'flush ruleset' '' \
|
||||
'# base ruleset' \
|
||||
'add table inet killswitch' '' \
|
||||
'add chain inet killswitch incoming { type filter hook input priority 0; policy drop; }' \
|
||||
'add rule inet killswitch incoming ct state established,related accept' \
|
||||
'add rule inet killswitch incoming iifname lo accept' '' \
|
||||
'add chain inet killswitch outgoing { type filter hook output priority 0; policy drop; }' \
|
||||
'add rule inet killswitch outgoing ct state established,related accept' \
|
||||
'add rule inet killswitch outgoing oifname lo accept' '' > $nftables_config_file
|
||||
|
||||
local_subnet=$(ip -4 route | grep 'scope link' | awk '{print $1}')
|
||||
printf '%s\n' \
|
||||
'# allow traffic to/from the Docker subnet' \
|
||||
"add rule inet killswitch incoming ip saddr $local_subnet accept" \
|
||||
"add rule inet killswitch outgoing ip daddr $local_subnet accept" '' >> $nftables_config_file
|
||||
|
||||
if [[ $SUBNETS ]]; then
|
||||
printf '# allow traffic to/from the specified subnets\n' >> $nftables_config_file
|
||||
for subnet in ${SUBNETS//,/ }; do
|
||||
ip route add "$subnet" via "$default_gateway" dev eth0
|
||||
printf '%s\n' \
|
||||
"add rule inet killswitch incoming ip saddr $subnet accept" \
|
||||
"add rule inet killswitch outgoing ip daddr $subnet accept" '' >> $nftables_config_file
|
||||
done
|
||||
fi
|
||||
|
||||
global_port=$(grep "^port " "$modified_config_file" | awk '{print $2}')
|
||||
global_protocol=$(grep "^proto " "$modified_config_file" | awk '{print $2}') # {$2 = substr($2, 1, 3)} 2
|
||||
remotes=$(grep "^remote " "$modified_config_file" | awk '{print $2, $3, $4}')
|
||||
|
||||
printf '# allow traffic to the VPN server(s)\n' >> $nftables_config_file
|
||||
ip_regex='^(([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))\.){3}([1-9]?[0-9]|1[0-9][0-9]|2([0-4][0-9]|5[0-5]))$'
|
||||
while IFS= read -r line; do
|
||||
IFS=' ' read -ra remote <<< "$line"
|
||||
address=${remote[0]}
|
||||
port=${remote[1]:-${global_port:-1194}}
|
||||
protocol=${remote[2]:-${global_protocol:-udp}}
|
||||
|
||||
if [[ $address =~ $ip_regex ]]; then
|
||||
printf '%s\n' \
|
||||
"add rule inet killswitch outgoing oifname eth0 ip daddr $address $protocol dport $port accept" >> $nftables_config_file
|
||||
else
|
||||
for ip in $(dig -4 +short "$address"); do
|
||||
printf '%s\n' \
|
||||
"add rule inet killswitch outgoing oifname eth0 ip daddr $ip $protocol dport $port accept" >> $nftables_config_file
|
||||
printf "%s %s\n" "$ip" "$address" >> /etc/hosts
|
||||
done
|
||||
fi
|
||||
done <<< "$remotes"
|
||||
|
||||
printf '%s\n' \
|
||||
'' '# allow traffic over the VPN interface' \
|
||||
"add rule inet killswitch incoming iifname tun0 accept" \
|
||||
"add rule inet killswitch outgoing oifname tun0 accept" >> $nftables_config_file
|
||||
|
||||
nft -f $nftables_config_file
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "info: kill switch is off"
|
||||
for subnet in ${SUBNETS//,/ }; do
|
||||
ip route add "$subnet" via "$default_gateway" dev eth0
|
||||
done
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
if is_enabled "$HTTP_PROXY" ; then
|
||||
scripts/run-http-proxy.sh &
|
||||
fi
|
||||
|
||||
if is_enabled "$SOCKS_PROXY" ; then
|
||||
scripts/run-socks-proxy.sh &
|
||||
fi
|
||||
|
||||
openvpn_args=(
|
||||
"--config" "$modified_config_file"
|
||||
"--auth-nocache"
|
||||
"--cd" "vpn"
|
||||
"--pull-filter" "ignore" "ifconfig-ipv6 "
|
||||
"--pull-filter" "ignore" "route-ipv6 "
|
||||
"--script-security" "2"
|
||||
"--up-restart"
|
||||
"--verb" "$VPN_LOG_LEVEL"
|
||||
)
|
||||
|
||||
if is_enabled "$USE_VPN_DNS" ; then
|
||||
openvpn_args+=(
|
||||
"--up" "/etc/openvpn/up.sh"
|
||||
"--down" "/etc/openvpn/down.sh"
|
||||
)
|
||||
fi
|
||||
|
||||
if [[ $VPN_AUTH_SECRET ]]; then
|
||||
openvpn_args+=("--auth-user-pass" "/run/secrets/$VPN_AUTH_SECRET")
|
||||
fi
|
||||
|
||||
openvpn "${openvpn_args[@]}" &
|
||||
openvpn_child=$!
|
||||
|
||||
wait $openvpn_child
|
|
@ -1,30 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
until ip link show tun0 2>&1 | grep -qv "does not exist"; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
proxy_config_file=config/http-proxy.conf
|
||||
|
||||
addr_eth0=$(ip address show eth0 | grep 'inet ' | awk '{split($2, inet, "/"); print inet[1]}')
|
||||
addr_tun0=$(ip address show tun0 | grep 'inet ' | awk '{split($2, inet, "/"); print inet[1]}')
|
||||
sed -i \
|
||||
-e "/Listen/c Listen $addr_eth0" \
|
||||
-e "/Bind/c Bind $addr_tun0" \
|
||||
$proxy_config_file
|
||||
|
||||
if [[ $HTTP_PROXY_USERNAME && $HTTP_PROXY_PASSWORD ]]; then
|
||||
echo 'info: starting http proxy with credentials'
|
||||
printf 'BasicAuth %s %s\n' "$HTTP_PROXY_USERNAME" "$HTTP_PROXY_PASSWORD" >> $proxy_config_file
|
||||
elif [[ -f "/run/secrets/$HTTP_PROXY_USERNAME_SECRET" && -f "/run/secrets/$HTTP_PROXY_PASSWORD_SECRET" ]]; then
|
||||
echo 'info: starting http proxy with credentials'
|
||||
printf 'BasicAuth %s %s\n' \
|
||||
"$(cat /run/secrets/"$HTTP_PROXY_USERNAME_SECRET")" \
|
||||
"$(cat /run/secrets/"$HTTP_PROXY_PASSWORD_SECRET")" >> $proxy_config_file
|
||||
else
|
||||
echo 'info: starting http proxy without credentials'
|
||||
fi
|
||||
|
||||
exec tinyproxy -d -c $proxy_config_file
|
|
@ -1,26 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
until ip link show tun0 2>&1 | grep -qv "does not exist"; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
proxy_config_file=config/socks-proxy.conf
|
||||
|
||||
if [[ $SOCKS_LISTEN_ON ]]; then
|
||||
sed -i "/internal: /c internal: $SOCKS_LISTEN_ON port = 1080" $proxy_config_file
|
||||
fi
|
||||
if [[ $SOCKS_PROXY_USERNAME && $SOCKS_PROXY_PASSWORD ]]; then
|
||||
printf 'info: starting socks proxy with credentials\n'
|
||||
id "$SOCKS_PROXY_USERNAME" &> /dev/null || useradd "$SOCKS_PROXY_USERNAME" -s /bin/false -M -p "$(mkpasswd "$SOCKS_PROXY_PASSWORD")"
|
||||
sed -i "/method: /c method: username" $proxy_config_file
|
||||
elif [[ -f "/run/secrets/$SOCKS_PROXY_USERNAME_SECRET" && -f "/run/secrets/$SOCKS_PROXY_PASSWORD_SECRET" ]]; then
|
||||
printf 'info: starting socks proxy with credentials\n'
|
||||
id "$(cat /run/secrets/"$SOCKS_PROXY_USERNAME_SECRET")" &> /dev/null || useradd "$(cat /run/secrets/"$SOCKS_PROXY_USERNAME_SECRET")" -s /bin/false -M -p "$(mkpasswd "$(cat /run/secrets/"$SOCKS_PROXY_PASSWORD_SECRET")")"
|
||||
sed -i "/method: /c method: username" $proxy_config_file
|
||||
else
|
||||
printf 'info: starting socks proxy without credentials\n'
|
||||
fi
|
||||
|
||||
exec sockd -f $proxy_config_file
|
|
@ -1,32 +1,13 @@
|
|||
# Use this file as an example if you need help writing your Compose files.
|
||||
# The commented-out parts may or may not be relevant to your setup.
|
||||
|
||||
services:
|
||||
vpn:
|
||||
image: ghcr.io/wfg/openvpn-client
|
||||
# build: .
|
||||
openvpn-client:
|
||||
image: ghcr.io/wfg/openvpn-client:latest
|
||||
container_name: openvpn-client
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
devices:
|
||||
- /dev/net/tun:/dev/net/tun
|
||||
environment:
|
||||
# - SUBNETS=192.168.10.0/24
|
||||
- HTTP_PROXY=on
|
||||
- SOCKS_PROXY=on
|
||||
# - PROXY_USERNAME_SECRET=username # <-- If used, these must match the name of a
|
||||
# - PROXY_PASSWORD_SECRET=password # <-- secret (NOT the file used by the secret)
|
||||
# volumes:
|
||||
# - ~/local/vpn:/data/vpn
|
||||
ports:
|
||||
- 1080:1080
|
||||
- 8088:8080
|
||||
# secrets:
|
||||
# - username
|
||||
# - password
|
||||
|
||||
# secrets:
|
||||
# username:
|
||||
# file: ~/local/secrets/username
|
||||
# password:
|
||||
# file: ~/local/secrets/password
|
||||
- ALLOWED_SUBNETS=192.168.10.0/24
|
||||
volumes:
|
||||
- ./local:/config
|
||||
restart: unless-stopped
|
||||
|
|
Loading…
Add table
Reference in a new issue