Skip to content
This repository has been archived by the owner on Mar 16, 2024. It is now read-only.

Commit

Permalink
Overhaul
Browse files Browse the repository at this point in the history
  • Loading branch information
wfg committed Jun 15, 2022
1 parent ea0b4dd commit ee14898
Show file tree
Hide file tree
Showing 18 changed files with 372 additions and 425 deletions.
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
*
!data/
!data/
85 changes: 40 additions & 45 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Publish
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+'

env:
IMAGE_NAME: openvpn-client
Expand All @@ -13,47 +13,42 @@ jobs:
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
- 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
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- id: tags
uses: docker/metadata-action@v4
with:
images: ${{ env.IMAGE_NAME }}
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:
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
local/
.local/
16 changes: 8 additions & 8 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
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
- 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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## 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.
Expand Down
31 changes: 15 additions & 16 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
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
FROM alpine:3.16

RUN apk add --no-cache \
bash \
bind-tools \
dante-server \
nftables \
openvpn \
tinyproxy

RUN mkdir -p /data/vpn
COPY data/ /data/

COPY data/ /data
ENV KILL_SWITCH=on
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

ENTRYPOINT ["/data/scripts/entry.sh"]
WORKDIR /data

ENTRYPOINT [ "scripts/entry.sh" ]
73 changes: 39 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# OpenVPN Client for Docker
## 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 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.

Expand All @@ -14,10 +14,6 @@ If you find something that doesn't work or have an idea for a new feature, issue
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.
Expand Down Expand Up @@ -62,22 +58,28 @@ services:
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. |
| `KEEP_DNS_UNCHANGED` | `off` | If `off`, the VPN server you connect to might override the DNS server used by the container. If `on`, the container will always use the DNS settings it had before connecting to the server. Usually, containers use the Docker internal DNS server by default. It allows to resolve IP addreses from container names, service names and Docker-specific names such as `host.docker.internal`. If DNS server is overriden, you won't be able to resolve such names. |
| `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. |
| `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_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`) |
| `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` | `on` | Whether or not to enable the network kill switch. |
| `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`.

##### Environment variable considerations
###### `HTTP_PROXY` and `SOCKS_PROXY`
Expand All @@ -90,25 +92,10 @@ ports:
- <host_port>:1080
```

##### `PROXY_USERNAME_SECRET`, `PROXY_PASSWORD_SECRET`, and `VPN_AUTH_SECRET`
##### `*_PROXY_USERNAME_SECRET`, `*_PROXY_PASSWORD_SECRET`, and `VPN_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.

### VPN Authentication
To provide the VPN user and password credentials, create a file called `passfile` in the config folder being mounted, right next to the vpn .conf (or .ovpn) file.

In the passfile, enter the username in the first line and password in the second line. For example:
```
gilbert
p@sswd123
```
Now in the vpn configuration file, such as my_vpn.ovpn, create a new line and enter the following:
```
auth-user-pass passfile
```
That should be enough. You can refer to this link for more details: https://help.yeastar.com/en/s-series/topic/openvpn-username-password-authentication.html#openvpn-create-account-password-for-each-client__section_uyg_sps_33b


### Using with other containers
Once you have your `openvpn-client` container up and running, you can tell other containers to use `openvpn-client`'s network stack which gives them the ability to utilize the VPN tunnel.
There are a few ways to accomplish this depending how how your container is created.
Expand Down Expand Up @@ -137,3 +124,21 @@ 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 change: 1 addition & 0 deletions build-variables
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
IMAGE_NAME=ghcr.io/wfg/openvpn-client
20 changes: 20 additions & 0 deletions build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/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)
11 changes: 3 additions & 8 deletions data/tinyproxy.conf → data/config/http-proxy.conf
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,11 @@ 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"
LogFile "/var/log/tinyproxy/tinyproxy.log"

LogLevel Info

MaxClients 100
MinSpareServers 5
MaxSpareServers 15
StartServers 10
DisableViaHeader yes
26 changes: 26 additions & 0 deletions data/config/socks-proxy.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
logoutput: /var/log/sockd.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
}
9 changes: 0 additions & 9 deletions data/scripts/dante_wrapper.sh

This file was deleted.

Loading

0 comments on commit ee14898

Please sign in to comment.