f4e5762d78c52024dfad15245960599140c813a6
[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
105 f=/etc/openvpn/client/$name.crt
106 if ! $shell "test -s $f"; then
107 # if common name is not unique, you get empty file. and if we didn't silence
108 # build-key, you'd see an error "TXT_DB error number 2"
109 echo "$0: error: $f is empty or otherwise bad. is this common name unique?"
110 exit 1
111 fi
112
113 $shell "dd of=/etc/openvpn/client/$name.conf" <<EOF
114 # From example config, from debian stretch to buster
115 client
116 dev tun
117 proto udp
118 remote $host 1194
119 resolv-retry infinite
120 nobind
121 persist-key
122 persist-tun
123 ca $name-ca.crt
124 cert $name.crt
125 key $name.key
126 # disabled for better performance
127 #comp-lzo
128 verb 3
129
130 # This script will update local dns
131 # to what the server sends, if it sends dns.
132 script-security 2
133 up "$script"
134 down "$script"
135
136 # matching server config
137 cipher AES-256-CBC
138
139 # example config has the commented line, but this other thing looks stronger,
140 # and I've seen it in a vpn provider I trust
141 # ns-cert-type server
142 remote-cert-tls server
143
144 # more resilient when running as nonroot
145 persist-key
146
147 # See comments in server side configuration.
148 # The minimum of the client & server config is what is used by openvpn.
149 reneg-sec 432000
150
151 tls-auth $name-ta.key 1
152 EOF
153
154 if [[ $client_host ]] && $custom_script; then
155 $shell "dd of=$script" <$script
156 $shell "chmod +x $script"
157 fi
158
159 $shell 'cd /etc/openvpn; for f in client/*; do ln -sf $f .; done'