util  Diff

Differences From Artifact [0b831e354e]:

To Artifact [4d7cc51848]:


     1      1   #!/usr/bin/env bash
     2      2   # [ʞ] vpn
     3      3   #  ~ lexi hale <lexi@hale.su>
     4      4   #  ® affero general public license
     5         -#  $ vpn (join | part | info | clean | key) <name>
            5  +#  $ vpn (join | part | info | clean | key | pid) <name>
     6      6   #  : vim:ft=bash
     7      7   
     8      8   # vpn is a simple wrapper around openvpn to make it suitable
     9      9   # for everyday  use. it  takes two  arguments: the  vpn, and
    10     10   # what to do with it.
    11     11   #   - vpn join <vpn>: opens a connection to vpn <name>
    12     12   #   - vpn part <vpn>: closes an active connection to <name>
    13     13   #   - vpn info <vpn>: reports the status of of a vpn
    14     14   #   - vpn key <host>: automatically  provisions  the  client
    15     15   #                     with RSA keys from  <host> and creates
    16     16   #                     a  configuration file if one does  not
    17     17   #                     already exist
    18     18   #   - vpn part <vpn>: closes an active connection to <vpn>
           19  +#    - vpn pid <vpn>: print the pid of an active connection
    19     20   #         - vpn help: display this text
    20     21   #
    21     22   # a number  of environment variables affect  the behavior of
    22     23   # vpn. these  are listed in  the source with  their defaults
    23     24   # and an  explantion of their  function. if you do  not wish
    24     25   # users  to be  able  to  change the  behavior  of vpn  with
    25     26   # setenv, you must  change the param invokation  to a simple
................................................................................
    34     35   # pivot to the  account that invoked the script  after it is
    35     36   # done with tasks that require privileged access.
    36     37   #
    37     38   # (it should  go without  saying, but ensure  you understand
    38     39   # the  security implications  before  editing  sudoers on  a
    39     40   # multiuser machine or  one that is directly  exposed to the
    40     41   # internet.)
           42  +#
           43  +# this   script   is   designed  to   automatically   handle
           44  +# certificate-based authentication. if there is a file named
           45  +# "ca.crt" in  the config  folder for  a script,  the script
           46  +# will  look for  files named  $vpn_cn.crt and  $vpn_cn.key,
           47  +# which are automatically passed  to openvpn (you don't need
           48  +# to name them in the  config file; this feature is designed
           49  +# to  enable the  syncing  of config  files across  multiple
           50  +# devices each with  their own certificate for  the host. it
           51  +# can also automatically  download the necessary certificate
           52  +# files  from the  server using  ssh (scp);  to enable  this
           53  +# feature, $vpn_srv_keydir  must be  set to the  location of
           54  +# the "pki" directory  on the server. users  may then invoke
           55  +# `vpn key  $server-url` to  create a  default configuration
           56  +# and  provision  the  device. $vpn_cn  should  probably  be
           57  +# set  to your  hostname  (the default)  or a  device-unique
           58  +# identifier issued by your organization.
           59  +
           60  +# TODO set up `key` mode so that it is able to accept
           61  +#      paths from the command line as well as  in the
           62  +#      $vpn_srv_keydir environment variable.
    41     63   
    42     64   _text_=$LINENO
    43     65   
    44     66   param(){ eval $1=\${$1:-$2}; }
    45     67   
    46     68   param vpn_basedir    ~/opt/vpn
    47     69   	# the directory in which vpn's logfiles are stored, 
................................................................................
    72     94   
    73     95   param USER			 $(whoami)
    74     96   	# the user who should own all files and processes
    75     97   	# created and destroyed by vpn
    76     98   
    77     99   param vpn_pidbox     $TMPDIR/pid.$USER
    78    100   	# a directory for storing pids in. this should be chmod
    79         -	# 700 and owned by the user invoking vpn, ideally
          101  +	# 700 and owned by the user invoking vpn, ideally on a
          102  +	# tmpfs
    80    103   
    81    104   param vpn_bin        openvpn
    82    105   	# the binary to use. if openvpn is not in your path,
    83    106   	# enter its absolute path here
    84    107   
    85    108   param vpn_script     $0
    86    109   	# a path to the executable
................................................................................
    87    110   
    88    111   param vpn_scrname    $(basename $vpn_script)
    89    112   	# the name of the executable
    90    113   
    91    114   # thus ends the admin-configurable portion of this script.
    92    115   # abandon all hope, ye who enter here
    93    116   
    94         -err(){ echo -e "\e[1;31merror:\e[m" $* ; exit 1; }
          117  +err(){ echo -e "\e[1;31merror:\e[m" $* >&2; exit 1; }
          118  +warn(){ echo -e "\e[1;33mwarn:\e[m " $* >&2; }
    95    119   assert(){ msg=$1; shift; test ! $@ && err $msg; }
    96    120   
    97    121   test "$1" == help || assert "incorrect number of arguments" $# -eq 2
    98    122   
    99    123   act=$1; target=$2
   100    124   vpnd=$vpn_confdir/$target
   101    125   conf=$vpnd/conf
