commit a83a99dc1df71ebc842d55695fa644a7078c8a5c Author: Sebastian Hugentobler Date: Thu Jun 22 09:45:46 2023 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..765efc1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*~ +.DS_Store +*.pkg.tar.zst +*.pkg.tar.zst.sig diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 0000000..b09f7bb --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,34 @@ +# Maintainer: Sebastian Hugentobler + +pkgname=fhnw-tools +pkgver=0.1.0 +pkgrel=2 +pkgdesc="Various tools to work with the FHNW infrastructure." +arch=("any") +url="https://code.vanwa.ch" +license=('MIT') +depends=("python-ocma" "openconnect" "pass" "sudo" "rsync" "rclone" "vpnc") +source=( + "fhnw-vpn" + "fhnw-ad-mount" + "fhnw-ad-sync" + "fhnw-teams-sync" + "fhnw-sync" +) +sha256sums=( + "fdbdab46cef6ca1413a4e870d4530210ac31edb231336a8a3f2b8f668bcdbce9" + "8e904a89afc434df3ce7538437aec237b7b3c08f5494246e849dc28e710723b7" + "b8cbf2c97bf2c55a91d35bf57c5f9f7a9dd7e63d710d6a5c8a519273a15927f3" + "86e25ae0c820f05e1b3335e593e9dc5f2f77f04f1dbbe534a5e2a8f1c19aa71d" + "983c922d2dc261b6c77e96c0d67c00fa4d24504ad0b0ef1a7fe9d762da9b7b8d" +) + +package() { + cd "$srcdir/" + + install -Dm 755 fhnw-vpn "$pkgdir/usr/bin/fhnw-vpn" + install -Dm 755 fhnw-ad-mount "$pkgdir/usr/bin/fhnw-ad-mount" + install -Dm 755 fhnw-ad-sync "$pkgdir/usr/bin/fhnw-ad-sync" + install -Dm 755 fhnw-teams-sync "$pkgdir/usr/bin/fhnw-teams-sync" + install -Dm 755 fhnw-sync "$pkgdir/usr/bin/fhnw-sync" +} diff --git a/fhnw-ad-mount b/fhnw-ad-mount new file mode 100755 index 0000000..ffb3529 --- /dev/null +++ b/fhnw-ad-mount @@ -0,0 +1,61 @@ +#!/usr/bin/env sh +set -o errexit + +export SUDO_PROMPT="sudo password: " + +mount_dir="/mnt/fhnw-share" +pass_path="accounts/fhnw/students.fhnw.ch" + +display_help() { + echo "Usage: $0 [option...] " >&2 + echo + echo " -m, ad mount directory [default: $mount_dir]" + echo " -p, path for pass to get user and password information [default: $pass_path]" + echo " -h, display this help and exit" + echo +} + +parse_args() { + while getopts ":hm:p:" opt; do + case $opt in + h) + display_help + exit 0 + ;; + m) + mount_dir=$OPTARG + ;; + p) + pass_path=$OPTARG + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + display_help + exit 1 + ;; + :) + echo "Option -$OPTARG requires an argument." >&2 + display_help + exit 1 + ;; + esac + done +} + +get_account_info() { + echo "getting account info from pass..." + acc_info="$(pass "$pass_path")" + acc_pw="$(echo "$acc_info" | head -n 1)" + acc_user="$(echo "$acc_info" | awk -F ': ' '/^login-shortcut:/ {print $2}')" +} + +mount_ad() { + echo "mounting ad..." + sudo mkdir -p "$mount_dir" + sudo mount -t cifs //fs.edu.ds.fhnw.ch/data "$mount_dir" \ + -o vers=3.0,username="$acc_user",password="$acc_pw",workgroup=EDU,iocharset=utf8,uid="$USER",gid="$USER" +} + +parse_args "$@" +get_account_info +mount_ad diff --git a/fhnw-ad-sync b/fhnw-ad-sync new file mode 100755 index 0000000..19a299e --- /dev/null +++ b/fhnw-ad-sync @@ -0,0 +1,81 @@ +#!/usr/bin/env sh +set -o errexit + +mount_dir="/mnt/fhnw-share" +dest_dir="$HOME/documents/fhnw/ad" +excluded_files=".DS_Store|Thumbs.db" +sync_dirs="" + +display_help() { + echo "Usage: $0 [option...] " >&2 + echo + echo " -m, mount directory for the ad (must already exist) [default: $mount_dir]" + echo " -d, root destination directory [default: $dest_dir]" + echo " -e, list of excluded files (pipe separated) [default: $excluded_files]" + echo " -s, list of ad directories, relative to the mount directory that will be synced (pipe separated)" + echo " -h, display this help and exit" + echo +} + +parse_args() { + while getopts ":hm:d:e:s:" opt; do + case $opt in + h) + display_help + exit 0 + ;; + m) + mount_dir=$OPTARG + ;; + d) + dest_dir=$OPTARG + ;; + e) + excluded_files=$OPTARG + ;; + s) + sync_dirs=$OPTARG + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + display_help + exit 1 + ;; + :) + echo "Option -$OPTARG requires an argument." >&2 + display_help + exit 1 + ;; + esac + done +} + +cleanup() { + rm "$exclude_file" +} +trap cleanup EXIT + +create_exclude_file() { + exclude_file="$(mktemp)" + + oldIFS=$IFS + IFS='|' + for ex in $excluded_files; do + echo "$ex" >>"$exclude_file" + done + IFS=$oldIFS +} + +sync_ad() { + echo "syncing ad..." + oldIFS=$IFS + IFS='|' + for s in $sync_dirs; do + rsync -avz --exclude-from "$exclude_file" --progress "$mount_dir/$s" "$dest_dir" + done + IFS=$oldIFS +} + +parse_args "$@" +create_exclude_file +sync_ad diff --git a/fhnw-sync b/fhnw-sync new file mode 100755 index 0000000..cf2a6ca --- /dev/null +++ b/fhnw-sync @@ -0,0 +1,132 @@ +#!/usr/bin/env sh +set -o errexit + +: "${XDG_CONFIG_HOME:="$HOME/.config"}" + +config_file="$XDG_CONFIG_HOME/fhnw-sync/config" +config_read=false + +set_defaults() { + ad_mount_dir="/mnt/fhnw-share" + ad_dest_dir="$HOME/documents/fhnw/ad" + ad_excluded_files=".DS_Store|Thumbs.db" + ad_sources="" + teams_dest_dir="$HOME/documents/fhnw/teams" + teams_sources="" + pass_path="accounts/fhnw/students.fhnw.ch" +} + +read_config() { + if [ -f "$config_file" ]; then + # shellcheck disable=1090 + . "$config_file" + fi +} + +display_help() { + set_defaults + echo "Usage: $0 [option...] " >&2 + echo + echo " -c, config file [default: $config_file]" + echo " -m, mount directory for the ad [default: $ad_mount_dir]" + echo " -d, root destination directory [default: $ad_dest_dir]" + echo " -e, list of excluded files in ad sync (pipe separated) [default: $ad_excluded_files]" + echo " -s, list of ad directories, relative to the mount directory that will be synced (pipe separated)" + echo " -r, root destination directory for teams sync [default: $teams_dest_dir]" + echo " -t, list of configured rclone teams sources (pipe separated)" + echo " -p, path for pass to get user and password information [default: $pass_path]" + echo " -h, display this help and exit" + echo + echo "If a config file exists, it is sourced by this script. The following keys are recognized:" + echo "- ad_mount_dir -> m" + echo "- ad_dest_dir -> d" + echo "- ad_excluded_files -> e" + echo "- ad_sources -> s" + echo "- teams_dest_dir -> r" + echo "- teams_sources -> t" + echo "- pass_path -> p" + echo + echo "Values from a config file get overwriten by cli args." + echo +} + +parse_args() { + while getopts ":hc:m:d:e:s:r:t:p:" opt; do + case $opt in + h) + display_help + exit 0 + ;; + c) + if [ $config_read = false ]; then + config_file=$OPTARG + read_config + config_read=true + parse_args "$@" + fi + ;; + m) + ad_mount_dir=$OPTARG + ;; + d) + ad_dest_dir=$OPTARG + ;; + e) + ad_excluded_files=$OPTARG + ;; + s) + ad_sources=$OPTARG + ;; + r) + teams_dest_dir=$OPTARG + ;; + t) + teams_sources=$OPTARG + ;; + p) + pass_path=$OPTARG + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + display_help + exit 1 + ;; + :) + echo "Option -$OPTARG requires an argument." >&2 + display_help + exit 1 + ;; + esac + done +} + +cleanup() { + if mountpoint -q "$ad_mount_dir"; then + echo "unmounting ad share..." + sudo umount --quiet "$ad_mount_dir" || true + fi + + if [ -f "$vpn_pid_file" ]; then + echo "stopping vpn..." + vpn_pid="$(sudo cat "$vpn_pid_file")" + sudo kill "$vpn_pid" + while ps -p "$vpn_pid" >/dev/null; do + sleep 1 + done + fi +} +trap cleanup 0 HUP INT QUIT ABRT TERM + +start_vpn() { + echo "starting vpn..." + vpn_pid_file="$(sudo mktemp)" + fhnw-vpn -m "bg" -t "$vpn_pid_file" -p "$pass_path" +} + +set_defaults +read_config +parse_args "$@" +start_vpn +fhnw-ad-mount -m "$ad_mount_dir" -p "$pass_path" +fhnw-ad-sync -m "$ad_mount_dir" -d "$ad_dest_dir" -e "$ad_excluded_files" -s "$ad_sources" +fhnw-teams-sync -d "$teams_dest_dir" -s "$teams_sources" diff --git a/fhnw-teams-sync b/fhnw-teams-sync new file mode 100755 index 0000000..16b76b2 --- /dev/null +++ b/fhnw-teams-sync @@ -0,0 +1,55 @@ +#!/usr/bin/env sh +set -o errexit + +dest_dir="$HOME/documents/fhnw/teams" +sources="" + +display_help() { + echo "Usage: $0 [option...] " >&2 + echo + echo " -d, root destination directory [default: $dest_dir]" + echo " -s, list of configured rclone teams sources (pipe separated)" + echo " -h, display this help and exit" + echo +} + +parse_args() { + while getopts ":hd:s:" opt; do + case $opt in + h) + display_help + exit 0 + ;; + d) + dest_dir=$OPTARG + ;; + s) + sources=$OPTARG + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + display_help + exit 1 + ;; + :) + echo "Option -$OPTARG requires an argument." >&2 + display_help + exit 1 + ;; + esac + done +} + +sync_teams() { + echo "syncing teams..." + oldIFS=$IFS + IFS='|' + for s in $sources; do + rclone sync --progress "$s:" "$dest_dir/$s" + done + IFS=$oldIFS + +} + +parse_args "$@" +sync_teams diff --git a/fhnw-vpn b/fhnw-vpn new file mode 100755 index 0000000..3f32622 --- /dev/null +++ b/fhnw-vpn @@ -0,0 +1,80 @@ +#!/usr/bin/env sh +set -o errexit + +mode="fg" +pid_path="" +pass_path="accounts/fhnw/students.fhnw.ch" +op_args="" + +display_help() { + echo "Usage: $0 [option...] " >&2 + echo + echo " -m, vpn mode (one of \"fg\", \"bg\") [default: $mode]" + echo " -t, path to pid file if run in \"bg\" mode [default: mktemp]" + echo " -p, path for pass to get user and password information [default: $pass_path]" + echo " -h, display this help and exit" + echo +} + +parse_args() { + while getopts ":hm:t:p:" opt; do + case $opt in + h) + display_help + exit 0 + ;; + m) + mode=$OPTARG + ;; + t) + pid_path=$OPTARG + ;; + p) + pass_path=$OPTARG + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + display_help + exit 1 + ;; + :) + echo "Option -$OPTARG requires an argument." >&2 + display_help + exit 1 + ;; + esac + done +} + +get_account_info() { + echo "getting password and token from pass..." + acc_info="$(pass "$pass_path")" + acc_pw="$(echo "$acc_info" | head -n 1)" + acc_user="$(echo "$acc_info" | awk -F ': ' '/^login:/ {print $2}')" + acc_token="$(echo "$acc_info" | awk -F ': ' '/^otp-secret:/ {print $2}')" +} + +connect_vpn() { + if [ "$mode" != "fg" ]; then + if [ -z "$pid_path" ]; then + pid_file="$(SUDO_PROMPT="sudo pw for pid: " sudo mktemp)" + else + pid_file="$pid_path" + fi + echo "vpn-pid: $pid_file" + op_args=" --pid-file=$pid_file --background" + fi + + echo "getting vpn cookie..." + eval "$(ocma -v -u "$acc_user" -p "$acc_pw" -m "$acc_token" --print-to-stdout)" + + op_args="$op_args --cookie=$VPN_COOKIE" + cmd="openconnect$op_args $VPN_HOST" + + # shellcheck disable=2086 + SUDO_PROMPT="sudo pw for vpn connection: " sudo $cmd +} + +parse_args "$@" +get_account_info +connect_vpn