Commit f197bef3 authored by Lukas Nemec's avatar Lukas Nemec

Added connection to balancer for 1H.

parent c0363816
import cachetools
import requests
import traceback
from haas_proxy import constants
class Balancer():
api_url = None
# Expiring cache for API result, expires in 1h.
cache = cachetools.TTLCache(1, constants.DEFAULT_BALANCER_CHECK_INTERVAL)
CACHE_KEY = 'API_RESP'
def __init__(self, api_url):
self.api_url = api_url
def load_api(self):
cached_resp = self.cache.get(self.CACHE_KEY)
if cached_resp is None:
try:
resp = requests.api.get(self.api_url)
except Exception:
traceback.print_exc()
return None
if resp.status_code != 200:
print("API returned invalid response: {}".format(resp.text))
return None
self.cache[self.CACHE_KEY] = resp.json()
return self.cache.get(self.CACHE_KEY)
@property
def host(self):
api_resp = self.load_api()
# load_api() may return None if there was error loading the API.
if api_resp is None:
return constants.DEFAULT_HONEYPOT_HOST
# in case someone breaks our balancer API to return wrong JSON.
api_host = api_resp.get('host')
if api_host is None:
return constants.DEFAULT_HONEYPOT_HOST
return api_host
@property
def port(self):
api_resp = self.load_api()
# load_api() may return None if there was error loading the API.
if api_resp is None:
return constants.DEFAULT_HONEYPOT_PORT
# in case someone breaks our balancer API to return wrong JSON.
api_port = api_resp.get('port')
if api_port is None:
return constants.DEFAULT_HONEYPOT_PORT
return api_port
......@@ -3,8 +3,10 @@ Constants to use for default run of application.
"""
DEFAULT_PORT = 2222
DEFAULT_HONEYPOT_HOST = 'haas-app.nic.cz'
DEFAULT_HONEYPOT_PORT = 10000
DEFAULT_HONEYPOT_HOST = '127.0.0.1'
DEFAULT_HONEYPOT_PORT = 2222
DEFAULT_BALANCER_CHECK_INTERVAL = 3600
DEFAULT_BALANCER_ADDRESS = 'http://127.0.0.1:5000'
# pylint: disable=line-too-long
DEFAULT_PUBLIC_KEY = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC2jdAE4EAAKikW6W/dDmWS/0lQ1jWM6c6Ef+KpGr+jW83/XIR2reWXeeDTIEluL20JV/P2+2bvVShNr4w8SWitcYKTpwkSgGYHo2vAQvXArx/CsRnTAP6NwrxuZoLNO52fMXQWSrqs0tEvkzYXR3PcR6Cq07RN7QkYNWctCYJxdw=='
......
......@@ -15,6 +15,7 @@ from twisted.conch.unix import SSHSessionForUnixConchUser
from twisted.internet import reactor, defer
from twisted.python import components
from haas_proxy.balancer import Balancer
from haas_proxy.utils import force_text
......@@ -29,7 +30,8 @@ class ProxyService(service.Service):
def startService(self):
# pylint: disable=no-member
self._port = reactor.listenTCP(self.args.port, ProxySSHFactory(self.args))
self._port = reactor.listenTCP(
self.args.port, ProxySSHFactory(self.args))
def stopService(self):
return self._port.stopListening()
......@@ -50,9 +52,12 @@ class ProxySSHFactory(factory.SSHFactory):
b'ssh-userauth': userauth.SSHUserAuthServer,
b'ssh-connection': connection.SSHConnection,
}
self.portal = cred.portal.Portal(ProxySSHRealm(), checkers=[ProxyPasswordChecker()])
self.portal = cred.portal.Portal(
ProxySSHRealm(), checkers=[ProxyPasswordChecker()])
ProxySSHSession.cmd_args = cmd_args
components.registerAdapter(ProxySSHSession, ProxySSHUser, session.ISession)
ProxySSHSession.balancer = Balancer(cmd_args.balancer_address)
components.registerAdapter(
ProxySSHSession, ProxySSHUser, session.ISession)
class ProxyPasswordChecker:
......@@ -114,8 +119,8 @@ class ProxySSHSession(SSHSessionForUnixConchUser):
Main function of SSH proxy - connects to honeypot and change password
to JSON with more information needed to tag activity with user's account.
"""
cmd_args = None # Will inject ProxySSHFactory.
balancer = None # Injected from ProxySSHFactory.
cmd_args = None # Injected from ProxySSHFactory.
# pylint: disable=invalid-name
def openShell(self, proto):
......@@ -133,7 +138,8 @@ class ProxySSHSession(SSHSessionForUnixConchUser):
gid=None,
usePTY=self.ptyTuple,
)
fcntl.ioctl(self.pty.fileno(), tty.TIOCSWINSZ, struct.pack('4H', *self.winSize))
fcntl.ioctl(self.pty.fileno(), tty.TIOCSWINSZ,
struct.pack('4H', *self.winSize))
self.avatar.conn.transport.transport.setTcpNoDelay(1)
@property
......@@ -148,9 +154,11 @@ class ProxySSHSession(SSHSessionForUnixConchUser):
'ssh',
'-o', 'UserKnownHostsFile=/dev/null',
'-o', 'StrictHostKeyChecking=no',
'-o', 'LogLevel=error', # Ignore warning of permanently added host to list of known hosts.
'-p', str(self.cmd_args.honeypot_port),
'{}@{}'.format(force_text(self.avatar.username), self.cmd_args.honeypot_host),
# Ignore warning of permanently added host to list of known hosts.
'-o', 'LogLevel=error',
'-p', str(self.balancer.port),
'{}@{}'.format(force_text(self.avatar.username),
self.balancer.host),
]
@property
......
......@@ -18,19 +18,22 @@ def read_key(filename, default):
try:
return open(filename, 'rb').read()
except Exception as exc:
raise usage.UsageError('Problem to read the key {}: {}'.format(filename, exc))
raise usage.UsageError(
'Problem reading the key {}: {}'.format(filename, exc))
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.'],
['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],
['balancer-address', None, constants.DEFAULT_BALANCER_ADDRESS],
['public-key'],
['private-key'],
['log-file', 'l', None, 'Turn on Python logging to this file. It\' wise to disable Twisted logging.'],
['log-level', None, 'warning', 'Possible options: error / warning / info / debug.'],
['log-file', 'l', None,
'Turn on Python logging to this file. It\' wise to disable Twisted logging.'],
['log-level', None, 'warning',
'Possible options: error / warning / info / debug.'],
]
@property
......@@ -42,12 +45,8 @@ class Options(usage.Options):
return self['port']
@property
def honeypot_host(self):
return self['honeypot-host']
@property
def honeypot_port(self):
return self['honeypot-port']
def balancer_address(self):
return self['balancer-address']
@property
def public_key(self):
......@@ -68,8 +67,10 @@ class Options(usage.Options):
def postOptions(self):
if not self['device-token']:
raise usage.UsageError('Device token is required')
self['public-key'] = read_key(self['public-key'], constants.DEFAULT_PUBLIC_KEY)
self['private-key'] = read_key(self['private-key'], constants.DEFAULT_PRIVATE_KEY)
self['public-key'] = read_key(self['public-key'],
constants.DEFAULT_PUBLIC_KEY)
self['private-key'] = read_key(self['private-key'],
constants.DEFAULT_PRIVATE_KEY)
if self['log-file']:
init_python_logging(self['log-file'], self['log-level'])
......
......@@ -29,6 +29,8 @@ setup(
install_requires=[
'twisted[conch]>={}'.format(TWISTED_VERSION),
'requests',
'cachetools',
],
extras_require={
'test': EXTRA_TEST_REQUIRE,
......
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