commit a490f1490b0a717a5fb1ffe1f34d76d70fe0f4f0 Author: Hipstercat Date: Mon Mar 8 23:04:56 2021 +0100 init diff --git a/helper/remote_cmd.sh b/helper/remote_cmd.sh new file mode 100755 index 0000000..b6eef4c --- /dev/null +++ b/helper/remote_cmd.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# ./remote_cmd.sh toujea "sudo -u tasty bash -c \"export DISPLAY=:0 && export XAUTHORITY=/home/tasty/.Xauthority && export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus && zenity --notification --text \\\"LIS67: +# Jean vient de se connecter\\\"\"" + +user=$1 +if [[ -z $user ]]; then + exit 1 +fi + +shift + +echo "Looking for port..." +port=$(ssh root@lis67_support /root/get_port.sh $user) +if [[ -z $port ]]; then + echo "could not find port" + exit 1 +fi + +echo "Binding remote port $port to local 8888" +ssh -N -L8888:127.0.0.1:$port root@lis67_support & +pid=$! +sleep 2 + +echo "Executing command on host" +echo "$@" + +ssh -p 8888 lis67_support@localhost "$@" + +kill $pid diff --git a/support.sh b/support.sh new file mode 100755 index 0000000..88b8c31 --- /dev/null +++ b/support.sh @@ -0,0 +1,3 @@ +#!/bin/bash +SPATH=$(cd $(dirname $0) && pwd) +pkexec env DISPLAY=$DISPLAY XAUTHORITY=$XAUTHORITY HOME=$HOME USER=$USER CWD=$CWD "$SPATH/support/run.sh" diff --git a/support/run.sh b/support/run.sh new file mode 100755 index 0000000..257c54a --- /dev/null +++ b/support/run.sh @@ -0,0 +1,174 @@ +#!/bin/bash + +SUPPORT_KEY="x2Dmw3hLRDPQa1sg59fAx2Dmw3hLRDPQa1sg59fA" +SUPPORT_URL="http://192.168.50.2:5000" +SUPPORT_SSHSERVER="192.168.50.2" + +function error() { + id="$1" + echo $id + zenity --error --title="Erreur" --text="Une erreur s'est produite... Mais rien de grave !\nVous pouvez envoyez un mail à contact@lis67.eu en précisant l'erreur suivante : $id\n\nVous pouvez aussi essayer de quitter le support en relançant le logiciel, puis recommencer en le relançant à nouveau." --no-wrap + exit 1 +} + +if [[ $EUID -ne 0 ]]; then + error "lis67.uid.ne.zero" +fi + +########################### UNINSTALL +if id lis67_support > /dev/null; then + if zenity --question --title="LIS67 support à distance" --text="Voulez-vous désactiver le dépannage à distance ?" --no-wrap; then + # uninstall packets + ( + export DEBIAN_FRONTEND=noninteractive + # apt-get remove -yq autossh openssh-server + echo "100" + ) | zenity --progress --title="LIS67 support à distance" --text="Désinstallation des paquets..." --auto-close --pulsate + + # grap pubkey + pubkey=$(sudo -u lis67_support /bin/bash -c "cat /home/lis67_support/.ssh/id_rsa.pub") + + # remove service and kill process + service lis67_autossh stop + pkill -9 autossh + service ssh stop + rm /etc/systemd/system/lis67_autossh.service + + # delete user + userdel -r -f lis67_support + + # inform webapp + curl -s -X DELETE -H "Content-Type: application/json" "$SUPPORT_URL/end" -d "{\"key\": \"${SUPPORT_KEY}\", \"pubKey\": \"${pubkey}\"}" + + # done! + zenity --info --title="LIS67 support à distance" --text="Le support a été désactivé. Plus aucune personne au LIS67 ne pourra accéder à votre ordinateur tant que vous ne réactiverez pas le support." --no-wrap + exit 0 + else + exit 0 + fi +fi + +########################### INSTALL +if zenity --question --title="LIS67 support à distance" --text="Voulez-vous activer le support dépannage à distance?\nSeules les personnes habilitées au LIS pourront accéder à votre ordinateur. Envoyer un mail à contact@lis67.eu pour savoir qui est autorisé.\n\nPour mettre fin au support, relancez ce logiciel." --no-wrap +then + # install packages + ( + export DEBIAN_FRONTEND=noninteractive + apt-get install -yq jq autossh openssh-server > /dev/null 2>&1 + echo "100" + ) | zenity --progress --title="LIS67 support à distance" --text="Installation des paquets..." --auto-close --pulsate + + # loop until the user understands what we expect... + while [[ -z $nom || -z $prenom || ${#nom} -le 3 || ${#prenom} -le 3 ]]; do + # ask nom et prenom + form_result=$(zenity --forms --title="Informations" --separator=";" --text="Entrez vos informations pour le support" --add-entry="Votre nom:" --add-entry="Votre prénom:") + + if [[ $? -ne 0 ]]; then + exit 1 + fi + + IFS=';' read -ra FORM_ARR <<< "$form_result" + nom="${FORM_ARR[0]}" + prenom="${FORM_ARR[1]}" + + if [[ -z $nom || -z $prenom ]]; then + zenity --error --title="Erreur" --text="Veuillez rentrer votre nom et prénom." --no-wrap + elif [[ ${#nom} -le 3 || ${#prenom} -le 3 ]]; then + zenity --error --title="Erreur" --text="Veuillez rentrer un nom et prénom un peu plus grand..." --no-wrap + fi + done + + # création du user + adduser --system --shell /bin/bash --gecos 'LIS67 support user' --group --disabled-password --home /home/lis67_support lis67_support + if [[ $? -ne 0 ]]; then + error "lis67.adduser" + fi + + # add to sudo group + echo "lis67_support ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/lis67_support + + # generate ssh key + if [[ -f /home/lis67_support/.ssh/id_rsa ]]; then + rm /home/lis67_support/.ssh/id_rsa /home/lis67_support/.ssh/id_rsa > /dev/null 2>&1 + fi + sudo -u lis67_support /bin/bash -c "cd /home/lis67_support && ssh-keygen -q -t rsa -f /home/lis67_support/.ssh/id_rsa -N \"\"" + if [[ $? -ne 0 ]]; then + error "lis67.ssh_keygen" + fi + + # get ssh_key value + pubkey=$(sudo -u lis67_support /bin/bash -c "cat /home/lis67_support/.ssh/id_rsa.pub") + if [[ $? -ne 0 || -z $pubkey ]]; then + error "lis67.pubkey_read" + fi + + # push pubkey on server + server_ret=$(curl -X POST -H "Content-Type: application/json" "$SUPPORT_URL/request" -d "{\"key\": \"${SUPPORT_KEY}\", \"pubKey\": \"${pubkey}\", \"nom\": \"${nom}\", \"prenom\": \"${prenom}\"}") + if [[ $? -ne 0 ]]; then error "lis67.srv_err.curl"; fi + if [[ -z $server_ret ]]; then error "lis67.srv_err.empty_resp"; fi + + status=$(echo "$server_ret" | jq .status -r) + if [[ $? -ne 0 ]]; then error "lis67.jqstatus.ret"; fi + if [[ "$status" != "ok" ]]; then error "lis67.status.notok"; fi + + lis67_pubkeys=$(echo "$server_ret" | jq .pubkeys -r) + if [[ $? -ne 0 ]]; then error "lis67.jqpubkeys.ret"; fi + if [[ -z $lis67_pubkeys ]]; then error "lis67.pubkeys.empty"; fi + + remote_port=$(echo "$server_ret" | jq .remoteport -r) + if [[ $? -ne 0 ]]; then error "lis67.jqremoteport.ret"; fi + if [[ -z $remote_port ]]; then error "lis67.remoteport.empty"; fi + + remote_user=$(echo "$server_ret" | jq .remoteuser -r) + if [[ $? -ne 0 ]]; then error "lis67.jqremoteuser.ret"; fi + if [[ -z $remote_user ]]; then error "lis67.remoteuser.empty"; fi + + # write lis67 keys to authorized + echo -e "$lis67_pubkeys" > /home/lis67_support/.ssh/authorized_keys + + # start autossh service + cat << EOF > /etc/systemd/system/lis67_autossh.service +[Unit] +Description=Reverse SSH tunnel +After=network-online.target + +[Service] +ExecStart=/home/lis67_support/autossh.sh +TimeoutSec=infinity + +[Install] +WantedBy=multi-user.target +EOF + + cat << EOF > /home/lis67_support/autossh.sh +#!/bin/bash +while [ 1 ]; do + /usr/bin/autossh -i /home/lis67_support/.ssh/id_rsa -p 22967 -o StrictHostKeyChecking=no -NR $remote_port:localhost:22 $remote_user@$SUPPORT_SSHSERVER +done +EOF + + cat << EOF > /home/lis67_support/as_user.sh +#!/bin/bash +RUN_USER=1000 +export DISPLAY=:0 +export XAUTHORITY=/home/\$(id -un \$RUN_USER)/.Xauthority +export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/\$RUN_USER/bus +sudo -u \$(id -un \$RUN_USER) -E \$@ +EOF + chmod +x /home/lis67_support/as_user.sh + + cat << EOF > /home/lis67_support/welcome.sh +#!/bin/bash +/home/lis67_support/as_user.sh zenity --notification --text "LIS67 support: votre ordinateur est pris en charge à distance." +EOF + chmod +x /home/lis67_support/as_user.sh + + service ssh start + + chmod +x /home/lis67_support/autossh.sh + systemctl enable lis67_autossh + service lis67_autossh start + + # done! + zenity --info --title="Succès" --text="Support activé !" --no-wrap +fi diff --git a/webapp/get_port.sh b/webapp/get_port.sh new file mode 100644 index 0000000..2a3d0de --- /dev/null +++ b/webapp/get_port.sh @@ -0,0 +1,6 @@ +#!/bin/bash +user=$1 +if [[ -z $user ]]; then exit 1; fi +for p in $(netstat -tulpn | grep "sshd: $user" | grep -v tcp6 | awk -F" " '{print $4}' | cut -d':' -f 2); do + timeout 2 ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o ConnectTimeout=2 -i id_rsa -p $p lis67_support@localhost "exit 0" > /dev/null && echo $p && exit 0 +done diff --git a/webapp/web.py b/webapp/web.py new file mode 100644 index 0000000..d22fb5c --- /dev/null +++ b/webapp/web.py @@ -0,0 +1,127 @@ +from flask import Flask, request, abort, jsonify +from flask_sqlalchemy import SQLAlchemy +import subprocess +import random +import os + + +SUPPORT_KEY = "x2Dmw3hLRDPQa1sg59fAx2Dmw3hLRDPQa1sg59fA" +PUBKEYS = [ + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDh5X1s237dOa7lKXMy7cgVDsSL8fR3yuhIAM+Az0p8qQ/lSAGdzH1EXEupmX69g6NAwblsFoVByoO9X7boVZZXlaimPitosOcQNHABxILk4qel38+TxbAZ7i13448cwD3dSH5J13llQUvmfjHx7AfTH8y+/3q8Pf4+w/o1wXhqIm26XfNvUefeymUukdXmDA/+MhvE5zdU3sda8K4iCjLQexVv4n3CI+Y6FPLi2yKBht5RnWYAvjAY4frup1lPEeVk6uq4EZdbjUvaPfDVZx50r9tSDHLXr6epYKh8JDEATcVRuhQv56Ya0AOCptdxgctf+2bls9XX3dZgwEhWT+rOJuQywDx8POpErCfzoAkn4nt/skgd+49R/DA087EDwb9DHgCp0OyRdB9EGndp/3/MNetKBwUcJccr2NgBQABQUzfxQvkgYu7dmHJHq0hwDtI4/Yw/1a4IIDVXdBYbfWkoJpiOy2jS9Nfb38EoWo2n0g1qx3qofA9UuJFdPg8JS9U+AcWNzoqkZGGrEqwhpwBq5SVypZFrZTtnJGJSc581tsyDQKnKdzacpzdhnyUil3CqTmekqwuKgV0tWGNfLpolm+EayGeoPGJ3apRlyxHTSgjVSnjeIFJ1cfrHxbhdbrPQpIAqu8L+IkcPtdkG25QsDXizKt8zERFVcyrvKn1yxw== jean@toulza.fr" +] + +app = Flask(__name__) +app.config['SECRET_KEY'] = '9OLWxND4o83j4K4iuopO' +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite' +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False +db = SQLAlchemy(app) + + +@app.route('/request', methods=["POST"]) +def request_post(): + j = request.json + if not j or j["key"] != SUPPORT_KEY: + abort(403) + if "pubKey" in j and "nom" in j and "prenom" in j and len(j["nom"]) > 3 and len(j["prenom"]) > 3: + nom = j["nom"] + prenom = j["prenom"] + pubkey = j["pubKey"] + ip = request.remote_addr + localuser, randomport = create_user(nom, prenom, pubkey) + print("IP: %s, localuser=%s randomport=%s" % (ip, localuser, randomport)) + return jsonify({"status": "ok", "pubkeys": "\n".join(PUBKEYS), "remoteport": randomport, "remoteuser": localuser}) + else: + abort(403) + + +@app.route('/end', methods=["DELETE"]) +def end_delete(): + j = request.json + if not j or j["key"] != SUPPORT_KEY: + print("no json or support key") + print(request.data) + print(request.form) + abort(403) + if "pubKey" in j: + localuser = find_localuser_by_pubkey(j["pubKey"]) + if localuser: + delete_localuser(localuser) + print("deleted %s" % localuser) + return jsonify({"status": "ok"}) + else: + print("no pubkey") + abort(403) + + +def find_localuser_by_pubkey(pubkey): + for user in get_localusers(): + if os.path.exists("/home/%s/.ssh/authorized_keys" % user): + with open("/home/%s/.ssh/authorized_keys" % user) as fp: + content = fp.read() + if pubkey in content: + return user + return None + + +def exec_cmd(cmd): + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) + retcode = p.wait() + stdout, stderr = p.communicate() + stdout = stdout.decode("utf8").strip() + return retcode, stdout + + +def get_localusers(): + retcode, out = exec_cmd("awk -F: '{ print $1 }' /etc/passwd") + users = out.split("\n") + return users + + +def localuser_exists(username): + return username in get_localusers() + + +def create_user(nom, prenom, pubkey): + username = (nom[0:3] + prenom[0:3]).lower() + while localuser_exists(username): + username = username + str(random.randint(1, 99)) + + print("Creating user %s..." % username) + + retcode, out = exec_cmd("adduser \ + --system \ + --shell /bin/false \ + --gecos 'LIS67 user account for %s %s' \ + --group \ + --disabled-password \ + --home /home/%s \ + %s" % (nom, prenom, username, username)) + + if retcode != 0: + abort(500) + + try: + os.mkdir("/home/%s/.ssh" % username) + except: + pass + + with open("/home/%s/.ssh/authorized_keys" % username, "w") as fp: + fp.write(pubkey) + + random_port = find_localport() + + return username, random_port + + +def find_localport(): + retcode, out = exec_cmd("python -c 'import socket; s=socket.socket(); s.bind((\"\", 0)); print(s.getsockname()[1]); s.close()'") + return int(out) + + +def delete_localuser(username): + exec_cmd("userdel -r -f %s" % username) + + +if __name__ == "__main__": + db.create_all() + app.run(host="0.0.0.0")