................................................................................
   142    166   		goodpid && err "\e[1m$target\e[m is already up!"
   143    167   
   144    168   		# make sure a private pid directory exists
   145    169   		test ! -e $vpn_pidbox &&
   146    170   			mkdir -p $vpn_pidbox
   147    171   		chmod 700 $vpn_pidbox
   148    172   
          173  +		# make sure a private base directory exists
          174  +		test ! -e $vpn_basedir && {
          175  +			mkdir -p $vpn_basedir && {
          176  +				chmod 700 $USER $vpn_pidbox
          177  +			} || {
          178  +				err '$vpn_basedir is not set or points to a nonexistent directory you cannot create.'
          179  +			}
          180  +		}
          181  +
          182  +		test -e $vpn_global || err "global configuration file $vpn_global does not exist or is not in $vpn_basedir"
          183  +
   149    184   		# check and see if we're using automatic
   150    185   		# host certificates; tell openvpn if so
   151    186   		test -e $vpnd/ca.crt && {
   152    187   			hostcert=$vpnd/$vpn_cn
   153    188   			cmd=(
   154    189   				--askpass
   155    190   				--ca   $vpnd/ca.crt
................................................................................
   168    203   
   169    204   	# kill an existing connection
   170    205   	( part | down | disc* | stop | p | d )
   171    206   		test -e $pidfile && {
   172    207   			goodpid && {
   173    208   				kill $(cat $pidfile) && rm -f $pidfile
   174    209   			} || {
   175         -				echo -e "\e[1;33mwarn:\e[m pidfile exists but does not name an openvpn process"
   176         -				echo " → removing pidfile for safety"
          210  +				warn 'pidfile exists but does not name an openvpn process'
          211  +				echo ' → removing pidfile for safety' >&2
   177    212   				rm -f $pidfile
   178    213   			}
   179    214   
   180    215   		} || {
   181         -			echo -ne "\e[1mvpn $target\e[m is not up"
          216  +			echo -ne "\e[1mvpn $target\e[m is not up" >&2
   182    217   		} ;;
   183    218   
   184    219   	# clean up dirty pidfiles
   185    220   	( clean | wipe | clear | fix | tidy ) clean=1 ;&
   186    221   	# return profile status
   187    222   	( info | stat* | detail* | i )
   188         -		echo -ne "\e[1mvpn $target:\e[m "
          223  +		echo -ne "\e[1mvpn $target:\e[m " >&2
   189    224   		test ! -e $pidfile && {
   190         -			echo -ne "\e[31mno\e[m current connection"
          225  +			echo -ne "\e[31mno\e[m current connection" >&2
   191    226   			pids=($(pidof openvpn))
   192    227   			let pidc=${#pids}
   193    228   			((pidc > 0)) && {
   194         -				echo ", but $pidc vpn instances are running"
          229  +				echo ", but $pidc vpn instances are running" >&2
   195    230   				exit 2
   196    231   			} || { echo; exit 0; }
   197    232   		} || {
   198         -			echo -n "pidfile exists"
          233  +			echo -n "pidfile exists" >&2
   199    234   			proc=$(ps ho fname $(cat $pidfile))
   200    235   			(($? != 0)) && {
   201         -				echo ", but there are no processes with that pid"
          236  +				echo ", but there are no processes with that pid" >&2
   202    237   				stale; exit 1
   203    238   			} || test "$proc" == "openvpn" && {
   204         -				echo -e " and openvpn is \e[32mrunning\e[m"
   205         -				exit 0
          239  +				echo -e " and openvpn is \e[32mrunning\e[m" >&2
   206    240   			} || {
   207         -				echo -e " but named process is \e[31mnot a vpn instance!\e[m"
          241  +				echo -e " but named process is \e[31mnot a vpn instance!\e[m" >&2
   208    242   				stale; exit 1
   209    243   			}
   210    244   		} ;;
          245  +
   211    246   	( help ) head -n $(expr $_text_ - 2) $vpn_script |
   212    247   		tail -n $(expr $_text_ - 2); exit 255;;
   213    248   
   214         -	( * ) err "action must be one of: join | part | info | clean" ;;
          249  +	( pid ) test -e $pidfile && cat $pidfile ||
          250  +		err "no pidfile exists for \e[1m$target\e[m; are you sure you're connected" ;;
          251  +
          252  +	( * ) err "action must be one of: join | part | info | clean | key | pid | help" ;;
   215    253   esac
   216    254   
          255  +exit 0