mirror of
https://github.com/wfg/docker-openvpn-client.git
synced 2025-04-25 12:37:05 -04:00
Compare commits
12 commits
Author | SHA1 | Date | |
---|---|---|---|
|
91bde63706 | ||
|
ee61a9ecdb | ||
|
e511db1436 | ||
|
99bb6571ae | ||
|
d3ce6295ff | ||
|
333fcc682a | ||
|
045d47050e | ||
|
b5489ea7ce | ||
|
ac8a5ba3d4 | ||
|
994836b5f0 | ||
|
e7c9e1ff6a | ||
|
bc1648f04a |
29 changed files with 147 additions and 943 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/
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
repos:
|
||||
- repo: https://github.com/norwoodj/helm-docs
|
||||
rev: v1.6.0
|
||||
hooks:
|
||||
- id: helm-docs
|
||||
args:
|
||||
- --chart-search-root=chart
|
||||
- --template-files=./_templates.gotmpl
|
||||
- --template-files=README.md.gotmpl
|
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" ]
|
71
README.md
71
README.md
|
@ -1,14 +1,15 @@
|
|||
# OpenVPN Client for Docker
|
||||
|
||||
Archived in favor of [a WireGuard version](https://github.com/wfg/docker-wireguard).
|
||||
|
||||
## What is this and what does it do?
|
||||
[`ghcr.io/wfg/openvpn-client`](https://github.com/users/wfg/packages/container/package/openvpn-client) is a containerized OpenVPN client.
|
||||
It has a kill switch built with `nftables` that kills Internet connectivity to the container if the VPN tunnel goes down for any reason.
|
||||
It also includes an HTTP proxy server ([Tinyproxy](https://tinyproxy.github.io/)) and a SOCKS proxy server ([Dante](https://www.inet.no/dante/index.html)).
|
||||
This allows hosts and non-containerized applications to use the VPN without having to run VPN clients on those hosts.
|
||||
It has a kill switch built with `iptables` that kills Internet connectivity to the container if the VPN tunnel goes down for any reason.
|
||||
|
||||
This image requires you to supply the necessary OpenVPN configuration file(s).
|
||||
Because of this, any VPN provider should work.
|
||||
|
||||
If you find something that doesn't work or have an idea for a new feature, issues and **pull requests are welcome**.
|
||||
If you find something that doesn't work or have an idea for a new feature, issues and **pull requests are welcome** (however, I'm not promising they will be merged).
|
||||
|
||||
## Why?
|
||||
Having a containerized VPN client lets you use container networking to easily choose which applications you want using the VPN instead of having to set up split tunnelling.
|
||||
|
@ -19,27 +20,27 @@ It also keeps you from having to install an OpenVPN client on the underlying hos
|
|||
You can either pull it from GitHub Container Registry or build it yourself.
|
||||
|
||||
To pull it from GitHub Container Registry, run
|
||||
```bash
|
||||
```
|
||||
docker pull ghcr.io/wfg/openvpn-client
|
||||
```
|
||||
|
||||
To build it yourself, run
|
||||
```bash
|
||||
docker build -t ghcr.io/wfg/openvpn-client https://github.com/wfg/docker-openvpn-client.git
|
||||
```
|
||||
docker build -t ghcr.io/wfg/openvpn-client https://github.com/wfg/docker-openvpn-client.git#:build
|
||||
```
|
||||
|
||||
### Creating and running a container
|
||||
The image requires the container be created with the `NET_ADMIN` capability and `/dev/net/tun` accessible.
|
||||
Below are bare-bones examples for `docker run` and Compose; however, you'll probably want to do more than just run the VPN client.
|
||||
See the sections below to learn how to use the [proxies](#http_proxy-and-socks_proxy) and have [other containers use `openvpn-client`'s network stack](#using-with-other-containers).
|
||||
See the below to learn how to have [other containers use `openvpn-client`'s network stack](#using-with-other-containers).
|
||||
|
||||
#### `docker run`
|
||||
```bash
|
||||
```
|
||||
docker run --detach \
|
||||
--name=openvpn-client \
|
||||
--cap-add=NET_ADMIN \
|
||||
--device=/dev/net/tun \
|
||||
--volume <path/to/config/dir>:/data/vpn \
|
||||
--volume <path/to/config/dir>:/config \
|
||||
ghcr.io/wfg/openvpn-client
|
||||
```
|
||||
|
||||
|
@ -54,45 +55,26 @@ services:
|
|||
devices:
|
||||
- /dev/net/tun
|
||||
volumes:
|
||||
- <path/to/config/dir>:/data/vpn
|
||||
- <path/to/config/dir>:/config
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
#### 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`. |
|
||||
"Truthy" values are the following: `true`, `t`, `yes`, `y`, `1`, `on`, `enable`, or `enabled`.
|
||||
| `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. |
|
||||
| `KILL_SWITCH` | `on` | Whether or not to enable the kill switch. Set to any "truthy" value[1] to enable. |
|
||||
|
||||
[1] "Truthy" values in this context are the following: `true`, `t`, `yes`, `y`, `1`, `on`, `enable`, or `enabled`.
|
||||
|
||||
##### Environment variable considerations
|
||||
###### `HTTP_PROXY` and `SOCKS_PROXY`
|
||||
If enabling the the proxy server(s), you'll want to publish the appropriate port(s) in order to access the server(s).
|
||||
To do that using `docker run`, add `-p <host_port>:8080` and/or `-p <host_port>:1080` where `<host_port>` is whatever port you want to use on the host.
|
||||
If you're using `docker-compose`, add the relevant port specification(s) from the snippet below to the `openvpn-client` service definition in your Compose file.
|
||||
```yaml
|
||||
ports:
|
||||
- <host_port>:8080
|
||||
- <host_port>:1080
|
||||
```
|
||||
###### `ALLOWED_SUBNETS`
|
||||
If you intend on connecting to containers that use the OpenVPN container's network stack (which you probably do), **you will probably want to use this variable**.
|
||||
Regardless of whether or not you're using the kill switch, the entrypoint script also adds routes to each of the `ALLOWED_SUBNETS` to allow network connectivity from outside of Docker.
|
||||
|
||||
##### `*_PROXY_USERNAME_SECRET`, `*_PROXY_PASSWORD_SECRET`, and `VPN_AUTH_SECRET`
|
||||
##### `AUTH_SECRET`
|
||||
Compose has support for [Docker secrets](https://docs.docker.com/engine/swarm/secrets/#use-secrets-in-compose).
|
||||
See the [Compose file](docker-compose.yml) in this repository for example usage of passing proxy credentials as Docker secrets.
|
||||
|
||||
|
@ -121,18 +103,11 @@ In both cases, replace `<host_port>` and `<container_port>` with the port used b
|
|||
Once you have container running `ghcr.io/wfg/openvpn-client`, run the following command to spin up a temporary container using `openvpn-client` for networking.
|
||||
The `wget -qO - ifconfig.me` bit will return the public IP of the container (and anything else using `openvpn-client` for networking).
|
||||
You should see an IP address owned by your VPN provider.
|
||||
```bash
|
||||
```
|
||||
docker run --rm -it --network=container:openvpn-client alpine wget -qO - ifconfig.me
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
#### `can't initialize iptables`
|
||||
If you see a message like the below in your logs, try setting `KILL_SWITCH` to `nftables`:
|
||||
```
|
||||
iptables v1.8.8 (legacy): can't initialize iptables table `filter': Table does not exist (do you need to insmod?)
|
||||
Perhaps iptables or your kernel needs to be upgraded.
|
||||
```
|
||||
|
||||
#### VPN authentication
|
||||
Your OpenVPN configuration file may not come with authentication baked in.
|
||||
To provide OpenVPN the necessary credentials, create a file (any name will work, but this example will use `credentials.txt`) next to the OpenVPN configuration file with your username on the first line and your password on the second line.
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
IMAGE_NAME=ghcr.io/wfg/openvpn-client
|
20
build.py
20
build.py
|
@ -1,20 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import subprocess
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('image_version', type=str)
|
||||
args = parser.parse_args()
|
||||
|
||||
docker_build_cmd = [
|
||||
'docker', 'build',
|
||||
'--build-arg', f'BUILD_DATE={str(datetime.datetime.now())}',
|
||||
'--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',
|
||||
'.',
|
||||
]
|
||||
subprocess.run(docker_build_cmd)
|
1
build/.dockerignore
Normal file
1
build/.dockerignore
Normal file
|
@ -0,0 +1 @@
|
|||
Dockerfile
|
14
build/Dockerfile
Normal file
14
build/Dockerfile
Normal file
|
@ -0,0 +1,14 @@
|
|||
FROM alpine:3.17
|
||||
|
||||
RUN apk add --no-cache \
|
||||
bash \
|
||||
bind-tools \
|
||||
iptables \
|
||||
ip6tables \
|
||||
openvpn
|
||||
|
||||
COPY . /usr/local/bin
|
||||
|
||||
ENV KILL_SWITCH=on
|
||||
|
||||
ENTRYPOINT [ "entry.sh" ]
|
50
build/entry.sh
Executable file
50
build/entry.sh
Executable file
|
@ -0,0 +1,50 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
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
|
||||
echo "no openvpn configuration file found" >&2
|
||||
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
|
44
build/killswitch.sh
Executable file
44
build/killswitch.sh
Executable file
|
@ -0,0 +1,44 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
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:?"config file not found by kill switch"}")
|
||||
global_protocol=$(awk '$1 == "proto" { print $2 }' "${config:?"config file not found by kill switch"}")
|
||||
remotes=$(awk '$1 == "remote" { print $2, $3, $4 }' "${config:?"config file not found by kill switch"}")
|
||||
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,23 +0,0 @@
|
|||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
|
@ -1,15 +0,0 @@
|
|||
apiVersion: v2
|
||||
name: openvpn-client
|
||||
description: A Helm chart for an OpenVPN client with HTTP and SOCKS5 proxies
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.1.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "v1.2.1"
|
|
@ -1,42 +0,0 @@
|
|||
# openvpn-client
|
||||
|
||||
  
|
||||
|
||||
A Helm chart for an OpenVPN client with HTTP and SOCKS5 proxies
|
||||
|
||||
## Values
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| affinity | object | `{}` | |
|
||||
| auth.enabled | bool | `false` | Whether to turn on authentication for the proxies |
|
||||
| auth.existingSecret | string | `""` | Existing secret containing the credentials for accessing the proxies. |
|
||||
| auth.proxyPassword | string | `""` | |
|
||||
| auth.proxyUsername | string | `""` | |
|
||||
| autoscaling.enabled | bool | `false` | |
|
||||
| autoscaling.maxReplicas | int | `100` | |
|
||||
| autoscaling.minReplicas | int | `1` | |
|
||||
| autoscaling.targetCPUUtilizationPercentage | int | `80` | |
|
||||
| configFiles.files | object | `{}` | OpenVPN config files |
|
||||
| configFiles.openVPNConfig | string | `""` | The OpenVPN config file to use. If this is unset, the first file with the extension `.conf` will be used. |
|
||||
| fullnameOverride | string | `""` | |
|
||||
| httpProxy.enabled | bool | `false` | The on/off status of Tinyproxy, the built-in HTTP proxy server. |
|
||||
| image.pullPolicy | string | `"IfNotPresent"` | |
|
||||
| image.repository | string | `"ghcr.io/wfg/openvpn-client"` | |
|
||||
| image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. |
|
||||
| keepDNSUnchanged.enabled | bool | `false` | Keep existing DNS configuration |
|
||||
| killSwitch.enabled | bool | `true` | The on/off status of the network kill switch. |
|
||||
| listenOn | string | `""` | Address the proxies will be listening on. Set to `0.0.0.0` to allow all IP addresses. |
|
||||
| nameOverride | string | `""` | |
|
||||
| nodeSelector | object | `{}` | |
|
||||
| podAnnotations | object | `{}` | |
|
||||
| replicaCount | int | `1` | |
|
||||
| resources | object | `{}` | |
|
||||
| service.type | string | `"ClusterIP"` | |
|
||||
| socksProxy.enabled | bool | `false` | The on/off status of Dante, the built-in SOCKS proxy server. |
|
||||
| subnets | list | `[]` | A list of one or more subnets to allow outside of the VPN tunnel. |
|
||||
| tolerations | list | `[]` | |
|
||||
| vpnLogLevel | int | `3` | OpenVPN verbosity (`1`-`11`) |
|
||||
|
||||
----------------------------------------------
|
||||
Autogenerated from chart metadata using [helm-docs v1.7.0](https://github.com/norwoodj/helm-docs/releases/v1.7.0)
|
|
@ -1,76 +0,0 @@
|
|||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "openvpn-client.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "openvpn-client.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "openvpn-client.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "openvpn-client.labels" -}}
|
||||
helm.sh/chart: {{ include "openvpn-client.chart" . }}
|
||||
{{ include "openvpn-client.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "openvpn-client.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "openvpn-client.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Convert boolean to on/off
|
||||
*/}}
|
||||
{{- define "openvpn-client.boolean" -}}
|
||||
{{- if .enabled }} "on" {{- else }} "off" {{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Define auth secret name
|
||||
*/}}
|
||||
{{- define "openvpn-client.authSecretName" -}}
|
||||
{{- if .Values.auth.existingSecret -}}
|
||||
{{- .Values.auth.existingSecret -}}
|
||||
{{- else -}}
|
||||
{{- include "openvpn-client.fullname" . | printf "%s-auth" }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Define config secret name
|
||||
*/}}
|
||||
{{- define "openvpn-client.configSecretName" -}}
|
||||
{{- include "openvpn-client.fullname" . | printf "%s-config" }}
|
||||
{{- end -}}
|
|
@ -1,12 +0,0 @@
|
|||
{{- if and .Values.auth.enabled (not .Values.auth.existingSecret) (or .Values.httpProxy.enabled .Values.socksProxy.enabled) -}}
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "openvpn-client.authSecretName" . }}
|
||||
labels:
|
||||
{{- include "openvpn-client.labels" . | nindent 4 }}
|
||||
type: kubernetes.io/basic-auth
|
||||
data:
|
||||
username: {{ .Values.auth.proxyUsername | b64enc | quote }}
|
||||
password: {{ .Values.auth.proxyPassword | b64enc | quote }}
|
||||
{{- end -}}
|
|
@ -1,12 +0,0 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "openvpn-client.configSecretName" . }}
|
||||
labels:
|
||||
{{- include "openvpn-client.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
data:
|
||||
{{- range $fileName, $fileContent := $.Values.configFiles.files }}
|
||||
{{ $fileName }}: |-
|
||||
{{- $fileContent | b64enc | nindent 4 }}
|
||||
{{- end }}
|
|
@ -1,108 +0,0 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "openvpn-client.fullname" . }}
|
||||
labels:
|
||||
{{- include "openvpn-client.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if not .Values.autoscaling.enabled }}
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "openvpn-client.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "openvpn-client.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
initContainers:
|
||||
- name: copy
|
||||
image: busybox
|
||||
command: ["/bin/sh", "-c", "cp -r /from/. /to"]
|
||||
volumeMounts:
|
||||
- name: openvpn-client
|
||||
mountPath: /from
|
||||
- name: configs
|
||||
mountPath: /to
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
securityContext:
|
||||
capabilities:
|
||||
add:
|
||||
- NET_ADMIN
|
||||
privileged: true
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
{{- if .Values.socksProxy.enabled }}
|
||||
- containerPort: 1080
|
||||
{{- end }}
|
||||
{{- if .Values.httpProxy.enabled }}
|
||||
- containerPort: 8080
|
||||
{{- end }}
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["ping", "-c", "3", "1.1.1.1"]
|
||||
env:
|
||||
- name: VPN_LOG_LEVEL
|
||||
value: {{ .Values.vpnLogLevel | quote }}
|
||||
- name: SOCKS_PROXY
|
||||
value: {{- include "openvpn-client.boolean" .Values.socksProxy }}
|
||||
- name: HTTP_PROXY
|
||||
value: {{- include "openvpn-client.boolean" .Values.httpProxy }}
|
||||
- name: KILL_SWITCH
|
||||
value: {{- include "openvpn-client.boolean" .Values.killSwitch }}
|
||||
- name: KEEP_DNS_UNCHANGED
|
||||
value: {{- include "openvpn-client.boolean" .Values.keepDNSUnchanged }}
|
||||
{{- if .Values.listenOn }}
|
||||
- name: LISTEN_ON
|
||||
value: {{ .Values.listenOn }}
|
||||
{{- end }}
|
||||
{{- if .Values.subnets }}
|
||||
- name: SUBNETS
|
||||
value: {{ join "," .Values.subnets | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.auth.enabled }}
|
||||
- name: PROXY_USERNAME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "openvpn-client.authSecretName" . }}
|
||||
key: username
|
||||
- name: PROXY_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "openvpn-client.authSecretName" . }}
|
||||
key: password
|
||||
{{- end }}
|
||||
{{- with .Values.configFiles.openVPNConfig }}
|
||||
- name: VPN_CONFIG_FILE
|
||||
value: {{ . }}
|
||||
{{- end }}
|
||||
volumeMounts:
|
||||
- mountPath: /data/vpn
|
||||
name: configs
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
volumes:
|
||||
- name: openvpn-client
|
||||
secret:
|
||||
secretName: {{ include "openvpn-client.configSecretName" . }}
|
||||
- name: configs
|
||||
emptyDir: {}
|
|
@ -1,28 +0,0 @@
|
|||
{{- if .Values.autoscaling.enabled }}
|
||||
apiVersion: autoscaling/v2beta1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "openvpn-client.fullname" . }}
|
||||
labels:
|
||||
{{- include "openvpn-client.labels" . | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "openvpn-client.fullname" . }}
|
||||
minReplicas: {{ .Values.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- end }}
|
|
@ -1,23 +0,0 @@
|
|||
{{- if or .Values.socksProxy.enabled .Values.httpProxy.enabled }}
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "openvpn-client.fullname" . }}
|
||||
labels:
|
||||
{{- include "openvpn-client.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
{{- if .Values.socksProxy.enabled }}
|
||||
- port: 1080
|
||||
protocol: TCP
|
||||
name: socks5
|
||||
{{- end }}
|
||||
{{- if .Values.httpProxy.enabled }}
|
||||
- port: 8080
|
||||
protocol: TCP
|
||||
name: http
|
||||
{{- end }}
|
||||
selector:
|
||||
{{- include "openvpn-client.selectorLabels" . | nindent 4 }}
|
||||
{{- end }}
|
|
@ -1,88 +0,0 @@
|
|||
# Default values for openvpn-client.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: ghcr.io/wfg/openvpn-client
|
||||
pullPolicy: IfNotPresent
|
||||
# -- Overrides the image tag whose default is the chart appVersion.
|
||||
tag: ""
|
||||
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
podAnnotations: {}
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
|
||||
resources:
|
||||
{}
|
||||
# limits:
|
||||
# cpu: 10m
|
||||
# memory: 64Mi
|
||||
# requests:
|
||||
# cpu: 10m
|
||||
# memory: 64Mi
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 100
|
||||
targetCPUUtilizationPercentage: 80
|
||||
# targetMemoryUtilizationPercentage: 80
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
||||
|
||||
httpProxy:
|
||||
# -- The on/off status of Tinyproxy, the built-in HTTP proxy server.
|
||||
enabled: false
|
||||
|
||||
socksProxy:
|
||||
# -- The on/off status of Dante, the built-in SOCKS proxy server.
|
||||
enabled: false
|
||||
|
||||
# -- Address the proxies will be listening on. Set to `0.0.0.0` to allow all IP addresses.
|
||||
listenOn: ""
|
||||
|
||||
auth:
|
||||
# -- Whether to turn on authentication for the proxies
|
||||
enabled: false
|
||||
# -- Existing secret containing the credentials for accessing the proxies.
|
||||
existingSecret: ""
|
||||
proxyUsername: ""
|
||||
proxyPassword: ""
|
||||
|
||||
# -- OpenVPN verbosity (`1`-`11`)
|
||||
vpnLogLevel: 3
|
||||
|
||||
# -- A list of one or more subnets to allow outside of the VPN tunnel.
|
||||
subnets:
|
||||
[]
|
||||
# - "192.168.0.0/24"
|
||||
# - "192.168.1.0/24"
|
||||
|
||||
killSwitch:
|
||||
# -- The on/off status of the network kill switch.
|
||||
enabled: true
|
||||
|
||||
keepDNSUnchanged:
|
||||
# -- Keep existing DNS configuration
|
||||
enabled: false
|
||||
|
||||
configFiles:
|
||||
# -- The OpenVPN config file to use. If this is unset, the first file with the extension `.conf` will be used.
|
||||
openVPNConfig: ""
|
||||
# -- OpenVPN config files
|
||||
files:
|
||||
{}
|
||||
# example.conf: |-
|
||||
# # Your OpenVPN configuration file
|
||||
# Line 1
|
||||
# Line 2
|
|
@ -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'
|
||||
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'
|
||||
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
Add a link
Reference in a new issue