minor improvements
[vpn-setup] / vpn-mk-client-cert
1 #!/bin/bash
2 # Copyright (C) 2016 Ian Kelling
3
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7
8 # http://www.apache.org/licenses/LICENSE-2.0
9
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 set -eE -o pipefail
17 trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
18
19 [[ $EUID == 0 ]] || exec sudo -E "$BASH_SOURCE" "$@"
20
21 readonly this_file="$(readlink -f -- "${BASH_SOURCE[0]}")"; cd "${this_file%/*}"
22
23
24 usage() {
25 cat <<'EOF'
26 usage: ${0##*/} VPN_SERVER_HOST
27
28 -b COMMON_NAME By default, use $CLIENT_HOST or if it is not given,
29 $HOSTNAME. If the cert already exists on the server,
30 with the CLIENT_NAME name, we use the existing one. See
31 comment below if we ever want to check existing common
32 names. They must be unique per server, so you can use
33 $(uuidgen) if needed. You used to be able to create
34 multiple with the same name, but not connect at the
35 same time, but now, the generator keeps track, so you
36 can't generate.
37
38 -c CLIENT_HOST Default is localhost. Else we ssh to root@CLIENT_HOST.
39 -n CONFIG_NAME default is client
40 -s SCRIPT_PATH Use custom up/down script at SCRIPT_PATH. copied to same path
41 on client, if client is not localhost.
42
43 Generate a client cert and config and install it on locally or on
44 CLIENT_HOST if given. Uses default config options, and expects be able
45 to ssh to VPN_SERVER_HOST and CLIENT_HOST as root, or if CLIENT_HOST is
46 localhost, just to sudo this script as root.
47
48
49
50
51 Note: Uses GNU getopt options parsing style
52 EOF
53 exit ${1:-0}
54 }
55
56 # to get the common name
57 # cn=$(s openssl x509 -noout -nameopt multiline -subject \
58 # -in /etc/openvpn/client/mail.crt | \
59 # sed -rn 's/^\s*commonName\s*=\s*(.*)/\1/p')
60
61
62 ####### begin command line parsing and checking ##############
63
64 shell="bash -c"
65 name=client
66 custom_script=false
67 script=/etc/openvpn/update-resolv-conf
68 client_host=$CLIENT_HOST
69
70 temp=$(getopt -l help hb:c:n:s: "$@") || usage 1
71 eval set -- "$temp"
72 while true; do
73 case $1 in
74 -b) common_name="$2"; shift 2 ;;
75 -c) client_host=$2; shell="ssh root@$client_host"; shift 2 ;;
76 -n) name="$2"; shift 2 ;;
77 -s) custom_script=true; script="$2"; shift 2 ;;
78 -h|--help) usage ;;
79 --) shift; break ;;
80 *) echo "$0: Internal error! unexpected args: $*" ; exit 1 ;;
81 esac
82 done
83
84 if [[ ! $common_name ]]; then
85 if [[ $client_host ]]; then
86 common_name=$client_host
87 else
88 common_name=$HOSTNAME
89 fi
90 fi
91
92 host=$1
93 [[ $host ]] || usage 1
94
95 ####### end command line parsing and checking ##############
96
97 # bash or else we get motd spam. note sleep 2, sleep 1 failed.
98 $shell '[[ -e /etc/openvpn ]] || apt install openvpn'
99 if ! ssh root@$host bash -s -- $name $common_name < client-cert-helper \
100 | $shell 'id -u | grep -xF 0 || s=sudo; $s tar xzv -C /etc/openvpn/client'; then
101 echo ssh root@$host cat /tmp/vpn-mk-client-cert.log:
102 ssh root@$host cat /tmp/vpn-mk-client-cert.log
103 exit 1
104 fi
105
106 port=$(echo '/^port/ {print $2}' | ssh root@$host awk -f - /etc/openvpn/server/$name.conf | tail -n1)
107
108
109 f=/etc/openvpn/client/$name.crt
110 if ! $shell "test -s $f"; then
111 # if common name is not unique, you get empty file. and if we didn't silence
112 # build-key, you'd see an error "TXT_DB error number 2"
113 echo "$0: error: $f is empty or otherwise bad. is this common name unique?"
114 exit 1
115 fi
116
117 $shell "dd of=/etc/openvpn/client/$name.conf" <<EOF
118 # From example config, from debian stretch to buster
119 client
120 dev tun
121 proto udp
122 remote $host $port
123 resolv-retry infinite
124 nobind
125 persist-key
126 persist-tun
127 ca ca-$name.crt
128 cert $name.crt
129 key $name.key
130 # disabled for better performance
131 #comp-lzo
132 verb 3
133
134 # matching server config
135 cipher AES-256-CBC
136
137 # example config has the commented line, but this other thing looks stronger,
138 # and I've seen it in a vpn provider I trust
139 # ns-cert-type server
140 remote-cert-tls server
141
142 # more resilient when running as nonroot
143 persist-key
144
145 # See comments in server side configuration.
146 # The minimum of the client & server config is what is used by openvpn.
147 reneg-sec 432000
148
149 tls-auth ta-$name.key 1
150 EOF
151
152 if [[ $script ]]; then
153 $shell "tee -a /etc/openvpn/client/$name.conf" <<EOF
154 # This script will update local dns
155 # to what the server sends, if it sends dns.
156 script-security 2
157 up "$script"
158 down "$script"
159 EOF
160
161 if [[ $client_host ]] && $custom_script; then
162 $shell "dd of=$script" <$script
163 $shell "chmod +x $script"
164 fi
165 fi
166
167 $shell 'cd /etc/openvpn; for f in client/*; do ln -sf $f .; done'