util  Check-in [75223838e1]

Overview
Comment:add vpn tool
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 75223838e1bae48168201470b2857ed20c47e2be32b5a2dd6c661568bdcc1aae
User & Date: lexi on 2019-05-03 15:21:43
Other Links: manifest | tags
Context
2019-05-04
02:38
updates check-in: 05feae3cd3 user: lexi tags: trunk
2019-05-03
15:21
add vpn tool check-in: 75223838e1 user: lexi tags: trunk
2019-05-01
01:03
refactor tenki check-in: 70991b55c3 user: lexi tags: trunk
Changes

Added vpn/conf version [d086ba0334].



















































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# [ʞ] vpn/conf
#  ~ lexi hale <lexi@hale.su>
#  © public domain / CC0

# this  is  the   master  configuration  file  for
# openvpn used by the  `vpn` utility. these should
# be  sane-enough defaults  for almost  all cases.
# server-specific  config  should   be  placed  in
# $server/conf in this directory

client
pull

group nobody

resolv-retry infinite
nobind

persist-key
persist-tun

log log
mute-replay-warnings
verb 3
mute 10

Added vpn/vpn version [8e3b5d4148].























































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#!/usr/bin/env bash
# [ʞ] vpn
#  ~ lexi hale <lexi@hale.su>
#  ® affero general public license
#  $ vpn (join | part | info | clean | key) <name>
#  : vim:ft=bash

# vpn is a simple wrapper around openvpn to make it suitable
# for everyday  use. it  takes two  arguments: the  vpn, and
# what to do with it.
#   - vpn join <vpn>: opens a connection to vpn <name>
#   - vpn part <vpn>: closes an active connection to <name>
#   - vpn info <vpn>: reports the status of of a vpn
#   - vpn key <host>: automatically  provisions  the  client
#                     with RSA keys from  <host> and creates
#                     a  configuration file if one does  not
#                     already exist
#   - vpn part <vpn>: closes an active connection to <vpn>
#         - vpn help: display this text
#
# a number  of environment variables affect  the behavior of
# vpn. these  are listed in  the source with  their defaults
# and an  explantion of their  function. if you do  not wish
# users  to be  able  to  change the  behavior  of vpn  with
# setenv, you must  change the param invokation  to a simple
# assignment.
#
# note  that you  may want  to  add a  visudo line  allowing
# %wheel  or  perhaps  even  all users  to  execute  openvpn
# without a  password; otherwise, only sudoers  will be able
# to  use vpn  and the  root password  will be  required for
# every state change.
__=$LINENO

param(){ eval $1=\${$1:-$2}; }

param vpn_basedir    ~/opt/vpn
	# the directory in which vpn's logfiles are stored, 
	# and possibly the script itself

param vpn_confdir    $vpn_basedir
	# the directory that contains the individual vpn
	# directories

param vpn_global     $vpn_basedir/conf
	# a configuration file that is applied to all vpns

param vpn_srv_keydir /srv/vpn/ca
	# the directory on the server where vpn client keys
	# are stored

param vpn_cn         $(uname -n)
	# the name of the device / user / resource connecting
	# `vpn key` will use this value to determine which
	# client keys to download. i recommend a one-key-per-
	# device setup with the certificate CN used to assign
	# a name in the tunnel's DNS server, but one-key-per-
	# user or one-key-period setups are also possible.

param TMPDIR		 /tmp
	# a directory for temporary files, preferably one that
	# does not persist across boots (e.g. a tmpfs)

param USER			 $(whoami)
	# the user who should own all files and processes
	# created and destroyed by vpn

param vpn_pidbox     $TMPDIR/pid.$USER
	# a directory for storing pids in. this should be chmod
	# 700 and owned by the user invoking vpn, ideally

param vpn_script     $0
	# a path to the executable

param vpn_scrname    $(basename $vpn_script)
	# the name of the executable

# thus ends the admin-configurable portion of this script.
# abandon all hope, ye who enter here

err(){ echo -e "\e[1;31merror:\e[m" $* ; exit 1; }
assert(){ msg=$1; shift; test ! $@ && err $msg; }

