Compare commits

...

12 Commits

Author SHA1 Message Date
Stefan Reimer 4cd0bc6a1f Merge pull request 'Configure Renovate' (#1) from renovate/configure into master
ZeroDownTime/zdt-openvpn/pipeline/head There was a failure building this commit Details
Reviewed-on: #1
2023-08-18 11:27:45 +00:00
Renovate Bot 96ea05775a chore(deps): add renovate.json
ZeroDownTime/zdt-openvpn/pipeline/pr-master There was a failure building this commit Details
2023-08-07 13:42:34 +00:00
Stefan Reimer 5f6ddeb5e9 Hook into CI
ZeroDownTime/zdt-openvpn/pipeline/head This commit looks good Details
2023-04-03 09:49:32 +00:00
Stefan Reimer c91d908868 Fix create_client 2023-04-03 09:45:22 +00:00
Stefan Reimer 32a341eeac Merge commit '047e160d2687f1f6465ecb55dae855a2cf0ef469' 2023-04-03 09:28:13 +00:00
Stefan Reimer 047e160d26 Squashed '.ci/' changes from 5554972..63421d1
63421d1 fix: prevent branch_name equals tag
47140c2 feat: append branch into tags if not main
4b62ada chore: improve messaging
a49cc0c chore: improve messaging
194afb4 chore: get ci working again
8ec9769 chore: get ci working again
fef4968 fix: do NOT push PRs to registry, other fixes
50a6d67 feat: ensure ARCH is only set to defined values
8fb40c7 fix: adjust trivy call to local podman
7378ea9 fix: fix trivy scan task to match new flow, add BRANCH env to Makefile
38cf7ab fix: more podman/buildah cleanups
aece7fc fix: Improve multi-arch manifest handling
80dabc2 feat: remove implicit dependencies, add help target, cleanup

git-subtree-dir: .ci
git-subtree-split: 63421d1fab0d2546de343697cbd1424914db6c31
2023-04-03 09:28:13 +00:00
Stefan Reimer 03deaac4a2 Upgrade to Alpine 3.17, adjust to latest OpenSSL 2023-04-03 08:54:03 +00:00
Stefan Reimer 59c8f09285 fix: minor tweaks, first trial of arm64 support 2022-10-11 14:59:40 +02:00
Stefan Reimer f06971948d Squashed '.ci/' changes from cb5faca..5554972
5554972 feat: multi-arch container images
da15d68 feat: handle nothing to cleanup gracefully
01df38b feat: move ecr-login into its own task
ea9c914 chore: test mermaid
19a782e chore: test mermaid
a47929d feat: switch to latest trivy cli syntax

git-subtree-dir: .ci
git-subtree-split: 55549729264a318c4c942957a36e8659553fece0
2022-10-11 14:42:07 +02:00
Stefan Reimer 7c3908d5a1 Merge commit 'f06971948d7ec59813cdc79276674a68eae8b156' 2022-10-11 14:42:07 +02:00
Stefan Reimer d0e29aea5d ci: migrate to Alpine 3.16 2022-07-07 16:32:46 +02:00
Stefan Reimer efd21719ca feat: first working release of ZDT OpenVPN 2022-07-07 14:44:39 +02:00
18 changed files with 307 additions and 821 deletions

View File

@ -1,64 +1,85 @@
# Parse version from latest git semver tag
GTAG=$(shell git describe --tags --match v*.*.* 2>/dev/null || git rev-parse --short HEAD 2>/dev/null)
TAG ?= $(shell echo $(GTAG) | awk -F '-' '{ print $$1 "-" $$2 }' | sed -e 's/-$$//')
GIT_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
GIT_TAG := $(shell git describe --tags --match v*.*.* 2>/dev/null || git rev-parse --short HEAD 2>/dev/null)
# EXTRA_TAGS supposed to be set at the caller, eg. $(shell echo $(TAG) | awk -F '.' '{ print $$1 "." $$2 }')
ifeq ($(TRIVY_REMOTE),)
TRIVY_OPTS := image
else
TRIVY_OPTS := client --remote ${TRIVY_REMOTE}
# append branch name to tag if NOT main nor master
TAG := $(GIT_TAG)
ifeq (,$(filter main master, $(GIT_BRANCH)))
ifneq ($(GIT_TAG), $(GIT_BRANCH))
TAG = $(GIT_TAG)-$(GIT_BRANCH)
endif
endif
.PHONY: build test scan push clean
# optionally set by the caller
EXTRA_TAGS :=
all: test
ARCH := amd64
ALL_ARCHS := amd64 arm64
_ARCH = $(or $(filter $(ARCH),$(ALL_ARCHS)),$(error $$ARCH [$(ARCH)] must be exactly one of "$(ALL_ARCHS)"))
build:
@docker image exists $(REGISTRY)/$(IMAGE):$(TAG) || \
docker build --rm -t $(REGISTRY)/$(IMAGE):$(TAG) --build-arg TAG=$(TAG) .
ifneq ($(TRIVY_REMOTE),)
TRIVY_OPTS := --server $(TRIVY_REMOTE)
endif
test: build rm-test-image
@test -f Dockerfile.test && \
{ docker build --rm -t $(REGISTRY)/$(IMAGE):$(TAG)-test --from=$(REGISTRY)/$(IMAGE):$(TAG) -f Dockerfile.test . && \
docker run --rm --env-host -t $(REGISTRY)/$(IMAGE):$(TAG)-test; } || \
.SILENT: ; # no need for @
.ONESHELL: ; # recipes execute in same shell
.NOTPARALLEL: ; # wait for this target to finish
.EXPORT_ALL_VARIABLES: ; # send all vars to shell
.PHONY: all # All targets are accessible for user
.DEFAULT: help # Running Make will run the help target
help: ## Show Help
grep -E '^[a-zA-Z_-]+:.*?## .*$$' .ci/podman.mk | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
build: ## Build the app
buildah build --rm --layers -t $(IMAGE):$(TAG)-$(_ARCH) --build-arg TAG=$(TAG) --build-arg ARCH=$(_ARCH) --platform linux/$(_ARCH) .
test: rm-test-image ## Execute Dockerfile.test
test -f Dockerfile.test && \
{ buildah build --rm --layers -t $(REGISTRY)/$(IMAGE):$(TAG)-test --from=$(REGISTRY)/$(IMAGE):$(TAG) -f Dockerfile.test --platform linux/$(_ARCH) . && \
podman run --rm --env-host -t $(REGISTRY)/$(IMAGE):$(TAG)-$(_ARCH)-test; } || \
echo "No Dockerfile.test found, skipping test"
scan: build
@echo "Scanning $(REGISTRY)/$(IMAGE):$(TAG) using Trivy"
@trivy $(TRIVY_OPTS) $(REGISTRY)/$(IMAGE):$(TAG)
scan: ## Scan image using trivy
echo "Scanning $(IMAGE):$(TAG)-$(_ARCH) using Trivy $(TRIVY_REMOTE)"
trivy image $(TRIVY_OPTS) localhost/$(IMAGE):$(TAG)-$(_ARCH)
push: build
@aws ecr-public get-login-password --region $(REGION) | docker login --username AWS --password-stdin $(REGISTRY)
@for t in $(TAG) latest $(EXTRA_TAGS); do echo "tag and push: $$t"; docker tag $(IMAGE):$(TAG) $(REGISTRY)/$(IMAGE):$$t && docker push $(REGISTRY)/$(IMAGE):$$t; done
# first tag and push all actual images
# create new manifest for each tag and add all available TAG-ARCH before pushing
push: ecr-login ## push images to registry
for t in $(TAG) latest $(EXTRA_TAGS); do \
echo "Tagging image with $(REGISTRY)/$(IMAGE):$${t}-$(ARCH)"
buildah tag $(IMAGE):$(TAG)-$(_ARCH) $(REGISTRY)/$(IMAGE):$${t}-$(_ARCH); \
buildah manifest rm $(IMAGE):$$t || true; \
buildah manifest create $(IMAGE):$$t; \
for a in $(ALL_ARCHS); do \
buildah manifest add $(IMAGE):$$t $(REGISTRY)/$(IMAGE):$(TAG)-$$a; \
done; \
echo "Pushing manifest $(IMAGE):$$t"
buildah manifest push --all $(IMAGE):$$t docker://$(REGISTRY)/$(IMAGE):$$t; \
done
clean: rm-test-image rm-image
ecr-login: ## log into AWS ECR public
aws ecr-public get-login-password --region $(REGION) | podman login --username AWS --password-stdin $(REGISTRY)
# Delete all untagged images
.PHONY: rm-remote-untagged
rm-remote-untagged:
@echo "Removing all untagged images from $(IMAGE) in $(REGION)"
@aws ecr-public batch-delete-image --repository-name $(IMAGE) --region $(REGION) --image-ids $$(for image in $$(aws ecr-public describe-images --repository-name $(IMAGE) --region $(REGION) --output json | jq -r '.imageDetails[] | select(.imageTags | not ).imageDigest'); do echo -n "imageDigest=$$image "; done)
clean: rm-test-image rm-image ## delete local built container and test images
rm-remote-untagged: ## delete all remote untagged images
echo "Removing all untagged images from $(IMAGE) in $(REGION)"
IMAGE_IDS=$$(for image in $$(aws ecr-public describe-images --repository-name $(IMAGE) --region $(REGION) --output json | jq -r '.imageDetails[] | select(.imageTags | not ).imageDigest'); do echo -n "imageDigest=$$image "; done) ; \
[ -n "$$IMAGE_IDS" ] && aws ecr-public batch-delete-image --repository-name $(IMAGE) --region $(REGION) --image-ids $$IMAGE_IDS || echo "No image to remove"
.PHONY: rm-image
rm-image:
@test -z "$$(docker image ls -q $(IMAGE):$(TAG))" || docker image rm -f $(IMAGE):$(TAG) > /dev/null
@test -z "$$(docker image ls -q $(IMAGE):$(TAG))" || echo "Error: Removing image failed"
test -z "$$(podman image ls -q $(IMAGE):$(TAG)-$(_ARCH))" || podman image rm -f $(IMAGE):$(TAG)-$(_ARCH) > /dev/null
test -z "$$(podman image ls -q $(IMAGE):$(TAG)-$(_ARCH))" || echo "Error: Removing image failed"
# Ensure we run the tests by removing any previous runs
.PHONY: rm-test-image
rm-test-image:
@test -z "$$(docker image ls -q $(IMAGE):$(TAG)-test)" || docker image rm -f $(IMAGE):$(TAG)-test > /dev/null
@test -z "$$(docker image ls -q $(IMAGE):$(TAG)-test)" || echo "Error: Removing test image failed"
test -z "$$(podman image ls -q $(IMAGE):$(TAG)-$(_ARCH)-test)" || podman image rm -f $(IMAGE):$(TAG)-$(_ARCH)-test > /dev/null
test -z "$$(podman image ls -q $(IMAGE):$(TAG)-$(_ARCH)-test)" || echo "Error: Removing test image failed"
# Convience task during dev of downstream projects
.PHONY: ci-pull-upstream
ci-pull-upstream:
ci-pull-upstream: ## pull latest shared .ci subtree
git stash && git subtree pull --prefix .ci ssh://git@git.zero-downtime.net/ZeroDownTime/ci-tools-lib.git master --squash && git stash pop
.PHONY: create-repo
create-repo:
create-repo: ## create new AWS ECR public repository
aws ecr-public create-repository --repository-name $(IMAGE) --region $(REGION)
.DEFAULT:
@echo "$@ not implemented. NOOP"

View File

@ -7,12 +7,14 @@ def call(Map config=[:]) {
label 'podman-aws-trivy'
}
}
stages {
stage('Prepare') {
// get tags
steps {
sh 'git fetch -q --tags ${GIT_URL} +refs/heads/${BRANCH_NAME}:refs/remotes/origin/${BRANCH_NAME}'
// pull tags
withCredentials([gitUsernamePassword(credentialsId: 'gitea-jenkins-user')]) {
sh 'git fetch -q --tags ${GIT_URL}'
}
sh 'make prepare || true'
}
}
@ -36,8 +38,7 @@ def call(Map config=[:]) {
TRIVY_OUTPUT = "reports/trivy.html"
}
steps {
sh 'mkdir -p reports'
sh 'make scan'
sh 'mkdir -p reports && make scan'
publishHTML target: [
allowMissing: true,
alwaysLinkToLastBuild: true,
@ -59,8 +60,9 @@ def call(Map config=[:]) {
}
}
// Push to ECR
// Push to container registry, skip if PR
stage('Push') {
when { not { changeRequest() } }
steps {
sh 'make push'
}

View File

@ -1,4 +1,4 @@
ARG ALPINE_VERSION=3.15
ARG ALPINE_VERSION=3.17
FROM alpine:${ALPINE_VERSION}
ARG ALPINE_VERSION
@ -12,7 +12,7 @@ RUN cd /etc/apk/keys && \
apk upgrade -U --available --no-cache && \
apk add --no-cache \
openvpn \
nftables \
iptables \
bash \
easy-rsa \
openvpn-auth-pam \
@ -25,7 +25,8 @@ RUN cd /etc/apk/keys && \
ENV OPENVPN=/etc/openvpn
ENV EASYRSA=/usr/share/easy-rsa \
EASYRSA_CRL_DAYS=3650 \
EASYRSA_PKI=$OPENVPN/pki
EASYRSA_PKI=$OPENVPN/pki \
EASYRSA_SILENT=1
VOLUME ["/etc/openvpn"]

5
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,5 @@
library identifier: 'zdt-lib@master', retriever: modernSCM(
[$class: 'GitSCMSource',
remote: 'https://git.zero-downtime.net/ZeroDownTime/ci-tools-lib.git'])
buildPodman name: 'zdt-openvpn'

View File

@ -1,47 +0,0 @@
#!/bin/bash
## @licence MIT <http://opensource.org/licenses/MIT>
## @author Copyright (C) 2015 Robin Schneider <ypid@riseup.net>
set -e
if [ -z "$OPENVPN" ]; then
export OPENVPN="$PWD"
fi
if ! source "$OPENVPN/ovpn_env.sh"; then
echo "Could not source $OPENVPN/ovpn_env.sh."
exit 1
fi
TARGET="$OPENVPN/server"
if [ -n "$1" ]; then
TARGET="$1"
fi
mkdir -p "${TARGET}"
## Ensure that no other keys then the one for the server is present.
rm -rf "$TARGET/pki/private" "$TARGET/pki/issued"
FILES=(
"openvpn.conf"
"ovpn_env.sh"
"pki/private/${OVPN_CN}.key"
"pki/issued/${OVPN_CN}.crt"
"pki/dh.pem"
"pki/ta.key"
"pki/ca.crt"
"ccd"
)
if [ -f "${OPENVPN}/pki/crl.pem" ]; then
FILES+=("pki/crl.pem")
fi
# Ensure the ccd directory exists, even if empty
mkdir -p "ccd"
# rsync isn't available to keep size down
# cp --parents isn't in busybox version
# hack the directory structure with tar
tar cf - -C "${OPENVPN}" "${FILES[@]}" | tar xvf - -C "${TARGET}"
echo "Created the openvpn configuration for the server: $TARGET"

37
bin/ovpn_create_client Executable file
View File

@ -0,0 +1,37 @@
#!/bin/bash
#
# Initialize the EasyRSA PKI
#
if [ "$DEBUG" == "1" ]; then
set -x
fi
set -e
if [ -z "$OPENVPN" ]; then
export OPENVPN="$PWD"
fi
[ -f $OPENVPN/server ] || { echo "Missing OpenVPN server setup!"; exit 1; }
cn="$1"
server="$(cat $OPENVPN/server)"
# generate client cert
if [ -f "$EASYRSA_PKI/issued/${cn}.crt" ]; then
echo "Certificate for \"${cn}\" already exists !" >&2
exit 1
fi
cat << EOF | easyrsa build-client-full "$cn" nopass
yes
EOF
# Generate OpenVPN users via google authenticator
mkdir -p /etc/openvpn/otp
# Skip confirmation if not running in interctive mode. Essential for integration tests.
google-authenticator --time-based --disallow-reuse --force --rate-limit=3 --rate-time=30 --window-size=3 \
-l "${cn}" -i "${server}" -s /etc/openvpn/otp/${cn}.google_authenticator --no-confirm -q

63
bin/ovpn_create_server Executable file
View File

@ -0,0 +1,63 @@
#!/bin/bash
#
# Initialize the EasyRSA PKI
#
if [ "$DEBUG" == "1" ]; then
set -x
else
exec 2> /dev/null
fi
set -e
# these might get lost if empty syncing to object storage
mkdir -p $OPENVPN/pki/reqs $OPENVPN/pki/issued $OPENVPN/pki/certs_by_serial $OPENVPN/otp $OPENVPN/ccd
touch $OPENVPN/otp/_empty $OPENVPN/ccd/_empty
# Finally generate server cert
cat << EOF | easyrsa build-server-full "$1" nopass
yes
EOF
# write server FQDN
echo "$1" > $OPENVPN/server
# static server config
cat <<EOF > $OPENVPN/openvpn.conf
port 1194
proto udp
dev tun
topology subnet
cipher AES-256-GCM
keepalive 10 120
user nobody
group nogroup
persist-key
persist-tun
status /var/run/openvpn-status.log
status-version 3
verb 3
verify-client-cert require
reneg-sec 0
plugin /usr/lib/openvpn/plugins/openvpn-plugin-auth-pam.so "openvpn login USERNAME password snafu pin PASSWORD"
#plugin /usr/lib/openvpn/plugins/openvpn-plugin-auth-pam.so "openvpn login USERNAME password PASSWORD pin OTP"
#auth-user-pass-optional
ifconfig-pool-persist ipp.txt
ca /etc/openvpn/ca.crt
dh /etc/openvpn/dh.pem
crl-verify /etc/openvpn/crl.pem
tls-auth /etc/openvpn/ta.key
# Include local config last
config /etc/openvpn/local.conf
EOF

View File

@ -1,412 +0,0 @@
#!/bin/bash
#
# Generate OpenVPN configs
#
TMP_PUSH_CONFIGFILE=$(mktemp -t vpn_push.XXXXXXX)
TMP_ROUTE_CONFIGFILE=$(mktemp -t vpn_route.XXXXXXX)
TMP_EXTRA_CONFIGFILE=$(mktemp -t vpn_extra.XXXXXXX)
#Traceback on Error and Exit come from https://docwhat.org/tracebacks-in-bash/
set -eu
_showed_traceback=f
traceback() {
# Hide the traceback() call.
local -i start=$(( ${1:-0} + 1 ))
local -i end=${#BASH_SOURCE[@]}
local -i i=0
local -i j=0
echo "Traceback (last called is first):" 1>&2
for ((i=${start}; i < ${end}; i++)); do
j=$(( $i - 1 ))
local function="${FUNCNAME[$i]}"
local file="${BASH_SOURCE[$i]}"
local line="${BASH_LINENO[$j]}"
echo " ${function}() in ${file}:${line}" 1>&2
done
}
on_error() {
local _ec="$?"
local _cmd="${BASH_COMMAND:-unknown}"
traceback 1
_showed_traceback=t
echo "The command ${_cmd} exited with exit code ${_ec}." 1>&2
}
trap on_error ERR
on_exit() {
echo "Cleaning up before Exit ..."
rm -f $TMP_PUSH_CONFIGFILE
rm -f $TMP_ROUTE_CONFIGFILE
rm -f $TMP_EXTRA_CONFIGFILE
local _ec="$?"
if [[ $_ec != 0 && "${_showed_traceback}" != t ]]; then
traceback 1
fi
}
trap on_exit EXIT
# Convert 1.2.3.4/24 -> 255.255.255.0
cidr2mask()
{
local i
local subnetmask=""
local cidr=${1#*/}
local full_octets=$(($cidr/8))
local partial_octet=$(($cidr%8))
for ((i=0;i<4;i+=1)); do
if [ $i -lt $full_octets ]; then
subnetmask+=255
elif [ $i -eq $full_octets ]; then
subnetmask+=$((256 - 2**(8-$partial_octet)))
else
subnetmask+=0
fi
[ $i -lt 3 ] && subnetmask+=.
done
echo $subnetmask
}
# Used often enough to justify a function
getroute() {
echo ${1%/*} $(cidr2mask $1)
}
usage() {
echo "usage: $0 [-d]"
echo " -u SERVER_PUBLIC_URL"
echo " [-e EXTRA_SERVER_CONFIG ]"
echo " [-E EXTRA_CLIENT_CONFIG ]"
echo " [-f FRAGMENT ]"
echo " [-n DNS_SERVER ...]"
echo " [-p PUSH ...]"
echo " [-r ROUTE ...]"
echo " [-s SERVER_SUBNET]"
echo
echo "optional arguments:"
echo " -2 Enable two factor authentication using Google Authenticator."
echo " -a Authenticate packets with HMAC using the given message digest algorithm (auth)."
echo " -b Disable 'push block-outside-dns'"
echo " -c Enable client-to-client option"
echo " -C A list of allowable TLS ciphers delimited by a colon (cipher)."
echo " -d Disable default route"
echo " -D Do not push dns servers"
echo " -k Set keepalive. Default: '10 60'"
echo " -m Set client MTU"
echo " -N Configure NAT to access external server network"
echo " -t Use TAP device (instead of TUN device)"
echo " -T Encrypt packets with the given cipher algorithm instead of the default one (tls-cipher)."
echo " -z Enable comp-lzo compression."
}
process_route_config() {
local ovpn_route_config=''
ovpn_route_config="$1"
# If user passed "0" skip this, assume no extra routes
[[ "$ovpn_route_config" == "0" ]] && break;
echo "Processing Route Config: '${ovpn_route_config}'"
[[ -n "$ovpn_route_config" ]] && echo "route $(getroute $ovpn_route_config)" >> "$TMP_ROUTE_CONFIGFILE"
}
process_push_config() {
local ovpn_push_config=''
ovpn_push_config="$1"
echo "Processing PUSH Config: '${ovpn_push_config}'"
[[ -n "$ovpn_push_config" ]] && echo "push \"$ovpn_push_config\"" >> "$TMP_PUSH_CONFIGFILE"
}
process_extra_config() {
local ovpn_extra_config=''
ovpn_extra_config="$1"
echo "Processing Extra Config: '${ovpn_extra_config}'"
[[ -n "$ovpn_extra_config" ]] && echo "$ovpn_extra_config" >> "$TMP_EXTRA_CONFIGFILE"
}
if [ "${DEBUG:-}" == "1" ]; then
set -x
fi
set -e
if [ -z "${OPENVPN:-}" ]; then
export OPENVPN="$PWD"
fi
if [ -z "${EASYRSA_PKI:-}" ]; then
export EASYRSA_PKI="$OPENVPN/pki"
fi
OVPN_AUTH=''
OVPN_CIPHER=''
OVPN_CLIENT_TO_CLIENT=''
OVPN_CN=''
OVPN_COMP_LZO=0
OVPN_DEFROUTE=1
OVPN_DEVICE="tun"
OVPN_DEVICEN=0
OVPN_DISABLE_PUSH_BLOCK_DNS=0
OVPN_DNS=1
OVPN_DNS_SERVERS=()
OVPN_ENV=${OPENVPN}/ovpn_env.sh
OVPN_EXTRA_CLIENT_CONFIG=()
OVPN_EXTRA_SERVER_CONFIG=()
OVPN_FRAGMENT=''
OVPN_KEEPALIVE="10 60"
OVPN_MTU=''
OVPN_NAT=0
OVPN_PORT=''
OVPN_PROTO=''
OVPN_PUSH=()
OVPN_ROUTES=()
OVPN_SERVER=192.168.255.0/24
OVPN_SERVER_URL=''
OVPN_TLS_CIPHER=''
# Import existing configuration if present
[ -r "$OVPN_ENV" ] && source "$OVPN_ENV"
# Parse arguments
while getopts ":a:e:E:C:T:r:s:du:bcp:n:k:DNm:f:tz2" opt; do
case $opt in
a)
OVPN_AUTH="$OPTARG"
;;
e)
mapfile -t TMP_EXTRA_SERVER_CONFIG <<< "$OPTARG"
for i in "${TMP_EXTRA_SERVER_CONFIG[@]}"; do
OVPN_EXTRA_SERVER_CONFIG+=("$i")
done
;;
E)
mapfile -t TMP_EXTRA_CLIENT_CONFIG <<< "$OPTARG"
for i in "${TMP_EXTRA_CLIENT_CONFIG[@]}"; do
OVPN_EXTRA_CLIENT_CONFIG+=("$i")
done
;;
C)
OVPN_CIPHER="$OPTARG"
;;
T)
OVPN_TLS_CIPHER="$OPTARG"
;;
r)
mapfile -t TMP_ROUTES <<< "$OPTARG"
for i in "${TMP_ROUTES[@]}"; do
OVPN_ROUTES+=("$i")
done
;;
s)
OVPN_SERVER="$OPTARG"
;;
d)
OVPN_DEFROUTE=0
OVPN_DISABLE_PUSH_BLOCK_DNS=1
;;
u)
OVPN_SERVER_URL="$OPTARG"
;;
b)
OVPN_DISABLE_PUSH_BLOCK_DNS=1
;;
c)
OVPN_CLIENT_TO_CLIENT=1
;;
p)
mapfile -t TMP_PUSH <<< "$OPTARG"
for i in "${TMP_PUSH[@]}"; do
OVPN_PUSH+=("$i")
done
;;
n)
mapfile -t TMP_DNS_SERVERS <<< "$OPTARG"
for i in "${TMP_DNS_SERVERS[@]}"; do
OVPN_DNS_SERVERS+=("$i")
done
;;
D)
OVPN_DNS=0
;;
N)
OVPN_NAT=1
;;
k)
OVPN_KEEPALIVE="$OPTARG"
;;
m)
OVPN_MTU="$OPTARG"
;;
t)
OVPN_DEVICE="tap"
;;
z)
OVPN_COMP_LZO=1
;;
2)
OVPN_OTP_AUTH=1
;;
f)
OVPN_FRAGMENT="$OPTARG"
;;
\?)
set +x
echo "Invalid option: -$OPTARG" >&2
usage
exit 1
;;
:)
set +x
echo "Option -$OPTARG requires an argument." >&2
usage
exit 1
;;
esac
done
# Create ccd directory for static routes
[ ! -d "${OPENVPN:-}/ccd" ] && mkdir -p ${OPENVPN:-}/ccd
# Server name is in the form "udp://vpn.example.com:1194"
if [[ "${OVPN_SERVER_URL:-}" =~ ^((udp|tcp|udp6|tcp6)://)?([0-9a-zA-Z\.\-]+)(:([0-9]+))?$ ]]; then
OVPN_PROTO=${BASH_REMATCH[2]};
OVPN_CN=${BASH_REMATCH[3]};
OVPN_PORT=${BASH_REMATCH[5]};
else
set +x
echo "Common name not specified, see '-u'"
usage
exit 1
fi
# Apply defaults. If dns servers were not defined with -n, use google nameservers
set +u
[ -z "$OVPN_DNS_SERVERS" ] && OVPN_DNS_SERVERS=("8.8.8.8" "8.8.4.4")
[ -z "$OVPN_PROTO" ] && OVPN_PROTO=udp
[ -z "$OVPN_PORT" ] && OVPN_PORT=1194
set -u
[ "${#OVPN_ROUTES[@]}" == "0" ] && [ "$OVPN_DEFROUTE" == "1" ] && OVPN_ROUTES+=("192.168.254.0/24")
# Preserve config
if [ -f "$OVPN_ENV" ]; then
bak_env=$OVPN_ENV.$(date +%s).bak
echo "Backing up $OVPN_ENV -> $bak_env"
mv "$OVPN_ENV" "$bak_env"
fi
# Save the current OVPN_ vars to the ovpn_env.sh file
(set | grep '^OVPN_') | while read -r var; do
echo "declare -x $var" >> "$OVPN_ENV"
done
conf=${OPENVPN:-}/openvpn.conf
if [ -f "$conf" ]; then
bak=$conf.$(date +%s).bak
echo "Backing up $conf -> $bak"
mv "$conf" "$bak"
fi
# Echo extra client configurations
if [ ${#OVPN_EXTRA_CLIENT_CONFIG[@]} -gt 0 ]; then
for i in "${OVPN_EXTRA_CLIENT_CONFIG[@]}"; do
echo "Processing Extra Client Config: $i"
done
fi
cat > "$conf" <<EOF
server $(getroute $OVPN_SERVER)
verb 3
key $EASYRSA_PKI/private/${OVPN_CN}.key
ca $EASYRSA_PKI/ca.crt
cert $EASYRSA_PKI/issued/${OVPN_CN}.crt
dh $EASYRSA_PKI/dh.pem
tls-auth $EASYRSA_PKI/ta.key
key-direction 0
keepalive $OVPN_KEEPALIVE
persist-key
persist-tun
proto $OVPN_PROTO
# Rely on Docker to do port mapping, internally always 1194
port 1194
dev $OVPN_DEVICE$OVPN_DEVICEN
status /tmp/openvpn-status.log
user nobody
group nogroup
EOF
if [ "${OVPN_DISABLE_PUSH_BLOCK_DNS}" == "1" ]; then
echo "Disable default push of 'block-outside-dns'"
else
process_push_config "block-outside-dns"
fi
[ -n "$OVPN_TLS_CIPHER" ] && echo "tls-cipher $OVPN_TLS_CIPHER" >> "$conf"
[ -n "$OVPN_CIPHER" ] && echo "cipher $OVPN_CIPHER" >> "$conf"
[ -n "$OVPN_AUTH" ] && echo "auth $OVPN_AUTH" >> "$conf"
[ -n "${OVPN_CLIENT_TO_CLIENT:-}" ] && echo "client-to-client" >> "$conf"
[ "$OVPN_COMP_LZO" == "1" ] && echo "comp-lzo" >> "$conf"
[ "$OVPN_COMP_LZO" == "0" ] && echo "comp-lzo no" >> "$conf"
[ -n "${OVPN_FRAGMENT:-}" ] && echo "fragment $OVPN_FRAGMENT" >> "$conf"
# Append route commands
if [ ${#OVPN_ROUTES[@]} -gt 0 ]; then
for i in "${OVPN_ROUTES[@]}"; do
process_route_config "$i"
done
echo -e "\n### Route Configurations Below" >> "$conf"
cat $TMP_ROUTE_CONFIGFILE >> "$conf"
fi
# Append push commands
[ "$OVPN_DNS" == "1" ] && for i in "${OVPN_DNS_SERVERS[@]}"; do
process_push_config "dhcp-option DNS $i"
done
if [ "$OVPN_COMP_LZO" == "0" ]; then
process_push_config "comp-lzo no"
fi
[ ${#OVPN_PUSH[@]} -gt 0 ] && for i in "${OVPN_PUSH[@]}"; do
process_push_config "$i"
done
echo -e "\n### Push Configurations Below" >> "$conf"
cat $TMP_PUSH_CONFIGFILE >> "$conf"
# Append optional OTP authentication support
if [ -n "${OVPN_OTP_AUTH:-}" ]; then
echo -e "\n\n# Enable OTP+PAM for user authentication" >> "$conf"
echo "plugin /usr/lib/openvpn/plugins/openvpn-plugin-auth-pam.so openvpn" >> "$conf"
echo "reneg-sec 0" >> "$conf"
fi
# Append extra server configurations
if [ ${#OVPN_EXTRA_SERVER_CONFIG[@]} -gt 0 ]; then
for i in "${OVPN_EXTRA_SERVER_CONFIG[@]}"; do
process_extra_config "$i"
done
echo -e "\n### Extra Configurations Below" >> "$conf"
cat $TMP_EXTRA_CONFIGFILE >> "$conf"
fi
set +e
# Clean-up duplicate configs
if diff -q "${bak_env:-}" "$OVPN_ENV" 2>/dev/null; then
echo "Removing duplicate back-up: $bak_env"
rm -fv "$bak_env"
fi
if diff -q "${bak:-}" "$conf" 2>/dev/null; then
echo "Removing duplicate back-up: $bak"
rm -fv "$bak"
fi
echo "Successfully generated config"

View File

@ -1,132 +0,0 @@
#!/bin/bash
#
# Get an OpenVPN client configuration file
#
if [ "$DEBUG" == "1" ]; then
set -x
fi
set -e
if [ -z "$OPENVPN" ]; then
export OPENVPN="$PWD"
fi
if ! source "$OPENVPN/ovpn_env.sh"; then
echo "Could not source $OPENVPN/ovpn_env.sh."
exit 1
fi
if [ -z "$EASYRSA_PKI" ]; then
export EASYRSA_PKI="$OPENVPN/pki"
fi
cn="$1"
parm="$2"
if [ ! -f "$EASYRSA_PKI/private/${cn}.key" ]; then
echo "Unable to find \"${cn}\", please try again or generate the key first" >&2
exit 1
fi
get_client_config() {
mode="$1"
echo "
client
nobind
dev $OVPN_DEVICE
remote-cert-tls server
remote $OVPN_CN $OVPN_PORT $OVPN_PROTO"
if [ "$OVPN_PROTO" == "udp6" ]; then
echo "remote $OVPN_CN $OVPN_PORT udp"
fi
if [ "$OVPN_PROTO" == "tcp6" ]; then
echo "remote $OVPN_CN $OVPN_PORT tcp"
fi
for i in "${OVPN_EXTRA_CLIENT_CONFIG[@]}"; do
echo "$i"
done
if [ "$mode" == "combined" ]; then
echo "
<key>
$(cat $EASYRSA_PKI/private/${cn}.key)
</key>
<cert>
$(openssl x509 -in $EASYRSA_PKI/issued/${cn}.crt)
</cert>
<ca>
$(cat $EASYRSA_PKI/ca.crt)
</ca>
key-direction 1
<tls-auth>
$(cat $EASYRSA_PKI/ta.key)
</tls-auth>
"
elif [ "$mode" == "separated" ]; then
echo "
key ${cn}.key
ca ca.crt
cert ${cn}.crt
tls-auth ta.key 1
"
fi
if [ "$OVPN_DEFROUTE" != "0" ];then
echo "redirect-gateway def1"
fi
if [ -n "$OVPN_MTU" ]; then
echo "tun-mtu $OVPN_MTU"
fi
if [ -n "$OVPN_TLS_CIPHER" ]; then
echo "tls-cipher $OVPN_TLS_CIPHER"
fi
if [ -n "$OVPN_CIPHER" ]; then
echo "cipher $OVPN_CIPHER"
fi
if [ -n "$OVPN_AUTH" ]; then
echo "auth $OVPN_AUTH"
fi
if [ -n "$OVPN_OTP_AUTH" ]; then
echo "auth-user-pass"
echo "auth-nocache"
fi
if [ "$OVPN_COMP_LZO" == "1" ]; then
echo "comp-lzo"
fi
if [ -n "$OVPN_OTP_AUTH" ]; then
echo reneg-sec 0
fi
}
dir="$OPENVPN/clients/$cn"
case "$parm" in
"separated")
mkdir -p "$dir"
get_client_config "$parm" > "$dir/${cn}.ovpn"
cp "$EASYRSA_PKI/private/${cn}.key" "$dir/${cn}.key"
cp "$EASYRSA_PKI/ca.crt" "$dir/ca.crt"
cp "$EASYRSA_PKI/issued/${cn}.crt" "$dir/${cn}.crt"
cp "$EASYRSA_PKI/ta.key" "$dir/ta.key"
;;
"" | "combined")
get_client_config "combined"
;;
"combined-save")
mkdir -p "$dir"
get_client_config "combined" > "$dir/${cn}-combined.ovpn"
;;
*)
echo "This script can produce the client configuration in two formats:" >&2
echo " 1. combined (default): All needed configuration and cryptographic material is in one file (Use \"combined-save\" to write the configuration file in the same path as the separated parameter does)." >&2
echo " 2. separated: Separated files." >&2
echo "Please specify one of those options as second parameter." >&2
;;
esac

View File

@ -1,25 +0,0 @@
#!/bin/bash
## @licence MIT <http://opensource.org/licenses/MIT>
## @author Copyright (C) 2015 Robin Schneider <ypid@riseup.net>
if [ -z "$OPENVPN" ]; then
export OPENVPN="$PWD"
fi
if ! source "$OPENVPN/ovpn_env.sh"; then
echo "Could not source $OPENVPN/ovpn_env.sh."
exit 1
fi
if [ -z "$EASYRSA_PKI" ]; then
export EASYRSA_PKI="$OPENVPN/pki"
fi
pushd "$EASYRSA_PKI"
for name in issued/*.crt; do
name=${name%.crt}
name=${name#issued/}
if [ "$name" != "$OVPN_CN" ]; then
ovpn_getclient "$name" separated
ovpn_getclient "$name" combined-save
fi
done
popd

93
bin/ovpn_getclient_config Executable file
View File

@ -0,0 +1,93 @@
#!/bin/bash
#
# Get an OpenVPN client configuration file
#
if [ "$DEBUG" == "1" ]; then
set -x
fi
set -e
if [ -z "$OPENVPN" ]; then
export OPENVPN="$PWD"
fi
if [ -z "$EASYRSA_PKI" ]; then
export EASYRSA_PKI="$OPENVPN/pki"
fi
[ -f $OPENVPN/server ] || { echo "Missing OpenVPN server setup!"; exit 1; }
cn="$1"
type="$2"
[ -z "$type" ] && type="ovpn"
server="$(cat $OPENVPN/server)"
set +x
if [ "$type" == "ovpn" -o "$type" == "combined" ]; then
if [ ! -f "$EASYRSA_PKI/private/${cn}.key" ]; then
echo "Unable to find certificate or key for \"${cn}\" !" >&2
exit 1
fi
echo "
client
dev tun
proto udp
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-GCM
auth-user-pass
reneg-sec 0
#static-challenge "Enter Google Authenticator Code:" 1
# comp-lzo yes
user nobody
verb 3
remote $server
<key>
$(cat $EASYRSA_PKI/private/${cn}.key)
</key>
<cert>
$(openssl x509 -in $EASYRSA_PKI/issued/${cn}.crt)
</cert>
<ca>
$(cat $EASYRSA_PKI/ca.crt)
</ca>
<tls-auth>
$(cat $EASYRSA_PKI/ta.key)
</tls-auth>
"
fi
# TOTP incl. QRCODE
if [ "$type" == "totp" -o "$type" == "combined" ]; then
if [ ! -f "${OPENVPN}/otp/${cn}.google_authenticator" ]; then
echo "Unable to find TOTP data for \"${cn}\" !" >&2
exit 1
fi
# secret is always first line
secret="$(head -1 ${OPENVPN}/otp/${cn}.google_authenticator)"
# remaining one time codes are at the bottom, max 5
onetime="$(tail -n +2 ${OPENVPN}/otp/${cn}.google_authenticator | grep -v \")"
# Output as UTF8 and remove all ANSI control to allow piping eg. into yopass
echo "otpauth://totp/${cn}?secret=${secret}&issuer=${server}" | qrencode -o - -t ANSIUTF8 | sed "s,\x1B\[[0-9;]*[a-zA-Z],,g"
echo "Your new secret key is: $secret"
echo "Your emergency scratch codes are:"
echo "$onetime"
fi

View File

@ -6,38 +6,21 @@
if [ "$DEBUG" == "1" ]; then
set -x
else
exec 2> /dev/null
fi
set -e
source "$OPENVPN/ovpn_env.sh"
# Specify "nopass" as arg[2] to make the CA insecure (not recommended!)
nopass=$1
# Provides a sufficient warning before erasing pre-existing files
easyrsa init-pki
# CA always has a password for protection in event server is compromised. The
# password is only needed to sign client/server certificates. No password is
# needed for normal OpenVPN operation.
easyrsa build-ca $nopass
# we rely on external KMS
echo "BastionOpenVPNRootCA" | easyrsa build-ca nopass
easyrsa gen-dh
openvpn --genkey secret $EASYRSA_PKI/ta.key
# Was nice to autoset, but probably a bad idea in practice, users should
# have to explicitly specify the common name of their server
#if [ -z "$cn"]; then
# #TODO: Handle IPv6 (when I get a VPS with IPv6)...
# ip4=$(dig +short myip.opendns.com @resolver1.opendns.com)
# ptr=$(dig +short -x $ip4 | sed -e 's:\.$::')
#
# [ -n "$ptr" ] && cn=$ptr || cn=$ip4
#fi
# For a server key with a password, manually init; this is autopilot
easyrsa build-server-full "$OVPN_CN" nopass
# Generate the CRL for client/server certificates revocation.
easyrsa gen-crl
echo "Successfully bootstrapped PKI"

View File

@ -3,10 +3,6 @@
if [ -z "$OPENVPN" ]; then
export OPENVPN="$PWD"
fi
if ! source "$OPENVPN/ovpn_env.sh"; then
echo "Could not source $OPENVPN/ovpn_env.sh."
exit 1
fi
if [ -z "$EASYRSA_PKI" ]; then
export EASYRSA_PKI="$OPENVPN/pki"
fi
@ -27,7 +23,7 @@ for name in issued/*.crt; do
name=${name%.crt}
name=${name#issued/}
if [ "$name" != "$OVPN_CN" ]; then
#if [ "$name" != "$OVPN_CN" ]; then
# check for revocation or expiration
command="openssl verify -crl_check -CAfile cacheck.pem $path"
result=$($command)
@ -47,7 +43,7 @@ for name in issued/*.crt; do
esac
fi
echo "$name,$begin,$end,$status"
fi
#fi
done
# Clean

View File

@ -1,34 +0,0 @@
#!/bin/bash
#
# Generate OpenVPN users via google authenticator
#
if ! source "$OPENVPN/ovpn_env.sh"; then
echo "Could not source $OPENVPN/ovpn_env.sh."
exit 1
fi
if [ "x$OVPN_OTP_AUTH" != "x1" ]; then
echo "OTP authentication not enabled, please regenerate configuration using -2 flag"
exit 1
fi
if [ -z $1 ]; then
echo "Usage: ovpn_otp_user USERNAME"
exit 1
fi
# Ensure the otp folder is present
[ -d /etc/openvpn/otp ] || mkdir -p /etc/openvpn/otp
# Binary is present in image, save an $user.google_authenticator file in /etc/openvpn/otp
if [ "$2" == "interactive" ]; then
# Authenticator will ask for other parameters. User can choose rate limit, token reuse policy and time window policy
# Always use time base OTP otherwise storage for counters must be configured somewhere in volume
google-authenticator --time-based --force -l "${1}@${OVPN_CN}" -s /etc/openvpn/otp/${1}.google_authenticator
else
# Skip confirmation if not running in interctive mode. Essential for integration tests.
google-authenticator --time-based --disallow-reuse --force --rate-limit=3 --rate-time=30 --window-size=3 \
-l "${1}@${OVPN_CN}" -s /etc/openvpn/otp/${1}.google_authenticator --no-confirm
fi

View File

@ -13,10 +13,6 @@ set -e
if [ -z "$OPENVPN" ]; then
export OPENVPN="$PWD"
fi
if ! source "$OPENVPN/ovpn_env.sh"; then
echo "Could not source $OPENVPN/ovpn_env.sh."
exit 1
fi
if [ -z "$EASYRSA_PKI" ]; then
export EASYRSA_PKI="$OPENVPN/pki"
fi
@ -24,16 +20,16 @@ fi
cn="$1"
if [ ! -f "$EASYRSA_PKI/private/${cn}.key" ]; then
echo "Unable to find \"${cn}\", please try again or generate the key first" >&2
echo "Unable to find \"${cn}\"" >&2
exit 1
fi
revoke_client_certificate(){
easyrsa revoke "$1"
echo "Generating the Certificate Revocation List :"
easyrsa gen-crl
cp -f "$EASYRSA_PKI/crl.pem" "$OPENVPN/crl.pem"
chmod 644 "$OPENVPN/crl.pem"
}
export EASYRSA_BATCH="True"
revoke_client_certificate "$cn"
easyrsa revoke "$cn"
echo "Revoked $cn"
echo "Updating crl.pem"
easyrsa gen-crl
rm -f $OPENVPN/pki/issued/$cn.crt $OPENVPN/pki/private/$cn.key $OPENVPN/pki/reqs/$cn.req $OPENVPN/otp/$cn.google_authenticator

View File

@ -12,31 +12,6 @@ set -e
cd $OPENVPN
# bootstrap things?
if [[ "$BOOTSTRAP_CA" == "TRUE" ]]; then
echo "bootstrapping ca"
#check if it already exist to not overwrite things
if [ -f "/etc/openvpn/ovpn_env.sh" ]; then
echo "config already initialized - skipping"
else
ovpn_genconfig -u $PROTOCOL://$VPN_HOSTNAME
source "$OPENVPN/ovpn_env.sh"
easyrsa init-pki
easyrsa --batch --req-cn=$CA_SERVERNAME build-ca nopass
easyrsa gen-dh
openvpn --genkey secret $EASYRSA_PKI/ta.key
# For a server key with a password, manually init; this is autopilot
easyrsa build-server-full "$OVPN_CN" nopass
# Generate the CRL for client/server certificates revocation.
easyrsa gen-crl
fi
fi
# Build runtime arguments array based on environment
USER_ARGS=("${@}")
ARGS=()
@ -64,10 +39,9 @@ function addArg {
# set up iptables rules and routing
# this allows rules/routing to be altered by supplying this function
# in an included file, such as ovpn_env.sh
function setupIptablesAndRouting {
$IPTABLES -t nat -C POSTROUTING -s $OVPN_SERVER -o $OVPN_NATDEVICE -j MASQUERADE 2>/dev/null || {
$IPTABLES -t nat -A POSTROUTING -s $OVPN_SERVER -o $OVPN_NATDEVICE -j MASQUERADE
$IPTABLES -t nat -C POSTROUTING -s $VPN_CIDR_RANGE -o $OVPN_NATDEVICE -j MASQUERADE 2>/dev/null || {
$IPTABLES -t nat -A POSTROUTING -s $VPN_CIDR_RANGE -o $OVPN_NATDEVICE -j MASQUERADE
}
for i in "${OVPN_ROUTES[@]}"; do
$IPTABLES -t nat -C POSTROUTING -s "$i" -o $OVPN_NATDEVICE -j MASQUERADE 2>/dev/null || {
@ -79,7 +53,10 @@ function setupIptablesAndRouting {
addArg "--config" "$OPENVPN/openvpn.conf"
source "$OPENVPN/ovpn_env.sh"
VPN_CIDR_RANGE="172.16.10.0/24"
# When using --net=host, use this to specify nat device.
OVPN_NATDEVICE=eth0
mkdir -p /dev/net
if [ ! -c /dev/net/tun ]; then
@ -90,43 +67,8 @@ if [ -d "$OPENVPN/ccd" ]; then
addArg "--client-config-dir" "$OPENVPN/ccd"
fi
# When using --net=host, use this to specify nat device.
[ -z "$OVPN_NATDEVICE" ] && OVPN_NATDEVICE=eth0
# Setup NAT forwarding if requested
if [ "$OVPN_DEFROUTE" != "0" ] || [ "$OVPN_NAT" == "1" ] ; then
# call function to setup iptables rules and routing
# this allows rules to be customized by supplying
# a replacement function in, for example, ovpn_env.sh
setupIptablesAndRouting
fi
# Use a copy of crl.pem as the CRL Needs to be readable by the user/group
# OpenVPN is running as. Only pass arguments to OpenVPN if it's found.
if [ "$EASYRSA_PKI/crl.pem" -nt "$OPENVPN/crl.pem" ]; then
cp -f "$EASYRSA_PKI/crl.pem" "$OPENVPN/crl.pem"
chmod 644 "$OPENVPN/crl.pem"
fi
if [ -r "$OPENVPN/crl.pem" ]; then
addArg "--crl-verify" "$OPENVPN/crl.pem"
fi
ip -6 route show default 2>/dev/null
if [ $? = 0 ]; then
echo "Checking IPv6 Forwarding"
if [ "$(</proc/sys/net/ipv6/conf/all/disable_ipv6)" != "0" ]; then
echo "Sysctl error for disable_ipv6, please run docker with '--sysctl net.ipv6.conf.all.disable_ipv6=0'"
fi
if [ "$(</proc/sys/net/ipv6/conf/default/forwarding)" != "1" ]; then
echo "Sysctl error for default forwarding, please run docker with '--sysctl net.ipv6.conf.default.forwarding=1'"
fi
if [ "$(</proc/sys/net/ipv6/conf/all/forwarding)" != "1" ]; then
echo "Sysctl error for all forwarding, please run docker with '--sysctl net.ipv6.conf.all.forwarding=1'"
fi
fi
setupIptablesAndRouting
echo "Starting openvpn_exporter"
openvpn_exporter --openvpn.status_paths /var/run/openvpn-status.log &

View File

@ -1,12 +0,0 @@
#!/bin/sh
#
# Get OpenVPN server status
#
if [ "$DEBUG" == "1" ]; then
set -x
fi
set -e
[-r /tmp/openvpn-status.log ] && tail -F /tmp/openvpn-status.log

9
renovate.json Normal file
View File

@ -0,0 +1,9 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":label(renovate)",
":semanticCommits"
],
"prHourlyLimit": 0
}