From 03f415499c37d621c6db2873014676a38b865119 Mon Sep 17 00:00:00 2001 From: Benjamin Dweck Date: Thu, 8 Oct 2020 14:30:00 +0200 Subject: [PATCH] Migrated over to dh_python and it builds! --- .gitignore | 1 + make-pkg.sh => build-deb.sh | 25 +++-- debian/README.Debian | 6 -- debian/compat | 2 + debian/control | 10 +- debian/copyright | 4 +- debian/postinst | 7 +- debian/postrm | 2 +- debian/preinst | 3 - debian/rules | 5 +- debian/source/include-binaries | 2 - debian/src/etc/torch/torch.conf | 19 ---- .../src/usr/share/torch-agent/torch-agent.py | 98 ------------------ debian/torch-agent.install | 3 +- example/torch-agent_0.0.1-1_all.deb | Bin 5192 -> 7200 bytes setup.py | 1 + torch-agent.service | 4 +- torch.conf | 3 +- 18 files changed, 41 insertions(+), 154 deletions(-) rename make-pkg.sh => build-deb.sh (62%) delete mode 100644 debian/README.Debian create mode 100644 debian/compat delete mode 100755 debian/source/include-binaries delete mode 100755 debian/src/etc/torch/torch.conf delete mode 100755 debian/src/usr/share/torch-agent/torch-agent.py diff --git a/.gitignore b/.gitignore index 324c2d5..82dc975 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .vagrant build +build_deb venv /dist/ /*.egg-info diff --git a/make-pkg.sh b/build-deb.sh similarity index 62% rename from make-pkg.sh rename to build-deb.sh index 6026f7a..b2df38a 100755 --- a/make-pkg.sh +++ b/build-deb.sh @@ -10,25 +10,32 @@ if [[ -z "${DEBEMAIL}" ]]; then exit 1 fi +BUILD_DIR=build_deb TORCH_VERSION=$(git describe --tags) DEBIAN_PKG=torch-agent-$TORCH_VERSION -PKG_ROOT=build/$DEBIAN_PKG -rf -rf $PKG_ROOT +rm -rf $BUILD_DIR +rm -rf dist -mkdir -p $PKG_ROOT -cp -r debian $PKG_ROOT +mkdir $BUILD_DIR + +python3 setup.py clean +python3 setup.py sdist +cp dist/$DEBIAN_PKG.tar.gz $BUILD_DIR/ +cd $BUILD_DIR +tar -xzmf $DEBIAN_PKG.tar.gz +cd .. + +PKG_ROOT=$BUILD_DIR/$DEBIAN_PKG mkdir -p $PKG_ROOT/src/etc/torch -cp torch.conf $PKG_ROOT/src/etc/torch/ - -mkdir -p $PKG_ROOT/src/usr/share/torch-agent -cp torch-agent.py $PKG_ROOT/src/usr/share/torch-agent/ +cp -r debian $PKG_ROOT/ cp torch-agent.service $PKG_ROOT/debian/ +cp torch.conf $PKG_ROOT/src/etc/torch/ cd $PKG_ROOT export USER=`whoami` dh_make --createorig -e $DEBEMAIL -s -y -dpkg-buildpackage -k$DEBEMAIL \ No newline at end of file +dpkg-buildpackage -k$DEBEMAIL diff --git a/debian/README.Debian b/debian/README.Debian deleted file mode 100644 index 699fb17..0000000 --- a/debian/README.Debian +++ /dev/null @@ -1,6 +0,0 @@ -torch-agent for Debian ---------------------- - - - - -- Benjamin Dweck Tue, 06 Oct 2020 15:53:02 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..9f1d48e --- /dev/null +++ b/debian/compat @@ -0,0 +1,2 @@ +11 + diff --git a/debian/control b/debian/control index 4052515..5ded10d 100644 --- a/debian/control +++ b/debian/control @@ -2,15 +2,17 @@ Source: torch-agent Section: net Priority: optional Maintainer: Benjamin Dweck -Build-Depends: debhelper-compat (= 12) +Build-Depends: debhelper (>=11~), dh-python, python3-all Standards-Version: 4.4.1 -Homepage: https://rudefox.io +Homepage: https://git.rudefox.io/bj/torch-agent +X-Python3-Version: >= 3.2 #Vcs-Browser: https://salsa.debian.org/debian/torch-agent #Vcs-Git: https://salsa.debian.org/debian/torch-agent.git Package: torch-agent Architecture: all -Depends: ssh, tor, python3-pip, ${misc:Depends} +Multi-Arch: foreign +Depends: ssh, tor, python3-pip, ${misc:Depends}, ${python3:Depends} Description: TORch is a solution for creating an SSH-via-Tor backdoor on a remote device as a means of fallback remote - management and initial headless device configuration. \ No newline at end of file + management and initial headless device configuration. diff --git a/debian/copyright b/debian/copyright index b1e631f..1a04b25 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,7 +1,7 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: torch-agent Upstream-Contact: bjdweck@gmail.com -Source: https://rudefox.io +Source: https://git.rudefox.io/bj/torch-agent Files: debian/* Copyright: 2020 Benjamin Dweck @@ -20,4 +20,4 @@ License: GPL-2+ along with this program. If not, see . On Debian systems, the complete text of the GNU General - Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". \ No newline at end of file + Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". diff --git a/debian/postinst b/debian/postinst index ed588b4..669ec00 100644 --- a/debian/postinst +++ b/debian/postinst @@ -53,11 +53,10 @@ case "$1" in useradd -r -g $GROUP $USER fi - chown $USER /etc/torch - chown $USER /etc/torch/torch.conf - chown $USER /usr/share/torch-agent/torch-agent.py + chown $USER /etc/torch + chown $USER /etc/torch/torch.conf - configure_tor_controller + configure_tor_controller ;; abort-upgrade|abort-remove|abort-deconfigure) diff --git a/debian/postrm b/debian/postrm index b95016c..7f60f00 100644 --- a/debian/postrm +++ b/debian/postrm @@ -23,7 +23,7 @@ USER="torch" case "$1" in purge|abort-install) rm -rf /etc/torch - rm -f /usr/share/torch-agent/torch-agent.py + if [ -x "$(command -v deluser)" ]; then deluser --quiet --system $USER > /dev/null || true else diff --git a/debian/preinst b/debian/preinst index 3e46feb..6ae9a66 100755 --- a/debian/preinst +++ b/debian/preinst @@ -16,10 +16,7 @@ set -e case "$1" in install|upgrade) - sudo -H pip3 install stem paho-mqtt PySocks - mkdir -p /etc/torch - mkdir -p /usr/share/torch-agent ;; abort-upgrade) diff --git a/debian/rules b/debian/rules index 1f72afe..7c9e5a3 100755 --- a/debian/rules +++ b/debian/rules @@ -13,9 +13,12 @@ # package maintainers to append LDFLAGS #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed +export PYBUILD_NAME=torch-agent +export PYBUILD_SYSTEM=distutils +export PYBUILD_DISABLE=test %: - dh $@ + dh $@ --with python3 --buildsystem=pybuild override_dh_installsystemd: dh_installsystemd --no-start --no-enable diff --git a/debian/source/include-binaries b/debian/source/include-binaries deleted file mode 100755 index 65663a9..0000000 --- a/debian/source/include-binaries +++ /dev/null @@ -1,2 +0,0 @@ -src/usr/share/torch-agent/torch-agent.py -src/etc/torch/torch.conf \ No newline at end of file diff --git a/debian/src/etc/torch/torch.conf b/debian/src/etc/torch/torch.conf deleted file mode 100755 index d9d0afd..0000000 --- a/debian/src/etc/torch/torch.conf +++ /dev/null @@ -1,19 +0,0 @@ -[tor] -ProxyPort = 9050 -ControllerPort = 9051 - -[ssh] -Port = 22 - -[mqtt] -BrokerHost = mqtt.example.com # OR example1i3uyrbfoi3fi.onion -BrokerPort = 1883 -ClientID = my-client -Topic = example/topic - -### Options for Using TLS - -#RequireCertificate = true -#CaFile = ca.crt -#CertFile = client.crt -#KeyFile = client.key \ No newline at end of file diff --git a/debian/src/usr/share/torch-agent/torch-agent.py b/debian/src/usr/share/torch-agent/torch-agent.py deleted file mode 100755 index 98fd542..0000000 --- a/debian/src/usr/share/torch-agent/torch-agent.py +++ /dev/null @@ -1,98 +0,0 @@ -from stem.control import Controller -import stem.connection -import paho.mqtt.client as mqtt -import ssl -import socks -import socket -import json -import configparser -import argparse -from datetime import datetime -from os import environ - -parser = argparse.ArgumentParser(description='Broadcast SSH hidden service hostname via MQTT') - -parser.add_argument('--config-dir', nargs='?', dest='configPath', default='/etc/torch', - help='configuration directory (default: /etc/torch)') - -args = parser.parse_args() - -configPath = args.configPath - -if "TORCH_CONFIG_DIR" in environ: - configPath = environ.get("TORCH_CONFIG_DIR") - -if not configPath.endswith("/"): - configPath = configPath + "/" - -print("Using torch configuration path: " + configPath) - -config = configparser.ConfigParser() -config.read(configPath + "torch.conf") - -torProxyPort = config['tor'].getint('ProxyPort', fallback = 9050) -torControllerPort = config['tor'].getint('ControllerPort', fallback = 9051) - -sshPort = config['ssh'].getint('Port', fallback = 22) - -mqttConfig = config['mqtt'] -mqttBrokerHost = mqttConfig.get('BrokerHost', fallback = "localhost") -mqttBrokerPort = mqttConfig.getint('BrokerPort', fallback = 1883) -clientID = mqttConfig.get('ClientID', fallback = socket.gethostname()) -mqttTopic = mqttConfig.get('Topic', fallback = "torch/%s/onion_url" % (clientID)) - -mqttRequireCertificate = mqttConfig.getboolean( - 'RequireCertificate', - fallback = False) - -mqttCaFile = configPath + mqttConfig.get('CaFile') -mqttCertFile = configPath + mqttConfig.get('CertFile') -mqttKeyFile = configPath + mqttConfig.get('KeyFile') - -with Controller.from_port(port = torControllerPort) as controller: - - protocolInfo = stem.connection.get_protocolinfo(controller) - - stem.connection.authenticate_safecookie( - controller, - protocolInfo.cookie_path) - - print("Connected to Tor on port %s" % (torControllerPort)) - - service = controller.create_ephemeral_hidden_service( - sshPort, - detached = True) - - onionAddress = "%s.onion" % (service.service_id) - - print("Created Tor Hidden Service for local port %s at %s" % (sshPort, onionAddress)) - -payload = { - 'clientId': clientID, - 'timestamp': datetime.now().strftime("%d-%b-%Y (%H:%M:%S.%f)"), - 'onionAddress': onionAddress, - 'sshPort': sshPort - } - -client = mqtt.Client() -protocol = "mqtt" - -if mqttRequireCertificate: - client.tls_set( - ca_certs = mqttCaFile, - certfile = mqttCertFile, - keyfile = mqttKeyFile, - cert_reqs=ssl.CERT_REQUIRED) - protocol = "mqtts" - -if mqttBrokerHost.endswith(".onion"): - client.proxy_set(proxy_type=socks.SOCKS5, proxy_addr="localhost", proxy_port=torProxyPort) - client.tls_insecure_set(True) - -client.connect(mqttBrokerHost, mqttBrokerPort, 60) -client.publish(mqttTopic, json.dumps(payload)) -print("Connected to MQTT Broker at %s://%s:%s/%s" % (protocol, mqttBrokerHost, mqttBrokerPort, mqttTopic)) -print("Published payload: " + json.dumps(payload)) - -client.disconnect() -print("Disconnected from MQTT Broker") diff --git a/debian/torch-agent.install b/debian/torch-agent.install index 4db3ce4..44d2d6f 100644 --- a/debian/torch-agent.install +++ b/debian/torch-agent.install @@ -1,2 +1 @@ -src/usr/share/torch-agent /usr/share/ -src/etc/torch /etc/ \ No newline at end of file +src/etc/torch /etc/ diff --git a/example/torch-agent_0.0.1-1_all.deb b/example/torch-agent_0.0.1-1_all.deb index ca57b2d675d9c5fa55e5218e96cde193a4cdc59b..0b6da1cd5abc9ebca90bb22c3fea0b45ac84ffb9 100644 GIT binary patch delta 6861 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 H00dcD=7MNl delta 4806 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 gPJ<>!EdT+H6@Z`t05cIm