mirror of
https://github.com/wfg/docker-openvpn-client.git
synced 2025-04-22 11:17:09 -04:00
Compare commits
32 commits
Author | SHA1 | Date | |
---|---|---|---|
|
91bde63706 | ||
|
ee61a9ecdb | ||
|
e511db1436 | ||
|
99bb6571ae | ||
|
d3ce6295ff | ||
|
333fcc682a | ||
|
045d47050e | ||
|
b5489ea7ce | ||
|
ac8a5ba3d4 | ||
|
994836b5f0 | ||
|
e7c9e1ff6a | ||
|
bc1648f04a | ||
|
51fa5f2e04 | ||
|
650d55fe76 | ||
|
6a1ad61a79 | ||
|
436e745c13 | ||
|
0dec9be496 | ||
|
ee14898805 | ||
|
a457130638 | ||
|
ea0b4dd271 | ||
|
574fec5212 | ||
|
4cbfa8fb55 | ||
|
283fe3305f | ||
|
015ebab5ca | ||
|
d1806f8aad | ||
|
ac66032038 | ||
|
de63b93ddb | ||
|
6150534f86 | ||
|
c3ca6fbaa5 | ||
|
fa2045ff68 | ||
|
de244a77f8 | ||
|
49fc0d7dfd |
29 changed files with 206 additions and 965 deletions
|
@ -1,2 +0,0 @@
|
|||
*
|
||||
!data/
|
46
.github/workflows/build.yaml
vendored
Normal file
46
.github/workflows/build.yaml
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: docker/setup-qemu-action@v2
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
|
||||
- uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- id: tags
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: ghcr.io/wfg/openvpn-client
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}},enable=${{ !startsWith(github.ref, 'refs/tags/v0.') }}
|
||||
|
||||
- id: build-args
|
||||
run: |
|
||||
ref=${{ github.ref }}
|
||||
vpatch=${ref##refs/*/}
|
||||
patch=${vpatch#v}
|
||||
echo "::set-output name=date::$(date --utc --iso-8601=seconds)"
|
||||
echo "::set-output name=version::$patch"
|
||||
|
||||
- uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: "{{defaultContext}}:build"
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6
|
||||
build-args: |
|
||||
BUILD_DATE=${{ steps.build-args.outputs.date }}
|
||||
IMAGE_VERSION=${{ steps.build-args.outputs.version }}
|
||||
tags: ${{ steps.tags.outputs.tags }}
|
||||
push: true
|
59
.github/workflows/publish.yml
vendored
59
.github/workflows/publish.yml
vendored
|
@ -1,59 +0,0 @@
|
|||
name: Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
|
||||
env:
|
||||
IMAGE_NAME: openvpn-client
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Log in to registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create tags
|
||||
id: tags
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: ghcr.io/wfg/openvpn-client
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
|
||||
- name: Create build args
|
||||
id: build-args
|
||||
run: |
|
||||
ref=${{ github.ref }}
|
||||
vpatch=${ref##refs/*/}
|
||||
patch=${vpatch#v}
|
||||
echo "::set-output name=date::$(date --utc --iso-8601=seconds)"
|
||||
echo "::set-output name=version::$patch"
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
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 }}
|
||||
push: true
|
19
.github/workflows/sweep.yml
vendored
19
.github/workflows/sweep.yml
vendored
|
@ -1,19 +0,0 @@
|
|||
name: Mark issues as stale
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 3 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
with:
|
||||
stale-issue-label: no-issue-activity
|
||||
stale-pr-label: no-pr-activity
|
||||
days-before-stale: 30
|
||||
days-before-close: -1
|
||||
exempt-assignees: wfg
|
||||
any-of-labels: needs-more-info
|
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
|
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,12 +0,0 @@
|
|||
# Changelog
|
||||
|
||||
## 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.
|
28
Dockerfile
28
Dockerfile
|
@ -1,28 +0,0 @@
|
|||
FROM alpine:3.15
|
||||
|
||||
ARG IMAGE_VERSION
|
||||
ARG BUILD_DATE
|
||||
|
||||
LABEL org.opencontainers.image.created="$BUILD_DATE"
|
||||
LABEL org.opencontainers.image.source="github.com/wfg/docker-openvpn-client"
|
||||
LABEL org.opencontainers.image.version="$IMAGE_VERSION"
|
||||
|
||||
ENV KILL_SWITCH=on \
|
||||
VPN_LOG_LEVEL=3 \
|
||||
HTTP_PROXY=off \
|
||||
SOCKS_PROXY=off
|
||||
|
||||
RUN apk add --no-cache \
|
||||
bash \
|
||||
bind-tools \
|
||||
dante-server \
|
||||
openvpn \
|
||||
tinyproxy
|
||||
|
||||
RUN mkdir -p /data/vpn
|
||||
|
||||
COPY data/ /data
|
||||
|
||||
HEALTHCHECK CMD ping -c 3 1.1.1.1 || exit 1
|
||||
|
||||
ENTRYPOINT ["/data/scripts/entry.sh"]
|
79
README.md
79
README.md
|
@ -1,49 +1,46 @@
|
|||
# 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 `iptables` 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.
|
||||
|
||||
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.
|
||||
It also keeps you from having to install an OpenVPN client on the underlying host.
|
||||
|
||||
The idea for this image came from a similar project by [qdm12](https://github.com/qdm12) that has since evolved into something bigger and more complex than I wanted to use.
|
||||
I decided to dissect it and take it in my own direction.
|
||||
I plan to keep everything here well-documented so this is not only a learning experience for me, but also anyone else that uses it.
|
||||
|
||||
## How do I use it?
|
||||
### Getting the image
|
||||
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
|
||||
```
|
||||
|
||||
|
@ -58,38 +55,26 @@ services:
|
|||
devices:
|
||||
- /dev/net/tun
|
||||
volumes:
|
||||
- <path/to/config/dir>:/data/vpn
|
||||
- <path/to/config/dir>:/config
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
#### Environment variables (alphabetical)
|
||||
#### Environment variables
|
||||
| Variable | Default (blank is unset) | Description |
|
||||
| --- | --- | --- |
|
||||
| `HTTP_PROXY` | `off` | The on/off status of Tinyproxy, the built-in HTTP proxy server. To enable, set to `on`. Any other value (including unset) will cause the proxy server to not start. It listens on port 8080. |
|
||||
| `KILL_SWITCH` | `on` | The on/off status of the network kill switch. |
|
||||
| `LISTEN_ON` | | Address the proxies will be listening on. Set to `0.0.0.0` to listen on all IP addresses. |
|
||||
| `PROXY_PASSWORD` | | Credentials for accessing the proxies. If `PROXY_PASSWORD` is specified, you must also specify `PROXY_USERNAME`. |
|
||||
| `PROXY_PASSWORD_SECRET` | | Docker secrets that contain the credentials for accessing the proxies. If `PROXY_PASSWORD_SECRET` is specified, you must also specify `PROXY_USERNAME_SECRET`. |
|
||||
| `PROXY_USERNAME` | | Credentials for accessing the proxies. If `PROXY_USERNAME` is specified, you must also specify `PROXY_PASSWORD`. |
|
||||
| `PROXY_USERNAME_SECRET` | | Docker secrets that contain the credentials for accessing the proxies. If `PROXY_USERNAME_SECRET` is specified, you must also specify `PROXY_PASSWORD_SECRET`. |
|
||||
| `SOCKS_PROXY` | `off` | The on/off status of Dante, the built-in SOCKS proxy server. To enable, set to `on`. Any other value (including unset) will cause the proxy server to not start. It listens on port 1080. |
|
||||
| `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. |
|
||||
| `VPN_AUTH_SECRET` | | Docker secret that contain the credentials for accessing the VPN. |
|
||||
| `VPN_CONFIG_FILE` | | The OpenVPN config file to use. If this is unset, the first file with the extension .conf will be used. |
|
||||
| `VPN_LOG_LEVEL` | `3` | OpenVPN verbosity (`1`-`11`) |
|
||||
| `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.
|
||||
|
||||
|
@ -118,6 +103,24 @@ 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
|
||||
#### 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.
|
||||
|
||||
For example:
|
||||
```
|
||||
vpn_username
|
||||
vpn_password
|
||||
```
|
||||
|
||||
In the OpenVPN configuration file, add the following line:
|
||||
```
|
||||
auth-user-pass credentials.txt
|
||||
```
|
||||
|
||||
This will tell OpenVPN to read `credentials.txt` whenever it needs credentials.
|
||||
|
|
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,41 +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. |
|
||||
| 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.5.0](https://github.com/norwoodj/helm-docs/releases/v1.5.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,106 +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 }}
|
||||
{{- 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,84 +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
|
||||
|
||||
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,9 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo -e "Running Dante SOCKS proxy server.\n"
|
||||
|
||||
until ip link show tun0 2>&1 | grep -qv "does not exist"; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
sockd -f /data/sockd.conf
|
|
@ -1,237 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
cleanup() {
|
||||
# When you run `docker stop` or any equivalent, a SIGTERM signal is sent to PID 1.
|
||||
# A process running as PID 1 inside a container is treated specially by Linux:
|
||||
# it ignores any signal with the default action. As a result, the process will
|
||||
# not terminate on SIGINT or SIGTERM unless it is coded to do so. Because of this,
|
||||
# I've defined behavior for when SIGINT and SIGTERM is received.
|
||||
if [[ -n "$openvpn_child" ]]; then
|
||||
echo "Stopping OpenVPN..."
|
||||
kill -TERM "$openvpn_child"
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
rm "$config_file_modified"
|
||||
echo "Exiting."
|
||||
exit 0
|
||||
}
|
||||
|
||||
# OpenVPN log levels are 1-11.
|
||||
# shellcheck disable=SC2153
|
||||
if [[ "$VPN_LOG_LEVEL" -lt 1 || "$VPN_LOG_LEVEL" -gt 11 ]]; then
|
||||
echo "WARNING: Invalid log level $VPN_LOG_LEVEL. Setting to default."
|
||||
vpn_log_level=3
|
||||
else
|
||||
vpn_log_level=$VPN_LOG_LEVEL
|
||||
fi
|
||||
|
||||
echo "
|
||||
---- Running with the following variables ----
|
||||
Kill switch: ${KILL_SWITCH:-off}
|
||||
HTTP proxy: ${HTTP_PROXY:-off}
|
||||
SOCKS proxy: ${SOCKS_PROXY:-off}
|
||||
Proxy username secret: ${PROXY_PASSWORD_SECRET:-none}
|
||||
Proxy password secret: ${PROXY_USERNAME_SECRET:-none}
|
||||
Allowing subnets: ${SUBNETS:-none}
|
||||
Using OpenVPN log level: $vpn_log_level
|
||||
Listening on: ${LISTEN_ON:-none}"
|
||||
|
||||
if [[ -n "$VPN_CONFIG_FILE" ]]; then
|
||||
config_file_original="/data/vpn/$VPN_CONFIG_FILE"
|
||||
elif [[ -n "$VPN_CONFIG_PATTERN" ]]; then
|
||||
# Capture the filename of the random .conf file according to the pattern to use as OpenVPN config.
|
||||
config_file_original=$(find /data/vpn -name "$VPN_CONFIG_PATTERN" 2> /dev/null | sort | shuf -n 1)
|
||||
else
|
||||
# Capture the filename of the random .conf file to use as the OpenVPN config.
|
||||
config_file_original=$(find /data/vpn -name "*.conf" 2> /dev/null | sort | shuf -n 1)
|
||||
fi
|
||||
|
||||
if [[ -z "$config_file_original" ]]; then
|
||||
>&2 echo "ERROR: No configuration file found. Please check your mount and file permissions. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Using configuration file: $config_file_original"
|
||||
|
||||
# Create a new configuration file to modify so the original is left untouched.
|
||||
config_file_modified="${config_file_original}.modified"
|
||||
|
||||
echo "Creating $config_file_modified and making required changes to that file."
|
||||
grep -Ev "(^up\s|^down\s)" "$config_file_original" > "$config_file_modified"
|
||||
|
||||
# These configuration file changes are required by Alpine.
|
||||
sed -i \
|
||||
-e 's/^proto udp$/proto udp4/' \
|
||||
-e 's/^proto tcp$/proto tcp4/' \
|
||||
"$config_file_modified"
|
||||
|
||||
echo "up /etc/openvpn/up.sh" >> "$config_file_modified"
|
||||
echo "down /etc/openvpn/down.sh" >> "$config_file_modified"
|
||||
|
||||
echo -e "Changes made.\n"
|
||||
|
||||
trap cleanup INT TERM
|
||||
|
||||
default_gateway=$(ip r | grep 'default via' | cut -d " " -f 3)
|
||||
if [[ "$KILL_SWITCH" == "on" ]]; then
|
||||
local_subnet=$(ip r | grep -v 'default via' | grep eth0 | tail -n 1 | cut -d " " -f 1)
|
||||
|
||||
echo "Creating VPN kill switch and local routes."
|
||||
|
||||
echo "Allowing established and related connections..."
|
||||
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
|
||||
|
||||
echo "Allowing loopback connections..."
|
||||
iptables -A INPUT -i lo -j ACCEPT
|
||||
iptables -A OUTPUT -o lo -j ACCEPT
|
||||
|
||||
echo "Allowing Docker network connections..."
|
||||
iptables -A INPUT -s "$local_subnet" -j ACCEPT
|
||||
iptables -A OUTPUT -d "$local_subnet" -j ACCEPT
|
||||
|
||||
echo "Allowing specified subnets..."
|
||||
# for every specified subnet...
|
||||
for subnet in ${SUBNETS//,/ }; do
|
||||
# create a route to it and...
|
||||
ip route add "$subnet" via "$default_gateway" dev eth0
|
||||
# allow connections
|
||||
iptables -A INPUT -s "$subnet" -j ACCEPT
|
||||
iptables -A OUTPUT -d "$subnet" -j ACCEPT
|
||||
done
|
||||
|
||||
echo "Allowing remote servers in configuration file..."
|
||||
global_port=$(grep "port " "$config_file_modified" | cut -d " " -f 2)
|
||||
global_protocol=$(grep "proto " "$config_file_modified" | cut -d " " -f 2 | cut -c1-3)
|
||||
remotes=$(grep "remote " "$config_file_modified")
|
||||
|
||||
echo " Using:"
|
||||
comment_regex='^[[:space:]]*[#;]'
|
||||
echo "$remotes" | while IFS= read -r line; do
|
||||
# Ignore comments.
|
||||
if ! [[ "$line" =~ $comment_regex ]]; then
|
||||
# Remove the line prefix 'remote '.
|
||||
line=${line#remote }
|
||||
|
||||
# Remove any trailing comments.
|
||||
line=${line%%#*}
|
||||
|
||||
# Split the line into an array.
|
||||
# The first element is an address (IP or domain), the second is a port,
|
||||
# and the fourth is a protocol.
|
||||
IFS=' ' read -r -a remote <<< "$line"
|
||||
address=${remote[0]}
|
||||
# Use port from 'remote' line, then 'port' line, then '1194'.
|
||||
port=${remote[1]:-${global_port:-1194}}
|
||||
# Use protocol from 'remote' line, then 'proto' line, then 'udp'.
|
||||
protocol=${remote[2]:-${global_protocol:-udp}}
|
||||
|
||||
# Map from OpenVPN tcp-client config option to tcp for iptables
|
||||
if [[ $protocol == "tcp-client" ]]; then
|
||||
protocol='tcp'
|
||||
fi
|
||||
|
||||
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]))$'
|
||||
if [[ "$address" =~ $ip_regex ]]; then
|
||||
echo " IP: $address PORT: $port PROTOCOL: $protocol"
|
||||
iptables -A OUTPUT -o eth0 -d "$address" -p "$protocol" --dport "$port" -j ACCEPT
|
||||
else
|
||||
for ip in $(dig -4 +short "$address"); do
|
||||
echo " $address (IP: $ip PORT: $port PROTOCOL: $protocol)"
|
||||
iptables -A OUTPUT -o eth0 -d "$ip" -p "$protocol" --dport "$port" -j ACCEPT
|
||||
echo "$ip $address" >> /etc/hosts
|
||||
done
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Allowing connections over VPN interface..."
|
||||
iptables -A INPUT -i tun0 -j ACCEPT
|
||||
iptables -A OUTPUT -o tun0 -j ACCEPT
|
||||
|
||||
echo "Preventing anything else..."
|
||||
iptables -P INPUT DROP
|
||||
iptables -P OUTPUT DROP
|
||||
iptables -P FORWARD DROP
|
||||
|
||||
echo -e "iptables rules created and routes configured.\n"
|
||||
else
|
||||
echo -e "WARNING: VPN kill switch is disabled. Traffic will be allowed outside of the tunnel if the connection is lost.\n"
|
||||
echo "Creating routes to specified subnets..."
|
||||
for subnet in ${SUBNETS//,/ }; do
|
||||
ip route add "$subnet" via "$default_gateway" dev eth0
|
||||
done
|
||||
echo -e "Routes created.\n"
|
||||
fi
|
||||
|
||||
if [[ "$HTTP_PROXY" == "on" ]]; then
|
||||
if [[ -n "$PROXY_USERNAME" ]]; then
|
||||
if [[ -n "$PROXY_PASSWORD" ]]; then
|
||||
echo "Configuring HTTP proxy authentication."
|
||||
echo -e "\nBasicAuth $PROXY_USERNAME $PROXY_PASSWORD" >> /data/tinyproxy.conf
|
||||
else
|
||||
echo "WARNING: Proxy username supplied without password. Starting HTTP proxy without credentials."
|
||||
fi
|
||||
elif [[ -f "/run/secrets/$PROXY_USERNAME_SECRET" ]]; then
|
||||
if [[ -f "/run/secrets/$PROXY_PASSWORD_SECRET" ]]; then
|
||||
echo "Configuring proxy authentication."
|
||||
echo -e "\nBasicAuth $(cat /run/secrets/$PROXY_USERNAME_SECRET) $(cat /run/secrets/$PROXY_PASSWORD_SECRET)" >> /data/tinyproxy.conf
|
||||
else
|
||||
echo "WARNING: Credentials secrets not read. Starting HTTP proxy without credentials."
|
||||
fi
|
||||
fi
|
||||
/data/scripts/tinyproxy_wrapper.sh &
|
||||
fi
|
||||
|
||||
if [[ "$SOCKS_PROXY" == "on" ]]; then
|
||||
if [[ -n "$LISTEN_ON" ]]; then
|
||||
sed -i "s/internal: eth0/internal: $LISTEN_ON/" /data/sockd.conf
|
||||
fi
|
||||
if [[ -n "$PROXY_USERNAME" ]]; then
|
||||
if [[ -n "$PROXY_PASSWORD" ]]; then
|
||||
echo "Configuring SOCKS proxy authentication."
|
||||
adduser -S -D -g "$PROXY_USERNAME" -H -h /dev/null "$PROXY_USERNAME"
|
||||
echo "$PROXY_USERNAME:$PROXY_PASSWORD" | chpasswd 2> /dev/null
|
||||
sed -i 's/socksmethod: none/socksmethod: username/' /data/sockd.conf
|
||||
else
|
||||
echo "WARNING: Proxy username supplied without password. Starting SOCKS proxy without credentials."
|
||||
fi
|
||||
elif [[ -f "/run/secrets/$PROXY_USERNAME_SECRET" ]]; then
|
||||
if [[ -f "/run/secrets/$PROXY_PASSWORD_SECRET" ]]; then
|
||||
echo "Configuring proxy authentication."
|
||||
adduser -S -D -g "$(cat /run/secrets/$PROXY_USERNAME_SECRET)" -H -h /dev/null "$(cat /run/secrets/$PROXY_USERNAME_SECRET)"
|
||||
echo "$(cat /run/secrets/$PROXY_USERNAME_SECRET):$(cat /run/secrets/$PROXY_PASSWORD_SECRET)" | chpasswd 2> /dev/null
|
||||
sed -i 's/socksmethod: none/socksmethod: username/' /data/sockd.conf
|
||||
else
|
||||
echo "WARNING: Credentials secrets not present. Starting SOCKS proxy without credentials."
|
||||
fi
|
||||
fi
|
||||
/data/scripts/dante_wrapper.sh &
|
||||
fi
|
||||
|
||||
openvpn_args=(
|
||||
"--config" "$config_file_modified"
|
||||
"--auth-nocache"
|
||||
"--cd" "/data/vpn"
|
||||
"--pull-filter" "ignore" "ifconfig-ipv6"
|
||||
"--pull-filter" "ignore" "route-ipv6"
|
||||
"--script-security" "2"
|
||||
"--up-restart"
|
||||
"--verb" "$vpn_log_level"
|
||||
)
|
||||
|
||||
if [[ -n "$VPN_AUTH_SECRET" ]]; then
|
||||
if [[ -f "/run/secrets/$VPN_AUTH_SECRET" ]]; then
|
||||
echo "Configuring OpenVPN authentication."
|
||||
openvpn_args+=("--auth-user-pass" "/run/secrets/$VPN_AUTH_SECRET")
|
||||
else
|
||||
echo "WARNING: OpenVPN credentials secrets not present."
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e "Running OpenVPN client.\n"
|
||||
|
||||
openvpn "${openvpn_args[@]}" &
|
||||
openvpn_child=$!
|
||||
|
||||
wait $openvpn_child
|
|
@ -1,20 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo -e "Running Tinyproxy HTTP proxy server.\n"
|
||||
|
||||
until ip link show tun0 2>&1 | grep -qv "does not exist"; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
get_addr() {
|
||||
ip a show dev "$1" | grep inet | cut -d " " -f 6 | cut -d "/" -f 1
|
||||
}
|
||||
|
||||
addr_eth=${LISTEN_ON:-$(get_addr eth0)}
|
||||
addr_tun=$(get_addr tun0)
|
||||
sed -i \
|
||||
-e "/Listen/c Listen $addr_eth" \
|
||||
-e "/Bind/c Bind $addr_tun" \
|
||||
/data/tinyproxy.conf
|
||||
|
||||
tinyproxy -d -c /data/tinyproxy.conf
|
|
@ -1,65 +0,0 @@
|
|||
# Logging
|
||||
logoutput: /var/log/sockd.log
|
||||
errorlog: stderr
|
||||
|
||||
# Server address specification
|
||||
internal: eth0 port = 1080
|
||||
external: tun0
|
||||
|
||||
# Authentication methods
|
||||
clientmethod: none
|
||||
socksmethod: none
|
||||
|
||||
# Server identities
|
||||
user.unprivileged: sockd
|
||||
|
||||
##
|
||||
## SOCKS client access rules
|
||||
##
|
||||
# Rule processing stops at the first match; no match results in blocking
|
||||
|
||||
# Block access to socks server from 192.0.2.22
|
||||
# client block {
|
||||
# # Block connections from 192.0.2.22/32
|
||||
# from: 192.0.2.22/24 to: 0.0.0.0/0
|
||||
# log: error # connect disconnect
|
||||
# }
|
||||
|
||||
# Allow all connections
|
||||
client pass {
|
||||
from: 0.0.0.0/0 to: 0.0.0.0/0
|
||||
log: error connect disconnect
|
||||
}
|
||||
|
||||
##
|
||||
## SOCKS command rules
|
||||
##
|
||||
# Rule processing stops at the first match; no match results in blocking
|
||||
|
||||
# Block communication with www.example.org
|
||||
# socks block {
|
||||
# from: 0.0.0.0/0 to: www.example.org
|
||||
# command: bind connect udpassociate
|
||||
# log: error # connect disconnect iooperation
|
||||
# }
|
||||
|
||||
# Generic pass statement - bind/outgoing traffic
|
||||
socks pass {
|
||||
from: 0.0.0.0/0 to: 0.0.0.0/0
|
||||
command: bind connect udpassociate
|
||||
log: error connect disconnect # iooperation
|
||||
}
|
||||
|
||||
# Block incoming connections/packets from ftp.example.org
|
||||
# socks block {
|
||||
# from: ftp.example.org to: 0.0.0.0/0
|
||||
# command: bindreply udpreply
|
||||
# log: error # connect disconnect iooperation
|
||||
# }
|
||||
|
||||
# Generic pass statement for incoming connections/packets
|
||||
socks pass {
|
||||
from: 0.0.0.0/0 to: 0.0.0.0/0
|
||||
command: bindreply udpreply
|
||||
log: error connect disconnect # iooperation
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
User tinyproxy
|
||||
Group tinyproxy
|
||||
|
||||
Port 8080
|
||||
Listen
|
||||
Bind
|
||||
|
||||
Timeout 600
|
||||
|
||||
DefaultErrorFile "/usr/share/tinyproxy/default.html"
|
||||
StatFile "/usr/share/tinyproxy/stats.html"
|
||||
LogFile "/var/log/tinyproxy/tinyproxy.log"
|
||||
|
||||
LogLevel Info
|
||||
|
||||
MaxClients 100
|
||||
MinSpareServers 5
|
||||
MaxSpareServers 15
|
||||
StartServers 10
|
|
@ -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:
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
devices:
|
||||
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