Compare commits

..

7 Commits

28 changed files with 164 additions and 386 deletions

1
.gitignore vendored
View File

@ -6,4 +6,3 @@ venv
/*.egg-info
*.pyc
__pycache__
.idea

View File

@ -13,21 +13,7 @@ A TORch solution consists of 3 processes:
* MQTT broker - Any MQTT broker, reachable via IPv4 or Tor, through TLS or insecure communications
* `torch-subscriber` - Listens for and handles onion hostname publications
Easily launch a TORch Node monitor service using the [`torch-subscriber-docker`](https://git.rudefox.io/bj/torch-subscriber-docker) project
## Installation
### Install Package from Rudefox Debian Repo
1. [Add the Rudefox Repo](https://rudefox.io/repo/) as an `apt` source
2. Install the `torch-agent` package
```bash
sudo apt install torch-agent
```
### Build Debian Package from Source
## Build Debian Package
If you don't already have a GPG key, generate one:
@ -35,11 +21,11 @@ If you don't already have a GPG key, generate one:
gpg --full-generate-key
```
Using the e-mail address you provided during GPG key generation, run `build-deb.sh` to build a Debian package and install it
Using the e-mail address you provided during GPG key generation, run `make-pkg.sh` to build a Debian package and install it
```bash
./build-deb.sh john@doe.com
apt update && apt install build/torch-agent_0.0.6-1_all.deb
./make-pkg.sh john@doe.com
apt update && apt install build/torch-agent_0.0.1-1_all.deb
```
This will:
@ -59,3 +45,7 @@ The configuation directory can be specified by
A fully configured example can be found [here](example)
See the sample [`torch.conf`](torch.conf) file for additional configuration options and details
## Roadmap
[ ] Utilize [dh_python3](https://www.debian.org/doc/manuals/debmake-doc/ch08.en.html#setup-py) in Debian package build

View File

@ -1,34 +1,33 @@
#!/bin/bash
if [[ -z "${DEB_EMAIL}" ]]; then
DEB_EMAIL="$1"
DPKG_BUILD_OPTS=-k"$DEB_EMAIL"
fi
if [[ -z "${DEB_EMAIL}" ]]; then
DEB_EMAIL="none@none.com"
DPKG_BUILD_OPTS="-us -uc"
fi
TORCH_VERSION=$(git describe --tags --abbrev=0)
PROJECT=torch-agent-$TORCH_VERSION
if [[ -z "${DEBEMAIL}" ]]; then
DEBEMAIL="$1"
fi
if [[ -z "${DEBEMAIL}" ]]; then
echo "E-mail address required for packaging signing with gpg key!"
echo "Usage: ./build-deb.sh EMAIL"
exit 1
fi
BUILD_DIR=dist
DEB_DIR=$BUILD_DIR/$PROJECT
rm -rf "${BUILD_DIR:?}"
mkdir -p "$DEB_DIR/src/etc/torch"
cp -r debian "$DEB_DIR/"
cp torch.conf "$DEB_DIR/src/etc/torch/"
rm -rf $BUILD_DIR/*
pip3 install -r requirements.txt
python3 setup.py sdist
cd $BUILD_DIR || exit
tar -xzmf "$PROJECT.tar.gz"
mkdir -p $DEB_DIR/src/etc/torch
cp -r debian $DEB_DIR/
cp torch.conf $DEB_DIR/src/etc/torch/
cd "$PROJECT" || exit
export USER
USER=$(whoami)
dh_make --createorig -e "$DEB_EMAIL" -s -y
dpkg-buildpackage $DPKG_BUILD_OPTS
cd $BUILD_DIR
tar -xzmf $PROJECT.tar.gz
cd $PROJECT
export USER=`whoami`
dh_make --createorig -e $DEBEMAIL -s -y
dpkg-buildpackage -k$DEBEMAIL

6
debian/changelog vendored
View File

@ -1,5 +1,5 @@
torch-agent (0.0.6-1) stable; urgency=medium
torch-agent (0.0.1-1) unstable; urgency=medium
* Update
* Initial release
-- Benjamin Dweck <bjdweck@gmail.com> Tue, 08 Oct 2020 19:46:22 +0200
-- Benjamin Dweck <bjdweck@gmail.com> Tue, 06 Oct 2020 15:53:02 +0200

2
debian/files vendored Normal file
View File

@ -0,0 +1,2 @@
torch-agent_0.0.1-1_all.deb net optional
torch-agent_0.0.1-1_amd64.buildinfo net optional

12
debian/postinst vendored
View File

@ -42,9 +42,7 @@ configure_tor_controller() {
fi
if [ $TORRC_CHANGED -eq 1 ]; then
if [ -d "/run/systemd/system" ]; then
systemctl reload tor
fi
systemctl reload tor
fi
}
@ -55,12 +53,10 @@ case "$1" in
useradd -r -g $GROUP $USER
fi
sudo -H pip3 install 'paho-mqtt>=1.5.1'
chown $USER /etc/torch
chown $USER /etc/torch/torch.conf
chown $USER /etc/torch
chown $USER /etc/torch/torch.conf
configure_tor_controller
configure_tor_controller
;;
abort-upgrade|abort-remove|abort-deconfigure)

View File

@ -1 +0,0 @@
OthermoduleName python-othermodule; PEP386

View File

@ -1,13 +0,0 @@
FROM ubuntu
RUN apt update && \
apt install -y sudo ssh tor curl python3-all
COPY dist/torch-agent_*_all.deb .
RUN apt install -y ./torch-agent_*_all.deb
COPY docker-tor/torch-agent.wrapper.sh /usr/bin/torch-agent.wrapper.sh
VOLUME [ "/etc/torch" ]
ENTRYPOINT [ "torch-agent.wrapper.sh" ]

View File

@ -1,29 +0,0 @@
# Tor Test Harness for TORch Agent
This is a Docker container for running the current development version of TORch Agent in an environment with a local Tor Proxy (without having to install and configure Tor on the development machine itself)
## Preparation
1. Build a Debian package from the current development version of TORch Agent
```bash
python -m venv venv && source venv/bin/activate
pip3 install -r requirements.txt
./build-deb.sh
```
2. Build the Docker image
```bash
docker build -f docker-tor/Dockerfile -t torch-agent .
```
3. Configure `torch-agent` by editing [`agent-conf/torch.conf`](./agent-conf/torch.conf)
* Be sure to update the onion hostname of the broker with the one you wish to test with
4. Launch the Docker container
```bash
docker run -it --rm -v "$(pwd)/docker-tor/agent-conf:/etc/torch" torch-agent
```

View File

@ -1,21 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDhDCCAmwCFFfe134gzLKm3ieNbeoxCvOhwsGxMA0GCSqGSIb3DQEBCwUAMIGE
MQswCQYDVQQGEwJVUzELMAkGA1UECAwCTlkxETAPBgNVBAcMCE5ldyBZb3JrMRUw
EwYDVQQKDAxFeGFtcGxlIEluYy4xHDAaBgNVBAMME2NhLm1xdHQuZXhhbXBsZS5j
b20xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUuY29tMB4XDTIwMTAwNjEx
MDQxMVoXDTMwMTAwNDExMDQxMVoweDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5Z
MREwDwYDVQQHDAhOZXcgWW9yazEVMBMGA1UECgwMRXhhbXBsZSBJbmMuMRAwDgYD
VQQDDAd2YWdyYW50MSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLmNvbTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALBR3WYBUoSM9taDBWn8MSU3
WW7z9EmutaWRKlNCf8rAVXrEGf3shtBb6MWdRjAKHcBDZaTtmW6o0XoXkKdGeWcm
6X0o4TwaROPE7HR8OtKsmPTxQ09gKfkwB+sb8+fIPrKq6VLWiSJqvc6RbZvoKXIa
WU/BV4q3HG9MvFB2AMNx9pmzLeeP/m323pAEU28oR/kGvuDJHSLO3cNd1U5ZKFFt
J1hHYugKM3uHlyQ44ozu6l2AexGgIYjA5/y1D/RuWaybdpppLS3RerhKDrvTkwWD
UALgTdxHnIT90NFCtGsZTzwmbqs/ibq1NtkHGiS0tGhJjftIiPJZfuNArVsoYF0C
AwEAATANBgkqhkiG9w0BAQsFAAOCAQEAeOEgOA8dJZGo/bT+2vnKm7VnJYrNgAax
B/X2kG3vLiayFgPYarQq8AjlqQCr8Dm1EqbUtnAhzSbQTX+v3oQBd7sRdlwfTYKa
bFEjhMeXhBgp/bWobq9FcwAL02wsAZh/gcbHAVrIwWmb42sbHTmrWY2jgJbX65jg
ameIn9p1j2CSJnC4Ju3B+btaCmksHI6uhuJVef/+pL53hs1z5MfehNvJaBkUzsya
nYAiCvrEQIzKlxROdBApZeQs0WFv0ktu6itzA3YsCMct1p3TNGwDUZ+cQqkTPhkB
KNT0VcmDNdavD59WsEfrgZe5/DXY+Q1yHzGmaq70y0U6SgTfOsslmQ==
-----END CERTIFICATE-----

View File

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAsFHdZgFShIz21oMFafwxJTdZbvP0Sa61pZEqU0J/ysBVesQZ
/eyG0FvoxZ1GMAodwENlpO2ZbqjReheQp0Z5ZybpfSjhPBpE48TsdHw60qyY9PFD
T2Ap+TAH6xvz58g+sqrpUtaJImq9zpFtm+gpchpZT8FXirccb0y8UHYAw3H2mbMt
54/+bfbekARTbyhH+Qa+4MkdIs7dw13VTlkoUW0nWEdi6Aoze4eXJDjijO7qXYB7
EaAhiMDn/LUP9G5ZrJt2mmktLdF6uEoOu9OTBYNQAuBN3EechP3Q0UK0axlPPCZu
qz+JurU22QcaJLS0aEmN+0iI8ll+40CtWyhgXQIDAQABAoIBAQCYYtiHbYMk3jQD
p49JT1YmRRT9aMhr2hxR8Ql0lheeYuY9yThxljfZ4mVmTYw4vrWB0n4JsfQWiL8q
1y0E9Uq9lQrdSjSH3mhFto9qCWhJZjR4FgBHnOQJ4rIlR65gV1eg0UgAeTxiS4Cq
BFSIF3mijRU9ces4DxP7OYXTwHjecLQXWzsENhlcowTCYOfxLM/8YvVRCv2cxPJh
/TL3qn1LD5/15lUb58+SqNAOREGACB+YG6rFShvUrxkq3ShtZdPUOzM5z+9xS8yO
Uh2aZDmxtMB6GnYbQNhfM274i71aVhf0++7s8VsiVo3C61+qKIYcX0LK8t2PnQ1H
cFn20hKhAoGBANfyLB5T+nZUGBysZSe76SoZUFWM0OsLixLq0+CqHvnTmIVMLQpv
k8RV2/g7rKCcaKghSENI2gwWy+EDfVGi/nGc+uJ+n4FX92F98MmmzFvuQXOp1AuY
Qvqr2XquFu5B6Jz5aOniOMHHSRl9JmZOLc+rs7COH7s/o5OPBT+1OpHJAoGBANEG
HBnAuxZ0XJtB7TvA9wW3GCRidCisRH8rG0cN4dE3UuCC+DQq1AV03NwM6pycwz15
ljpkb+WtbE6iKG3sfj3vWhwkakcEnJKXGCVbKmMXI0L2sMnQB4bJ4TtXGDRJlLKg
XuwBsEmN8pM8IqRmnSpTrVbpCo4vZ/29c8l9+YP1AoGAcgIBGOHtUZuEP18k6J1k
tD05FHGLuwwVGJ+xzOMEB5GW7IkTHndZ5EYQJDYdJY5uEpW/uQY1WDyQ1vMornkH
LKRcMEf5nif7CxWaklvleIOJOq9mq9hvRDiGUSaoJJHXZUioAxLUNDoqdbKFG24a
8ZENBSGDzzACBF11v/TGP0ECgYEAp+WCeOUo4jaBlGx2RIMRaNPTXpZ+u5T0SDm1
5XMvKkCIH7LT8ANe3ysppM/zO+1nnl+l3i2C/Dg7QUZbt0A5f0JdXTGa9IStx8n6
KTd7arDRMB67jr+86/YJJwMkfAuGl5zd4jDRC6Qrbzzkjq2mHLOuDpuOUPufSl/9
O6Im5GkCgYA4zFE3ztSV6nq5EH3A7VmAsLiyRXBJfOlLXIBApv46/t4ivS4C3Esu
8LR+Vvx2XLWkpkrm9kR1wIutNx6x1pRkSGDKyRXn5+cU4A2Lw9y2BbwDhj+WiHkM
zCuL+8l2px2f3K1YPE3oGWHZZJScDwa03yjVzrSPDr766XBtH7W4vg==
-----END RSA PRIVATE KEY-----

View File

@ -1,23 +0,0 @@
-----BEGIN CERTIFICATE-----
MIID6zCCAtOgAwIBAgIUQJye5MbZVMpOpu87TmmlN/KOBj0wDQYJKoZIhvcNAQEL
BQAwgYQxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOWTERMA8GA1UEBwwITmV3IFlv
cmsxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEcMBoGA1UEAwwTY2EubXF0dC5leGFt
cGxlLmNvbTEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBsZS5jb20wHhcNMjAx
MDA2MTA1OTE4WhcNMzAxMDA0MTA1OTE4WjCBhDELMAkGA1UEBhMCVVMxCzAJBgNV
BAgMAk5ZMREwDwYDVQQHDAhOZXcgWW9yazEVMBMGA1UECgwMRXhhbXBsZSBJbmMu
MRwwGgYDVQQDDBNjYS5tcXR0LmV4YW1wbGUuY29tMSAwHgYJKoZIhvcNAQkBFhFh
ZG1pbkBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
ALVndltaj3SX1zuJB0F5woIMUZCHopkgiO027/qE10IgM6SN4lFhR7wR0B/9PXrf
zn1xTC63w9xd3GbnlJvcWhWbh/m1t2Qx2mIvOmoELY8wlY6/V6NzjSgju7mZi0u6
aitU+LXINNuGS+JhVpc54QQ8M9o0jKlnNGnEmPjv6uhbNXx3f8lw3eqSr1ZqmpGd
UQVYKsNYBVzSqsnh/sn/KnGYn/nmpsKRWeLhoslJ3zDjaM/Y4NYol11nWFIPYCk5
7rzzxES/WdWLLnZ2W59YCT54YOGFqXE7oYgReD+Og2YwnGVQQpDcvb2HyIZL/2pa
oC6avMo/eC8HbSxwUKCnj1cCAwEAAaNTMFEwHQYDVR0OBBYEFEC8a9l0rpIdUqCS
i4NJwXlqUoLeMB8GA1UdIwQYMBaAFEC8a9l0rpIdUqCSi4NJwXlqUoLeMA8GA1Ud
EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGzxJWZdVozskr5yie2uetpL
aAReCaHEdWudRN3wVW1Dpm2sE23x4VFiJ+Uow9k21GgtGUsAIFqRgb3/QO+ipODC
GRwZmVopkuOaHfCz+tO8xqjvGHCidhhhNFdR9uVkWHoPKsQsvap0SSk12KMtFBRo
3rmeQwPP4qEPFEwc2U0hCUMsIUvMSt3KrA/j+aMRzOXU7QIMFbcYEF1IaGJz1RMh
h1VCXaUlL2liVTWU4XgudB8rMOuETec7un9hzoBVOWHxXdRrGPaoN4+zWiLRCDXO
6wapOhkmTOXuZY/NcMMwTmdJKTEQBD6XIQamv91Ne2bT89LHpcp1LjbaCz+UAxg=
-----END CERTIFICATE-----

View File

@ -1,16 +0,0 @@
[tor]
ControllerPort = 9051
[ssh]
Port = 22
[mqtt]
BrokerHost = wmzin3o2dvd4h2iu4mrf4zqbvgscgi27kd5afzvhgchghjdpqk7cmaqd.onion
BrokerPort = 1883
ClientID = myagent
Topic = torch/myagent/wake
RequireCertificate = false
CaFile = ca.crt
CertFile = agent.crt
KeyFile = agent.key

View File

@ -1,10 +0,0 @@
#!/bin/bash
tor &
while ! curl -s --socks5 127.0.0.1:9050 'https://check.torproject.org/' | grep -qm1 Congratulations
do
sleep 0.5
done
torch-agent $1 $2 $3 $4

View File

@ -35,10 +35,19 @@ In a separate terminal window, run the subscriber:
#### Run TORch Agent in Vagrant
[Build](..) the latest source into a Debian package and copy it to `example/`
```bash
cd ..
./make-pkg.sh john@doe.com
cp -f build/torch-agent_0.0.1-1_all.deb example/
cd example
```
Run the Vagrant box in a third terminal window:
```bash
vagrant up
```
You should see that the broker received a connection from the Vagrant box at boot up and the subscriber received the onion hostname. You can use a local `tor` proxy to connect to the vagrant box using SSH and the onion hostname.
You should see that the broker received a connection from the Vagrant box at boot up and the subscriber received the onion hostname. You can use a local `tor` proxy to connect to the vagrant box using SSH and the onion hostname.

16
example/Vagrantfile vendored
View File

@ -64,19 +64,17 @@ Vagrant.configure("2") do |config|
# Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
# documentation for more information about their specific syntax and use.
config.vm.provision "file", source: "torch-agent_0.0.1-1_all.deb", destination: "~/"
config.vm.provision "file", source: "agent-config", destination: "~/torch-conf"
config.vm.provision "shell", inline: <<-SHELL
sudo -- sh -c "echo '10.0.2.2 mqtt.example.com' >> /etc/hosts"
sudo -- sh -c "echo '10.0.2.2 mqtt.example.com' >> /etc/hosts"
sudo apt update
wget -qO - https://rudefox.io/repo/gpg | sudo apt-key add
sudo add-apt-repository "deb https://repo.rudefox.io/repository/apt-release focal main"
sudo apt update
sudo apt install -y torch-agent
sudo cp -f torch-conf/* /etc/torch/
sudo apt install -y ./torch-agent_0.0.1-1_all.deb
sudo cp -f torch-conf/* /etc/torch/
sudo chown -R torch /etc/torch
sudo systemctl enable torch-agent

View File

@ -1,15 +1,7 @@
websockets_log_level 9
listener 8883
connection_messages true
log_type all
listener 1883
cafile /mosquitto/config/ca.crt
#keyfile /mosquitto/config/mqtt.example.com.key
#certfile /mosquitto/config/mqtt.example.com.crt
require_certificate true
use_identity_as_username true
listener 8883
websockets_log_level 9
cafile /mosquitto/config/ca.crt
keyfile /mosquitto/config/mqtt.example.com.key
certfile /mosquitto/config/mqtt.example.com.crt

View File

@ -1,3 +1,3 @@
#!/usr/bin/bash
docker run -it --rm --user $(echo $UID) --name mosquitto -p 8883:8883 -p 1883:1883 -v $(pwd)/broker-config:/mosquitto/config eclipse-mosquitto
docker run -it --rm --user $(echo $UID) --name mosquitto -p 8883:8883 -v $(pwd)/broker-config:/mosquitto/config eclipse-mosquitto

View File

@ -1,3 +1,3 @@
#!/usr/bin/bash
mosquitto_sub -L mqtts://mqtt.example.com/torch/\+/onion_url --cafile subscriber-config/ca.crt --key subscriber-config/subscriber.key --cert subscriber-config/subscriber.crt
mosquitto_sub -L mqtts://mqtt.example.com/it/torch/\+/ssh_onion --cafile subscriber-config/ca.crt --key subscriber-config/subscriber.key --cert subscriber-config/subscriber.crt

Binary file not shown.

View File

@ -1,7 +0,0 @@
#!/bin/bash
USERNAME=$1
PASSWORD=$2
VERSION=$3
curl -u "$USERNAME:$PASSWORD" -H "Content-Type: multipart/form-data" --data-binary "@dist/torch-agent_${VERSION}-1_all.deb" "https://repo.rudefox.io/repository/apt-release/"

View File

@ -1,3 +0,0 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"

View File

@ -1,6 +1,5 @@
pip~=20.2.4
setuptools~=50.3.2
stdeb3~=0.9.0.post2
paho-mqtt~=1.5.1
wheel>=0.35.1
setuptools>=44.0.0
stem>=1.8.0
PySocks
paho-mqtt>=1.5.1
PySocks>=1.7.1

View File

@ -1,25 +0,0 @@
[metadata]
name = torch-agent
version = attr: torch_agent.__version__
author = Benjamin Dweck
author_email = bjdweck@gmail.com
description = TORch: Illuminate the Way to your Node
url = https://git.rudefox.io/bj/torch-agent
classifiers =
Programming Language :: Python :: 3
License :: OSI Approved :: MIT License
[options]
packages = find:
install_requires =
paho-mqtt~=1.5.1
setuptools~=50.3.1
pip~=20.2.3
stem
PySocks
[options.entry_points]
console_scripts = torch-agent=torch_agent.torch_agent:main
[options.packages.find]
exclude=test

View File

@ -1,4 +1,31 @@
#!/usr/bin/env python3
import setuptools
setuptools.setup()
with open("README.md", "r") as fh:
long_description = fh.read()
setuptools.setup(
name="torch-agent",
version="0.0.1",
author="B.J. Dweck",
author_email="bjdweck@gmail.com",
description="TORch: Iluminate the Way to your Node",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://git.rudefox.io/bj/torch-agent",
packages=setuptools.find_packages(),
install_requires=[
'stem',
'paho-mqtt',
'PySocks',
],
entry_points = {
'console_scripts': ['torch-agent=torch_agent.torch_agent:main'],
},
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
],
python_requires='>=3.6',
)

View File

@ -1,47 +1,19 @@
#################
# The `tor` section heading is required
[tor]
# Optional: The Tor onion proxy host and port (default: 127.0.0.1:9050)
#ProxyHost 127.0.0.1
ProxyPort = 9050
# Optional: The Tor controller port (default: 9051) for creating new hidden services
ControllerPort = 9051
#################
# The `ssh` section heading is required
[ssh]
# Optional: Local SSH sevice port (default: 22)
Port = 22
#################
# The `mqtt` section heading is required
[mqtt]
# Optional: The MQTT broker host and port (default: localhost:1883)
# Can be either IPv4 or Tor onion hostname
BrokerHost = mqtt.example.com
#BrokerHost = example1i3uyrbfoi3fi.onion
BrokerHost = mqtt.example.com # OR example1i3uyrbfoi3fi.onion
BrokerPort = 1883
# Optional: ID that will be used as an MQTT client ID when connecting to the broker (defaults to the current host's hostname)
ClientID = my-client
Topic = example/topic
# Optional: Topic to be used when publishing connection info (defaults to 'torch/[hostname]/onion_url')
#Topic = example/topic
### Options for Using TLS
### Optional: TLS Options
#
# Note: when CaFile, CertFile and KeyFile are ALL defined, then TLS (MQTTS) is used to connect to the broker. Otherwise MQTT is used.
#
# Optional: Whether or not TORch Agent will verify the hostname of the broker and require it to match the name on the certificate the broker provides. This will be automatically DISABLED for connections to Tor onion hosts
#RequireCertificate = true
#CaFile = ca.crt
#CertFile = client.crt
#KeyFile = client.key

View File

@ -1 +1 @@
__version__ = "0.0.6"
__version__ = "0.0.1"

View File

@ -1,127 +1,97 @@
import argparse
import configparser
import json
import socket
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
import paho.mqtt.publish as publish
import socks
import stem.connection
from stem.control import Controller
def main():
parser = argparse.ArgumentParser(description='Broadcast SSH hidden service hostname via MQTT')
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)')
parser.add_argument('--config-dir', nargs='?', dest='configPath', default='/etc/torch',
help='configuration directory (default: /etc/torch)')
args = parser.parse_args()
args = parser.parse_args()
config_path = args.configPath
configPath = args.configPath
if "TORCH_CONFIG_DIR" in environ:
config_path = environ.get("TORCH_CONFIG_DIR")
if "TORCH_CONFIG_DIR" in environ:
configPath = environ.get("TORCH_CONFIG_DIR")
if not config_path.endswith("/"):
config_path = config_path + "/"
if not configPath.endswith("/"):
configPath = configPath + "/"
print("Using torch configuration path: " + config_path)
print("Using torch configuration path: " + configPath)
config = configparser.ConfigParser()
config = configparser.ConfigParser()
config.read(configPath + "torch.conf")
configuration_file_path = config_path + "torch.conf"
print("Reading configuration file at '%s'" % configuration_file_path)
config.read(configuration_file_path)
torProxyPort = config['tor'].getint('ProxyPort', fallback = 9050)
torControllerPort = config['tor'].getint('ControllerPort', fallback = 9051)
tor_proxy_host = config['tor'].get('ProxyHost', fallback="127.0.0.1")
tor_proxy_port = config['tor'].getint('ProxyPort', fallback=9050)
tor_controller_port = config['tor'].getint('ControllerPort', fallback=9051)
sshPort = config['ssh'].getint('Port', fallback = 22)
ssh_port = 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))
mqtt_config = config['mqtt']
mqtt_broker_host = mqtt_config.get('BrokerHost', fallback="localhost")
mqtt_broker_port = mqtt_config.getint('BrokerPort', fallback=1883)
mqtt_broker_using_tor = mqtt_broker_host.endswith(".onion")
mqtt_client_id = mqtt_config.get('ClientID', fallback=socket.gethostname())
mqtt_topic = mqtt_config.get('Topic', fallback="torch/%s/onion_url" % mqtt_client_id)
mqttRequireCertificate = mqttConfig.getboolean(
'RequireCertificate',
fallback = False)
mqtt_require_certificate = mqtt_config.getboolean(
'RequireCertificate',
fallback=False)
mqttCaFile = configPath + mqttConfig.get('CaFile')
mqttCertFile = configPath + mqttConfig.get('CertFile')
mqttKeyFile = configPath + mqttConfig.get('KeyFile')
mqtt_ca_file = mqtt_config.get('CaFile', fallback=None)
mqtt_ca_file = config_path + mqtt_ca_file
mqtt_cert_file = mqtt_config.get('CertFile', fallback=None)
mqtt_cert_file = config_path + mqtt_cert_file
mqtt_key_file = mqtt_config.get('KeyFile', fallback=None)
mqtt_key_file = config_path + mqtt_key_file
with Controller.from_port(port = torControllerPort) as controller:
protocolInfo = stem.connection.get_protocolinfo(controller)
mqtt_use_tls = \
mqtt_ca_file is not None and \
mqtt_cert_file is not None and \
mqtt_key_file is not None
stem.connection.authenticate_safecookie(
controller,
protocolInfo.cookie_path)
print("Connected to Tor on port %s" % (torControllerPort))
print("Connecting to local TOR controller on port %s" % tor_controller_port)
service = controller.create_ephemeral_hidden_service(sshPort, detached = True)
with Controller.from_port(port=tor_controller_port) as controller:
onionAddress = "%s.onion" % (service.service_id)
protocol_info = stem.connection.get_protocolinfo(controller)
print("Created Tor Hidden Service for local port %s at %s" % (sshPort, onionAddress))
stem.connection.authenticate_safecookie(controller, protocol_info.cookie_path)
payload = {
'clientId': clientID,
'timestamp': datetime.now().strftime("%d-%b-%Y (%H:%M:%S.%f)"),
'onionAddress': onionAddress,
'sshPort': sshPort
}
print("Creating TOR Hidden Service...")
client = mqtt.Client()
protocol = "mqtt"
service = controller.create_ephemeral_hidden_service(ssh_port, detached=True)
if mqttRequireCertificate:
client.tls_set(
ca_certs = mqttCaFile,
certfile = mqttCertFile,
keyfile = mqttKeyFile,
cert_reqs=ssl.CERT_REQUIRED)
protocol = "mqtts"
onion_address = "%s.onion" % service.service_id
if mqttBrokerHost.endswith(".onion"):
client.proxy_set(proxy_type=socks.SOCKS5, proxy_addr="localhost", proxy_port=torProxyPort)
client.tls_insecure_set(True)
print("Created Tor Hidden Service for local service on port %s at %s" % (ssh_port, onion_address))
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))
protocol = "mqtt"
tls_args = None
proxy_args = None
cert_required = ssl.CERT_OPTIONAL
if mqtt_require_certificate:
cert_required = ssl.CERT_REQUIRED
if mqtt_broker_using_tor:
cert_required = ssl.CERT_OPTIONAL
proxy_args = {
'proxy_type': socks.SOCKS5,
'proxy_addr': tor_proxy_host,
'proxy_port': tor_proxy_port
}
if mqtt_use_tls:
protocol = "mqtts"
tls_args = {
'ca_certs': mqtt_ca_file,
'certfile': mqtt_cert_file,
'keyfile': mqtt_key_file,
'cert_reqs': cert_required
}
print("Publishing to MQTT broker: %s://%s:%s/%s" % (protocol, mqtt_broker_host, mqtt_broker_port, mqtt_topic))
if mqtt_broker_using_tor:
print("--> Using TOR proxy: %s:%s" % (tor_proxy_host, tor_proxy_port))
payload = json.dumps({
'clientId': mqtt_client_id,
'timestamp': datetime.now().strftime("%d-%b-%Y (%H:%M:%S.%f)"),
'onionAddress': onion_address,
'sshPort': ssh_port
})
publish.single(mqtt_topic,
payload,
qos=1,
hostname=mqtt_broker_host,
port=mqtt_broker_port,
client_id=mqtt_client_id,
tls=tls_args,
proxy_args=proxy_args)
client.disconnect()
print("Disconnected from MQTT Broker")