Commit 7601418e authored by Michal Horejsek's avatar Michal Horejsek

Support of run as daemon

parent fcc62076
__pycache__
*.pyc
*.pyo
.cache
*.cache
.eggs
haas_proxy.egg-info
......@@ -10,3 +10,6 @@ dist
*.deb
*.rpm
*.tar
haas.log
haas.pid
.PHONY: all prepare-dev test lint run-py2 run-py3 build clean
# Normally it would be just twistd but it runs always with Python 2 in this moment.
TWISTD_CMD=-c "from twisted.scripts.twistd import run; run()"
TWISTD_RUN_ARGS=-l haas.log --pidfile haas.pid haas_proxy -p 2220 -d 42
FPM_CMD=fpm -f -d sshpass -m 'haas@nic.cz' -s python
FPM_CMD_PY2=${FPM_CMD} --python-bin /usr/bin/python2 --python-package-name-prefix python
FPM_CMD_PY3=${FPM_CMD} --python-bin /usr/bin/python3 --python-package-name-prefix python3
......@@ -31,12 +35,12 @@ test:
if [ `which python3` ]; then python3 -m pytest test_haas_proxy.py; fi
lint:
python3 -m pylint --rcfile=pylintrc haas_proxy.py
python3 -m pylint --rcfile=pylintrc haas_proxy twisted/plugins/haas_proxy_plugin.py
run-py2:
sudo python2 haas_proxy.py --device-token 42
sudo python2 ${TWISTD_CMD} ${TWISTD_RUN_ARGS}
run-py3:
sudo python3 haas_proxy.py --device-token 42
sudo python3 ${TWISTD_CMD} ${TWISTD_RUN_ARGS}
build:
# Debian packages
......
"""
Proxy for project Honepot as a Service by CZ.NIC. This proxy is needed to
tag SSH activity with your account ID so you can watch your log online.
Script has hardcoded address of honeypot running at CZ.NIC. It shouldn't
be changed but if does or you need to use proxy or send it to own honeypot,
use optional arguments `--honeypot-host` and `--honeypot-port`.
Script contains one pre-generated key. If you want to use own, create one
with the following command:
$ ssh-keygen -t rsa -b 4096
Store it in some path and then pass it as arguments:
--public-key "$(< /path/id_rsa.pub)" --private-key "$(< /path/id_rsa)"
Example usage:
$ twistd -l /tmp/haas.log --pidfile /tmp/haas.pid haas_proxy --device-token XXX
"""
from haas_proxy import constants
from haas_proxy.proxy import ProxyService
"""
Constants to use for default run of application.
"""
DEFAULT_PORT = 2222
DEFAULT_HONEYPOT_HOST = 'haas-app.nic.cz'
DEFAULT_HONEYPOT_PORT = 10000
# pylint: disable=line-too-long
DEFAULT_PUBLIC_KEY = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC2jdAE4EAAKikW6W/dDmWS/0lQ1jWM6c6Ef+KpGr+jW83/XIR2reWXeeDTIEluL20JV/P2+2bvVShNr4w8SWitcYKTpwkSgGYHo2vAQvXArx/CsRnTAP6NwrxuZoLNO52fMXQWSrqs0tEvkzYXR3PcR6Cq07RN7QkYNWctCYJxdw=='
DEFAULT_PRIVATE_KEY = """-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQC2jdAE4EAAKikW6W/dDmWS/0lQ1jWM6c6Ef+KpGr+jW83/XIR2
reWXeeDTIEluL20JV/P2+2bvVShNr4w8SWitcYKTpwkSgGYHo2vAQvXArx/CsRnT
AP6NwrxuZoLNO52fMXQWSrqs0tEvkzYXR3PcR6Cq07RN7QkYNWctCYJxdwIDAQAB
AoGAMOy4v1XKUUD7WiSd0kS1fDvmzj9agrV2n5QWjvOYQJOuFa4Z4iSgz4PeeTbB
90HGmyZzP9IIuEO+VXOixdV2s/DQ9fGjaBUb95+tYu94KM3tIq9B3kETQwtl+TxE
FywSM9kQGt65ob26K6BbOIZPnF2e6rMEy0pD1UJ2vKDs1wECQQDsEmXZtqh7ktC6
MIKmXegADwEiwnQN+lnboAXDNVQCMWKiWg/Ih4NpDoG9x+OIuVRRz5jEHYyRz8nt
/yvnsRZhAkEAxfbfWWZT+TjwbiSj2/rHg0+2W0LxiJhJJNZDaL/Ad0KcW702CoAc
xWk5uC4dzS9xq9fULN0IYXmPe/5vNZ5m1wJAD3E4pmAzbznwW22W7kkQRwi0O1Db
BJsOy7YRCm7vmuEeIZ6gj66Foxam2AI+WRA+eseIp7ODIXqlK+NYPOSxoQJAYbMt
F5oA54bKYhGDLRXfUVcN0IyBV8CQmLWGHzRDcJhXQo9nFFeV23fLHLLl0lYP65dh
B6Mud6zeu3set3+tkQJBAK8bVknHYkapijQNoM7slRZqgeBUImktJI0qq+YTEspr
za4ElES2AJye2cxYTx8zn59ppadHV2GIJZpj+hJvkzU=
-----END RSA PRIVATE KEY-----"""
#!/usr/bin/env python
"""
Proxy for project Honepot as a Service by CZ.NIC. This proxy is needed to
tag SSH activity with your account ID so you can watch your log online.
Script has hardcoded address of honeypot running at CZ.NIC. It shouldn't
be changed but if does or you need to use proxy or send it to own honeypot,
use optional arguments `--honeypot-host` and `--honeypot-port`.
Script contains one pre-generated key. If you want to use own, create one
with the following command:
$ ssh-keygen -t rsa -b 4096
Store it in some path and then pass it as:
--public-key "$(< /path/id_rsa.pub)" --private-key "$(< /path/id_rsa)"
Example usage:
$ haas_proxy.py --device-token XXX
Implementation of SSH proxy using Twisted.
"""
import argparse
import fcntl
import json
import os
import pwd
import struct
import sys
import tty
from twisted import cred
from twisted.application import service
from twisted.conch.avatar import ConchUser
from twisted.conch.ssh import factory, keys, userauth, connection, session
from twisted.conch.unix import SSHSessionForUnixConchUser
from twisted.internet import reactor, defer
from twisted.python import log
from twisted.python import components
from haas_proxy.utils import force_text
DEFAULT_PORT = 2222
DEFAULT_HONEYPOT_HOST = 'haas-app.nic.cz'
DEFAULT_HONEYPOT_PORT = 10000
# pylint: disable=line-too-long
DEFAULT_PUBLIC_KEY = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC2jdAE4EAAKikW6W/dDmWS/0lQ1jWM6c6Ef+KpGr+jW83/XIR2reWXeeDTIEluL20JV/P2+2bvVShNr4w8SWitcYKTpwkSgGYHo2vAQvXArx/CsRnTAP6NwrxuZoLNO52fMXQWSrqs0tEvkzYXR3PcR6Cq07RN7QkYNWctCYJxdw=='
DEFAULT_PRIVATE_KEY = """-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQC2jdAE4EAAKikW6W/dDmWS/0lQ1jWM6c6Ef+KpGr+jW83/XIR2
reWXeeDTIEluL20JV/P2+2bvVShNr4w8SWitcYKTpwkSgGYHo2vAQvXArx/CsRnT
AP6NwrxuZoLNO52fMXQWSrqs0tEvkzYXR3PcR6Cq07RN7QkYNWctCYJxdwIDAQAB
AoGAMOy4v1XKUUD7WiSd0kS1fDvmzj9agrV2n5QWjvOYQJOuFa4Z4iSgz4PeeTbB
90HGmyZzP9IIuEO+VXOixdV2s/DQ9fGjaBUb95+tYu94KM3tIq9B3kETQwtl+TxE
FywSM9kQGt65ob26K6BbOIZPnF2e6rMEy0pD1UJ2vKDs1wECQQDsEmXZtqh7ktC6
MIKmXegADwEiwnQN+lnboAXDNVQCMWKiWg/Ih4NpDoG9x+OIuVRRz5jEHYyRz8nt
/yvnsRZhAkEAxfbfWWZT+TjwbiSj2/rHg0+2W0LxiJhJJNZDaL/Ad0KcW702CoAc
xWk5uC4dzS9xq9fULN0IYXmPe/5vNZ5m1wJAD3E4pmAzbznwW22W7kkQRwi0O1Db
BJsOy7YRCm7vmuEeIZ6gj66Foxam2AI+WRA+eseIp7ODIXqlK+NYPOSxoQJAYbMt
F5oA54bKYhGDLRXfUVcN0IyBV8CQmLWGHzRDcJhXQo9nFFeV23fLHLLl0lYP65dh
B6Mud6zeu3set3+tkQJBAK8bVknHYkapijQNoM7slRZqgeBUImktJI0qq+YTEspr
za4ElES2AJye2cxYTx8zn59ppadHV2GIJZpj+hJvkzU=
-----END RSA PRIVATE KEY-----"""
def main():
"""
Main endpoint to run script.
"""
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument(
'-d', '--device-token',
dest='device_token',
required=True,
help='Your ID at honeypot.labs.nic.cz. If you don\'t have one, sign up first.',
)
parser.add_argument(
'-p', '--port',
dest='port',
type=int,
default=DEFAULT_PORT,
help='Port to listen to, default {}.'.format(DEFAULT_PORT),
)
parser.add_argument('--honeypot-host', dest='honeypot_host', default=DEFAULT_HONEYPOT_HOST)
parser.add_argument('--honeypot-port', dest='honeypot_port', default=DEFAULT_HONEYPOT_PORT)
parser.add_argument('--public-key', dest='public_key', default=DEFAULT_PUBLIC_KEY)
parser.add_argument('--private-key', dest='private_key', default=DEFAULT_PRIVATE_KEY)
args = parser.parse_args()
log.startLogging(sys.stderr)
reactor.listenTCP(args.port, ProxySSHFactory(args)) # pylint: disable=no-member
reactor.run() # pylint: disable=no-member
def force_text(value):
class ProxyService(service.Service):
"""
Helper to deal with bytes and str in Python 2 vs. Python 3. Needed to have
always username and password as a string (i Python 3 it's bytes).
Service to be able to run it daemon with ``twistd`` command.
"""
if issubclass(type(value), str):
return value
if isinstance(value, bytes):
return str(value, 'utf-8')
return str(value)
def __init__(self, args):
self.args = args
self._port = None
def startService(self):
# pylint: disable=no-member
self._port = reactor.listenTCP(self.args.port, ProxySSHFactory(self.args))
def stopService(self):
return self._port.stopListening()
# pylint: disable=abstract-method
......@@ -224,7 +157,3 @@ class ProxySSHSession(SSHSessionForUnixConchUser):
'remote_port': peer.port,
}
return json.dumps(password_data)
if __name__ == '__main__':
main()
"""
Useful functions used by HaaS proxy.
"""
def force_text(value):
"""
Helper to deal with bytes and str in Python 2 vs. Python 3. Needed to have
always username and password as a string (i Python 3 it's bytes).
"""
if issubclass(type(value), str):
return value
if isinstance(value, bytes):
return str(value, 'utf-8')
return str(value)
......@@ -22,8 +22,10 @@ if sys.version_info < (3, 0):
setup(
name='haas-proxy',
version='1.0',
packages=[],
scripts=['haas_proxy.py'],
packages=[
'haas_proxy',
'twisted.plugins',
],
install_requires=[
'twisted[conch]>={}'.format(TWISTED_VERSION),
......
......@@ -8,7 +8,8 @@ except ImportError:
import pytest
from haas_proxy import force_text, ProxySSHSession
from haas_proxy.proxy import ProxySSHSession
from haas_proxy.utils import force_text
@pytest.fixture
......
"""
Twisted plugin to be able tu run it directly with ``twistd`` command.
"""
# pylint: disable=missing-docstring,invalid-name
from twisted.python import usage
from twisted.plugin import IPlugin
from twisted.application.service import IServiceMaker
from zope.interface import implementer
from haas_proxy import ProxyService, constants, __doc__ as haas_proxy_doc
class Options(usage.Options):
optParameters = [
['device-token', 'd', None, 'Your ID at honeypot.labs.nic.cz. If you don\'t have one, sign up first.'],
['port', 'p', constants.DEFAULT_PORT, 'Port to listen to.', int],
['honeypot-host', None, constants.DEFAULT_HONEYPOT_HOST],
['honeypot-port', None, constants.DEFAULT_HONEYPOT_PORT],
['public-key'],
['private-key'],
]
@property
def device_token(self):
return self['device-token']
@property
def port(self):
return self['port']
@property
def honeypot_host(self):
return self['honeypot-host']
@property
def honeypot_port(self):
return self['honeypot-port']
@property
def public_key(self):
return self['public-key']
@property
def private_key(self):
return self['private-key']
def postOptions(self):
if not self['device-token']:
raise usage.UsageError('Device token is required')
if not self['public-key']:
self['public-key'] = constants.DEFAULT_PUBLIC_KEY
if not self['private-key']:
self['private-key'] = constants.DEFAULT_PRIVATE_KEY
def getSynopsis(self):
return super(Options, self).getSynopsis() + '\n' + haas_proxy_doc
@implementer(IServiceMaker, IPlugin)
class MyServiceMaker(object):
tapname = 'haas_proxy'
description = 'Start HaaS proxy'
options = Options
def makeService(self, options):
return ProxyService(options)
service_maker = MyServiceMaker()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment