Skip to content

Commit

Permalink
python: implement usage of TLS-PSK and updated README
Browse files Browse the repository at this point in the history
  • Loading branch information
joergsteffens authored and franku committed Jan 21, 2019
1 parent e907879 commit aedbf00
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 22 deletions.
45 changes: 36 additions & 9 deletions python-bareos/README.rst
Expand Up @@ -5,31 +5,58 @@ Python module to access a http://www.bareos.org backup system.

`python-bareos` packages are included in the Bareos core distribution since bareos >= 17.2.

TLS-PSK
-------

Since Bareos >= 18.2.4 can secure its connections via TLS-PSK (Transport-Layer-Security Pre-Shared-Key).

This subset of TLS is currently not supported by the Python SSL module.
To enable this feature in `python-bareos` the Python module 'sslpsk` must be installed:

* `pip install sslpsk`





calling bareos-director user agent commands
-----------------------------------------------
-------------------------------------------

.. code:: python
import bareos.bsock
password=bareos.bsock.Password("secret")
directorconsole=bareos.bsock.DirectorConsole(address="localhost", port=9101, password=password)
print directorconsole.call("help")
password=bareos.bsock.Password('secret')
directorconsole=bareos.bsock.DirectorConsole(address='localhost', port=9101, password=password)
print directorconsole.call('help')
...
To connected to a named console instead, use the `name` parameter:

.. code:: python
directorconsole=bareos.bsock.DirectorConsole(address='localhost', port=9101, name='user1', password=password)
simple version of the bconsole in Python
--------------------------------------------

.. code:: python
import bareos.bsock
password=bareos.bsock.Password("secret")
directorconsole=bareos.bsock.DirectorConsole(address="localhost", port=9101, password=password)
password=bareos.bsock.Password('secret')
directorconsole=bareos.bsock.DirectorConsole(address='localhost', port=9101, password=password)
directorconsole.interactive()
...
or use the included bconsole.py script:

..
bconsole.py --debug --name=user1 --password=secret --port 9101 localhost


use JSON objects of API mode 2
----------------------------------

Expand All @@ -38,7 +65,7 @@ Requires: bareos >= 15.2
.. code:: python
import bareos.bsock
password=bareos.bsock.Password("secret")
directorconsole=bareos.bsock.DirectorConsoleJson(address="localhost", port=9101, password=password)
directorconsole.call("list pools")
password=bareos.bsock.Password('secret')
directorconsole=bareos.bsock.DirectorConsoleJson(address='localhost', port=9101, password=password)
directorconsole.call('list pools')
...
10 changes: 8 additions & 2 deletions python-bareos/bareos/bsock/directorconsole.py
Expand Up @@ -13,9 +13,15 @@ def __init__(self,
port=9101,
dirname=None,
name="*UserAgent*",
password=None):
password=None,
tls_psk_enable=True,
tls_psk_require=False
):
super(DirectorConsole, self).__init__()
self.connect(address, port, dirname, ConnectionType.DIRECTOR)
self.tls_psk_enable=tls_psk_enable
self.tls_psk_require=tls_psk_require
self.identity_prefix = u'R_CONSOLE'
self.connect(address, port, dirname, ConnectionType.DIRECTOR, name, password)
self.auth(name=name, password=password, auth_success_regex=b'^1000 OK.*$')
self._init_connection()

Expand Down
12 changes: 10 additions & 2 deletions python-bareos/bareos/bsock/filedaemon.py
Expand Up @@ -15,9 +15,17 @@ def __init__(self,
port=9102,
dirname=None,
name=None,
password=None):
password=None,
tls_psk_enable=True,
tls_psk_require=False
):
super(FileDaemon, self).__init__()
self.connect(address, port, dirname, ConnectionType.FILEDAEMON)
self.tls_psk_enable=tls_psk_enable
self.tls_psk_require=tls_psk_require
# Well, we are not really a Director,
# but using the interface provided for Directors.
self.identity_prefix = u'R_DIRECTOR'
self.connect(address, port, dirname, ConnectionType.FILEDAEMON, name, password)
self.auth(name=name, password=password, auth_success_regex=b'^2000 OK Hello.*$')
self._init_connection()