test "$1" == help || assert "incorrect number of arguments" $# -eq 2

act=$1; target=$2
vpnd=$vpn_confdir/$target
conf=$vpnd/conf

test "$act" == key && {
	mkdir -p $vpnd
	test -e $conf || {
		echo dev tun > $conf
		echo proto udp >> $conf
		echo remote $target 1194 >> $conf
	}
}

assert "target \e[1m$target\e[m does not exist!" -e $vpn_confdir/$target
assert "target \e[1m$target\e[m has no configuration file" -e $conf

pidfile="$vpn_pidbox/openvpn.$target"

stale() {
	((clean == 1)) && {
		echo " → deleting stale pidfile"
		rm -f $pidfile
	} || {
		echo -e " - run \e[1m$vpn_scrname clean\e[m to clean up"
	}
}

goodpid(){
	test -e $pidfile || return 1
	local proc=$(ps ho fname $(cat $pidfile)) || return 2
	test "$proc" == openvpn || return 2
	return 0
}
	

case $act in
	# automatically provision machine
	( key | setup | provision | download | dl | k ) 
		scp $target:$vpn_srv_keydir/pki/{ca.crt,{issued,private}/$vpn_cn.\*} $vpnd/ || err "could not retrieve keys for \e[1m$target\e[m; ensure ssh is properly configured and machine keys are assigned to the correct user" ;;

	# connect to the openvpn server
	( join | up | connect | con | start | j | u )
		# don't clobber an existing connection
		goodpid && err "\e[1m$target\e[m is already up!"

		# make sure a private pid directory exists
		test ! -e $vpn_pidbox &&
			mkdir -p $vpn_pidbox
		chmod 700 $vpn_pidbox

		# check and see if we're using automatic
		# host certificates; tell openvpn if so
		test -e $vpnd/ca.crt && {
			hostcert=$vpnd/$vpn_cn
			cmd=(
				--askpass
				--ca   $vpnd/ca.crt
				--cert $hostcert.crt
				--key  $hostcert.key
			)
		}

		touch $pidfile # make sure invoker owns it
		chmod 600 $pidfile # bc fuck you, that's why
		sudo openvpn --daemon --config $vpn_global \
		                      --config $conf       \
							  --user $USER         \
							  ${cmd[@]}            \
		                      --writepid $pidfile || rm $pidfile;;

	# kill an existing connection
	( part | down | disc* | stop | p | d )
		test -e $pidfile && {
			goodpid && {
				kill $(cat $pidfile) && rm -f $pidfile
			} || {
				echo -e "\e[1;33mwarn:\e[m pidfile exists but does not name an openvpn process"
				echo " → removing pidfile for safety"
				rm -f $pidfile
			}

		} || {
			echo -ne "\e[1mvpn $target\e[m is not up"
		} ;;

	# clean up dirty pidfiles
	( clean | wipe | clear | fix | tidy ) clean=1 ;&
	# return profile status
	( info | stat* | detail* | i )
		echo -ne "\e[1mvpn $target:\e[m "
		test ! -e $pidfile && {
			echo -ne "\e[31mno\e[m current connection"
			pids=($(pidof openvpn))
			let pidc=${#pids}
			((pidc > 0)) && {
				echo ", but $pidc vpn instances are running"
				exit 2
			} || { echo; exit 0; }
		} || {
			echo -n "pidfile exists"
			proc=$(ps ho fname $(cat $pidfile))
			(($? != 0)) && {
				echo ", but there are no processes with that pid"
				stale; exit 1
			} || test "$proc" == "openvpn" && {
				echo -e " and openvpn is \e[32mrunning\e[m"
				exit 0
			} || {
				echo -e " but named process is \e[31mnot a vpn instance!\e[m"
				stale; exit 1
			}
		} ;;
	( help ) head -n $(expr $__ - 1) $vpn_script |
		tail -n $(expr $__ - 2); exit 255;;

	( * ) err "action must be one of: join | part | info | clean" ;;
esac