#!/bin/sh # Hitron remote support tunnel. Lets Hitron support reach this device's console for up to 12 hours. # Everything lives in /tmp/hwts-tunnel and is removed when the session ends. Stop it at any time: # sh /tmp/hwts-tunnel/stop HOST=tunnel.hitrontools.com API=https://$HOST D=/tmp/hwts-tunnel die() { echo "hwts-tunnel: $*" >&2; exit 1; } [ "$(id -u)" = 0 ] || die "run this as root" command -v curl >/dev/null 2>&1 || die "curl not found" [ -f "$D/stop" ] && sh "$D/stop" >/dev/null 2>&1 umask 077 rm -rf "$D" mkdir -p "$D/.ssh" || die "cannot create $D" if command -v dropbearkey >/dev/null 2>&1 && command -v dbclient >/dev/null 2>&1; then dropbearkey -t ed25519 -f "$D/key" >/dev/null 2>&1 || die "could not generate a key" PUB=$(dropbearkey -y -f "$D/key" 2>/dev/null | grep '^ssh-' | cut -d' ' -f1,2) CLIENT=dbclient elif command -v ssh-keygen >/dev/null 2>&1 && command -v ssh >/dev/null 2>&1; then ssh-keygen -q -t ed25519 -N '' -f "$D/key" || die "could not generate a key" PUB=$(cut -d' ' -f1,2 "$D/key.pub") CLIENT=ssh else die "no SSH client on this device" fi [ -n "$PUB" ] || die "could not read the generated key" # What support connects to: telnet if it is listening, otherwise SSH. LAN=$(ip -4 -o addr show br-lan 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -1) [ -n "$LAN" ] || LAN=$(uci -q get network.lan.ipaddr 2>/dev/null) [ -n "$LAN" ] || LAN=127.0.0.1 LISTENING=$(netstat -ltn 2>/dev/null) if echo "$LISTENING" | grep -qE '[:.]23[[:space:]]'; then SERVICE=telnet; TARGET=23 elif echo "$LISTENING" | grep -qE '[:.]22[[:space:]]'; then SERVICE=ssh; TARGET=22 else die "neither telnet nor SSH is running on this device; contact Hitron support" fi # Only what identifies the device to support. /etc/issue can hold more than the version, so take that one line only. safe() { printf '%s' "$1" | tr -cd 'A-Za-z0-9 ._:/+()-' | cut -c1-60; } NAME=$(safe "$(cat /proc/sys/kernel/hostname 2>/dev/null)") MODEL=$(safe "$(cat /tmp/sysinfo/model 2>/dev/null)") FW=$(safe "$(sed -n 's/^.*Software Version[^:]*:[[:space:]]*//p' /etc/issue 2>/dev/null | head -1)") [ -n "$FW" ] || FW=$(safe "$(cat /etc/openwrt_version 2>/dev/null)") MAC=$(safe "$(cat /sys/class/net/br-lan/address 2>/dev/null)") R=$(curl -fsS -m 30 -H 'Content-Type: application/json' \ -d "{\"pubkey\":\"$PUB\",\"service\":\"$SERVICE\",\"host\":\"$NAME\",\"model\":\"$MODEL\",\"fw\":\"$FW\",\"mac\":\"$MAC\"}" \ "$API/api/open") || { rm -rf "$D"; die "could not register with $HOST (check the internet connection and the device clock)"; } val() { printf '%s\n' "$R" | sed -n "s/^$1=//p" | head -1; } ID=$(val ID); TOKEN=$(val TOKEN); PORT=$(val PORT); TTL=$(val TTL); TUSER=$(val USER) [ -n "$ID" ] && [ -n "$TOKEN" ] && [ -n "$PORT" ] && [ -n "$TTL" ] && [ -n "$TUSER" ] || { rm -rf "$D"; die "unexpected reply from $HOST"; } cat > "$D/.ssh/known_hosts" <<'KNOWN_HOSTS' tunnel.hitrontools.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG/L3RD/GYBuX/sF869t+vVHf1jQjIz3Ss6Lmo1BxKZH tunnel.hitrontools.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDaHB3QRSZ8haErAamPPlVyoRIi2IIyD8QBIjnCZCno3ld8sAGPe6YLUikmv4QQDF5pvg91KxQFU/sRin2eBDn7OjJZCiZdZqH7O0dwcXAr887l/1Fk9uW/V1zqXrkoHgG1j0zG7d5o99xHPPlAK9hbJJKvUjFtJGivkuFBPls4MAdefz0F+st/yhB5bS5nNgycdfzcaGgpxscHT/n9WPQRvADCXBsqU6p8AO4TKoUHq2s6PvavcTbhlVO66dk0feL8N2Vcsbqmga+ToqEi/BzOwweAKMMtuxJpP2J4HeW97OjgAltqhfSf+7pESyVUFxDqMA3KYE5uCwhwe1FLWR7sdPxkNx2dAyEgrm8+qZKRz7bv9ybo8r46DO9b9JpCbzdCD2wTBJ7qWuw/ULCvEVoSSEUochbmLvczp2j/XNGVyUI8zvVvv8GHbaxxYdyf24Z9Xz4qjXfLj3dlsy0phD4Xan+by4DTQtxUXCpjKq4J5m/8XXsTmpPrJOt+poxmQd8= KNOWN_HOSTS if [ "$CLIENT" = dbclient ]; then FORWARD="HOME=$D dbclient -N -T -K 30 -i $D/key -R 127.0.0.1:$PORT:$LAN:$TARGET $TUSER@$HOST" else FORWARD="ssh -N -T -i $D/key -o UserKnownHostsFile=$D/.ssh/known_hosts -o StrictHostKeyChecking=yes -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 -R 127.0.0.1:$PORT:$LAN:$TARGET $TUSER@$HOST" fi # The background loop keeps the forward up. It uses uptime, not the clock, because device clocks can be wrong. cat > "$D/run" </dev/null) [ "\$S" = closed ] && break if [ -z "\$PID" ] || ! kill -0 "\$PID" 2>/dev/null; then $FORWARD >$D/log 2>&1 & PID=\$! echo "\$PID" > $D/forward.pid fi sleep 30 done [ -n "\$PID" ] && kill "\$PID" 2>/dev/null curl -fsS -m 15 -X POST "$API/api/close/$ID?t=$TOKEN" >/dev/null 2>&1 rm -rf $D EOF cat > "$D/stop" </dev/null [ -f $D/forward.pid ] && kill "\$(cat $D/forward.pid)" 2>/dev/null curl -fsS -m 15 -X POST "$API/api/close/$ID?t=$TOKEN" >/dev/null 2>&1 rm -rf $D echo "Hitron support session $ID stopped." EOF if command -v setsid >/dev/null 2>&1; then setsid nohup sh "$D/run" >/dev/null 2>&1 /dev/null 2>&1 "$D/run.pid" STATE=waiting i=0 while [ "$i" -lt 8 ]; do sleep 2 STATE=$(curl -fsS -m 10 "$API/api/status/$ID?t=$TOKEN" 2>/dev/null) [ "$STATE" = up ] && break i=$((i + 1)) done echo echo " Hitron support session: $ID" echo if [ "$STATE" = up ]; then echo " Connected. Give this ID to Hitron support." else echo " Registered, but not connected yet. It keeps trying; give this ID to Hitron support." [ -s "$D/log" ] && tail -2 "$D/log" fi echo " The session ends by itself after 12 hours. To stop it now: sh $D/stop" echo