Expand Down
103 changes: 94 additions & 9 deletions python-bareos/bareos/bsock/lowlevel.py
Expand Up @@ -17,9 +17,21 @@
import re
from select import select
import socket
import ssl
import struct
import sys
import time
import warnings

# Try to load the sslpsk module,
# with implement TLS-PSK (Transport Layer Security - Pre-Shared-Key)
# on top of the ssl module.
# If it is not available, we continue anyway,
# but don't use TLS-PSK.
try:
import sslpsk
except ImportError:
warnings.warn(u'Connection encryption via TLS-PSK is not available, as the module sslpsk is not installed.')

class LowLevel(object):
"""
Expand All @@ -36,37 +48,110 @@ def __init__(self):
self.dirname = None
self.socket = None
self.auth_credentials_valid = False
self.tls_psk_enable = True
self.tls_psk_require = False
self.connection_type = None
# identity_prefix have to be set in each class
self.identity_prefix = u'R_NONE'
self.receive_buffer = b''

def connect(self, address, port, dirname, type):

def connect(self, address, port, dirname, type, name = None, password = None):
self.address = address
self.port = port
self.port = int(port)
if dirname:
self.dirname = dirname
else:
self.dirname = address
self.connection_type = type
self.name = name
self.password = password

return self.__connect()


def __connect(self):
connected = False
if self.tls_psk_require and not self.is_tls_psk_available():
raise ConnectionError(u'TLS-PSK is required, but sslpsk module not loaded/available.')

if self.tls_psk_enable and self.is_tls_psk_available():
try:
self.__connect_tls_psk()
except (ConnectionError, ssl.SSLError) as e:
if self.tls_psk_require:
raise
else:
self.logger.warn(u'Failed to connect via TLS-PSK. Trying plain connection.')
else:
connected = True
self.logger.debug("Encryption: {}".format(self.socket.cipher()))

if not connected:
self.__connect_plain()
connected = True
self.logger.debug("Encryption: None")

return connected


def __connect_plain(self):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# initialize
try:
self.socket = socket.create_connection((self.address, self.port))
#self.socket = socket.create_connection((self.address, self.port))
self.socket.connect((self.address, self.port))
except socket.gaierror as e:
self._handleSocketError(e)
raise ConnectionError(
"failed to connect to host " + str(self.address) + ", port " + str(self.port) + ": " + str(e))
"failed to connect to host {}, port {}: {}".format(self.address, self.port, str(e)))
else:
self.logger.debug("connected to " + str(self.address) + ":" + str(self.port))
self.logger.debug("connected to {}:{}".format(self.address, self.port))

return True


def __connect_tls_psk(self):
'''
Connect and establish a TLS-PSK connection on top of the connection.
'''
self.__connect_plain()
# wrap socket with TLS-PSK
client_socket = self.socket
identity = self.get_tls_psk_identity()
if isinstance(self.password, Password):
password = self.password.md5()
else:
raise ConnectionError(u'No password provides.')
self.logger.debug("identity = {}, password = {}".format(identity, password))
try:
self.socket = sslpsk.wrap_socket(
client_socket,
psk=(password, identity),
ciphers='ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH',
#ssl_version=ssl.PROTOCOL_TLSv1_2,
server_side=False)
except ssl.SSLError as e:
# raise ConnectionError(
# "failed to connect to host {}, port {}: {}".format(self.address, self.port, str(e)))
# Using a general raise keep more information about the type of error.
raise
return True


def get_tls_psk_identity(self):
'''Bareos TLS-PSK excepts the identiy is a specific format.'''
return u'{}{}{}'.format(self.identity_prefix, chr(0x1e), str(self.name))

def is_tls_psk_available(self):
'''Checks if we have all required modules for TLS-PSK.'''
return 'sslpsk' in sys.modules

def auth(self, name, password, auth_success_regex):
'''
login to the bareos-director
if the authenticate success return True else False
dir: the director location
Login to the Bareos Director.
If the authenticate succeeds return True else False.
dir: the director name
name: own name.
'''
if not isinstance(password, Password):
Expand Down Expand Up @@ -97,7 +182,7 @@ def _init_connection(self):


def disconnect(self):
''' disconnect '''
'''disconnect'''
# TODO
pass

Expand Down

0 comments on commit aedbf00

Please sign in to comment.