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