#!/bin/bash # I, Ian Kelling, follow the GNU license recommendations at # https://www.gnu.org/licenses/license-recommendations.en.html. They # recommend that small programs, < 300 lines, be licensed under the # Apache License 2.0. This file contains or is part of one or more small # programs. If a small program grows beyond 300 lines, I plan to switch # its license to GPL. # Copyright 2024 Ian Kelling # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -eE -o pipefail trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR [[ $EUID == 0 ]] || exec sudo -E "$BASH_SOURCE" "$@" usage() { cat <<'EOF' usage: ${0##*/} [OPTIONS] [IPV6_ADDR/BITS] -4 Prefix of range for ipv4, default 10.8.0 -6 IP6_NETWORK Do ip6 nat for this network. ipv6 will work without nat, but you may want it in certain circumstances. -d Do not push dns -i INTERFACE_NAME name of tun interface -n NAME default = server. 2 servers on the same host need different names. -p PORT default 1194 -r Do not push default route -s Do not start openvpn -h --help print help IPV6_ADDR/BITS Ipv6 address of the vpn interface. Sets up a vpn server which pushes gateway route and dns server so all traffic goes through the vpn. requires systemd, and might have some debian specific paths. For ipv6, we assume ipv6_addr routes to the server. You can save all the keys by storing /etc/openvpn/easy-rsa-NAME/keys, and the script will not generate them if it sees they exist already. For future updates to this script, this is a good place to take inspiration. https://github.com/angristan/openvpn-install/blob/master/openvpn-install.sh Note: Uses GNU getopt options parsing style EOF exit $1 } dns=true route=true start=true ip4=10.8.0 name=server temp=$(getopt -l help 4:6:di:n:p:rsh "$@") || usage 1 eval set -- "$temp" while true; do case $1 in -4) ip4=$2; shift 2 ;; -6) ip6net=$2; shift 2 ;; -d) dns=false; shift ;; -i) ifname=$2; shift 2 ;; -n) name=$2; shift 2 ;; -p) port=$2; shift 2 ;; -r) route=false; shift ;; -s) start=false; shift ;; -h|--help) usage ;; --) shift; break ;; *) echo "$0: Internal error! unexpected args: $*" ; exit 1 ;; esac done read -r ip6 ip6route <<<"$@" source /a/bin/distro-functions/src/package-manager-abstractions pi-nostart openvpn openssl easy-rsa uuid-runtime if [[ -e /lib/systemd/system/openvpn-server@.service ]]; then vpn_service=openvpn-server@$name else vpn_service=openvpn@$name fi rsadir=/etc/openvpn/easy-rsa-$name mkdir -p $rsadir cd $rsadir cp -r /usr/share/easy-rsa/* . if [[ -e openssl-1.0.0.cnf && ! -e openssl.cnf ]]; then # there's a debian bug about this. ln -s openssl-1.0.0.cnf openssl.cnf fi server_dir=/etc/openvpn/server mkdir -p $server_dir chmod 700 $server_dir conf=$server_dir/$name.conf new=true ca_origin=$rsadir/pki/ca.crt keyfiles=( $rsadir/pki/private/$name.key $rsadir/pki/issued/$name.crt ) if [[ -e /etc/openvpn/easy-rsa/build-ca ]]; then new=false ca_origin=$rsadir/ca.crt keyfiles=( $rsadir/keys/$name.key $rsadir/keys/$name.crt ) fi keys_exist=true for f in ${keyfiles[@]}; do if [[ ! -e $f ]]; then keys_exist=false break fi done f=$server_dir/dh2048.pem if [[ ! -e $f ]]; then openssl dhparam -out $f 2048 fi f=$server_dir/ta-$name.key if [[ ! -e $f ]]; then openvpn --genkey --secret $server_dir/ta-$name.key fi if ! $keys_exist; then # newer sample configs (post stretch) use ta.key. no harm making it for earlier oses if $new; then echo 'set_var EASYRSA_NS_SUPPORT "yes"' >vars ./easyrsa init-pki ./easyrsa --batch build-ca nopass ./easyrsa --days=3650 build-server-full $name nopass else # dun care about settning cert cn etc from the non-example values source vars # doesnt exist in buster ./clean-all # note: removes and creates /etc/openvpn/easy-rsa/keys # accept default prompts echo -e '\n\n\n\n\n\n\n\n' | ./build-ca # This builds the server's key/cert. argument is the name of the file, # but it also is the default common name of the cert. # 'server' is the default name in our conf file for the name of the file # and I've seen no reason to change it. # Note, this is not idempotent. { echo -e '\n\n\n\n\n\n\n\n\n\n'; sleep 1; echo -e 'y\ny\n'; } | ./build-key-server $name ./build-dh fi fi if [[ -e /usr/share/doc/openvpn/examples/sample-config-files/server.conf ]]; then cat /usr/share/doc/openvpn/examples/sample-config-files/server.conf >$conf else # pre-bullsye name gzip -dc /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz >$conf fi cafile=$server_dir/ca-$name.crt cp $ca_origin $cafile cp ${keyfiles[@]} $server_dir # for legacy systems for f in ${keyfiles[@]} $cafile; do ln -sf server/${f##*/} /etc/openvpn done cat >>$conf <>$conf <>$conf <>$conf <>/etc/sysctl.conf <<'EOF' net.ipv6.conf.all.forwarding=1 EOF fi if $route; then cat >>$conf <<'EOF' # Be the default gateway for clients. push "redirect-gateway def1" EOF if [[ $ip6 ]]; then cat >>$conf <<'EOF' push "route-ipv6 2000::/3" EOF fi fi if [[ $port ]]; then cat >>$conf <>/etc/sysctl.conf <<'EOF' net.ipv4.ip_forward=1 EOF sysctl -p /etc/sysctl.conf gw=$(ip route | sed -rn 's/^default via .* dev (\S+).*/\1/p' | head -n1) d=/etc/systemd/system/$vpn_service.service.d mkdir -p $d f=$d/nat.conf cat >$f <>$f <