#!/bin/sh # Copyright (c) 2001-2004 Todd T. Fries # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. [ "${SYSCONFDIR}" ] || SYSCONFDIR=/etc [ "${TRUEPREFIX}" ] || TRUEPREFIX=/usr/local afsp=${TRUEPREFIX}/libexec/openafs PATH=${TRUEPREFIX}/sbin:${TRUEPREFIX}/bin:$PATH DIR=`mktemp -d /tmp/_openafs.XXXXXXXXXX` || exit 1 trap 'rm -rf $DIR; exit 1' 0 1 2 3 13 15 OUTPUT=$DIR/_1 defuser="$USER" if [ "$defuser" = "root" ]; then if [ "$SUDO_USER" ]; then defuser="$SUDO_USER" else defuser="todd" fi fi # (borrowed from install.sub) # Ask for user input. # # $1 = the question to ask the user # $2 = the default answer # # Save the user input (or the default) in $resp. # # Allow the user to escape to shells ('!') or execute commands # ('!foo') before entering the input. ask() { local _question=$1 _default=$2 set -o noglob while :; do echo -n "$_question " [[ -z $_default ]] || echo -n "[$_default] " read resp case $resp in !) echo "Type 'exit' to return to install." sh ;; !*) eval ${resp#?} ;; *) : ${resp:=$_default} break ;; esac done set +o noglob } # Ask for user input until a non-empty reply is entered. # # $1 = the question to ask the user # $2 = the default answer # # Save the user input (or the default) in $resp. ask_until() { resp= while [[ -z $resp ]] ; do ask "$1" "$2" done } # Ask the user for a y or n, and insist on 'y', 'yes', 'n' or 'no'. # # $1 = the question to ask the user # $2 = the default answer (assumed to be 'n' if empty). # # Return 'y' or 'n' in $resp. ask_yn() { local _q=$1 _a=${2:-no} _resp typeset -l _resp while :; do ask "$_q" "$_a" _resp=$resp case $_resp in y|yes) resp=y ; return ;; n|no) resp=n ; return ;; esac done } # Logging routine # # $1 = -c or 1st arg # $2 = ... # # log all arguments sc=0 log() { local prompt=":" if [ "$1" = "-c" ]; then prompt="#" shift else let sc=sc+1 fi dfmt="%H:%M:%S" printf "%s %02d%s " "$(date +"${dfmt}")" $sc "$prompt" # use echo, because printf(1) treats varargs as one arg per line, ugh echo "$@" } # Create a principal in kerberos. # # $1 = principal # $2 = extra arg.. # # Delete the principal first before re-adding it to make sure proper # attributes exist. kadd() { local principal=$1 log creating principal: $principal shift kadmin del $principal > /dev/null 2>&1 log -c kadmin add "$@" $principal kadmin add \ --{pw-,}expiration-time=never \ --max-ticket-life="1 month" \ --max-renewable-life="2 months" \ --attributes="" \ "$@" $principal } # Re-try a command until success. # # $@ = full command to try # retry() { local try=1 log -c "$@" while ! $@ do let try=try+1 log -c "$@ : try $try" sleep 2 done } # Make an afs volume. # # $1 = volume name # $2 = volume mount point # # Any user can read volumes created here. mkvol() { local vol=$1 mnt=$2 log "Creating afs volume $1 to be mounted at $mnt" retry vos create $h /vicepa $vol retry fs mkm $mnt $vol retry fs sa $mnt system:anyuser rl } cat <<__EOT =========================================================================== Welcome to the OpenAFS server1 setup script! This script will assist you in setting up your first afs server. It will use OpenAFS for the AFS server, but arla's afsd that comes with OpenBSD for the AFS client. It will use heimdal KerberosV that comes with OpenBSD. It presumes you have previously successfully setup a KerberosV realm, you have the password to an administrative principal in the KerberosV realm, you are running it as root, and you have created at least one partition for OpenAFS to use for data storage. Partitions should be mounted under /vicepa, /vicepb, /vicepc, etc. =========================================================================== __EOT # # Sanity checks # # Require root. if [[ `/usr/bin/whoami` != "root" ]] then echo "Please run this script as root. Thanks." exit fi if [[ `mount|grep " /vicepa "` = "" ]] then echo "Could not find any filesystem mounted at /vicepa" echo "Without this OpenAFS will not function." echo "Please mount a partition under /vicepa" echo "A /vicepa directory will not work as" echo "OpenAFS" exit fi # # Setup site specific variables # # Ask user for variables if not passed on the command line. if ! [ "$6" ] then cat <<__EOT The hostname for this afs server should resolve in dns but definitely reside in /etc/hosts. e.g. afs0.example.com. __EOT while :; do ask_until "System hostname?" "$(hostname)" h=${resp} if [[ `grep $h /etc/hosts` = "" ]] then echo "Could not find /etc/hosts entry for $h." continue fi if [[ `grep $(hostname) /etc/hosts` = "" ]] then echo "Could not find /etc/hosts entry for $(hostname)." echo "(afs processes use the system hostname..)" continue fi break done cat <<__EOT The IPv4 IP address for this afs server should resolve in dns but definitely reside in /etc/hosts. e.g. 192.168.1.200. __EOT while :; do ask_until "System IP?" "$(host $h | \ awk '/has address/{print $4}')" ip=${resp} if [[ `grep $ip /etc/hosts` = "" ]] then echo "Could not find /etc/hosts entry for $ip." continue fi break done cat <<__EOT The cell name is typically a dns name. e.g. example.com. __EOT ask_until "AFS Cell Name?" "${h#*.}" c=${resp} cat <<__EOT The realm name is the KerberosV REALM, typically the capitalized dns name. e.g. REALM.COM. Use something different at the expense of your sanity. Really. __EOT ask_until "KerberosV REALM?" "$(echo "$c"|tr "[a-z]" "[A-Z]")" R=${resp} cat <<__EOT This is an existing KerberosV principal with the ability to create and delete other kerberos principals. e.g. $defuser/admin. __EOT ask_until "KerberosV principal for kerberos administration?" \ "$defuser/admin" p=${resp} echo ask_until "Is this the first server setup in the $c cell?" "Y" case "${resp}" in [Yy]*) first=1;; *) first=0;; esac if [ first -eq 0 ]; then set -A sservers fs echo ask_until "What is the primary afs server's name (e.g. afs0.example.com)?" psn=$resp ask_until "What is the primary afs server's IP (e.g. 10.1.2.3)?" psip=$resp cat <<__EOT This principal should already exist from the 1st afs server setup, and will be used to administer afs in a similar way that \`root' can administrate OpenBSD. Tread lightly when using this principal. __EOT else set -A sservers buserver ptserver vlserver fs cat <<__EOT Just a sample user to create a basic homedir and account on afs with. __EOT ask_until "KerberosV principal for example user?" "$defuser" u=${resp} cat <<__EOT This principal will be deleted if it exists, then created with specific attributes. It will be used to administer afs in a similar way that \`root' can administrate OpenBSD. Tread lightly when using this principal. __EOT fi ask_until "KerberosV principal for afs administration?" \ "$defuser/afs" A=${resp} slist="" i=0 while [ i -lt ${#sservers[*]} ]; do slist="$slist ${sservers[$i]}" let i=i+1 done slist="${slist# *}" cat <<__EOT Each afs server can run one or more openafs server processes: vlserver(8) - volume location server, keeps track of volume locations ptserver(8) - protection server for users, groups, and permissions buserver(8) - permit backups to occur for vlserver and ptserver databases fs - fileserver, volserver, salvager (for hosts that store files) __EOT ask_until "Servers to run on $h?" "$slist" set -A sservers $resp else # for advanced users, this script can be started with the above # pre-populated via arguments h="$1" ip="$2" c="$3" R="$4" p="$5" A="$6" u="$7" fi slist="" i=0 vl2=0 while [ i -lt ${#sservers[*]} ]; do if [ "${sservers[$i]}" = "vlserver" -a first -eq 0 ]; then # we are a 2ndary server running vlserver, save to CellServDB vl2=1 fi slist="$slist ${sservers[$i]}" let i=i+1 done slist="${slist# *}" pp=$(echo "$p"|sed 's,/,.,') pA=$(echo "$A"|sed 's,/,.,') cat <<__EOT Confirm these look correct: hostname : $h IP address : $ip cellname : $c realm : $R krb admin : $p (pts name: $pp) afs admin : $A (pts name: $pA) afs servers: $slist __EOT if [ first -eq 1 ]; then cat <<__EOT sample user: $u __EOT else cat <<__EOT prim. name : $psn prim. ip : $psip __EOT fi cat <<__EOT The next step *DESTROYS* all existing OpenAFS configuration on this system, including any openafs data on all /vicep* partitions! __EOT ask_yn "Are you really sure that you're ready to proceed?" [[ $resp == n ]] && { echo "Ok, try again later.\n" ; exit ; } echo log "preparing ${SYSCONFDIR}/{open,}afs /usr/afs /var/openafs..." # Initialize the filesystems initfs() { kdestroy pkill -9 afsd umount /afs > /dev/null 2>&1 if [ -d /var/spool/afs ]; then log clearing afs cache retry rm -rf -- "/var/spool/afs/*" retry mkdir -p /var/spool/afs fi log stopping any pre-existing arla and/or openafs daemons if [ "$(pgrep bosserver)" ] then bos shutdown localhost -noauth -wait > /dev/null 2>&1 fi if [ "$(pgrep bosserver)" ] then bos shutdown localhost -localauth -wait > /dev/null 2>&1 fi pkill -9 buserver ptserver vlserver fileserver volserver bos bosserver retry rm -rf ${SYSCONFDIR}/openafs /usr/afs /var/openafs retry rm -rf /vicep*/{V*,AFSIDat,Lock} retry rm -f /etc/kerberosV/krb5.keytab retry mkdir -p ${SYSCONFDIR}/openafs/server /usr/afs /var/spool/afs retry mkdir -m 700 /var/openafs echo "f $ip" > /var/openafs/NetInfo echo "$R" > ${SYSCONFDIR}/openafs/server/krb.conf ln -s /var/openafs/db /usr/afs/db ln -s ${SYSCONFDIR}/openafs/server /usr/afs/etc } # borrowed from /etc/security, backup CellServDB _fnchg() { echo "$1" | sed 's/^\///;s/\//_/g' } # be extra cautious backupcsdb() { if [ -s $CURdb ]; then diff -ua $CURdb $csdb > $OUTPUT if [ -s $OUTPUT ]; then cp -p $CURdb $BACKdb cp -p $csdb $CURdb chown root:wheel $CURdb $BACKdb fi else cp -p $csdb $CURdb chown root:wheel $CURdb fi } # update the CellServDB's writecsdb() { # put back arla's CellServDB when this script exits log "Adding cell $c to CellServDB" trap 'rm -rf $DIR; cp $CURdb $csdb; exit 1' 0 1 2 3 13 15 echo ">$c # $c" > $DIR/CellServDB if [ first -eq 0 ]; then echo "$psip #$psn" >> $DIR/CellServDB if [ vl2 -eq 1 ]; then echo "$ip #$h" >> $DIR/CellServDB fi else echo "$ip #$h" >> $DIR/CellServDB fi cat $DIR/CellServDB | \ tee -a $csdb ${SYSCONFDIR}/openafs/server/CellServDB } writethiscell() { echo $c | tee ${SYSCONFDIR}/openafs/server/ThisCell > /etc/afs/ThisCell } hostkeytab() { kadd host/$h --random-key retry kadmin ext host/$h chmod 0400 /etc/kerberosV/krb5.keytab } saveafskey() { log creating ${SYSCONFDIR}/openafs/server/KeyFile retry kadmin ext -k $DIR/afsv5key afs/$c log -c ktutil copy $DIR/afsv5key AFSKEYFILE:${SYSCONFDIR}/openafs/server/KeyFile ktutil copy $DIR/afsv5key AFSKEYFILE:${SYSCONFDIR}/openafs/server/KeyFile retry chmod 600 ${SYSCONFDIR}/openafs/server/KeyFile retry rm $DIR/afsv5key } createafskey() { kadd afs/$c --random-key } addexampleuser() { log When asked for a password below, the answer will set it. let sc=sc-1 kadd $A } disablearla() { log "Disabling arla admin commands (use openafs commands on servers):" retry chmod 644 /usr/sbin/{bos,pts,vos,fs} } setuppts() { log setting up pts memberships, $pA as initial afs admin retry pts createuser -name $u -id `id -u $u` -cell $c -noauth retry pts createuser -name $pA -cell $c -noauth retry pts adduser $pA system:administrators -cell $c -noauth retry pts mem system:administrators -cell $c -noauth retry pts listentries -cell $c -noauth retry bos addhost localhost $h -noauth } initcellp1() { log creating root.afs retry vos create $h /vicepa root.afs -noauth retry vos create $h /vicepa root.cell -noauth sync;sync sleep 5 sync;sync } initcellp2() { log setting permissions/creating volumes retry fs sa /afs system:anyuser rl retry fs flushvolume /afs retry fs mkm /afs/.$c root.cell -cell $c -rw -fast retry fs flushvolume /afs/.$c retry fs sa /afs/.$c system:anyuser rl retry fs flushvolume /afs/.$c cat <<__EOT> /afs/.$c/robots.txt # noticed at ualberta.ca, attempt to prevent robots from traversing afs User-Agent: * Disallow: / __EOT mkvol user /afs/.$c/u mkvol user.$defuser /afs/.$c/u/$defuser mkvol mirror /afs/.$c/mirror retry vos addsite $h /vicepa mirror retry vos release mirror log Add some remote afs cells # significant remote afs cells of note, and/or install mirrors # as a basic set of remote cells for our example root.afs set -A rc \ $c \ ualberta.ca \ stacken.kth.se \ grand.central.org \ su.se \ mrow.org i=0 while [ i -lt ${#rc[*]} ] do log -c fs mkm /afs/${rc[$i]} root.cell -cell ${rc[$i]} -fast fs mkm /afs/${rc[$i]} root.cell -cell ${rc[$i]} -fast let i=i+1 done } # add files of note to /etc/changelist addchg() { while [ "$1" ] do f="$1" shift if egrep "^${f}$" /etc/changelist > /dev/null 2>&1; then continue fi echo "$f" >> /etc/changelist done } log updating /etc/afs/CellServDB ${SYSCONFDIR}/openafs/server/CellServDB csdb=/etc/afs/CellServDB CURdb=/var/backups/$(_fnchg $csdb).current BACKdb=/var/backups/$(_fnchg $csdb).backup initfs backupcsdb writecsdb writethiscell if [ "$(pgrep bosserver)" ] then pkill bosserver fi log authenticating $p@$R retry kinit $p@$R hostkeytab # only 1st afs server if [ first -eq 1 ]; then createafskey fi saveafskey # only 1st afs server if [ first -eq 1 ]; then addexampleuser fi disablearla log starting unauthenticated bosserver retry bosserver -log -syslog -noauth retry bos setcellname localhost $c -noauth log creating server entries with bos i=0 fs=0 while [ i -lt ${#sservers[*]} ]; do srv="${sservers[$i]}" case "$srv" in fs) fs=1 ;; *) retry bos create localhost $srv simple $afsp/$srv -cell $c -noauth ;; esac let i=i+1 done # only 1st afs server if [ first -eq 1 ]; then setuppts fi retry bos adduser localhost $pA -cell $c -noauth retry bos shutdown localhost -cell $c -noauth -wait log getting status of bos config retry bos status localhost -noauth -cell $c -long fs=1 if [ fs -eq 1 ]; then log creating fs entry with bos retry bos create localhost fs fs $afsp/{fileserver,volserver,salvager} -cell $c -noauth retry bos restart localhost -all -cell $c -noauth fi log getting partition list retry vos listpart localhost -noauth # only 1st afs server if [ first -eq 1 ]; then initcellp1 fi retry bos shutdown localhost -cell $c -noauth -wait pkill -HUP bosserver sleep 3 # don't put back CellServDB trap 'rm -rf $DIR; exit 1' 0 1 2 3 13 15 log starting authenticated bosserver retry bosserver -log -syslog retry bos restart localhost -all -cell $c -localauth log starting afs client retry mkdir -p /afs [ "$(mount | egrep "^/afs")" ] || mount -t nnpfs /dev/nnpfs0 /afs retry /usr/libexec/afsd -z --log=/var/log/afsd.log log authenticating $A retry kinit $A retry pts listentries # do this on reboot grep "^afs=YES" /etc/rc.conf > /dev/null 2>&1 || \ grep "^afs=YES" /etc/rc.conf.local > /dev/null 2>&1 || \ echo afs=YES >> /etc/rc.conf.local retry ls /afs # only 1st afs server if [ first -eq 1 ]; then initcellp2 fi log adding replication sites for root.afs, root.cell retry vos addsite $h /vicepa root.afs retry vos addsite $h /vicepa root.cell log release of replicated volumes retry vos release root.afs retry vos release root.cell log restart afsd so it sees RO replicated volumes pkill afsd retry /usr/libexec/afsd -z --log=/var/log/afsd.log addchg /etc/kerberosV/krb5.conf addchg "+/etc/kerberosV/krb5.keytab" addchg /etc/afs/{CellServDB,ThisCell,afsd.conf} addchg ${SYSCONFDIR}/openafs/{BosConfig,server/{CellServDB,ThisCell,UserList,krb.conf}} addchg "+${SYSCONFDIR}/openafs/server/KeyFile" addchg /var/openafs/NetInfo addchg "+/var/openafs/sysid" addchg "+/var/openafs/db/bdb.DB0" addchg "+/var/openafs/db/bdb.DBSYS1" addchg "+/var/openafs/db/prdb.DB0" addchg "+/var/openafs/db/prdb.DBSYS1" addchg "+/var/openafs/db/vldb.DB0" addchg "+/var/openafs/db/vldb.DBSYS1" # Pat on the back. cat <<__EOT CONGRATULATIONS! Your OpenAFS server setup has been successfully completed, and is now running. Please read ${TRUEPREFIX}/share/openafs/README.OpenBSD for further details; be sure to note the startup and shutdown script examples. __EOT