-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
nikolaenya
authored and
nikolaenya
committed
Nov 22, 2023
1 parent
f52c529
commit 7c6126c
Showing
4 changed files
with
415 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
FROM alpine as builder | ||
|
||
ARG OPENFORTIVPN_VERSION=v1.21.0 | ||
ARG GLIDER_VERSION=v0.16.3 | ||
|
||
# Build openfortivpn binary | ||
RUN apk add --no-cache \ | ||
openssl-dev \ | ||
ppp \ | ||
ca-certificates \ | ||
curl \ | ||
&& apk add --no-cache --virtual .build-deps \ | ||
automake \ | ||
autoconf \ | ||
g++ \ | ||
gcc \ | ||
make \ | ||
go \ | ||
build-base \ | ||
&& mkdir -p "/usr/src/openfortivpn" \ | ||
&& cd "/usr/src/openfortivpn" \ | ||
&& curl -Ls "https://github.com/adrienverge/openfortivpn/archive/${OPENFORTIVPN_VERSION}.tar.gz" \ | ||
| tar xz --strip-components 1 \ | ||
&& aclocal \ | ||
&& autoconf \ | ||
&& automake --add-missing \ | ||
&& ./configure --prefix=/usr --sysconfdir=/etc \ | ||
&& make \ | ||
&& make install | ||
|
||
# Build glider proxy binary | ||
RUN mkdir -p /go/src/github.com/nadoo/glider && \ | ||
curl -sL https://github.com/nadoo/glider/archive/${GLIDER_VERSION}.tar.gz \ | ||
| tar xz -C /go/src/github.com/nadoo/glider --strip-components=1 && \ | ||
cd /go/src/github.com/nadoo/glider && \ | ||
awk '/^\s+_/{if (!/http/ && !/socks5/ && !/mixed/) $0="//"$0} {print}' feature.go > feature.go.tmp && \ | ||
mv feature.go.tmp feature.go && \ | ||
go build -v -ldflags "-s -w" | ||
|
||
# Clean build deps | ||
RUN apk del .build-deps | ||
|
||
# Build final image | ||
FROM alpine | ||
|
||
RUN apk add --no-cache \ | ||
ca-certificates \ | ||
openssl \ | ||
ppp \ | ||
curl \ | ||
su-exec \ | ||
socat \ | ||
dpkg \ | ||
bash\ | ||
wget \ | ||
iptables \ | ||
net-tools \ | ||
iproute2 \ | ||
inotify-tools | ||
|
||
COPY --from=builder /usr/bin/openfortivpn /usr/bin/openfortivpn | ||
COPY --from=builder /go/src/github.com/nadoo/glider/glider /usr/bin/glider | ||
COPY ./docker-entrypoint.sh /usr/bin/ | ||
COPY ./inotifywait.sh /usr/bin/ | ||
COPY ./docker-healcheck.sh /usr/bin/ | ||
|
||
RUN chmod +x \ | ||
/usr/bin/docker-entrypoint.sh \ | ||
/usr/bin/docker-healcheck.sh \ | ||
/usr/bin/inotifywait.sh | ||
|
||
RUN mkdir /tmp/2fa/ | ||
RUN chmod -R 777 /tmp/2fa/ | ||
|
||
ENTRYPOINT ["docker-entrypoint.sh"] | ||
|
||
ENV VPN_ADDR="" | ||
ENV VPN_USER="" | ||
ENV VPN_PASS="" | ||
ENV VPN_2FA_DIR="/tmp/2fa/" | ||
ENV VPN_2FA_FILE="/tmp/2fa/2fa.txt" | ||
ENV ENABLE_IPTABLES_LEGACY="" | ||
ENV ENABLE_PORT_FORWARDING="" | ||
ENV SOCKS_PROXY_PORT="8443" | ||
|
||
EXPOSE 8443/tcp | ||
|
||
HEALTHCHECK --interval=30s --timeout=5s \ | ||
CMD bash /usr/bin/docker-healcheck.sh || pkill -SIGILL -f 1 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
#!/bin/bash | ||
|
||
# exit on any script failure | ||
set -e -o pipefail | ||
|
||
trap "echo The script is terminated by SIGINT; exit" SIGINT | ||
trap "echo The script is terminated by SIGTERM; exit" SIGTERM | ||
trap "echo The script is terminated by SIGKILL; exit" SIGKILL | ||
|
||
if [ "$ENTRYDEBUG" == "TRUE" ]; then | ||
# print shell input lines as they are read | ||
set -v | ||
fi | ||
|
||
# ensure the ppp device exists | ||
[ -c /dev/ppp ] || su-exec root mknod /dev/ppp c 108 0 | ||
|
||
# make folder for logs (available outside container) | ||
export LOGS_FOLDER=${VPN_2FA_DIR}/logs | ||
rm -rf ${LOGS_FOLDER} | ||
mkdir -p ${LOGS_FOLDER} | ||
|
||
|
||
file_env() { | ||
local var="$1" | ||
local fileVar="${var}_FILE" | ||
local def="${2:-}" | ||
if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then | ||
echo >&2 "error: both $var and $fileVar are set (but are exclusive)" | ||
exit 1 | ||
fi | ||
local val="$def" | ||
if [ "${!var:-}" ]; then | ||
val="${!var}" | ||
elif [ "${!fileVar:-}" ]; then | ||
val="$(<"${!fileVar}")" | ||
fi | ||
export "$var"="$val" | ||
unset "$fileVar" | ||
} | ||
|
||
check_required_variabled() { | ||
if [ -z "$VPN_ADDR" -o -z "$VPN_USER" -o -z "$VPN_PASS" ]; then | ||
echo "`date` [INIT] Variables VPN_ADDR, VPN_USER and VPN_PASS must be set." | ||
exit 1 | ||
fi | ||
|
||
if [ ! -d "$VPN_2FA_DIR" ]; then | ||
echo "`date` [INIT] The 2FA directory not exist. Please fill variable VPN_2FA_DIR with valid directory." | ||
exit 1 | ||
fi | ||
} | ||
|
||
docker_env_setup() { | ||
file_env 'VPN_ADDR' | ||
file_env 'VPN_USER' | ||
file_env 'VPN_PASS' | ||
file_env 'VPN_2FA_DIR' '/tmp/2fa/' | ||
file_env 'VPN_2FA_FILE' '/tmp/2fa/2fa.txt' | ||
file_env 'ENABLE_IPTABLES_LEGACY' | ||
file_env 'ENABLE_PORT_FORWARDING' | ||
file_env 'SOCKS_PROXY_PORT' 8443 | ||
} | ||
|
||
port_forwarding_setup() { | ||
# generate regex search string | ||
r="^" # required start of variable name | ||
r="${r}\(PORT_FORWARD\|REMOTE_ADDR\)[^=]*=" # Required variable name | ||
r="${r}\(\(tcp\|udp\):\)\?" # optional tcp or udp | ||
r="${r}\(\(\d\{1,5\}\):\)\?" # optional LOCAL_PORT | ||
r="${r}[a-zA-Z0-9.-]\+" # required REMOTE_HOST (ip or hostname) | ||
r="${r}:\d\{1,5\}" # required REMOTE_PORT | ||
r="${r}$" # required end of variable contents | ||
|
||
# create a space separated list of forwarded ports. Pause immediate script | ||
# termination on non-zero exits to permit use without port forwarding. | ||
set +e | ||
forwards=$( | ||
env \ | ||
| grep "${r}" \ | ||
| cut -d= -f2- | ||
) | ||
set -e | ||
|
||
# remove our old socat entries from ip-up | ||
sed '/^socat/d' -i /etc/ppp/ip-up | ||
|
||
# iterate over all REMOTE_ADDR.* environment variables and create ppp ip-up | ||
# scripts | ||
for forward in ${forwards}; do | ||
|
||
# replace colons with spaces add them into a bash array | ||
colons=$(echo "${forward}" | grep -o ':' | wc -l) | ||
|
||
if [ "${colons}" -eq "3" ]; then | ||
PROTOCOL=$(echo "${forward}" | cut -d: -f1) | ||
LOCAL_PORT=$(echo "${forward}" | cut -d: -f2) | ||
REMOTE_HOST=$(echo "${forward}" | cut -d: -f3) | ||
REMOTE_PORT=$(echo "${forward}" | cut -d: -f4) | ||
|
||
elif [ "${colons}" -eq "2" ]; then | ||
PROTOCOL="tcp" | ||
LOCAL_PORT=$(echo "${forward}" | cut -d: -f1) | ||
REMOTE_HOST=$(echo "${forward}" | cut -d: -f2) | ||
REMOTE_PORT=$(echo "${forward}" | cut -d: -f3) | ||
|
||
elif [ "${colons}" -eq "1" ]; then | ||
PROTOCOL="tcp" | ||
LOCAL_PORT="1111" | ||
REMOTE_HOST=$(echo "${forward}" | cut -d: -f1) | ||
REMOTE_PORT=$(echo "${forward}" | cut -d: -f2) | ||
|
||
else | ||
echo '`date` [INIT] ERROR: unrecognized PORT_FORWARD(*) value: "%s"\n' "${address}" >&2 | ||
exit 1 | ||
fi | ||
|
||
# use ppp's ip-up script to start the socat tunnels. In testing, this works | ||
# well with one exception being hostname resolution doesnt happen within the | ||
# VPN. | ||
# for future attemps at solving this issue: dig/drill resolve properly after | ||
# VPN is established whereas `getent hosts` and whatver ping/ssh use do not. | ||
# it seems potentially related to musl and would be worth testing if this | ||
# docker image should base of debian instead of alpine. | ||
echo "socat ${PROTOCOL}-l:${LOCAL_PORT},fork,reuseaddr ${PROTOCOL}:${REMOTE_HOST}:${REMOTE_PORT} &" \ | ||
>> "/etc/ppp/ip-up" | ||
echo "`date` [INIT] INFO: -> socat {LOCAL_PORT}->${REMOTE_HOST}:${REMOTE_PORT}" | ||
done | ||
} | ||
|
||
start_proxy() { | ||
PROXY_LOG=${LOGS_FOLDER}/proxy.log | ||
rm -f $PROXY_LOG | ||
echo "`date` [INIT] Starting glider proxy on port ${SOCKS_PROXY_PORT}." | ||
/usr/bin/glider -verbose -listen :${SOCKS_PROXY_PORT} &>$PROXY_LOG & disown | ||
echo "`date` [INIT] -> proxy log: ${PROXY_LOG}" | ||
|
||
if [ $? -eq 0 ]; then | ||
echo "`date` [INIT] OK proxy started." | ||
else | ||
echo "`date` [INIT] ERROR while starting proxy! Exiting now." | ||
exit 1 | ||
fi | ||
} | ||
|
||
run_2fa_listener() { | ||
rm -f "$VPN_2FA_FILE" | ||
touch "$VPN_2FA_FILE" | ||
chmod 777 "$VPN_2FA_FILE" | ||
|
||
echo "`date` [INIT] Checking iptables..." | ||
if [ -n "$ENABLE_IPTABLES_LEGACY" ]; then | ||
echo "`date` [INIT] Using 'iptables-legacy' for masquerading." | ||
LEGACY_CMD=$(iptables --version | grep legacy) | ||
if [ ! -z "$VPN_2FA_DIR" ]; then | ||
echo "`date` [INIT] Legacy iptables already enabled." | ||
else | ||
echo "`date` [INIT] Running update-alternatives" | ||
update-alternatives --set iptables /usr/sbin/iptables-legacy | ||
if [ $? -eq 0 ]; then | ||
echo "`date` [INIT] OK update-alternatives" | ||
else | ||
echo "`date` [INIT] ERROR update-alternatives" | ||
fi | ||
fi | ||
else | ||
echo "`date` [INIT] NOT enabling 'iptables-legacy' for masquerading." | ||
fi | ||
|
||
echo "`date` [INIT] Setting up masquerading with iptables..." | ||
# setup masquerade, to allow the container to act as a gateway | ||
for iface in $(ip a | grep eth | grep inet | awk '{print $2}'); do | ||
iptables -t nat -A POSTROUTING -s "$iface" -j MASQUERADE | ||
if [ $? -eq 0 ]; then | ||
echo "`date` [INIT] -> OK ipdatbles" | ||
else | ||
echo "`date` [INIT] -> ERROR iptables" | ||
fi | ||
done | ||
echo "`date` [INIT] Iptables setup DONE" | ||
echo "`date` [INIT] Killing any running listeners." | ||
pkill -15 -f -e "inotifywait" || echo "`date` [INIT] No process to kill." | ||
sleep 2 | ||
echo "`date` [INIT] Looping listener execution." | ||
while [ true ]; do | ||
echo "`date` [INIT] 2FA Token Listener Start. Waiting for new token." | ||
/usr/bin/inotifywait.sh | ||
echo "`date` [INIT] 2FA Token Listener Terminated!" | ||
sleep 10 | ||
done | ||
} | ||
|
||
_main() { | ||
check_required_variabled | ||
docker_env_setup | ||
start_proxy | ||
if [ -n "$ENABLE_PORT_FORWARDING" ]; then | ||
echo "`date` [INIT] Setting up port forwarding..." | ||
port_forwarding_setup | ||
else | ||
echo "`date` [INIT] Port forwarding is NOT enabled." | ||
fi | ||
# run listener that will monitoring for 2FA file changes | ||
run_2fa_listener | ||
} | ||
|
||
_main "$@" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#!/usr/bin/env bash | ||
set -Eeo pipefail | ||
|
||
file_env() { | ||
local var="$1" | ||
local fileVar="${var}_FILE" | ||
local def="${2:-}" | ||
if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then | ||
echo >&2 "error: both $var and $fileVar are set (but are exclusive)" | ||
exit 1 | ||
fi | ||
local val="$def" | ||
if [ "${!var:-}" ]; then | ||
val="${!var}" | ||
elif [ "${!fileVar:-}" ]; then | ||
val="$(<"${!fileVar}")" | ||
fi | ||
export "$var"="$val" | ||
unset "$fileVar" | ||
} | ||
|
||
docker_setup_env() { | ||
file_env 'VPN_INTERFACE_NAME' 'ppp0' | ||
} | ||
|
||
check_vpn_interface_exist() { | ||
declare -r RES=$(ip -o a show | cut -d ' ' -f 2 | grep "$VPN_INTERFACE_NAME") | ||
if [ -z "$RES" ]; then | ||
echo "[ERR] The interface \"$VPN_INTERFACE_NAME\" not found!" | ||
exit 1 | ||
fi | ||
} | ||
|
||
_main() { | ||
docker_setup_env | ||
|
||
check_vpn_interface_exist | ||
} | ||
|
||
_main "$@" |
Oops, something went wrong.