improve license notices
[vpn-setup] / vpn-server-setup
1 #!/bin/bash
2 # I, Ian Kelling, follow the GNU license recommendations at
3 # https://www.gnu.org/licenses/license-recommendations.en.html. They
4 # recommend that small programs, < 300 lines, be licensed under the
5 # Apache License 2.0. This file contains or is part of one or more small
6 # programs. If a small program grows beyond 300 lines, I plan to switch
7 # its license to GPL.
8
9 # Copyright 2024 Ian Kelling
10
11 # Licensed under the Apache License, Version 2.0 (the "License");
12 # you may not use this file except in compliance with the License.
13 # You may obtain a copy of the License at
14
15 # http://www.apache.org/licenses/LICENSE-2.0
16
17 # Unless required by applicable law or agreed to in writing, software
18 # distributed under the License is distributed on an "AS IS" BASIS,
19 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 # See the License for the specific language governing permissions and
21 # limitations under the License.
22
23
24 set -eE -o pipefail
25 trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
26
27 [[ $EUID == 0 ]] || exec sudo -E "$BASH_SOURCE" "$@"
28
29 usage() {
30 cat <<'EOF'
31 usage: ${0##*/} [OPTIONS] [IPV6_ADDR/BITS]
32
33 -4 Prefix of range for ipv4, default 10.8.0
34 -6 IP6_NETWORK Do ip6 nat for this network. ipv6 will work without nat,
35 but you may want it in certain circumstances.
36 -d Do not push dns
37 -i INTERFACE_NAME name of tun interface
38 -n NAME default = server. 2 servers on the same host need different names.
39 -p PORT default 1194
40 -r Do not push default route
41 -s Do not start openvpn
42 -h --help print help
43
44 IPV6_ADDR/BITS Ipv6 address of the vpn interface.
45
46 Sets up a vpn server which pushes gateway route and dns server so all
47 traffic goes through the vpn. requires systemd, and might have some
48 debian specific paths.
49
50 For ipv6, we assume ipv6_addr routes to the server.
51
52 You can save all the keys by storing /etc/openvpn/easy-rsa-NAME/keys, and
53 the script will not generate them if it sees they exist already.
54
55 For future updates to this script, this is a good place to
56 take inspiration.
57 https://github.com/angristan/openvpn-install/blob/master/openvpn-install.sh
58
59 Note: Uses GNU getopt options parsing style
60 EOF
61 exit $1
62 }
63
64 dns=true
65 route=true
66 start=true
67 ip4=10.8.0
68 name=server
69 temp=$(getopt -l help 4:6:di:n:p:rsh "$@") || usage 1
70 eval set -- "$temp"
71 while true; do
72 case $1 in
73 -4) ip4=$2; shift 2 ;;
74 -6) ip6net=$2; shift 2 ;;
75 -d) dns=false; shift ;;
76 -i) ifname=$2; shift 2 ;;
77 -n) name=$2; shift 2 ;;
78 -p) port=$2; shift 2 ;;
79 -r) route=false; shift ;;
80 -s) start=false; shift ;;
81 -h|--help) usage ;;
82 --) shift; break ;;
83 *) echo "$0: Internal error! unexpected args: $*" ; exit 1 ;;
84 esac
85 done
86
87 read -r ip6 ip6route <<<"$@"
88
89 source /a/bin/distro-functions/src/package-manager-abstractions
90
91 pi-nostart openvpn openssl easy-rsa uuid-runtime
92
93 if [[ -e /lib/systemd/system/openvpn-server@.service ]]; then
94 vpn_service=openvpn-server@$name
95 else
96 vpn_service=openvpn@$name
97 fi
98 rsadir=/etc/openvpn/easy-rsa-$name
99 mkdir -p $rsadir
100 cd $rsadir
101 cp -r /usr/share/easy-rsa/* .
102 if [[ -e openssl-1.0.0.cnf && ! -e openssl.cnf ]]; then
103 # there's a debian bug about this.
104 ln -s openssl-1.0.0.cnf openssl.cnf
105 fi
106
107 server_dir=/etc/openvpn/server
108 mkdir -p $server_dir
109 chmod 700 $server_dir
110 conf=$server_dir/$name.conf
111
112
113 new=true
114 ca_origin=$rsadir/pki/ca.crt
115 keyfiles=(
116 $rsadir/pki/private/$name.key
117 $rsadir/pki/issued/$name.crt
118 )
119 if [[ -e /etc/openvpn/easy-rsa/build-ca ]]; then
120 new=false
121 ca_origin=$rsadir/ca.crt
122 keyfiles=(
123 $rsadir/keys/$name.key
124 $rsadir/keys/$name.crt
125 )
126 fi
127
128 keys_exist=true
129 for f in ${keyfiles[@]}; do
130 if [[ ! -e $f ]]; then
131 keys_exist=false
132 break
133 fi
134 done
135
136 f=$server_dir/dh2048.pem
137 if [[ ! -e $f ]]; then
138 openssl dhparam -out $f 2048
139 fi
140
141 f=$server_dir/ta-$name.key
142 if [[ ! -e $f ]]; then
143 openvpn --genkey --secret $server_dir/ta-$name.key
144 fi
145
146
147 if ! $keys_exist; then
148 # newer sample configs (post stretch) use ta.key. no harm making it for earlier oses
149 if $new; then
150 echo 'set_var EASYRSA_NS_SUPPORT "yes"' >vars
151 ./easyrsa init-pki
152 ./easyrsa --batch build-ca nopass
153 ./easyrsa --days=3650 build-server-full $name nopass
154 else
155 # dun care about settning cert cn etc from the non-example values
156 source vars
157 # doesnt exist in buster
158 ./clean-all # note: removes and creates /etc/openvpn/easy-rsa/keys
159 # accept default prompts
160 echo -e '\n\n\n\n\n\n\n\n' | ./build-ca
161
162 # This builds the server's key/cert. argument is the name of the file,
163 # but it also is the default common name of the cert.
164 # 'server' is the default name in our conf file for the name of the file
165 # and I've seen no reason to change it.
166 # Note, this is not idempotent.
167 { echo -e '\n\n\n\n\n\n\n\n\n\n'; sleep 1; echo -e 'y\ny\n'; } | ./build-key-server $name
168 ./build-dh
169 fi
170 fi
171
172 if [[ -e /usr/share/doc/openvpn/examples/sample-config-files/server.conf ]]; then
173 cat /usr/share/doc/openvpn/examples/sample-config-files/server.conf >$conf
174 else
175 # pre-bullsye name
176 gzip -dc /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz >$conf
177 fi
178
179 cafile=$server_dir/ca-$name.crt
180 cp $ca_origin $cafile
181 cp ${keyfiles[@]} $server_dir
182 # for legacy systems
183 for f in ${keyfiles[@]} $cafile; do
184 ln -sf server/${f##*/} /etc/openvpn
185 done
186
187 cat >>$conf <<EOF
188
189 # I cat an extra blank line to start because the example config does
190 # not have a final newline. ....
191
192 # not in example config, but openvpn outputs a warning about insecure
193 # cipher without a setting like this (the default i can understand due
194 # to compatibility issues, but not changing the example config... not
195 # cool).
196 # requires the same setting on the client side.
197 cipher AES-256-CBC
198 # just sets up the ability to have client specific configs
199 client-config-dir /etc/openvpn/client-config
200
201 # duplicate in newer sample configs
202 tls-auth ta-$name.key 0 # This file is secret
203
204 # depending on sample config, this may not be there, which means i can't
205 # talk to $ip4.1, there might be some other way, but stretch's
206 # sample config says:
207 # Should be subnet (addressing via IP)
208 # unless Windows clients v2.0.9 and lower have to
209 # be supported (then net30, i.e. a /30 per client)
210 # Defaults to net30 (not recommended)
211 topology subnet
212
213 status /var/log/openvpn/openvpn-status-$name.log
214 ifconfig-pool-persist /var/log/openvpn/ipp-$name.txt
215 ca ca-$name.crt
216 cert $name.crt
217 key $name.key
218 client-config-dir /etc/openvpn/client-config-$name
219 server $ip4.0 255.255.255.0
220 EOF
221 mkdir -p /etc/openvpn/client-config-$name
222
223 # dh improve security,
224 # remove comp-lzo to increase perf
225 sed -i --follow-symlinks -f - $conf <<'EOF'
226 s/^dh dh1024.pem/dh dh2048.pem/
227 /^comp-lzo.*/d
228 EOF
229
230
231 if $dns; then
232 # Be the dns server for clients
233 cat >>$conf <<EOF
234 push "dhcp-option DNS $ip4.1"
235 EOF
236 fi
237
238 if [[ $ifname ]]; then
239 cat >>$conf <<EOF
240 dev $ifname
241 EOF
242 fi
243
244 if [[ $ip6 ]]; then
245 cat >>$conf <<EOF
246 push tun-ipv6 # legacy option that flidas needs, has no harm.
247 # the ::1 is not used, i just put a short valid address there
248 ifconfig-ipv6 $ip6 ::1
249 EOF
250
251 sed -i --follow-symlinks '/^ *net.ipv6.conf.all.forwarding=.*/d' /etc/sysctl.conf
252 cat >>/etc/sysctl.conf <<'EOF'
253 net.ipv6.conf.all.forwarding=1
254 EOF
255
256 fi
257
258
259 if $route; then
260 cat >>$conf <<'EOF'
261 # Be the default gateway for clients.
262 push "redirect-gateway def1"
263 EOF
264 if [[ $ip6 ]]; then
265 cat >>$conf <<'EOF'
266 push "route-ipv6 2000::/3"
267 EOF
268 fi
269 fi
270 if [[ $port ]]; then
271 cat >>$conf <<EOF
272 port $port
273 EOF
274 fi
275
276
277 sed -i --follow-symlinks '/^ *net\.ipv4\.ip_forward=.*/d' /etc/sysctl.conf
278 cat >>/etc/sysctl.conf <<'EOF'
279 net.ipv4.ip_forward=1
280 EOF
281 sysctl -p /etc/sysctl.conf
282
283 gw=$(ip route | sed -rn 's/^default via .* dev (\S+).*/\1/p' | head -n1)
284
285 d=/etc/systemd/system/$vpn_service.service.d
286 mkdir -p $d
287 f=$d/nat.conf
288 cat >$f <<EOF
289 [Service]
290 ExecStartPre=/sbin/iptables -t nat -A POSTROUTING -s $ip4.0/24 -o $gw -j MASQUERADE
291 ExecStopPost=/sbin/iptables -t nat -D POSTROUTING -s $ip4.0/24 -o $gw -j MASQUERADE
292 EOF
293 if [[ $ip6net ]]; then
294 cat >>$f <<EOF
295 ExecStartPre=/sbin/ip6tables -t nat -A POSTROUTING -s $ip6net -o $gw -j MASQUERADE
296 ExecStopPost=/sbin/ip6tables -t nat -D POSTROUTING -s $ip6net -o $gw -j MASQUERADE
297 EOF
298 systemctl daemon-reload # needed if the file was already there
299
300 if $start; then
301 systemctl enable $vpn_service
302 systemctl restart $vpn_service
303 fi
304 fi