diff --git a/utils/README.md b/utils/README.md new file mode 100644 index 00000000..8ac23c5e --- /dev/null +++ b/utils/README.md @@ -0,0 +1 @@ +`users_sync.sh` has been created to automatically give members of the Armbian organization access to https://users.armbian.com (WIP) diff --git a/utils/users_sync.sh b/utils/users_sync.sh new file mode 100644 index 00000000..16885a76 --- /dev/null +++ b/utils/users_sync.sh @@ -0,0 +1,173 @@ +#!/bin/bash + + +### TODO +# add blocklist/keeplist +# add update/refresh keys on existing user +# make sure concealed members are grabbed as well: https://docs.github.com/en/rest/orgs/members?apiVersion=2022-11-28#list-organization-members + + +### CONFIG +# where are the user directories? +# NO trailing slash! +# the owner of the parent directory must be "root"! +# configure nginx accordingly +USERPATH=/armbianusers + +# which group is used to catch and jail users into their sftp chroot? +SFTPGROUP=sftponly + +# classic token from any organization member with "read:org" permission +TOKEN=xxx + +# the organization you want to read members from +ORG=armbian + +# Users that shall not get access +BLOCKLIST='armbianworker|examplemember1|examplemember2' + +# enable debugging +DEBUG=yes + +### END CONFIG + + +### DO NOT EDIT BELOW! ### + + +### CHECKS +# Check if curl is installed +command -v curl >/dev/null 2>&1 || echo >&2 "\"curl\" not found. Aborting." + +# Check if jq is installed +command -v jq >/dev/null 2>&1 || echo >&2 "\"jq\" not found. Aborting." + + +# validate token +RESPONSE=$(curl -sS -f -I -H "Authorization: token $TOKEN" https://api.github.com | grep -i x-oauth-scopes |grep -c read:org) +if [[ $RESPONSE != 1 ]]; then + echo "Token invalid or lacking permission." + echo "Exiting..." + exit 1 +fi + +# check for root +if [ "$EUID" -ne 0 ]; then + echo "Please run as root" + echo "Exiting..." + exit 1 +fi + +# check for existing sftp group +if ! getent group sftponly &> /dev/null +then + echo "\"$SFTPGROUP\" group does not exist. Use \"groupadd $SFTPGROUP\" to add." + echo "" + echo "Add this to your \"sshd_config\" if not done already." + echo "" + echo "Match Group $SFTPGROUP" + echo " ChrootDirectory $USERPATH" + echo " ForceCommand internal-sftp" + echo " AllowTcpForwarding no" + echo "" + echo "Exiting..." + exit 1 +fi +### END CHECKS + + +### FUNCTIONS + +grab_keys() { +# $1 = username + echo "Trying to grab ssh keys for $1" + mkdir -p "$USERPATH"/"$1"/.ssh + curl -s https://github.com/"$1".keys > "$USERPATH"/"$1"/.ssh/authorized_keys + chown -R "$1":"$SFTPGROUP" "$USERPATH"/"$1"/.ssh + chmod 700 "$USERPATH"/"$1"/.ssh + chmod 600 "$USERPATH"/"$1"/.ssh/authorized_keys + + # Check if grabbed stuff are actual ssh keys. + # curl response for members w/o keys is "not found" but exit code is still 0 + # so this needs to be worked around + CHECK_KEYS=$(grep -c -E "^ssh" "$USERPATH"/"$1"/.ssh/authorized_keys) + if [[ $CHECK_KEYS != 0 ]]; then + echo "$i - $CHECK_KEYS key/s for $1 imported" + else + echo "(!) $1 - Either grabbing failed or $i does not have ssh key on git" + echo "(!) $1 won't be able to login" + rm "$USERPATH"/"$1"/.ssh/authorized_keys + fi +} + + +# grab a list of current remote org members, filter blocked ones +echo "Grabbing a list of all current members of \"$ORG\"." +echo "Excluded by blocklist are \"$BLOCKLIST\"." +ORGMEMBERS=$(curl -L -s \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $TOKEN" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/orgs/$ORG/members | jq -r ".[].login" \ + | grep -v -E -- "$BLOCKLIST" ) +if [ $DEBUG == "yes" ]; then echo -e "DEBUG: \$ORGMEMBERS:\n$ORGMEMBERS\n\n"; fi + +# Grab a list of local directories... +# We assume that existing directory means locally existing user as well +cd $USERPATH || exit 1 +LOCALMEMBERS=$(echo -n "`ls -d -- */`" | sed 's/\///g' | tr '\n' ' ') +echo "Already existing members at \"$USERPATH\": \"$LOCALMEMBERS\"." +# ...and make it comparable for shell (remove trailing slash, replace newline with | and add round brackets) +LOCALMEMBERS_COMPARE=$(echo -n "`ls -d -- */`" | sed 's/\///g' | tr '\n' '|' | sed -r 's/^/\(/' | sed -r 's/$/\)/') +if [ $DEBUG == "yes" ]; then echo -e "DEBUG: \$LOCALMEMBERS_COMPARE:\n$LOCALMEMBERS_COMPARE\n\n"; fi + +# loop through remote org members and add if not existing +for i in $ORGMEMBERS; do + + if ! [[ $i =~ $LOCALMEMBERS_COMPARE ]]; then # skip locally already existing users + + # create local user and directory + echo "$i - no local directory found. Creating..." + if ! useradd -m -s /bin/false -G "$SFTPGROUP" -d "$USERPATH"/"$i" "$i" + then + echo "$i's directory could not be created for whatever reason" + exit 1 + fi + echo "$i - user and directory created" + + # grab ssh keys and put into user's .ssh/authorized_keys file + grab_keys "$i" + + else + echo "$i - local directory found. Trying to update keys..." + grab_keys "$i" + + fi +done + +echo "" +echo "Removing no longer existing members of \"$ORG\"" +echo "" +### remove local users not exsting in remote org +# make list of remote organization members comparable +ORGMEMBERS_COMPARE=$(echo "$ORGMEMBERS" | tr '\n' ' ' | sed 's/\ /\|/g'| sed -r 's/^/\(/' | sed -r 's/\|$/\)/') +if [ $DEBUG == "yes" ]; then echo -e "\nDEBUG: \$ORGMEMBERS_COMPARE:\n$ORGMEMBERS_COMPARE\n\n"; fi +if [ $DEBUG == "yes" ]; then echo -e "\nDEBUG: \$LOCALMEMBERS:\n$LOCALMEMBERS\n\n"; fi +# loop through org members and compare against local list +for i in $LOCALMEMBERS; do + + if [[ $i =~ $ORGMEMBERS_COMPARE ]]; then # compare local user against list of remote org members. If not found carry on + echo "$i is still member of \"$ORG\". Skipping..." + else + echo -e "$i is not or no longer in the list of \"$ORG\" members or has been blocklisted.\nRemove the user and their files? (y/N)\nTHIS CANNOT BE UNDONE!\n" + read -n 1 INPUT + : "${INPUT:=n}" + if [ $INPUT == "y" ]; then + userdel --remove "$i" + else + echo "Keeping $i" + fi + fi +done + +